1. 数据库连接池

1.1 数据库连接池概念

  • 数据库连接背景
    • 数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
  • 数据库连接池
    • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显提高对数据库操作的性能。
  • 数据库连接池原理

01.png

1.2 自定义连接池

1.2.1 DataSource接口概述

  • java.sql.DataSource接口:数据源(也称为:数据库连接池)。java官方提供的数据库连接池规范(接口)
  • 如果想完成数据库连接池技术,就必须实现DataSource接口
  • 核心功能:获取数据库连接对象:Connection getConnection();

    1.2.2 自定义数据库连接池

    image.png
    项目目录如下:开始就是导入jar包;复制基础篇的配置文件和JDBCUtils工具类;然后创建数据库连接池,其实现DataSource接口,实现方法,方法很多,但重要的只有getConnection;方法体中就是上面的几步,很简单,方法只有两个,重写的只有getConnection,至于这里的集合,用的是线程安全的,为了让ArrayList线程安全,使用了Collections工具类。
    image.png

    1. /*
    2. 自定义连接池类
    3. */
    4. public class MyDataSource implements DataSource{
    5. // 1. 定义集合容器,用于保存多个数据库连接对象
    6. private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
    7. //2. 静态代码块,生成10个数据库连接保存到集合中
    8. static {
    9. for (int i = 0; i < 10; i++) {
    10. Connection con = JDBCUtils.getConnection();
    11. pool.add(con);
    12. }
    13. }
    14. //4. 返回连接池的大小
    15. public int getSize() {
    16. return pool.size();
    17. }
    18. // 3. 从池中返回一个数据库连接
    19. @Override
    20. public Connection getConnection() {
    21. if(pool.size() > 0) {
    22. //从池中获取数据库连接
    23. return pool.remove(0);
    24. }else {
    25. throw new RuntimeException("连接数量已用尽");
    26. }
    27. }
    28. @Override
    29. public Connection getConnection(String username, String password) throws SQLException {
    30. return null;
    31. }
    32. @Override
    33. public <T> T unwrap(Class<T> iface) throws SQLException {
    34. return null;
    35. }
    36. @Override
    37. public boolean isWrapperFor(Class<?> iface) throws SQLException {
    38. return false;
    39. }
    40. @Override
    41. public PrintWriter getLogWriter() throws SQLException {
    42. return null;
    43. }
    44. @Override
    45. public void setLogWriter(PrintWriter out) throws SQLException {
    46. }
    47. @Override
    48. public void setLoginTimeout(int seconds) throws SQLException {
    49. }
    50. @Override
    51. public int getLoginTimeout() throws SQLException {
    52. return 0;
    53. }
    54. @Override
    55. public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    56. return null;
    57. }
    58. }

    1.3 自定义连接池测试

    1. public class MyDataSourceTest {
    2. public static void main(String[] args) throws Exception{
    3. //创建数据库连接池对象
    4. MyDataSource dataSource = new MyDataSource();
    5. System.out.println("使用之前连接池数量:" + dataSource.getSize());
    6. //获取数据库连接对象
    7. Connection con = dataSource.getConnection();
    8. System.out.println(con.getClass());// JDBC4Connection
    9. //查询学生表全部信息
    10. String sql = "SELECT * FROM student";
    11. PreparedStatement pst = con.prepareStatement(sql);
    12. ResultSet rs = pst.executeQuery();
    13. while(rs.next()) {
    14. System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));
    15. }
    16. //释放资源
    17. rs.close();
    18. pst.close();
    19. //目前的连接对象close方法,是直接关闭连接,而不是将连接归还池中
    20. con.close();
    21. System.out.println("使用之后连接池数量:" + dataSource.getSize());
    22. }
    23. }

    1.4 归还连接(4种方式)

    对于上面的数据库连接,是关闭的,而非归还到容器,这显然不是我们想要的。

    继承方式(其实不行)

    继承方式是我们最容易想到的,我们继承这个类,重写方法就可以完成归还。其中连接类是JDBC4Connection。

  1. 继承方式归还数据库连接的思想。
    1. 通过打印连接对象,发现DriverManager获取的链接实现类是JDBC4Connection(上面的System.out.println(con.getClass());// JDBC4Connection)
    2. 那我们就可以自定义一个类,继承JDBC4Connection这个类,重写close()方法,完成连接对象的归还
  2. 继承方式归还数据库连接的实现步骤:

image.png
继承这个类时IDEA提示:必须创建和父类匹配的构造方法!!!这是为什么呢?还是很有意思的,这些Java小知识。
https://blog.csdn.net/qq_35324400/article/details/103633880
image.png

  1. /*
  2. 自定义Connection类
  3. */
  4. public class MyConnection1 extends JDBC4Connection {
  5. //声明连接对象和连接池集合对象
  6. private Connection con;
  7. private List<Connection> pool;
  8. //通过构造方法给成员变量赋值
  9. public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url,Connection con,List<Connection> pool) throws SQLException {
  10. super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
  11. this.con = con;
  12. this.pool = pool;
  13. }
  14. //重写close()方法,将连接归还给池中
  15. @Override
  16. public void close() throws SQLException {
  17. pool.add(con);
  18. }
  19. }
  • 3.但是这种方式行不通,通过查看JDBC工具类获取连接的方法我们发现:我们虽然自定义了一个子类,完成了归还连接的操作。但是DriverManager获取的还是JDBC4Connection这个对象,并不是我们的子类对象。而我们又不能整体去修改驱动包中类的功能! ```java //将之前的连接对象换成自定义的子类对象 private static MyConnection1 con;

//4.获取数据库连接的方法 public static Connection getConnection() { try { //等效于:MyConnection1 con = new JDBC4Connection(); 语法错误!父类指向子类 con = DriverManager.getConnection(url,username,password); } catch (SQLException e) { e.printStackTrace(); }

  1. return con;

}

  1. <a name="wiuHv"></a>
  2. ### 装饰设计模式
  3. > 这个好好学学,很不容易有设计模式的案例
  4. > 这个对于理解装饰设计模式很有用
  5. > 所谓装饰,无非是装饰一个类的功能,想更换或者增强功能
  6. > 比如I接口有A的实现类,原本肯定是在哪里用这个A实现类
  7. > 但是呢?我现在觉得A实现类的某个方法不满足我们的需求,但是其它方法满足
  8. > 怎么办???
  9. > 搞一个B类,实现I接口,然后用构造方法将A的实例传过来,对其进行包装。
  10. > 对于不需要的方法,换一下,其它方法,就用A对象的方法。
  11. > 然后调用A对象的地方呢?包装成B对象返回即可。
  12. > 缺点??
  13. > 可能有大量的方法需要在自定义类中进行重写
  14. 1.装饰设计模式归还数据库连接的思想。
  15. - 我们可以自定义一个类,实现Connection接口。这样就具备了和JDBC4Connection相同的行为了(即相同方法)
  16. - 重写close()方法,完成连接的归还。其余的功能还调用mysql驱动包实现类原有的方法即可。
  17. 2.装饰设计模式归还数据库连接的实现步骤<br />1.自定义一个类,实现Connection接口<br />2.定义Connection连接对象和连接池容器<br />3.通过有参构造完成对成员变量的赋值<br />4.重写close()方法,将连接对象添加到池中<br />5.剩余方法,只需要调用mysql驱动包的连接对象完成即可<br />6.在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。
  18. ```java
  19. /*
  20. 自定义Connection类。通过装饰设计模式,实现和mysql驱动包中的Connection实现类相同的功能!
  21. 实现步骤:
  22. 1.定义一个类,实现Connection接口
  23. 2.定义Connection连接对象和连接池容器对象的变量
  24. 3.提供有参构造方法,接收连接对象和连接池对象,对变量赋值
  25. 4.在close()方法中,完成连接的归还
  26. 5.剩余方法,只需要调用mysql驱动包的连接对象完成即可
  27. */
  28. public class MyConnection2 implements Connection {
  29. //2.定义Connection连接对象和连接池容器对象的变量
  30. private Connection con;
  31. private List<Connection> pool;
  32. //3.提供有参构造方法,接收连接对象和连接池对象,对变量赋值
  33. public MyConnection2(Connection con,List<Connection> pool) {
  34. this.con = con;
  35. this.pool = pool;
  36. }
  37. //4.在close()方法中,完成连接的归还
  38. @Override
  39. public void close() throws SQLException {
  40. pool.add(con);
  41. }
  42. // 5.剩余方法,只需要调用mysql驱动包的连接对象完成即可
  43. @Override
  44. public Statement createStatement() throws SQLException {
  45. return con.createStatement();
  46. }
  47. @Override
  48. public PreparedStatement prepareStatement(String sql) throws SQLException {
  49. return con.prepareStatement(sql);
  50. }
  51. @Override
  52. public CallableStatement prepareCall(String sql) throws SQLException {
  53. return con.prepareCall(sql);
  54. }
  55. @Override
  56. public String nativeSQL(String sql) throws SQLException {
  57. return con.nativeSQL(sql);
  58. }
  59. @Override
  60. public void setAutoCommit(boolean autoCommit) throws SQLException {
  61. con.setAutoCommit(autoCommit);
  62. }
  63. @Override
  64. public boolean getAutoCommit() throws SQLException {
  65. return con.getAutoCommit();
  66. }
  67. @Override
  68. public void commit() throws SQLException {
  69. con.commit();
  70. }
  71. @Override
  72. public void rollback() throws SQLException {
  73. con.rollback();
  74. }
  75. @Override
  76. public boolean isClosed() throws SQLException {
  77. return con.isClosed();
  78. }
  79. @Override
  80. public DatabaseMetaData getMetaData() throws SQLException {
  81. return con.getMetaData();
  82. }
  83. @Override
  84. public void setReadOnly(boolean readOnly) throws SQLException {
  85. con.setReadOnly(readOnly);
  86. }
  87. @Override
  88. public boolean isReadOnly() throws SQLException {
  89. return con.isReadOnly();
  90. }
  91. @Override
  92. public void setCatalog(String catalog) throws SQLException {
  93. con.setCatalog(catalog);
  94. }
  95. @Override
  96. public String getCatalog() throws SQLException {
  97. return con.getCatalog();
  98. }
  99. @Override
  100. public void setTransactionIsolation(int level) throws SQLException {
  101. con.setTransactionIsolation(level);
  102. }
  103. @Override
  104. public int getTransactionIsolation() throws SQLException {
  105. return con.getTransactionIsolation();
  106. }
  107. @Override
  108. public SQLWarning getWarnings() throws SQLException {
  109. return con.getWarnings();
  110. }
  111. @Override
  112. public void clearWarnings() throws SQLException {
  113. con.clearWarnings();
  114. }
  115. @Override
  116. public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
  117. return con.createStatement(resultSetType,resultSetConcurrency);
  118. }
  119. @Override
  120. public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  121. return con.prepareStatement(sql,resultSetType,resultSetConcurrency);
  122. }
  123. @Override
  124. public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  125. return con.prepareCall(sql,resultSetType,resultSetConcurrency);
  126. }
  127. @Override
  128. public Map<String, Class<?>> getTypeMap() throws SQLException {
  129. return con.getTypeMap();
  130. }
  131. @Override
  132. public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
  133. con.setTypeMap(map);
  134. }
  135. @Override
  136. public void setHoldability(int holdability) throws SQLException {
  137. con.setHoldability(holdability);
  138. }
  139. @Override
  140. public int getHoldability() throws SQLException {
  141. return con.getHoldability();
  142. }
  143. @Override
  144. public Savepoint setSavepoint() throws SQLException {
  145. return con.setSavepoint();
  146. }
  147. @Override
  148. public Savepoint setSavepoint(String name) throws SQLException {
  149. return con.setSavepoint(name);
  150. }
  151. @Override
  152. public void rollback(Savepoint savepoint) throws SQLException {
  153. con.rollback(savepoint);
  154. }
  155. @Override
  156. public void releaseSavepoint(Savepoint savepoint) throws SQLException {
  157. con.releaseSavepoint(savepoint);
  158. }
  159. @Override
  160. public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  161. return con.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
  162. }
  163. @Override
  164. public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  165. return con.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
  166. }
  167. @Override
  168. public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  169. return con.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
  170. }
  171. @Override
  172. public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
  173. return con.prepareStatement(sql,autoGeneratedKeys);
  174. }
  175. @Override
  176. public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
  177. return con.prepareStatement(sql,columnIndexes);
  178. }
  179. @Override
  180. public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
  181. return con.prepareStatement(sql,columnNames);
  182. }
  183. @Override
  184. public Clob createClob() throws SQLException {
  185. return con.createClob();
  186. }
  187. @Override
  188. public Blob createBlob() throws SQLException {
  189. return con.createBlob();
  190. }
  191. @Override
  192. public NClob createNClob() throws SQLException {
  193. return con.createNClob();
  194. }
  195. @Override
  196. public SQLXML createSQLXML() throws SQLException {
  197. return con.createSQLXML();
  198. }
  199. @Override
  200. public boolean isValid(int timeout) throws SQLException {
  201. return con.isValid(timeout);
  202. }
  203. @Override
  204. public void setClientInfo(String name, String value) throws SQLClientInfoException {
  205. con.setClientInfo(name,value);
  206. }
  207. @Override
  208. public void setClientInfo(Properties properties) throws SQLClientInfoException {
  209. con.setClientInfo(properties);
  210. }
  211. @Override
  212. public String getClientInfo(String name) throws SQLException {
  213. return con.getClientInfo(name);
  214. }
  215. @Override
  216. public Properties getClientInfo() throws SQLException {
  217. return con.getClientInfo();
  218. }
  219. @Override
  220. public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
  221. return con.createArrayOf(typeName,elements);
  222. }
  223. @Override
  224. public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
  225. return con.createStruct(typeName,attributes);
  226. }
  227. @Override
  228. public void setSchema(String schema) throws SQLException {
  229. con.setSchema(schema);
  230. }
  231. @Override
  232. public String getSchema() throws SQLException {
  233. return con.getSchema();
  234. }
  235. @Override
  236. public void abort(Executor executor) throws SQLException {
  237. con.abort(executor);
  238. }
  239. @Override
  240. public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
  241. con.setNetworkTimeout(executor,milliseconds);
  242. }
  243. @Override
  244. public int getNetworkTimeout() throws SQLException {
  245. return con.getNetworkTimeout();
  246. }
  247. @Override
  248. public <T> T unwrap(Class<T> iface) throws SQLException {
  249. return con.unwrap(iface);
  250. }
  251. @Override
  252. public boolean isWrapperFor(Class<?> iface) throws SQLException {
  253. return con.isWrapperFor(iface);
  254. }
  255. }
  • 自定义连接池类

