原文地址:https://my.oschina.net/u/218421/blog/38576

使用DataSourceUtils进行Connection的管理
由上节代码可知,JdbcTemplate在获取Connection的时候,并不是直接调用DataSource的getConnection(),而是调用了如下的代码:

Connection con = DataSourceUtils.getConnection(getDataSource());

为什么要这么做呢?
实际上,如果对于一个功能带一的JdbcTemplate来说,调用如下的代码就够了:

Connection con = dataSource.getConnection();

只不过,spring所提供的JdbcTemplate要关注更多的东西,所以,在从dataSource取得连接的时候,需要多做一些事情。

org.springframework.jdbc.datasource.DataSourceUtils所提供的方法,用来从指定的DataSource中获取或者释放连接,它会将取得的Connection绑定到当前的线程,以便在使用Spring所提供的统一事务抽象层进行事务管理的时候使用。

为什么要使用NativeJdbcExtractor
在execute()方法中可以看到:

if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}

通过该处理,获取的将是相应的驱动程序所提供的实现类,而不是相应的代理对象。
JdbcTemplate内部定义了一个NativeJdbcExtractor类型的实例变量:

/** Custom NativeJdbcExtractor */
private NativeJdbcExtractor nativeJdbcExtractor;

当我们想用驱动对象所提供的原始API的时候,可以通过JdbcTemplate的如下代码:

public void setNativeJdbcExtractor(NativeJdbcExtractor extractor) {
this.nativeJdbcExtractor = extractor;
}

这样将会获取真正的目标对象而不是代理对象。

spring默认提供面向Commons DBCP、C3P0、Weblogic、Websphere等数据源的NativeJdbcExtractor的实现类: CommonsDbcpNativeJdbcExtractor:为Jakarta Commons DBCP数据库连接池所提供的NativeJdbcExtractor实现类 C3P0NativeJdbcExtractor:为C3P0数据库连接池所提供的NativeJdbcExtractor实现类 WebLogicNativeJdbcExtractor:为Weblogic所准备的NativeJdbcExtractor实现类

WebSphereNativeJdbcExtractor:为WebSphere所准备的NativeJdbcExtractor实现类

控制JdbcTemplate的行为 JdbcTemplate在使用Statement或者PreparedStatement等进行具体的数据操作之前,会调用如下的代码:

protected void applyStatementSettings(Statement stmt) throws SQLException {
int fetchSize = getFetchSize();
if (fetchSize > 0) {
stmt.setFetchSize(fetchSize);
}
int maxRows = getMaxRows();
if (maxRows > 0) {
stmt.setMaxRows(maxRows);
}
DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
}

这样便可以设置Statement每次抓取的行数 等等。

SQLException到DataAccessException的转译 因为JdbcTemplate直接操作的是JDBC API,所以它需要捕获在此期间可能发生的SQLException,处理的宗旨是将SQLException 转译到spring的数据访问异常层次体系,以统一数据访问异常的处理方式,这个工作主要是交给了SQLExceptionTranslator,该 接口的定义如下:

package org.springframework.jdbc.support;

import java.sql.SQLException;

import org.springframework.dao.DataAccessException;

/**

 *
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.dao.DataAccessException
*/
public interface SQLExceptionTranslator { DataAccessException translate(String task, String sql, SQLException ex); }

该接口有两个主要的实现类,SQLErrorCodeSQLExceptionTranslator和SQLStateSQLExceptionTranslator,如下所示:

SQLExceptionSubclassTranslator是Spring2.5新加的实现类,主要用于JDK6发布的将JDBC4版本中新定义的异常体系转化为spring的异常体系,对于之前的版本,该类派不上用场。
SQLErrorCodeSQLExceptionTranslator会基于SQLExcpetion所返回的ErrorCode进行异常转译。通常情况下,根据各个数据库提供商所提供的ErrorCode进行分析要比基于SqlState的方式要准确的多。默认情况下,JdbcTemplate会采用SQLErrorCodeSQLExceptionTranslator进行SQLException的转译,当ErrorCode无法提供足够的信息的时候,会转而求助SQLStateSQLExceptionTranslator。
如果JdbcTemplate默认的SQLErrorCodeSQLExceptionTranslator无法满足当前异常转译的需要,我们可以扩展SQLErrorCodeSQLExceptionTranslator,使其支持更多的情况,有两种方法进行扩展:提供其子类或者在classpath下提供相应的配置文件,

