单数据源配置
数据源是一种用来提高数据库连接性能的常规手段,数据源会负责维持一个数据库连接池,当程序创建数据源实例时,系统会一次性地创建多个数据库连接,并把这些数据库连接保存在连接池中。当程序需要进行数据库访问时,无须重新获得数据库连接,而是从连接池中取出一个空闲的数据库连接,当程序使用数据库连接访问结束后,无须关闭数据库连接,而是将数据库连接归还给连接池即可。通过这种方式,就可比避免频繁地获取数据库连接,关闭数据库连接所导致的性能下降。
无论采用哪一种数据源配置,首先需要有数据库驱动包和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文件即可。