这里只是更改了这里!!!
image.png

  1. public class MyDataSource implements DataSource{
  2. //定义集合容器,用于保存多个数据库连接对象
  3. private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
  4. //静态代码块,生成10个数据库连接保存到集合中
  5. static {
  6. for (int i = 0; i < 10; i++) {
  7. Connection con = JDBCUtils.getConnection();
  8. pool.add(con);
  9. }
  10. }
  11. //返回连接池的大小
  12. public int getSize() {
  13. return pool.size();
  14. }
  15. //从池中返回一个数据库连接
  16. @Override
  17. public Connection getConnection() {
  18. if(pool.size() > 0) {
  19. //从池中获取数据库连接
  20. Connection con = pool.remove(0);
  21. //通过自定义连接对象进行包装
  22. MyConnection2 mycon = new MyConnection2(con,pool);
  23. //返回包装后的连接对象
  24. return mycon;
  25. }else {
  26. throw new RuntimeException("连接数量已用尽");
  27. }
  28. }
  29. }

3.装饰设计模式归还数据库连接存在的问题。

  • 实现Connection接口之后,有大量的方法需要在自定义类中进行重写。

    适配器设计模式

    这个可以解决上面的缺点!! 这个适配器类只是一个中间类,对于其它方法,全部还是用JDBC4Connection类的方法。 首先还是I接口,A实现类,但是我们不想要A中的一个方法,其它方法满足。A对象在一处被使用。 这个中间类自然要实现Connection接口,然后通过构造方法传入A对象,所有方法都用A对象的,除了不想要的(这个我们也不重写),因此该中间类需要是抽象方法。 下面就是定义一个类,其实是和A同等级别的,实现这个中间类,重写不想要的方法即可。至于构造传参什么就不需要说的 至于使用,和上面的一样,就是在连接池中对原有的连接对象进行包装即可。 适配器模式问题??? 虽然自定义连接类很简单,但是适配器是我们写的,还是很麻烦!! 甚至有点换汤不换药。当然如果你有好几个close方法,那么用适配器模式的话,想要保留的方法在这里只需要写一次即可。