我们先大致看一下SQLErrorCodeSQLExceptionTranslator的大致调用规则,然后再从代码层面上研究下,r进行转译的大致的流程如下:
1、SQLErrorCodeSQLExceptionTranslator定义了如下的自定义异常转译的方法:

protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {
return null;
}

程序流程首先会检查该自定义转译的方法是否能够对当前的SQLException进行转译,如果可以,直接返回DataAccessException类型,如果为null,表示无法转译,程序将执行下一步,由上面代码可以看到该方法直接返回null,所以,流程要进入下一步。
2、使用org.springframework.jdbc.support.SQLErrorCodesFactory所加载的SQLErrorCodes进行异常转译,其中,SQLErrorCodesFactory加载SQLErrorCodes的流程为:
1>使用org/springframework/jdbc/support/sql-error-codes.xml路径下记载了各个数据库提供商的配置文件,提取相应的SQLErrorCodes。
2>如果发现当前应用的根目录下存在名称为sql-error-codes.xml的配置文件,则加载该文件并覆盖默认的ErrorCodes定义。

3、如果基于ErrorCode的异常转译还是没法搞定的话,SQLErrorCodeSQLExceptionTranslator只能求助于SQLStateSQLExceptionTranslator或者SQLExceptionSubclassTranslator

下面从代码层面上剖析之:
假若JdbcTemplate的如下模板方法在执行的过程中发生了异常:

public Object execute(StatementCallback action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
stmt = conToUse.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
Object result = action.doInStatement(stmtToUse);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

会执行catch块中的

throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);

getExceptionTranslator()如下定义:

public synchronized SQLExceptionTranslator getExceptionTranslator() {
if (this.exceptionTranslator == null) {
DataSource dataSource = getDataSource();
if (dataSource != null) {
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
}
else {
this.exceptionTranslator = new SQLStateSQLExceptionTranslator();
}
}
return this.exceptionTranslator;
}

dataSource不为null,所以创建了SQLErrorCodeSQLExceptionTranslator,看下其构造方法:

public SQLErrorCodeSQLExceptionTranslator(DataSource dataSource) {
this();
setDataSource(dataSource);
}

this()代码为:

public SQLErrorCodeSQLExceptionTranslator() {
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) {
setFallbackTranslator(new SQLExceptionSubclassTranslator());
}
else {
setFallbackTranslator(new SQLStateSQLExceptionTranslator());
}
}

如果JDK版本大于或等于6,备份了一个SQLExceptionSubclassTranslator类型的Translator,否则备份一个SQLStateSQLExceptionTranslator
setDataSource(DataSource dataSource)通过SQLErrorCodesFactory创建一个SQLErrorCodes类型的变量:

public void setDataSource(DataSource dataSource) {
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource);
}

SQLErrorCodesFactory采用了单例模式,在其构造方法中依然利用了BeanFactory,传入的文件为xml bean配置文件:

protected SQLErrorCodesFactory() {
Map errorCodes = null; try {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf); // Load default SQL error codes.
Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
if (resource != null && resource.exists()) {
bdr.loadBeanDefinitions(resource);
}
else {
logger.warn("Default sql-error-codes.xml not found (should be included in spring.jar)");
} // Load custom SQL error codes, overriding defaults.
resource = loadResource(SQL_ERROR_CODE_OVERRIDE_PATH);
if (resource != null && resource.exists()) {
bdr.loadBeanDefinitions(resource);
logger.info("Found custom sql-error-codes.xml file at the root of the classpath");
} // Check all beans of type SQLErrorCodes.
errorCodes = lbf.getBeansOfType(SQLErrorCodes.class, true, false);
if (logger.isInfoEnabled()) {
logger.info("SQLErrorCodes loaded: " + errorCodes.keySet());
}
}
catch (BeansException ex) {
logger.warn("Error loading SQL error codes from config file", ex);
errorCodes = Collections.EMPTY_MAP;
} this.errorCodesMap = errorCodes;
}

