You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.md 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. # DBCP
  2. ## 简介
  3. DBCP用于创建和管理连接,利用连接池的方式复用连接减少了资源开销。
  4. 连接池的参数可以采用`properties`文件来配置:配置包括驱动、链接、账号密码,连接池基本参数,事务相关参数,连接测试的参数以及内存回收参数等。
  5. DBCP对外交互主要是一个`BasicDataDource`,用于设置连接池参数和获取连接对象,作用有点类似于JDK的`DriverManager`。通过源码可以看到,`BasicDataSource`内部有一个`dataSource` 和`connectionPool`字段。
  6. `dataSource`用于从连接池中获取连接。
  7. `connectionPool`用于创建,存储和管理池中的连接,里面有一个`Map`对象和`LinkedBlockingDeque`对象,分别存储着所有连接和空闲连接,构成所谓的“池”。
  8. ## 使用例子
  9. ### 需求
  10. 使用DBCP连接池获取连接对象,对用户数据进行增删改查。
  11. ### 工程环境
  12. JDK:1.8.0_201
  13. maven:3.6.1
  14. IDE:Spring Tool Suites4 for Eclipse
  15. mysql驱动:8.0.15
  16. mysql:5.7
  17. ### 主要步骤
  18. DBCP对外交互主要是一个`BasicDataDource`,用于设置连接池参数和获取连接对象。
  19. 1. 通过`BasicDataSourceFactory.createDataSource(properties)`设置连接池参数,并获得`BasicDataDource`对象;
  20. 2. 获取连接对象:调用`BasicDataDource`对象的`getConnection()`方法获取`Connection`对象。
  21. ### 创建表
  22. ```sql
  23. CREATE DATABASE `demo`CHARACTER SET utf8 COLLATE utf8_bin;
  24. User `demo`;
  25. CREATE TABLE `user` (
  26. `id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',
  27. `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名',
  28. `age` int(10) unsigned DEFAULT NULL COMMENT '用户年龄',
  29. `gmt_create` datetime DEFAULT NULL COMMENT '记录创建时间',
  30. `gmt_modified` datetime DEFAULT NULL COMMENT '记录最后修改时间',
  31. PRIMARY KEY (`id`),
  32. UNIQUE KEY `uk_name` (`name`)
  33. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
  34. ```
  35. ### 创建项目
  36. 项目类型Maven Project,打包方式jar
  37. ### 引入依赖
  38. ```xml
  39. <!-- junit -->
  40. <dependency>
  41. <groupId>junit</groupId>
  42. <artifactId>junit</artifactId>
  43. <version>4.12</version>
  44. <scope>test</scope>
  45. </dependency>
  46. <!-- dbcp -->
  47. <dependency>
  48. <groupId>org.apache.commons</groupId>
  49. <artifactId>commons-dbcp2</artifactId>
  50. <version>2.6.0</version>
  51. </dependency>
  52. <!-- log4j -->
  53. <dependency>
  54. <groupId>log4j</groupId>
  55. <artifactId>log4j</artifactId>
  56. <version>1.2.17</version>
  57. </dependency>
  58. <!-- mysql驱动的jar包 -->
  59. <dependency>
  60. <groupId>mysql</groupId>
  61. <artifactId>mysql-connector-java</artifactId>
  62. <version>8.0.15</version>
  63. </dependency>
  64. ```
  65. ### 编写jdbc.prperties
  66. 路径:resources目录下
  67. ```properties
  68. #数据库配置
  69. driverClassName=com.mysql.cj.jdbc.Driver
  70. url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
  71. username=root
  72. password=root
  73. ```
  74. ### 编写JDBCUtil用于获得连接对象
  75. 这里设置工具类的目的是避免多个线程使用同一个连接对象,并提供了释放资源的方法(注意,考虑到重用性,这里并不会关闭连接)。
  76. 路径:`cn.zzs.jdbc`
  77. ```java
  78. /**
  79. * @ClassName: JDBCUtil
  80. * @Description: 用于获取数据库连接对象的工具类
  81. * @author: zzs
  82. * @date: 2019年8月31日 下午9:05:08
  83. */
  84. public class JDBCUtil {
  85. private static DataSource dataSource;
  86. private static ThreadLocal<Connection> tl = new ThreadLocal<>();
  87. private static Object obj = new Object();
  88. static {
  89. init();
  90. }
  91. /**
  92. *
  93. * @Title: getConnection
  94. * @Description: 获取数据库连接对象的方法,线程安全
  95. * @author: zzs
  96. * @date: 2019年8月31日 下午9:22:29
  97. * @return: Connection
  98. */
  99. public static Connection getConnection(){
  100. //从当前线程中获取连接对象
  101. Connection connection = tl.get();
  102. //判断为空的话,创建连接并绑定到当前线程
  103. if(connection == null) {
  104. synchronized (obj) {
  105. if(tl.get() == null) {
  106. connection = createConnection();
  107. tl.set(connection);
  108. }
  109. }
  110. }
  111. return connection;
  112. }
  113. /**
  114. *
  115. * @Title: release
  116. * @Description: 释放资源
  117. * @author: zzs
  118. * @date: 2019年8月31日 下午9:39:24
  119. * @param conn
  120. * @param statement
  121. * @return: void
  122. */
  123. public static void release(Connection conn,Statement statement,ResultSet resultSet) {
  124. if(resultSet!=null) {
  125. try {
  126. resultSet.close();
  127. } catch (SQLException e) {
  128. System.err.println("关闭ResultSet对象异常");
  129. e.printStackTrace();
  130. }
  131. }
  132. if(statement != null) {
  133. try {
  134. statement.close();
  135. } catch (SQLException e) {
  136. System.err.println("关闭Statement对象异常");
  137. e.printStackTrace();
  138. }
  139. }
  140. //注意:这里不关闭连接
  141. if(conn!=null) {
  142. try {
  143. //如果连接失效的话,从当前线程的绑定中删除
  144. if(!conn.isValid(3)) {
  145. tl.remove();
  146. }
  147. } catch (SQLException e) {
  148. System.err.println("校验连接有效性");
  149. e.printStackTrace();
  150. }
  151. }
  152. }
  153. /**
  154. *
  155. * @Title: createConnection
  156. * @Description: 创建数据库连接
  157. * @author: zzs
  158. * @date: 2019年8月31日 下午9:27:03
  159. * @return: Connection
  160. */
  161. private static Connection createConnection(){
  162. Connection conn = null;
  163. //获得连接
  164. try {
  165. conn = dataSource.getConnection();
  166. } catch (SQLException e) {
  167. System.err.println("从数据源获取连接失败");
  168. e.printStackTrace();
  169. }
  170. return conn;
  171. }
  172. /**
  173. * @Title: init
  174. * @Description: 根据指定配置文件创建数据源对象
  175. * @author: zzs
  176. * @date: 2019年9月1日 上午10:53:05
  177. * @return: void
  178. */
  179. private static void init() {
  180. //导入配置文件
  181. Properties properties = new Properties();
  182. InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
  183. try {
  184. properties.load(in);
  185. //根据配置文件内容获得数据源对象
  186. dataSource = BasicDataSourceFactory.createDataSource(properties);
  187. } catch (IOException e) {
  188. System.err.println("导入配置文件出错");
  189. e.printStackTrace();
  190. } catch (Exception e) {
  191. System.err.println("根据指定配置文件创建数据源出错");
  192. e.printStackTrace();
  193. }
  194. }
  195. }
  196. ```
  197. ### 编写测试类
  198. 路径:test目录下的`cn.zzs.jdbc`
  199. #### 添加用户
  200. 注意:这里引入了事务
  201. ```java
  202. /**
  203. * 测试添加用户
  204. * @throws SQLException
  205. */
  206. @Test
  207. public void saveUser() throws Exception {
  208. //创建sql
  209. String sql = "insert into user values(null,?,?,?,?)";
  210. //获得连接
  211. Connection connection = JDBCUtil.getConnection();
  212. PreparedStatement statement = null;
  213. try {
  214. //设置非自动提交
  215. connection.setAutoCommit(false);
  216. //获得Statement对象
  217. statement = connection.prepareStatement(sql);
  218. //设置参数
  219. statement.setString(1, "zzs001");
  220. statement.setInt(2, 18);
  221. statement.setDate(3, new Date(System.currentTimeMillis()));
  222. statement.setDate(4, new Date(System.currentTimeMillis()));
  223. //执行
  224. statement.executeUpdate();
  225. //提交事务
  226. connection.commit();
  227. } catch (Exception e) {
  228. System.out.println("异常导致操作回滚");
  229. connection.rollback();
  230. e.printStackTrace();
  231. } finally {
  232. //释放资源
  233. JDBCUtil.release(connection, statement,null);
  234. }
  235. }
  236. ```
  237. #### 更新用户
  238. ```java
  239. /**
  240. * 测试更新用户
  241. */
  242. @Test
  243. public void updateUser() throws Exception {
  244. //创建sql
  245. String sql = "update user set age = ?,gmt_modified = ? where name = ?";
  246. //获得连接
  247. Connection connection = JDBCUtil.getConnection();
  248. PreparedStatement statement = null;
  249. try {
  250. //设置非自动提交
  251. connection.setAutoCommit(false);
  252. //获得Statement对象
  253. statement = connection.prepareStatement(sql);
  254. //设置参数
  255. statement.setInt(1, 19);
  256. statement.setDate(2, new Date(System.currentTimeMillis()));
  257. statement.setString(3, "zzs001");
  258. //执行
  259. statement.executeUpdate();
  260. //提交事务
  261. connection.commit();
  262. } catch (Exception e) {
  263. System.out.println("异常导致操作回滚");
  264. connection.rollback();
  265. e.printStackTrace();
  266. } finally {
  267. //释放资源
  268. JDBCUtil.release(connection, statement,null);
  269. }
  270. }
  271. ```
  272. #### 查询用户
  273. ```java
  274. /**
  275. * 测试查找用户
  276. */
  277. @Test
  278. public void findUser() throws Exception {
  279. //创建sql
  280. String sql = "select * from user where name = ?";
  281. //获得连接
  282. Connection connection = JDBCUtil.getConnection();
  283. PreparedStatement statement = null;
  284. ResultSet resultSet = null;
  285. try {
  286. //获得Statement对象
  287. statement = connection.prepareStatement(sql);
  288. //设置参数
  289. statement.setString(1, "zzs001");
  290. //执行
  291. resultSet = statement.executeQuery();
  292. //遍历结果集
  293. while (resultSet.next()) {
  294. String name = resultSet.getString(2);
  295. int age = resultSet.getInt(3);
  296. System.out.println("用户名:" + name + ",年龄:" + age);
  297. }
  298. } finally {
  299. //释放资源
  300. JDBCUtil.release(connection, statement,resultSet);
  301. }
  302. }
  303. ```
  304. #### 删除用户
  305. ```java
  306. /**
  307. * 测试删除用户
  308. */
  309. @Test
  310. public void deleteUser() throws Exception {
  311. //创建sql
  312. String sql = "delete from user where name = ?";
  313. //获得连接
  314. Connection connection = JDBCUtil.getConnection();
  315. PreparedStatement statement = null;
  316. try {
  317. //设置非自动提交
  318. connection.setAutoCommit(false);
  319. //获得Statement对象
  320. statement = connection.prepareStatement(sql);
  321. //设置参数
  322. statement.setString(1, "zzs001");
  323. //执行
  324. statement.executeUpdate();
  325. //提交事务
  326. connection.commit();
  327. } catch (Exception e) {
  328. System.out.println("异常导致操作回滚");
  329. connection.rollback();
  330. e.printStackTrace();
  331. } finally {
  332. //释放资源
  333. JDBCUtil.release(connection, statement,null);
  334. }
  335. }
  336. ```
  337. ### dbcp配置文件详解
  338. #### 数据库基本配置
  339. ```properties
  340. driverClassName=com.mysql.cj.jdbc.Driver
  341. url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
  342. username=root
  343. password=root
  344. ```
  345. #### 连接数据相关参数
  346. ```properties
  347. #-------------连接数据相关参数--------------------------------
  348. #初始化连接:连接池启动时创建的初始化连接数量
  349. #默认为0
  350. initialSize=0
  351. #最大活动连接
  352. #连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
  353. #默认为8
  354. maxActive=8
  355. #最大空闲连接
  356. #连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
  357. #默认为8
  358. maxIdle=8
  359. #最小空闲连接
  360. #连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
  361. #默认为0
  362. minIdle=0
  363. #最大等待时间
  364. #当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待
  365. #默认无限
  366. maxWait=-1
  367. ```
  368. #### 事务相关的属性
  369. ```properties
  370. #-------------事务相关的属性--------------------------------
  371. #连接池创建的连接的默认的auto-commit状态
  372. #默认为true
  373. defaultAutoCommit=false
  374. #连接池创建的连接的默认的read-only状态. 如果没有设置则setReadOnly方法将不会被调用. (某些驱动不支持只读模式,比如:Informix)
  375. #默认值由驱动决定
  376. #defaultReadOnly=false
  377. #连接池创建的连接的默认的TransactionIsolation状态
  378. #可用值为下列之一:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
  379. #默认值由驱动决定
  380. defaultTransactionIsolation=REPEATABLE_READ
  381. #连接池创建的连接的默认的catalog
  382. #defaultCatalog
  383. ```
  384. #### 连接检查情况
  385. ```properties
  386. #-------------连接检查情况--------------------------------
  387. #SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录
  388. validationQuery= select 1
  389. #指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.
  390. #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
  391. #默认为true
  392. testOnBorrow=true
  393. #指明是否在归还到池中前进行检验
  394. #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
  395. #默认为false
  396. testOnReturn=false
  397. #是否开启空闲资源监测。
  398. #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
  399. #默认为false
  400. testWhileIdle= true
  401. #空闲资源的检测周期(单位为毫秒)。默认-1:不检测。建议设置,周期自行选择。timeBetweenEvictionRunsMillis=30000
  402. #做空闲资源检测时,每次的采样数。默认3。
  403. #可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测。
  404. numTestsPerEvictionRun=3
  405. #资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。
  406. #默认值1000*60*30 = 30分钟。建议默认,或根据自身业务选择。
  407. minEvictableIdleTimeMillis=1800000
  408. ```
  409. #### 缓存语句
  410. ```properties
  411. #-------------缓存语句--------------------------------
  412. #开启池的prepared statement 池功能
  413. #注意: 确认连接还有剩余资源可以留给其他statement
  414. #默认为false
  415. poolPreparedStatements=false
  416. #statement池能够同时分配的打开的statements的最大数量, 如果设置为0表示不限制
  417. #默认为0
  418. maxOpenPreparedStatements=0
  419. ```
  420. #### 连接泄漏回收参数
  421. ```properties
  422. #-------------连接泄漏回收参数--------------------------------
  423. #标记是否删除泄露的连接,如果他们超过了removeAbandonedTimout的限制.
  424. #如果设置为true, 连接被认为是被泄露并且可以被删除,如果空闲时间超过removeAbandonedTimeout.
  425. #设置为true可以为写法糟糕的没有关闭连接的程序修复数据库连接.
  426. #默认为false
  427. removeAbandoned=false
  428. #泄露的连接可以被删除的超时值, 单位秒
  429. #默认为300
  430. removeAbandonedTimeout=300
  431. #标记当Statement或连接被泄露时是否打印程序的stack traces日志。
  432. #被泄露的Statements和连接的日志添加在每个连接打开或者生成新的Statement,因为需要生成stack trace。
  433. #默认为false
  434. logAbandoned=false
  435. #如果开启"removeAbandoned",那么连接在被认为泄露时可能被池回收.
  436. #这个机制在(getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)时被触发.
  437. #举例当maxActive=20, 活动连接为18,空闲连接为1时可以触发"removeAbandoned".
  438. #但是活动连接只有在没有被使用的时间超过"removeAbandonedTimeout"时才被删除,默认300秒.在resultset中游历不被计算为被使用.
  439. ```
  440. #### 其他
  441. ```properties
  442. #-------------其他--------------------------------
  443. #控制PoolGuard是否容许获取底层连接
  444. #默认为false
  445. accessToUnderlyingConnectionAllowed=false
  446. #如果容许则可以使用下面的方式来获取底层物理连接:
  447. # Connection conn = ds.getConnection();
  448. # Connection dconn = ((DelegatingConnection) conn).getInnermostDelegate();
  449. # ...
  450. # conn.close();
  451. ```
  452. > 学习使我快乐!!

No Description

Contributors (1)