1.适配器设计模式归还数据库连接的思想

  • 我们可以提供一个适配器类,实现Connection接口,将所有方法进行实现(除了close方法)
  • 自定义连接类只需要继承这个适配器类,重写需要改进的close()方法即可

2.适配器设计模式归还数据库连接的实现步骤。
1.定义一个适配器,实现Connectio接口。
2.定义Connection连接对象的成员变量。
3.通过有参构造方法完成对成员变量的赋值。
4.重写所有方法(除了close),调用mysql驱动包的链接对象完成即可。
5.定义一个连接池,继承适配器类。
6.定义Connection链接对象和连接池对象的成员变量,并通过有参构造进行赋值。
7.重写close()方法,完成归还连接。
8.在自定义连接池中,将获取的链接对象通过自定义连接对象进行包装。

  1. /*
  2. 适配器抽象类。实现Connection接口。
  3. 实现所有的方法,调用mysql驱动包中Connection连接对象的方法
  4. */
  5. public abstract class MyAdapter implements Connection {
  6. // 定义数据库连接对象的变量
  7. private Connection con;
  8. // 通过构造方法赋值
  9. public MyAdapter(Connection con) {
  10. this.con = con;
  11. }
  12. // 所有的方法,均调用mysql的连接对象实现
  13. @Override
  14. public Statement createStatement() throws SQLException {
  15. return con.createStatement();
  16. }
  17. @Override
  18. public PreparedStatement prepareStatement(String sql) throws SQLException {
  19. return con.prepareStatement(sql);
  20. }
  21. @Override
  22. public CallableStatement prepareCall(String sql) throws SQLException {
  23. return con.prepareCall(sql);
  24. }
  25. @Override
  26. public String nativeSQL(String sql) throws SQLException {
  27. return con.nativeSQL(sql);
  28. }
  29. @Override
  30. public void setAutoCommit(boolean autoCommit) throws SQLException {
  31. con.setAutoCommit(autoCommit);
  32. }
  33. @Override
  34. public boolean getAutoCommit() throws SQLException {
  35. return con.getAutoCommit();
  36. }
  37. @Override
  38. public void commit() throws SQLException {
  39. con.commit();
  40. }
  41. @Override
  42. public void rollback() throws SQLException {
  43. con.rollback();
  44. }
  45. @Override
  46. public boolean isClosed() throws SQLException {
  47. return con.isClosed();
  48. }
  49. @Override
  50. public DatabaseMetaData getMetaData() throws SQLException {
  51. return con.getMetaData();
  52. }
  53. @Override
  54. public void setReadOnly(boolean readOnly) throws SQLException {
  55. con.setReadOnly(readOnly);
  56. }
  57. @Override
  58. public boolean isReadOnly() throws SQLException {
  59. return con.isReadOnly();
  60. }
  61. @Override
  62. public void setCatalog(String catalog) throws SQLException {
  63. con.setCatalog(catalog);
  64. }
  65. @Override
  66. public String getCatalog() throws SQLException {
  67. return con.getCatalog();
  68. }
  69. @Override
  70. public void setTransactionIsolation(int level) throws SQLException {
  71. con.setTransactionIsolation(level);
  72. }
  73. @Override
  74. public int getTransactionIsolation() throws SQLException {
  75. return con.getTransactionIsolation();
  76. }
  77. @Override
  78. public SQLWarning getWarnings() throws SQLException {
  79. return con.getWarnings();
  80. }
  81. @Override
  82. public void clearWarnings() throws SQLException {
  83. con.clearWarnings();
  84. }
  85. @Override
  86. public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
  87. return con.createStatement(resultSetType,resultSetConcurrency);
  88. }
  89. @Override
  90. public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  91. return con.prepareStatement(sql,resultSetType,resultSetConcurrency);
  92. }
  93. @Override
  94. public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
  95. return con.prepareCall(sql,resultSetType,resultSetConcurrency);
  96. }
  97. @Override
  98. public Map<String, Class<?>> getTypeMap() throws SQLException {
  99. return con.getTypeMap();
  100. }
  101. @Override
  102. public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
  103. con.setTypeMap(map);
  104. }
  105. @Override
  106. public void setHoldability(int holdability) throws SQLException {
  107. con.setHoldability(holdability);
  108. }
  109. @Override
  110. public int getHoldability() throws SQLException {
  111. return con.getHoldability();
  112. }
  113. @Override
  114. public Savepoint setSavepoint() throws SQLException {
  115. return con.setSavepoint();
  116. }
  117. @Override
  118. public Savepoint setSavepoint(String name) throws SQLException {
  119. return con.setSavepoint(name);
  120. }
  121. @Override
  122. public void rollback(Savepoint savepoint) throws SQLException {
  123. con.rollback(savepoint);
  124. }
  125. @Override
  126. public void releaseSavepoint(Savepoint savepoint) throws SQLException {
  127. con.releaseSavepoint(savepoint);
  128. }
  129. @Override
  130. public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  131. return con.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
  132. }
  133. @Override
  134. public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  135. return con.prepareStatement(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
  136. }
  137. @Override
  138. public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
  139. return con.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
  140. }
  141. @Override
  142. public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
  143. return con.prepareStatement(sql,autoGeneratedKeys);
  144. }
  145. @Override
  146. public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
  147. return con.prepareStatement(sql,columnIndexes);
  148. }
  149. @Override
  150. public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
  151. return con.prepareStatement(sql,columnNames);
  152. }
  153. @Override
  154. public Clob createClob() throws SQLException {
  155. return con.createClob();
  156. }
  157. @Override
  158. public Blob createBlob() throws SQLException {
  159. return con.createBlob();
  160. }
  161. @Override
  162. public NClob createNClob() throws SQLException {
  163. return con.createNClob();
  164. }
  165. @Override
  166. public SQLXML createSQLXML() throws SQLException {
  167. return con.createSQLXML();
  168. }
  169. @Override
  170. public boolean isValid(int timeout) throws SQLException {
  171. return con.isValid(timeout);
  172. }
  173. @Override
  174. public void setClientInfo(String name, String value) throws SQLClientInfoException {
  175. con.setClientInfo(name,value);
  176. }
  177. @Override
  178. public void setClientInfo(Properties properties) throws SQLClientInfoException {
  179. con.setClientInfo(properties);
  180. }
  181. @Override
  182. public String getClientInfo(String name) throws SQLException {
  183. return con.getClientInfo(name);
  184. }
  185. @Override
  186. public Properties getClientInfo() throws SQLException {
  187. return con.getClientInfo();
  188. }
  189. @Override
  190. public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
  191. return con.createArrayOf(typeName,elements);
  192. }
  193. @Override
  194. public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
  195. return con.createStruct(typeName,attributes);
  196. }
  197. @Override
  198. public void setSchema(String schema) throws SQLException {
  199. con.setSchema(schema);
  200. }
  201. @Override
  202. public String getSchema() throws SQLException {
  203. return con.getSchema();
  204. }
  205. @Override
  206. public void abort(Executor executor) throws SQLException {
  207. con.abort(executor);
  208. }
  209. @Override
  210. public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
  211. con.setNetworkTimeout(executor,milliseconds);
  212. }
  213. @Override
  214. public int getNetworkTimeout() throws SQLException {
  215. return con.getNetworkTimeout();
  216. }
  217. @Override
  218. public <T> T unwrap(Class<T> iface) throws SQLException {
  219. return con.unwrap(iface);
  220. }
  221. @Override
  222. public boolean isWrapperFor(Class<?> iface) throws SQLException {
  223. return con.isWrapperFor(iface);
  224. }
  225. }
  • 自定义连接类

    1. /*
    2. 自定义Connection连接类。通过适配器设计模式。完成close()方法的重写
    3. 1.定义一个类,继承适配器父类
    4. 2.定义Connection连接对象和连接池容器对象的变量
    5. 3.提供有参构造方法,接收连接对象和连接池对象,对变量赋值
    6. 4.在close()方法中,完成连接的归还
    7. */
    8. public class MyConnection3 extends MyAdapter {
    9. //2.定义Connection连接对象和连接池容器对象的变量
    10. private Connection con;
    11. private List<Connection> pool;
    12. //3.提供有参构造方法,接收连接对象和连接池对象,对变量赋值
    13. public MyConnection3(Connection con,List<Connection> pool) {
    14. super(con); // 将接收的数据库连接对象给适配器父类传递
    15. this.con = con;
    16. this.pool = pool;
    17. }
    18. //4.在close()方法中,完成连接的归还
    19. @Override
    20. public void close() throws SQLException {
    21. pool.add(con);
    22. }
    23. }
  • 自定义连接池类