可知首先会读取org.springframework.jdbc.support下的sql-error-codes.xml文件,如果classpath下也有该文件,则覆盖之,
这样便生成了sqlErrorCodes
getExceptionTranslator().translate("StatementCallback", getSql(action), ex)的方法如下所示:

public DataAccessException translate(String task, String sql, SQLException ex) {
Assert.notNull(ex, "Cannot translate a null SQLException");
if (task == null) {
task = "";
}
if (sql == null) {
sql = "";
} DataAccessException dex = doTranslate(task, sql, ex);
if (dex != null) {
// Specific exception match found.
return dex;
}
// Looking for a fallback...
SQLExceptionTranslator fallback = getFallbackTranslator();
if (fallback != null) {
return fallback.translate(task, sql, ex);
}
// We couldn't identify it more precisely.
return new UncategorizedSQLException(task, sql, ex);
}

doTranslate(task, sql, ex)让子类实现,在这个例子中即是SQLErrorCodeSQLExceptionTranslator,代码如下:

protected DataAccessException doTranslate(String task, String sql, SQLException ex) {
SQLException sqlEx = ex;
if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) {
SQLException nestedSqlEx = sqlEx.getNextException();
if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) {
logger.debug("Using nested SQLException from the BatchUpdateException");
sqlEx = nestedSqlEx;
}
} // First, try custom translation from overridden method.
DataAccessException dex = customTranslate(task, sql, sqlEx);
if (dex != null) {
return dex;
} // Check SQLErrorCodes with corresponding error code, if available.
if (this.sqlErrorCodes != null) {
String errorCode = null;
if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {
errorCode = sqlEx.getSQLState();
}
else {
errorCode = Integer.toString(sqlEx.getErrorCode());
} if (errorCode != null) {
// Look for defined custom translations first.
CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations();
if (customTranslations != null) {
for (int i = 0; i < customTranslations.length; i++) {
CustomSQLErrorCodesTranslation customTranslation = customTranslations[i];
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0) {
if (customTranslation.getExceptionClass() != null) {
DataAccessException customException = createCustomException(
task, sql, sqlEx, customTranslation.getExceptionClass());
if (customException != null) {
logTranslation(task, sql, sqlEx, true);
return customException;
}
}
}
}
}
// Next, look for grouped error codes.
if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new BadSqlGrammarException(task, sql, sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new InvalidResultSetAccessException(task, sql, sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new TransientDataAccessResourceException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx);
}
}
} // We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
if (logger.isDebugEnabled()) {
String codes = null;
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) {
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode();
}
else {
codes = "Error code '" + sqlEx.getErrorCode() + "'";
}
logger.debug("Unable to translate SQLException with " + codes + ", will now try the fallback translator");
} return null;
}

可知假如该方法返回的是null,translate方法会调用SQLExceptionSubclassTranslator或者SQLStateSQLExceptionTranslator的translate的方法转译这个异常。

在SQLErrorCodeSQLExceptionTranslator转译异常的过程中,我们可以在两个地方插入自定义的转译异常:
1、在customTranslate(String task, String sql, SQLException sqlEx)方法中,通过子类化SQLErrorCodeSQLExceptionTranslator,重写该方法。
2、在classpath下提供sql-error-codes.xml文件。
下面是使用这两种方式进行自定义转译的具体实施情况。
1、扩展SQLErrorCodeSQLExceptionTranslator
该方法最直接有效,却不够方便,需要子类化并且覆写它的customTranslate方法,

package com.google.spring.jdbc;

