数据源是一种用来提高数据库连接性能的常规手段,数据源会负责维持一个数据库连接池,当程序创建数据源实例时,系统会一次性地创建多个数据库连接,并把这些数据库连接保存在连接池中。当程序需要进行数据库访问时,无须重新获得数据库连接,而是从连接池中取出一个空闲的数据库连接,当程序使用数据库连接访问结束后,无须关闭数据库连接,而是将数据库连接归还给连接池即可。通过这种方式,就可比避免频繁地获取数据库连接,关闭数据库连接所导致的性能下降。
无论采用哪一种数据源配置,首先需要有数据库驱动包和spring-jdbc模块,这里采用MySQL,所以添加以下依赖:
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--Spring JDBC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
一、普通DriverManagerDataSource
DriverManagerDataSource是Spring自带的数据源插件,没有连接池的概念。即每次都会创建新的数据库连接。 不推荐使用,会有以下问题:
- 增加系统性能开销。
- 数据库操作效率低,每次都要创建、释放链接无意增加操作时间。
- 如果有长连接未释放,会导致数据库连接不够使用
在resourcs文件夹下创建dmd.properties
# DriverManagerDataSource
dmd.jdbc.driverClassName=com.mysql.jdbc.Driver
dmd.jdbc.url=jdbc:mysql://127.0.0.1:3305/spring?characterEncoding=utf-8
dmd.jdbc.username=root
dmd.jdbc.password=123456
数据库配置类
public class DmdSource {
@Value("${dmd.jdbc.driverClassName}")
private String driverClassName;
@Value("${dmd.jdbc.url}")
private String url;
@Value("${dmd.jdbc.username}")
private String username;
@Value("${dmd.jdbc.password}")
private String password;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* 创建数据源对象
*
* @return DataSource
*/
@Bean(name = "defaultDataSource")
public DataSource defaultDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
修改Spring根容器配置类:SpringConfig
在类上添加以下内容,用以将数据库配置类加入到Spring IoC容器中
@Import(value = DmdSource.class)
@PropertySource(value = {"classpath:dmd.properties"}) //读取resources下的配置文件
测试数据库配置
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = SpringConfig.class),
@ContextConfiguration(classes = SpringMVCConfig.class)
})
public class DbTest {
@Autowired
private DataSource defaultDataSource;
@Test
public void test() throws SQLException {
// 测试数据库是否正确连接
System.out.println(defaultDataSource.getConnection());
}
}
二、DruidDataSource
添加druid依赖
<!-- druid dataSource -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
主要类:com.alibaba.druid.pool.DruidDataSource
可配置属性
name: 数据源名称,多数据源下有用,例:druid-1
driverClassName:数据库驱动类型,根据url自动识别,可以不配置,例:com.mysql.jdbc.Driver
url:连接数据库的url,例:jdbc:mysql://127.0.0.1:3305/spring?characterEncoding=utf-8
username:用户名,例:root
password:密码,例:123456。可以使用druid提供的ConfigTools进行加密,命令java -cp druid-1.1.16.jar com.alibaba.druid.filter.config.ConfigTools 123456,配置加密的密码后需要配置connectionProperties,包含config.decrypt=true
initialSize:初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时, 默认是0
maxActive:最大连接池数量,默认是8个
minIdle: 最小连接池数量
maxWait:取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。例:10000
poolPreparedStatements: 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。默认是false
maxOpenPreparedStatements: 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。
validationQuery:用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。例:select 1
testOnBorrow:申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。例:true
testOnReturn: 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。例: false
testWhileIdle:建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。例:true
timeBetweenEvictionRunsMillis:关闭空闲连接的检测时间间隔,单位毫秒
minEvictableIdleTimeMillis:连接的最小生存时间,单位毫秒
connectionInitSqls:物理连接初始化的时候执行的sql(集合)。例:["set names utf8mb4;"]
filters:属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall。例:stat,wall,log4j
defaultAutoCommit:配置提交方式,默认就是true,可以不用配置。例:true
useGlobalDataSourceStat: 是否合并多个DruidDataSource的监控数据,例:false
asyncInit:asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间。例:true
timeBetweenLogStatsMillis:配置监控统计日志的输出间隔,单位毫秒,每次输出所有统计数据会重置,酌情开启。例:120000
clearFiltersEnable:是否启动清除过滤器。例:true
useUnfairLock: 是否使用非公平锁。例:true
resetStatEnable: 是否启动重置功能,重置后,会导致所有计数器清零,重新计数。例:true
notFullTimeoutRetryCount: 设置获取连接时的重试次数,-1为不重试。
failFast:设置获取连接出错时是否马上返回错误,true为马上返回。例:true
breakAfterAcquireFailure:true表示向数据库请求连接失败后,就算后端数据库恢复正常也不进行重连,客户端对pool的请求都拒绝掉.false表示新的请求都会尝试去数据库请求connection.默认为false。
maxWaitThreadCount:druid的丢弃策略。默认值是-1,表示不启用,大于0表示启用。意思就是在连接不够用时最多让多少个业务线程发生阻塞,不会造成大量的线程阻塞。例:2
phyTimeoutMillis:连接不管是否空闲,存活phyTimeoutMillis后强制回收,用于Destroy线程清理连接的时候的检测时间。例:3600000
maxEvictableIdleTimeMillis:连接的最大存活时间,如果连接的最大时间大于maxEvictableIdleTimeMillis,则无视最小连接数强制回收。例:3600000
keepAlive:打开后,增强timeBetweenEvictionRunsMillis的周期性连接检查,minIdle内的空闲连接,每次检查强制验证连接有效性。例:true
killWhenSocketReadTimeout: socket连接超时时间, 单位秒
initVariants:
initGlobalVariants:
connectProperties:通过connectProperties属性来打开mergeSql功能;慢SQL记录。例:spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
maxPoolPreparedStatementPerConnectionSize:设置PSCache值,例:20
timeBetweenConnectErrorMillis:连接出错后重试时间间隔,单位毫秒。例:300000
queryTimeout: 查询的超时时间,单位秒。
并且从借出时间起已超过removeAbandonedTimeout时间,则强制归还连接到连接池中。removeAbandoned:连接泄露检查,打开removeAbandoned功能 , 连接从连接池借出后,长时间不归还,将触发强制回连接。回收周期随timeBetweenEvictionRunsMillis进行,如果连接为从连接池借出状态,并且未执行任何sql,例:true
removeAbandonedTimeout:连接泄露检查超时时间, 单位秒
logAbandoned:关闭abanded连接时输出错误日志,这样出现连接泄露时可以通过错误日志定位忘记关闭连接的位置。例:true
transactionQueryTimeout:事务超时时间,单位秒
defaultTransactionIsolation:指定连接的事务的默认隔离级别。
transactionThresholdMillis:事务使用时长,单位毫秒。
connectionErrorRetryAttempts:连接出错后再尝试连接三次
在resourcs文件夹下创建druiddb.properties
# DruidDataSource + mysql
druid.jdbc.name=druid-default
druid.jdbc.driverClassName=com.mysql.jdbc.Driver
druid.jdbc.url=jdbc:mysql://127.0.0.1:3305/spring?characterEncoding=utf-8&useSSL=false
druid.jdbc.username=root
druid.jdbc.password=123456
druid.jdbc.initialSize=1
druid.jdbc.maxActive=10
druid.jdbc.minIdle=1
druid.jdbc.maxWait=10000
druid.jdbc.testOnBorrow=true
druid.jdbc.testOnReturn=false
druid.jdbc.testWhileIdle=true
druid.jdbc.timeBetweenEvictionRunsMillis=60000
druid.jdbc.minEvictableIdleTimeMillis=300000
druid.jdbc.poolPreparedStatements=true
druid.jdbc.maxOpenPreparedStatements=20
druid.jdbc.asyncInit= true
druid.jdbc.filters=stat
druid.jdbc.validationQuery=select 1
druid数据库配置类
public class DruidDBConfig {
@Value("${druid.jdbc.name}")
private String dataSourceName;
@Value("${druid.jdbc.driverClassName}")
private String driverClassName;
@Value("${druid.jdbc.url}")
private String url;
@Value("${druid.jdbc.username}")
private String username;
@Value("${druid.jdbc.password}")
private String password;
@Value("${druid.jdbc.initialSize}")
private Integer initialSize;
@Value("${druid.jdbc.maxActive}")
private Integer maxActive;
@Value("${druid.jdbc.minIdle}")
private Integer minIdle;
@Value("${druid.jdbc.maxWait}")
private Integer maxWait;
@Value("${druid.jdbc.poolPreparedStatements}")
private Boolean poolPreparedStatements;
@Value("${druid.jdbc.maxOpenPreparedStatements}")
private Integer maxOpenPreparedStatements;
@Value("${druid.jdbc.validationQuery}")
private String validationQuery;
@Value("${druid.jdbc.testOnBorrow}")
private Boolean testOnBorrow;
@Value("${druid.jdbc.testOnReturn}")
private Boolean testOnReturn;
@Value("${druid.jdbc.testWhileIdle}")
private Boolean testWhileIdle;
@Value("${druid.jdbc.timeBetweenEvictionRunsMillis}")
private Long timeBetweenEvictionRunsMillis;
@Value("${druid.jdbc.minEvictableIdleTimeMillis}")
private Long minEvictableIdleTimeMillis;
@Value("${druid.jdbc.filters}")
private String filters;
@Value("${druid.jdbc.asyncInit}")
private Boolean asyncInit;
/**
* 创建数据源对象
* 返回值不能返回 javax.sql.DataSource, 否则在web监控时数据源tab源无任何显示
* @return DruidDataSource
*/
@Bean(name="druidDataSource",destroyMethod = "close", initMethod = "init")
public DruidDataSource druidDataSource() throws SQLException {
DruidDataSource ds = new DruidDataSource();
ds.setName(dataSourceName);
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setInitialSize(initialSize);
ds.setMaxActive(maxActive);
ds.setMinIdle(minIdle);
ds.setMaxWait(maxWait);
ds.setPoolPreparedStatements(poolPreparedStatements);
ds.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
ds.setValidationQuery(validationQuery);
ds.setTestOnBorrow(testOnBorrow);
ds.setTestOnReturn(testOnReturn);
ds.setTestWhileIdle(testWhileIdle);
ds.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
ds.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
ds.setFilters(filters);
ds.setAsyncInit(asyncInit);
return ds;
}
}
同理,只需要在SpringConfig中将@Import的DmdSource改为DruidDBConfig及修改读取的properties文件即可。
druid的web监控配置
druid也提供简单的界面化监控。
在resources目录下创建druid-web.properties ,内容如下:
# druid 监控配置
# 映射的URL
druid.stat-view-servlet.urlPattern=/druid/*
# 白名单,如果不配置或value为空,则允许所有
druid.stat-view-servlet.allow=
# 黑名单,与白名单存在相同IP时,优先于白名单
druid.stat-view-servlet.deny=
# 用户名
druid.stat-view-servlet.loginUsername=root
# 密码
druid.stat-view-servlet.loginPassword=sunshine
# 禁用HTML页面上的“Reset All”功能
druid.stat-view-servlet.resetEnable=false
druid.web-stat-filter.enabled=true
druid.web-stat-filter.urlPattern=/*
# 排除一些不必要的url,比如.js,gif等等
druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*,/static/*
# 默认sessionStatMaxCount是1000个,你也可以按需要进行配置
druid.web-stat-filter.sessionStatMaxCount=1000
# session统计功能
druid.web-stat-filter.sessionStatEnable=false
# druid 0.2.7版本开始支持profile,配置profileEnable能够监控单个url调用的sql列表
druid.web-stat-filter.profileEnable=true
# 监控当前COOKIE的用户
druid.web-stat-filter.principalCookieName=USER_COOKIE
# 监控当前SESSION的用户
druid.web-stat-filter.principalSessionName=USER_SESSION
启动时注册用到的Servlet和Filter
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.web.WebApplicationInitializer;
public class ApplicationInitializer implements WebApplicationInitializer {
private static Properties druidWebProperties = new Properties();
private static final String DRUID_STAT_VIEW_SERVLET_PREFIX = "druid.stat-view-servlet.";
private static final String DRUID_WEB_STAT_FILTER_PREFIX = "druid.web-stat-filter.";
static {
System.out.println("读取外部指定配置");
//读取Properties文件
try {
druidWebProperties = PropertiesLoaderUtils.loadAllProperties("druid-web.properties");
} catch (IOException e) {
throw new RuntimeException("启动时读取配置文件appCommon.properties失败:" + e.getMessage());
}
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.registerServlet(servletContext);
this.registerFilter(servletContext);
}
public void registerServlet(ServletContext servletContext){
ServletRegistration.Dynamic druidStatViewServlet = servletContext.addServlet("DruidStatViewServlet", StatViewServlet.class);
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("allow",druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "allow"));
initParams.put("deny",druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "deny"));
initParams.put("loginUsername",druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "loginUsername"));
initParams.put("loginPassword",druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "loginPassword"));
initParams.put("resetEnable",druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "resetEnable"));
druidStatViewServlet.setInitParameters(initParams);
druidStatViewServlet.addMapping(druidWebProperties.getProperty(DRUID_STAT_VIEW_SERVLET_PREFIX + "urlPattern"));
}
private void registerFilter(ServletContext servletContext){
FilterRegistration.Dynamic druidStatFilter = servletContext.addFilter("DruidStatFilter", WebStatFilter.class);
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("enabled",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "enabled"));
initParams.put("exclusions",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "exclusions"));
initParams.put("sessionStatMaxCount",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "sessionStatMaxCount"));
initParams.put("sessionStatEnable",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "sessionStatEnable"));
initParams.put("profileEnable",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "profileEnable"));
initParams.put("principalCookieName",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "principalCookieName"));
initParams.put("principalSessionName",druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "principalSessionName"));
druidStatFilter.setInitParameters(initParams);
druidStatFilter.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false,
druidWebProperties.getProperty(DRUID_WEB_STAT_FILTER_PREFIX + "urlPattern"));
}
}
启动项目,访问http://ip:port/应用上下文/druid,输入用户名和密码:root/sunshine。
三、C3p0数据源
添加依赖:
<!-- C3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.19</version>
</dependency>
主要类:com.mchange.v2.c3p0.ComboPooledDataSource
在resources目录下创建c3p0.properties
# ComboPooledDataSource + mysql
c3p0.jdbc.driverClass=com.mysql.jdbc.Driver
c3p0.jdbc.jdbcUrl=jdbc:mysql://localhost:3305/spring?useSSL=false&characterEncoding=UTF-8
c3p0.jdbc.user=root
c3p0.jdbc.password=123456
c3p0.jdbc.minPoolSize=1
c3p0.jdbc.maxPoolSize=100
c3p0.jdbc.initialPoolSize=5
c3p0.jdbc.maxIdleTime=60
c3p0.jdbc.acquireIncrement=10
c3p0.jdbc.maxStatements=10
c3p0.jdbc.idleConnectionTestPeriod=30
c3p0.jdbc.acquireRetryAttempts=30
c3p0.jdbc.breakAfterAcquireFailure=true
c3p0.jdbc.testConnectionOnCheckout=false
c3p0.jdbc.automaticTestTable=true
c3p0.jdbc.checkoutTimeout=15000
c3p0.jdbc.numHelperThreads=10
c3p0.jdbc.testConnectionOnCheckin=true
c3p0数据库配置类
public class C3p0DBConfig {
@Value("${c3p0.jdbc.driverClass}")
private String driverClass;
@Value("${c3p0.jdbc.jdbcUrl}")
private String jdbcUrl;
@Value("${c3p0.jdbc.user}")
private String user;
@Value("${c3p0.jdbc.password}")
private String password;
@Value("${c3p0.jdbc.minPoolSize}")
private Integer minPoolSize;
@Value("${c3p0.jdbc.maxPoolSize}")
private Integer maxPoolSize;
@Value("${c3p0.jdbc.initialPoolSize}")
private Integer initialPoolSize;
@Value("${c3p0.jdbc.maxIdleTime}")
private Integer maxIdleTime;
@Value("${c3p0.jdbc.acquireIncrement}")
private Integer acquireIncrement;
@Value("${c3p0.jdbc.maxStatements}")
private Integer maxStatements;
@Value("${c3p0.jdbc.idleConnectionTestPeriod}")
private Integer idleConnectionTestPeriod;
@Value("${c3p0.jdbc.acquireRetryAttempts}")
private Integer acquireRetryAttempts;
@Value("${c3p0.jdbc.breakAfterAcquireFailure}")
private boolean breakAfterAcquireFailure;
@Value("${c3p0.jdbc.testConnectionOnCheckout}")
private boolean testConnectionOnCheckout;
@Value("${c3p0.jdbc.automaticTestTable}")
private boolean automaticTestTable;
@Value("${c3p0.jdbc.checkoutTimeout}")
private Integer checkoutTimeout;
@Value("${c3p0.jdbc.numHelperThreads}")
private Integer numHelperThreads;
@Value("${c3p0.jdbc.testConnectionOnCheckin}")
private boolean testConnectionOnCheckin;
@Bean("c3p0DataSource")
public ComboPooledDataSource c3p0DataSource() throws PropertyVetoException {
ComboPooledDataSource pooledDataSource = new ComboPooledDataSource();
pooledDataSource.setDriverClass(driverClass);
pooledDataSource.setJdbcUrl(jdbcUrl);
pooledDataSource.setUser(user);
pooledDataSource.setPassword(password);
pooledDataSource.setMinPoolSize(minPoolSize);
pooledDataSource.setMaxPoolSize(maxPoolSize);
pooledDataSource.setInitialPoolSize(initialPoolSize);
pooledDataSource.setMaxIdleTime(maxIdleTime);
pooledDataSource.setAcquireIncrement(acquireIncrement);
pooledDataSource.setMaxStatements(maxStatements);
pooledDataSource.setIdleConnectionTestPeriod(idleConnectionTestPeriod);
pooledDataSource.setAcquireRetryAttempts(acquireRetryAttempts);
pooledDataSource.setBreakAfterAcquireFailure(breakAfterAcquireFailure);
pooledDataSource.setTestConnectionOnCheckout(testConnectionOnCheckout);
pooledDataSource.setCheckoutTimeout(checkoutTimeout);
pooledDataSource.setNumHelperThreads(numHelperThreads);
pooledDataSource.setTestConnectionOnCheckin(testConnectionOnCheckin);
return pooledDataSource;
}
}
同理,只需要在SpringConfig中将@Import的类改为C3p0DBConfig及修改读取的properties文件即可。
时刻与技术进步,每天一点滴,日久一大步!!! 本博客只为记录,用于学习,如有冒犯,请私信于我。