使用时,也只是包装
image.png

  1. public class MyDataSource implements DataSource{
  2. //定义集合容器,用于保存多个数据库连接对象
  3. private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
  4. //静态代码块,生成10个数据库连接保存到集合中
  5. static {
  6. for (int i = 0; i < 10; i++) {
  7. Connection con = JDBCUtils.getConnection();
  8. pool.add(con);
  9. }
  10. }
  11. //返回连接池的大小
  12. public int getSize() {
  13. return pool.size();
  14. }
  15. //从池中返回一个数据库连接
  16. @Override
  17. public Connection getConnection() {
  18. if(pool.size() > 0) {
  19. //从池中获取数据库连接
  20. Connection con = pool.remove(0);
  21. //通过自定义连接对象进行包装
  22. //MyConnection2 mycon = new MyConnection2(con,pool);
  23. MyConnection3 mycon = new MyConnection3(con,pool);
  24. //返回包装后的连接对象
  25. return mycon;
  26. }else {
  27. throw new RuntimeException("连接数量已用尽");
  28. }
  29. }
  30. }

3.适配器设计模式归还数据库连接存在的问题。

  • 自定义连接类虽然很简洁了,但适配器类还是我们自己编写的,也比较的麻烦

    动态代理

    动态代理学习

    印子: ```java // 这个是一个类 public class Student { public void eat(String name) {

    1. System.out.println("学生吃" + name);

    }

    public void study() {

    1. System.out.println("在家自学");

    } }

// 这里是使用这个类 public class Test { public static void main(String[] args) { Student stu = new Student(); stu.eat(“米饭”); stu.study(); } }

  1. 但是我们现在有一个很不合理的要求!!!
  2. ```java
  3. /*
  4. * 要求:在不改变Study类中任何代码的前提下,通过study方法输出一句话:来黑马学习
  5. * */