import java.sql.SQLException;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.UncategorizedDataAccessException;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; public class SimpleSQLErrorCodeSQLExceptinTranslator extends SQLErrorCodeSQLExceptionTranslator
{
@Override
protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx)
{
if(sqlEx.getErrorCode()==111)
{
StringBuilder builder = new StringBuilder();
builder.append("unexpected data access exception raised when executing ");
builder.append(task);
builder.append(" with SQL>");
builder.append(sql);
return new UnknownUncategorizedDataAccessException(builder.toString(),sqlEx);
}
return null;
} private class UnknownUncategorizedDataAccessException extends UncategorizedDataAccessException
{
public UnknownUncategorizedDataAccessException(String msg, Throwable cause) {
super(msg, cause);
}
}
}

在这里,假设当数据库返回的错误代码为111的时候,将抛出UnknownUncategorizedDataAccessException类型的异常(或者是其它自定义的DataAccessException)除此之外,返回null以保证其它的异常转译依然采用超类的逻辑进行。
为了能使自定义的转译其作用,我们需要让JdbcTemplate使用我们的SimpleSQLErrorCodeSQLExceptinTranslator,而不是默认的SQLErrorCodeSQLExceptionTranslator,所以,需要如下代码所示,将SimpleSQLErrorCodeSQLExceptinTranslator设置给JdbcTemplate:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
JdbcTemplate jdbc = (JdbcTemplate)applicationContext.getBean("jdbc");
DataSource dataSource = (DataSource)applicationContext.getBean("dataSource");
SimpleSQLErrorCodeSQLExceptinTranslator simpleSQLErrorCodeSQLExceptinTranslator = new SimpleSQLErrorCodeSQLExceptinTranslator();
simpleSQLErrorCodeSQLExceptinTranslator.setDataSource(dataSource);
jdbc.setExceptionTranslator(simpleSQLErrorCodeSQLExceptinTranslator);

在classpath下放置一个sql-error-codes.xml文件,格式要与默认的文件格式相同。

实际上,它就是一个基本的基于DTD的Spring IOC容器的配置文件,只不过class是固定的。该配置文件对每个数据库类型均提供了一个org.springframework.jdbc.support.SQLErrorCodes的定义。假若我们有另外一个数据库AnotherDb,要扩展该转译,我们有两种方式:
1、

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="AnotherDB" class="org.springframework.jdbc.support.SQLErrorCodes">
<property name="databaseProductName">
<value>AnotherDB*</value>
</property>
<property name="badSqlGrammarCodes">
<value>001</value>
</property>
<property name="dataIntegrityViolationCodes">
<value>002</value>
</property>
<property name="dataAccessResourceFailureCodes">
<value>0031,0032</value>
</property>
<property name="transientDataAccessResourceCodes">
<value>004</value>
</property>
<property name="deadlockLoserCodes">
<value>0051,0052</value>
</property>
</bean>
</beans>

2、设置customTranslations属性:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="AnotherDB" class="org.springframework.jdbc.support.SQLErrorCodes">
<property name="databaseProductName">
<value>AnotherDB*</value>
</property>
<property name="customTranslations">
<list>
<bean class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">
<property name="errorCodes">111</property>
<property name="exceptionClass">
org.springframework.dao.IncorrectResultSizeDataAccessException
</property>
</bean>
</list>
</property>
</bean>
</beans>

至此,spring的异常转译部分全部分析完毕!