动态代理介绍:

  • 动态代理:在不改变目标对象方法的情况下对方法进行增强
  • 组成

被代理对象:真实的对象
代理对象:内存中的一个对象

  • 要求

代理对象必须和被代理对象实现相同的接口

  • 实现

Proxy.newProxyInstance()
首先要有接口

  1. public interface StudentInterface {
  2. void eat(String name);
  3. void study();
  4. }
  5. public class Student implements StudentInterface {
  6. public void eat(String name) {
  7. System.out.println("学生吃" + name);
  8. }
  9. public void study() {
  10. System.out.println("在家自学");
  11. }
  12. }

然后直接在使用处代理该对象

  1. public class Test {
  2. public static void main(String[] args) {
  3. Student stu = new Student();
  4. /*
  5. * 要求:在不改变Study类中任何代码的前提下,通过study方法输出一句话:来黑马学习
  6. * */
  7. StudentInterface si = (StudentInterface) Proxy.newProxyInstance(stu.getClass().getClassLoader(), new Class[]{StudentInterface.class}, new InvocationHandler() {
  8. /*
  9. * 执行Student类中所有的方法都会经过invoke方法
  10. * 对method方法进行判断
  11. * 如果是study,则对其增强
  12. * 如果不是,还调用写生对象原有的功能即可
  13. * */
  14. @Override
  15. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  16. if (method.getName().equals("study")) {
  17. System.out.println("来黑马学习");
  18. return null;
  19. } else {
  20. return method.invoke(stu, args);
  21. }
  22. }
  23. });
  24. si.eat("米饭");
  25. si.study();
  26. }
  27. }

动态代理实现连接归还

动态代理非常简单,直接在使用处修改即可。

  1. public class MyDataSource implements DataSource{
  2. //定义集合容器,用于保存多个数据库连接对象
  3. private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
  4. //静态代码块,生成10个数据库连接保存到集合中
  5. static {
  6. for (int i = 0; i < 10; i++) {
  7. Connection con = JDBCUtils.getConnection();
  8. pool.add(con);
  9. }
  10. }
  11. //返回连接池的大小
  12. public int getSize() {
  13. return pool.size();
  14. }
  15. //动态代理方式
  16. @Override
  17. public Connection getConnection() {
  18. if(pool.size() > 0) {
  19. //从池中获取数据库连接
  20. Connection con = pool.remove(0);
  21. Connection proxyCon = (Connection)Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
  22. /*
  23. 执行Connection实现类所有方法都会经过invoke
  24. 如果是close方法,则将连接还回池中
  25. 如果不是,直接执行实现类的原有方法
  26. */
  27. @Override
  28. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  29. if(method.getName().equals("close")) {
  30. pool.add(con);
  31. return null;
  32. }else {
  33. return method.invoke(con,args);
  34. }
  35. }
  36. });
  37. return proxyCon;
  38. }else {
  39. throw new RuntimeException("连接数量已用尽");
  40. }
  41. }
  42. //从池中返回一个数据库连接
  43. /*@Override
  44. public Connection getConnection() {
  45. if(pool.size() > 0) {
  46. //从池中获取数据库连接
  47. Connection con = pool.remove(0);
  48. //通过自定义连接对象进行包装
  49. //MyConnection2 mycon = new MyConnection2(con,pool);
  50. MyConnection3 mycon = new MyConnection3(con,pool);
  51. //返回包装后的连接对象
  52. return mycon;
  53. }else {
  54. throw new RuntimeException("连接数量已用尽");
  55. }
  56. }*/
  57. }

1.5 开源连接池的使用

C3P0

1.C3P0数据库连接池的使用步骤。
1.导入ja包(需要两个)
2.导入配置文件到src目录下(最好是这个目录,因为是C3P0默认目录,会到这里查找配置文件)
3.创建C3P0连接池对象
4.获取数据库连接进行使用
注意:C3P0的配置文件会自动加载,但是必须叫c3p0-config.xml或c3p0-config.properties
image.png
配置文件

  1. <c3p0-config>
  2. <!-- 使用默认的配置读取连接池对象 -->
  3. <default-config>
  4. <!-- 连接参数 -->
  5. <property name="driverClass">com.mysql.jdbc.Driver</property>
  6. <property name="jdbcUrl">jdbc:mysql://192.168.59.129:3306/db14</property>
  7. <property name="user">root</property>
  8. <property name="password">root</property>
  9. <!-- 连接池参数 -->
  10. <!-- 初始化的连接数量-->
  11. <property name="initialPoolSize">5</property>
  12. <!-- 最大连接数量-->
  13. <property name="maxPoolSize">10</property>
  14. <!-- 超时时间,当你用完10个,想要用第十一个的时候,会等待三秒钟,超过时间,就会告诉你,用完了-->
  15. <property name="checkoutTimeout">3000</property>
  16. </default-config>
  17. <!-- 这个是指定一个名称,如果你不指定,则C3P0创建连接池用上面的默认配置-->
  18. <!-- 如果指定了名称,则用这里的配置-->
  19. <named-config name="otherc3p0">
  20. <!-- 连接参数 -->
  21. <property name="driverClass">com.mysql.jdbc.Driver</property>
  22. <property name="jdbcUrl">jdbc:mysql://localhost:3306/db15</property>
  23. <property name="user">root</property>
  24. <property name="password">root</property>
  25. <!-- 连接池参数 -->
  26. <property name="initialPoolSize">5</property>
  27. <property name="maxPoolSize">8</property>
  28. <property name="checkoutTimeout">1000</property>
  29. </named-config>
  30. </c3p0-config>

直接使用

  1. /*
  2. 使用C3P0连接池
  3. 1.导入jar包
  4. 2.导入配置文件到src目录下
  5. 3.创建c3p0连接池对象
  6. 4.获取数据库连接进行使用
  7. */
  8. public class C3P0Demo1 {
  9. public static void main(String[] args) throws Exception{
  10. //创建c3p0连接池对象 用空参构造,则是用的默认配置
  11. DataSource dataSource = new ComboPooledDataSource();
  12. //获取数据库连接进行使用
  13. Connection con = dataSource.getConnection();
  14. //查询全部学生信息
  15. String sql = "SELECT * FROM student";
  16. PreparedStatement pst = con.prepareStatement(sql);
  17. ResultSet rs = pst.executeQuery();
  18. while(rs.next()) {
  19. System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));
  20. }
  21. //释放资源
  22. rs.close();
  23. pst.close();
  24. con.close(); // 将连接对象归还池中
  25. }
  26. }

配置演示,能拿到10个连接。如果要拿11个,3s钟后报错。close方法是归还

  1. public class C3P0Demo2 {
  2. public static void main(String[] args) throws Exception{
  3. //创建c3p0连接池对象
  4. DataSource dataSource = new ComboPooledDataSource();
  5. //获取数据库连接进行使用
  6. for(int i = 1; i <= 11; i++) {
  7. Connection con = dataSource.getConnection();
  8. System.out.println(i + ":" + con);
  9. if(i == 5) {
  10. con.close();
  11. }
  12. }
  13. }
  14. }

Druid

Druid使用

  • Druid

    • 基本使用

      1. /*
      2. Druid连接池
      3. 1.导入jar包(1个jar包)
      4. 2.编写配置文件,放在src目录下
      5. 3.通过Properties集合加载配置文件(这个不会自动加载配置文件,需要自己读取)
      6. 4.通过Druid连接池工厂类获取数据库连接池对象
      7. 5.获取数据库连接,进行使用
      8. 注意:Druid不会自动加载配置文件,需要我们手动加载,但是文件的名称可以自定义。
      9. */
      10. public class DruidDemo1 {
      11. public static void main(String[] args) throws Exception{
      12. //通过Properties集合加载配置文件
      13. InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
      14. Properties prop = new Properties();
      15. prop.load(is);
      16. //通过Druid连接池工厂类获取数据库连接池对象
      17. DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
      18. //获取数据库连接,进行使用
      19. Connection con = dataSource.getConnection();
      20. //查询全部学生信息
      21. String sql = "SELECT * FROM student";
      22. PreparedStatement pst = con.prepareStatement(sql);
      23. ResultSet rs = pst.executeQuery();
      24. while(rs.next()) {
      25. System.out.println(rs.getInt("sid") + "\t" + rs.getString("name") + "\t" + rs.getInt("age") + "\t" + rs.getDate("birthday"));
      26. }
      27. //释放资源
      28. rs.close();
      29. pst.close();
      30. con.close(); // 将连接对象归还池中
      31. }
      32. }

      自定义数据库连接池工具类

      ```java / 数据库连接池工具类 / public class DataSourceUtils { //1.私有构造方法 private DataSourceUtils(){}

      //2.定义DataSource数据源变量 private static DataSource dataSource;

      //3.提供静态代码块,完成配置文件的加载和获取连接池对象 static { try{

      1. //加载配置文件
      2. InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
      3. Properties prop = new Properties();
      4. prop.load(is);
      5. //获取数据库连接池对象
      6. dataSource = DruidDataSourceFactory.createDataSource(prop);

      } catch(Exception e) {

      1. e.printStackTrace();

      } }

      //4.提供获取数据库连接的方法 public static Connection getConnection() { Connection con = null; try {

      1. con = dataSource.getConnection();

      } catch (SQLException e) {

      1. e.printStackTrace();

      } return con; }

      //5.提供获取数据库连接池的方法(为什么要有提供连接池的方法,因为后面我们自定义框架需要连接池对象) public static DataSource getDataSource() { return dataSource; }

      //6.提供释放资源的方法 public static void close(Connection con, Statement stat, ResultSet rs) { if(con != null) {

      1. try {
      2. con.close();
      3. } catch (SQLException e) {
      4. e.printStackTrace();
      5. }

      }

      if(stat != null) {

      1. try {
      2. stat.close();
      3. } catch (SQLException e) {
      4. e.printStackTrace();
      5. }

      }

      if(rs != null) {

      1. try {
      2. rs.close();
      3. } catch (SQLException e) {
      4. e.printStackTrace();
      5. }

      } }

      // 后期增删改,没有rs对象,即结果集对象 public static void close(Connection con, Statement stat) { close(con,stat,null); }

}

  1. 测试工具类略!
  2. <a name="jIjYV"></a>
  3. # 2. 自定义JDBC框架(JDBCTemplate)——这里视频没有看
  4. 后面我们要学习JDBC框架了,其就是对JDBC的封装,我们提前先自己定义一个。
  5. <a name="VSzst"></a>
  6. ## 2.1 分析前一天案例中的重复代码
  7. - dao层的重复代码
  8. - 定义必要的信息、获取数据库的连接、释放资源都是重复的代码!
  9. - 而我们最终的核心功能仅仅只是执行一条sql语句而已啊!
  10. - 所以我们可以抽取出一个JDBC模板类,来封装一些方法(updatequery),专门帮我们执行增删改查的sql语句!
  11. - 将之前那些重复的操作,都抽取到模板类中的方法里。就能大大简化我们的使用步骤!
  12. <a name="w1nlS"></a>
  13. ## 2.2 自定义JDBC框架
  14. <a name="S4aTL"></a>
  15. ### 2.2.1数据库的源信息
  16. - DataBaseMetaData(了解):数据库的源信息
  17. - java.sql.DataBaseMetaData:封装了整个数据库的综合信息
  18. - 例如:
  19. - String getDatabaseProductName():获取数据库产品的名称
  20. - int getDatabaseProductVersion():获取数据库产品的版本号
  21. - ParameterMetaData:参数的源信息
  22. - java.sql.ParameterMetaData:封装的是预编译执行者对象中每个参数的类型和属性
  23. - 这个对象可以通过预编译执行者对象中的getParameterMetaData()方法来获取
  24. - 核心功能:
  25. - int getParameterCount():获取sql语句中参数的个数
  26. - ResultSetMetaData:结果集的源信息
  27. - java.sql.ResultSetMetaData:封装的是结果集对象中列的类型和属性
  28. - 这个对象可以通过结果集对象中的getMetaData()方法来获取
  29. - 核心功能:
  30. - int getColumnCount():获取列的总数
  31. - String getColumnName(int i):获取列名
  32. <a name="Dnaub"></a>
  33. ### 2.2.2JDBCTemplate类增删改功能的编写
  34. ```java
  35. public class JDBCTemplate {
  36. private DataSource dataSource;
  37. private Connection con;
  38. private PreparedStatement pst;
  39. private ResultSet rs;
  40. public JDBCTemplate(DataSource dataSource) {
  41. this.dataSource = dataSource;
  42. }
  43. //专用于执行增删改sql语句的方法
  44. public int update(String sql,Object...objs) {
  45. int result = 0;
  46. try{
  47. con = dataSource.getConnection();
  48. pst = con.prepareStatement(sql);
  49. //获取sql语句中的参数源信息
  50. ParameterMetaData pData = pst.getParameterMetaData();
  51. //获取sql语句中参数的个数
  52. int parameterCount = pData.getParameterCount();
  53. //判断参数个数是否一致
  54. if(parameterCount != objs.length) {
  55. throw new RuntimeException("参数个数不匹配");
  56. }
  57. //为sql语句中的?占位符赋值
  58. for (int i = 0; i < objs.length; i++) {
  59. pst.setObject(i+1,objs[i]);
  60. }
  61. //执行sql语句
  62. result = pst.executeUpdate();
  63. } catch(Exception e) {
  64. e.printStackTrace();
  65. } finally {
  66. //释放资源
  67. DataSourceUtils.close(con,pst);
  68. }
  69. //返回结果
  70. return result;
  71. }
  72. }