Spring JDBC最佳实践(2)的更多相关文章

  1. Spring JDBC最佳实践(1)

    原文地址:https://my.oschina.net/u/218421/blog/38513 Spring提供了两种使用JDBC API的最佳实践,一种是以JdbcTemplate为核心的基于Tem ...

  2. Spring JDBC最佳实践(3)

    原文地址:https://my.oschina.net/u/218421/blog/38598 spring jdbc包提供了JdbcTemplate和它的两个兄弟SimpleJdbcTemplate ...

  3. Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂Spring Validation.本文会详细介绍Spring Validation各种场景下 ...

  4. 微服务电商项目发布重大更新,打造Spring Cloud最佳实践!

    Spring Cloud实战电商项目mall-swarm地址:转发+关注 私信我获取地址 系统架构图   系统架构图 项目组织结构 mall├── mall-common-- 工具类及通用代码模块├─ ...

  5. Spring Boot 16 条最佳实践

    Spring Boot是最流行的用于开发微服务的Java框架.在本文中,我将与你分享自2016年以来我在专业开发中使用Spring Boot所采用的最佳实践.这些内容是基于我的个人经验和一些熟知的Sp ...

  6. 面试题_76_to_81_Java 最佳实践的面试问题

    包含 Java 中各个部分的最佳实践,如集合,字符串,IO,多线程,错误和异常处理,设计模式等等. 76)Java 中,编写多线程程序的时候你会遵循哪些最佳实践?(答案)这是我在写Java 并发程序的 ...

  7. 开涛spring3(7.5) - 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践

    7.5 集成Spring JDBC及最佳实践 大多数情况下Spring JDBC都是与IOC容器一起使用.通过配置方式使用Spring JDBC. 而且大部分时间都是使用JdbcTemplate类(或 ...

  8. Spring Boot学习笔记2——基本使用之最佳实践[z]

    前言 在上一篇文章Spring Boot 学习笔记1——初体验之3分钟启动你的Web应用已经对Spring Boot的基本体系与基本使用进行了学习,本文主要目的是更加进一步的来说明对于Spring B ...

  9. spring boot 使用及最佳实践

    第一部分,spring boot 文档 Spring boot的使用 使用maven进行构建 用户可以通过继承spring-boot-starter-parent来获取默认的依赖. l  默认java ...

随机推荐

  1. delegate里的Invoke和BeginInvoke

    Invoke和BeginInvoke都是调用委托实体的方法,前者是同步调用,即它运行在主线程上,当Invode处理时间长时,会出现阻塞的情况,而BeginInvod是异步操作,它会从新开启一个线程,所 ...

  2. istio流量管理

    目录 1 准备工作 1.1 在k8s部署istio 1.2 istio自动注入 1.3 应用部署要求 2 负载均衡 3 流量迁移:金丝雀发布 3.1 发布应用 3.2 创建目标规则:Destinati ...

  3. drf--版本控制

    目录 作用 内置版本控制 局部使用 全局使用 案例 源码分析 作用 因为程序在不停的更新迭代,新版和旧版差别比较大,但此时有些用户没用进行更新,还是访问的旧版,此时就需要进行版本控制,让用户体验更好 ...

  4. python3 + robotframework ride 乱码问题

    执行ride时乱码问题 如果被执行的文件所在路径中含有中文,执行时可能会报如下错误 robotframework版本:robotframework-ride 1.7.3.1python 3.7.3 解 ...

  5. 【转载】C#使用typeof运算符获取对象变量的具体类型Type

    在C#的实际类型操作过程中,有时候需要通过typeof方法获取对象的类型对应的Type变量即具体类型,例如在获取DataTable中某一列的具体数据类型的时候,我们就会用到typeof方法来获取具体的 ...

  6. jQuery每秒刷新

    显示当前时间 setInterval( function getNowTime() { var nowTime = new Date(); var nowYear = nowTime.getFullY ...

  7. ES6环境搭配(一)

    一.Node(NodeJS.Node.js)的安装:1.下载官网下载地址:https://nodejs.org/en/ 2.安装a.Linux先将安装包解压,然后进行环境变量的配置即可b.window ...

  8. mysql的my.cnf

    配置参数详解 [client] #客户端设置,即客户端默认的连接参数port = 3307   #默认连接端口socket = /data/mysqldata/3307/mysql.sock #用于本 ...

  9. Js中replace替换所有*

    var t = '***感**谢**有**你***'; var r = t.replace(/\*/g,''); //\为转义字符 g表示全局 console.log(r) //感谢有你

  10. notepad++ 设置运行python脚本

    按F5 在输入框中输入: cmd /k python “$(FULL_CURRENT_PATH)” &PAUSE & EXIT python路径必须在环境变量中. 否则需要输入完整的p ...