2.2.3JDBCTemplate类查询功能的编写

  • 实体类

    1. /*
    2. 学生实体类
    3. */
    4. public class Student {
    5. private Integer sid;
    6. private String name;
    7. private Integer age;
    8. private Date birthday;
    9. public Student() {
    10. }
    11. public Student(Integer sid, String name, Integer age, Date birthday) {
    12. this.sid = sid;
    13. this.name = name;
    14. this.age = age;
    15. this.birthday = birthday;
    16. }
    17. public Integer getSid() {
    18. return sid;
    19. }
    20. public void setSid(Integer sid) {
    21. this.sid = sid;
    22. }
    23. public String getName() {
    24. return name;
    25. }
    26. public void setName(String name) {
    27. this.name = name;
    28. }
    29. public Integer getAge() {
    30. return age;
    31. }
    32. public void setAge(Integer age) {
    33. this.age = age;
    34. }
    35. public Date getBirthday() {
    36. return birthday;
    37. }
    38. public void setBirthday(Date birthday) {
    39. this.birthday = birthday;
    40. }
    41. @Override
    42. public String toString() {
    43. return "Student{" +
    44. "sid=" + sid +
    45. ", name='" + name + '\'' +
    46. ", age=" + age +
    47. ", birthday=" + birthday +
    48. '}';
    49. }
    50. }
  • ResultSetHandler接口

    1. /*
    2. 用于处理结果集的接口
    3. */
    4. public interface ResultSetHandler<T> {
    5. //处理结果集的抽象方法。
    6. <T> T handler(ResultSet rs);
    7. }
  • BeanHandler实现类

    1. /*
    2. 实现类1:用于完成将查询出来的一条记录,封装到Student对象中
    3. */
    4. public class BeanHandler<T> implements ResultSetHandler<T> {
    5. //1.声明对象类型变量
    6. private Class<T> beanClass;
    7. //2.有参构造对变量赋值
    8. public BeanHandler(Class<T> beanClass) {
    9. this.beanClass = beanClass;
    10. }
    11. /*
    12. 将ResultSet结果集中的数据封装到beanClass类型对象中
    13. */
    14. @Override
    15. public T handler(ResultSet rs) {
    16. //3.声明对象
    17. T bean = null;
    18. try{
    19. //4.创建传递参数的对象
    20. bean = beanClass.newInstance();
    21. //5.判断是否有结果集
    22. if(rs.next()) {
    23. //6.得到所有的列名
    24. //6.1先得到结果集的源信息
    25. ResultSetMetaData rsmd = rs.getMetaData();
    26. //6.2还要得到有多少列
    27. int columnCount = rsmd.getColumnCount();
    28. //6.3遍历列数
    29. for(int i = 1; i <= columnCount; i++) {
    30. //6.4得到每列的列名
    31. String columnName = rsmd.getColumnName(i);
    32. //6.5通过列名获取数据
    33. Object columnValue = rs.getObject(columnName);
    34. //6.6列名其实就是对象中成员变量的名称。于是就可以使用列名得到对象中属性的描述器(get和set方法)
    35. PropertyDescriptor pd = new PropertyDescriptor(columnName.toLowerCase(),beanClass);
    36. //6.7获取set方法
    37. Method writeMethod = pd.getWriteMethod();
    38. //6.8执行set方法,给成员变量赋值
    39. writeMethod.invoke(bean,columnValue);
    40. }
    41. }
    42. } catch (Exception e) {
    43. e.printStackTrace();
    44. }
    45. //7.将对象返回
    46. return bean;
    47. }
    48. }
  • BeanListHandler实现类

    1. /*
    2. 实现类2:用于将结果集封装到集合中
    3. */
    4. public class BeanListHandler<T> implements ResultSetHandler<T> {
    5. //1.声明对象变量
    6. private Class<T> beanClass;
    7. //2.有参构造为变量赋值
    8. public BeanListHandler(Class<T> beanClass) {
    9. this.beanClass = beanClass;
    10. }
    11. @Override
    12. public List<T> handler(ResultSet rs) {
    13. //3.创建集合对象
    14. List<T> list = new ArrayList<>();
    15. try{
    16. //4.遍历结果集对象
    17. while(rs.next()) {
    18. //5.创建传递参数的对象
    19. T bean = beanClass.newInstance();
    20. //6.得到所有的列名
    21. //6.1先得到结果集的源信息
    22. ResultSetMetaData rsmd = rs.getMetaData();
    23. //6.2还要得到有多少列
    24. int columnCount = rsmd.getColumnCount();
    25. //6.3遍历列数
    26. for(int i = 1; i <= columnCount; i++) {
    27. //6.4得到每列的列名
    28. String columnName = rsmd.getColumnName(i);
    29. //6.5通过列名获取数据
    30. Object columnValue = rs.getObject(columnName);
    31. //6.6列名其实就是对象中成员变量的名称。于是就可以使用列名得到对象中属性的描述器(get和set方法)
    32. PropertyDescriptor pd = new PropertyDescriptor(columnName.toLowerCase(),beanClass);
    33. //6.7获取set方法
    34. Method writeMethod = pd.getWriteMethod();
    35. //6.8执行set方法,给成员变量赋值
    36. writeMethod.invoke(bean,columnValue);
    37. }
    38. //7.将对象保存到集合中
    39. list.add(bean);
    40. }
    41. } catch (Exception e) {
    42. e.printStackTrace();
    43. }
    44. //8.返回结果
    45. return list;
    46. }
    47. }
  • ScalarHandler实现类

    1. /*
    2. 实现类3:用于返回一个聚合函数的查询结果
    3. */
    4. public class ScalarHandler<T> implements ResultSetHandler<T> {
    5. @Override
    6. public Long handler(ResultSet rs) {
    7. //1.声明一个变量
    8. Long value = null;
    9. try{
    10. //2.判断是否有结果
    11. if(rs.next()) {
    12. //3.获取结果集的源信息
    13. ResultSetMetaData rsmd = rs.getMetaData();
    14. //4.获取第一列的列名
    15. String columnName = rsmd.getColumnName(1);
    16. //5.根据列名获取值
    17. value = rs.getLong(columnName);
    18. }
    19. } catch(Exception e) {
    20. e.printStackTrace();
    21. }
    22. //6.将结果返回
    23. return value;
    24. }
    25. }
  • JDBCTemplate类 ```java public class JDBCTemplate { private DataSource dataSource; private Connection con; private PreparedStatement pst; private ResultSet rs;

    public JDBCTemplate(DataSource dataSource) {

    1. this.dataSource = dataSource;

    }

    /*

    1. 专用于执行聚合函数sql语句的方法

    */ public Long queryForScalar(String sql, ResultSetHandler rsh, Object…objs) {

    1. Long result = null;
    2. try{
    3. con = dataSource.getConnection();
    4. pst = con.prepareStatement(sql);
    5. //获取sql语句中的参数源信息
    6. ParameterMetaData pData = pst.getParameterMetaData();
    7. int parameterCount = pData.getParameterCount();
    8. //判断参数个数是否一致
    9. if(parameterCount != objs.length) {
    10. throw new RuntimeException("参数个数不匹配");
    11. }
    12. //为sql语句中的?占位符赋值
    13. for (int i = 0; i < objs.length; i++) {
    14. pst.setObject(i+1,objs[i]);
    15. }
    16. //执行sql语句
    17. rs = pst.executeQuery();
    18. //通过ScalarHandler方式对结果进行处理
    19. result = rsh.handler(rs);
    20. } catch(Exception e) {
    21. e.printStackTrace();
    22. } finally {
    23. //释放资源
    24. DataSourceUtils.close(con,pst,rs);
    25. }
    26. //将结果返回
    27. return result;

    }

    /*

    1. 专用于查询所有记录sql语句的方法

    */ public List queryForList(String sql, ResultSetHandler rsh, Object…objs) {

    1. List<T> list = new ArrayList<>();
    2. try{
    3. con = dataSource.getConnection();
    4. pst = con.prepareStatement(sql);
    5. //获取sql语句中的参数源信息
    6. ParameterMetaData pData = pst.getParameterMetaData();
    7. int parameterCount = pData.getParameterCount();
    8. //判断参数个数是否一致
    9. if(parameterCount != objs.length) {
    10. throw new RuntimeException("参数个数不匹配");
    11. }
    12. //为sql语句中的?占位符赋值
    13. for (int i = 0; i < objs.length; i++) {
    14. pst.setObject(i+1,objs[i]);
    15. }
    16. //执行sql语句
    17. rs = pst.executeQuery();
    18. //通过BeanListHandler方式对结果进行处理
    19. list = rsh.handler(rs);
    20. } catch(Exception e) {
    21. e.printStackTrace();
    22. } finally {
    23. //释放资源
    24. DataSourceUtils.close(con,pst,rs);
    25. }
    26. //将结果返回
    27. return list;

    }

  1. /*
  2. 专用于执行查询一条记录sql语句的方法
  3. */
  4. public <T> T queryForObject(String sql, ResultSetHandler<T> rsh, Object...objs) {
  5. T obj = null;
  6. try{
  7. con = dataSource.getConnection();
  8. pst = con.prepareStatement(sql);
  9. //获取sql语句中的参数源信息
  10. ParameterMetaData pData = pst.getParameterMetaData();
  11. int parameterCount = pData.getParameterCount();
  12. //判断参数个数是否一致
  13. if(parameterCount != objs.length) {
  14. throw new RuntimeException("参数个数不匹配");
  15. }
  16. //为sql语句中的?占位符赋值
  17. for (int i = 0; i < objs.length; i++) {
  18. pst.setObject(i+1,objs[i]);
  19. }
  20. //执行sql语句
  21. rs = pst.executeQuery();
  22. //通过BeanHandler方式对结果进行处理
  23. obj = rsh.handler(rs);
  24. } catch(Exception e) {
  25. e.printStackTrace();
  26. } finally {
  27. //释放资源
  28. DataSourceUtils.close(con,pst,rs);
  29. }
  30. //将结果返回
  31. return obj;
  32. }

}

  1. <a name="ASERQ"></a>
  2. ### 2.2.4 测试自定义JDBC框架的使用
  3. ```java
  4. public class JDBCTemplateTest {
  5. //创建JDBCTemplate对象
  6. JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
  7. @Test
  8. public void selectScalar() {
  9. //查询student表的记录条数
  10. String sql = "SELECT COUNT(*) FROM student";
  11. Long count = template.queryForScalar(sql, new ScalarHandler<Long>());
  12. System.out.println(count);
  13. }
  14. @Test
  15. public void selectAll() {
  16. //查询所有学生信息
  17. String sql = "SELECT * FROM student";
  18. List<Student> list = template.queryForList(sql, new BeanListHandler<Student>(Student.class));
  19. for(Student stu : list) {
  20. System.out.println(stu);
  21. }
  22. }
  23. @Test
  24. public void selectOne() {
  25. //查询张三这条记录
  26. String sql = "SELECT * FROM student WHERE sid=?";
  27. //通过BeanHandler将结果封装成一个Student对象
  28. Student stu = template.queryForObject(sql, new BeanHandler<Student>(Student.class), 1);
  29. System.out.println(stu);
  30. }
  31. @Test
  32. public void insert() {
  33. //新增周七记录
  34. String sql = "INSERT INTO student VALUES (?,?,?,?)";
  35. Object[] params = {5,"周七",27,"2007-07-07"};
  36. int result = template.update(sql, params);
  37. System.out.println(result);
  38. }
  39. @Test
  40. public void delete() {
  41. //删除周七这条记录
  42. String sql = "DELETE FROM student WHERE sid=?";
  43. int result = template.update(sql, 5);
  44. System.out.println(result);
  45. }
  46. @Test
  47. public void update() {
  48. //修改张三的年龄为33
  49. String sql = "UPDATE student SET age=? WHERE name=?";
  50. Object[] params = {33,"张三"};
  51. int result = template.update(sql,params);
  52. System.out.println(result);
  53. }
  54. }