学习资源

  1. 视频课程
  2. 时长统计小工具

学习目录

  1. 概述
  2. 数据库连接
  3. PreparedStatement实现CRUD操作
  4. 操作BLOB类型字段
  5. 批量插入
  6. 数据库事务
  7. DAO及相关实现类
  8. 数据库连接池
  9. Apache-DBUtils实现CRUD操作

学习笔记

一、概述

  • 特点
    • 独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口
    • 标准的方法方便访问数据库
    • 不同数据库提供了统一途径
  • 存储技术分类
    • JDBC (基石)
    • JDO
    • 第三方 O/R 工具,如 Hibernate、Mybatis
  • 编写步骤
    1. 导入java.sql包
    2. JDBC-ODBC桥方式(SQL SERVER) / 附加相应厂商提供的驱动(Oracle、MySQL)
    3. 加载并注册驱动程序
    4. 创建 Connection 对象
    5. 创建 Statement对象
    6. 执行SQL语句
    7. 使用 ResultSet 对象
    8. 关闭 ResultSet 对象
    9. 关闭 Statement 对象
    10. 关闭 Connection 对象

二、数据库连接

  • url

    • 语法:jdbc:mysql://主机IP:数据库端口号/数据库名
    • jdbc:mysql 是协议
    • 数据库名区分大小写 ```java // 方式一: @Test public void testConnection1() throws SQLException { Driver driver = new com.mysql.jdbc.Driver();

      // url String url = “jdbc:mysql://...:/“;

      // 用户名和密码 Properties properties = new Properties(); properties.setProperty(“user”, “*“); // 设置用户名 properties.setProperty(“password”, “**“); // 设置密码

      Connection connect = driver.connect(url, properties);

      System.out.println(connect); // 不报错就代表连接成功了 }

/**

  1. * 对方式一的迭代,程序中不出现第三方的API,是程序具有更好的移植性
  2. * @throws Exception
  3. */

@Test public void testConnection2() throws Exception { // 1. 获取Driver实现类对象,使用反射 Class clazz = Class.forName(“com.mysql.jdbc.Driver”); Driver driver = (Driver) clazz.newInstance();

  1. // 2. 提供要链接的数据库
  2. String url = "jdbc:mysql://********:********/********";
  3. // 3. 用户名和密码
  4. Properties properties = new Properties();
  5. properties.setProperty("user", "********");
  6. properties.setProperty("password", "********");
  7. // 4. 获取连接
  8. Connection connect = driver.connect(url, properties);
  9. System.out.println(connect);

}

/**

  1. * 方式三:使用DriverManager替换Driver
  2. */

@Test public void testConnection3 () throws Exception { // 1. 获取Driver实现类对象,使用反射 Class clazz = Class.forName(“com.mysql.jdbc.Driver”); Driver driver = (Driver) clazz.newInstance();

  1. // 2. 提供要链接的数据库
  2. String url = "jdbc:mysql://********/*****";
  3. String user = "********";
  4. String psd = "********";
  5. // 注册驱动
  6. DriverManager.registerDriver(driver);
  7. // 获取连接
  8. Connection connection = DriverManager.getConnection(url, user, psd);
  9. System.out.println(connection);

}

/**

  1. * 方式四:优化方式三,只需加载驱动,不需要再注册驱动了
  2. */

@Test public void testConnection4 () throws Exception { // 1. 提供要链接的数据库
String url = “jdbc:mysql://**/“; String user = “**“; String psd = “**“;

  1. // 2. 获取Driver实现类对象,使用反射
  2. // 相比方式三,省略了注册驱动的操作,mysql自动注册了驱动
  3. // 其实这行也可以注释掉一样可以成功连接数据库,只不过这样不安全,所以不能这样使用
  4. // 原因是jar包中缓存了第三方API名
  5. Class.forName("com.mysql.jdbc.Driver");
  6. // 3. 获取连接
  7. Connection connection = DriverManager.getConnection(url, user, psd);
  8. System.out.println(connection);

}

/**

  1. * 方式五(final版):将数据库连接所需的基本信息声明在配置文件中
  2. * 暴露的内容少
  3. * 解耦,配置和代码解耦
  4. * 如果修改配置文件信息,避免程序重新打包
  5. */

@Test public void testConnection5() throws Exception { // 1. 读取配置文件中基本信息 InputStream resourceAsStream = ConnectionTest.class.getClassLoader().getResourceAsStream(“jdbc/jdbc.properties”);

  1. Properties properties = new Properties();
  2. properties.load(resourceAsStream);
  3. String user = properties.getProperty("user");
  4. String password = properties.getProperty("password");
  5. String url = properties.getProperty("url");
  6. String driverClass = properties.getProperty("driverClass");
  7. // 2. 加载驱动
  8. Class.forName(driverClass);
  9. // 3. 获取连接
  10. Connection conn = DriverManager.getConnection(url, user, password);
  11. System.out.println(conn);

}

  1. <a name="PtvBY"></a>
  2. ## 三、PreparedStatement实现CRUD操作
  3. - 一个数据库连接就是一个Socket连接
  4. - java.sql 有以下三个接口
  5. - Statement (已经不使用了 )
  6. - 无法防止SQL恶意注入问题,避免方式就是转而使用 PreparetdStatement
  7. - **PrepatedStatement** 主要使用(是Statement的子接口)
  8. - CallableStatement 用于执行SQ L存储过程
  9. - 表名与类名不想同时
  10. - 必须声明sql时,使用类的属性名来命名字段的别名
  11. - 使用 ResultMetaData 时,需要使用 getColumnLabel() 来替换 getColumnName(),获取列的别名。没起别名 getColumnLabel 获取的就是表中的列名
  12. - ORM编程思想
  13. - 一个数据**表**对应一个**类**
  14. - 一条**记录**对应一个**对象**
  15. - 一个**字段**对应一个**属性**
  16. **
  17. - 执行代码区别
  18. - `prepareStatement.`**`execute`**`()` 返回true / false, 代表是否查询成功
  19. - `prepareStatement.`**`executeUpdate`**`()` 返回数字类型,代表update 影响了多少行记录
  20. - `prepareStatement.`**`executeQuery`**`()`
  21. **
  22. - 代码
  23. - 【封装】数据连接与关闭
  24. ```java
  25. /**
  26. * [封装] 数据库连接函数
  27. * 1. 读取配置 properties
  28. * 2. 加载驱动 Class.forName
  29. * 3. 获取连接 DriverManager.getConnection
  30. * @return Connection 数据库连接对象实例
  31. * @throws Exception
  32. */
  33. public static Connection getConnection() throws Exception {
  34. // 1. 读取配置
  35. InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc/jdbc.properties");
  36. Properties pro = new Properties();
  37. pro.load(resourceAsStream);
  38. String user = pro.getProperty("user");
  39. String password = pro.getProperty("password");
  40. String url = pro.getProperty("url");
  41. String driverClass = pro.getProperty("driverClass");
  42. // 2. 加载驱动
  43. Class.forName(driverClass);
  44. // 3. 获取连接
  45. Connection conn = DriverManager.getConnection(url, user, password);
  46. return conn;
  47. }
  48. /**
  49. * [封装] 关闭连接和Statement
  50. * @param conn 数据库连接
  51. * @param preparedStatement 这里使用了父接口声明
  52. */
  53. public static void closeResource(Connection conn, Statement preparedStatement) {
  54. // 7. 资源关闭
  55. if (preparedStatement != null) {
  56. try {
  57. preparedStatement.close();
  58. } catch (SQLException throwables) {
  59. throwables.printStackTrace();
  60. }
  61. }
  62. if (conn != null) {
  63. try {
  64. conn.close();
  65. } catch (SQLException throwables) {
  66. throwables.printStackTrace();
  67. }
  68. }
  69. }
  70. /**
  71. * [封装] 关闭连接和Statement 还有resultSet
  72. * @param conn 数据库连接
  73. * @param preparedStatement 这里使用了父接口声明
  74. */
  75. public static void closeResource(Connection conn, Statement preparedStatement, ResultSet resultSet) {
  76. // 7. 资源关闭
  77. closeResource(conn, preparedStatement);
  78. if (resultSet != null) {
  79. try {
  80. resultSet.close();
  81. } catch (SQLException throwables) {
  82. throwables.printStackTrace();
  83. }
  84. }
  85. }
  • 增 ****改 ``sql @Test public void testOtherTable() { // 这里因为order是关键字,所以需要加单引号区分 String sql = "updateorder` set order_name=? where order_id=?”; commonSql(sql, “DD”, 2); }

/**

  • 通用的增删改操作
  • 占位符个数应该和可变形参长度相同 */ public void commonSql(String sql, Object …args) { Connection connection = null; PreparedStatement ps = null; try {

    1. // 1. 获取数据库连接
    2. connection = JDBCUtils.getConnection();
    3. // 2. 预编译sql语句
    4. ps = connection.prepareStatement(sql);
    5. // 3. 填充占位符
    6. for (int i = 0; i < args.length; i++) {
    7. ps.setObject(i+1, args[i]);
    8. }
    9. // 4. 执行
    10. ps.execute();

    } catch (Exception e) {

    1. e.printStackTrace();

    } finally {

    1. // 5. 关闭资源
    2. JDBCUtils.closeResource(connection, ps);

    } } ```

    • ```sql public Order orderForQuery(String sql, Object …args) { Connection conn = null; PreparedStatement ps = null; ResultSet resultSet = null; try {

      1. // 1. 连接数据库

      conn = JDBCUtils.getConnection(); // 2. 预编译sql语句 ps = conn.prepareStatement(sql);

      1. // 3. 填充占位符

      for (int i = 0; i < args.length; i++) {

      1. ps.setObject(i+1, args[i]);

      } // 4. 获取结果集 resultSet = ps.executeQuery(); // 5. 获取元数据 ResultSetMetaData metaData = resultSet.getMetaData(); // 6. 获取总列数 int columnCount = metaData.getColumnCount();

      if (resultSet.next()) {

      1. Order order = new Order();
      2. for (int i = 0; i < columnCount; i++) {
      3. // 7. 获取结果集中的属性
      4. Object object = resultSet.getObject(i + 1);
      5. // 8. 获取列的别名(如果列没有设置别名时使用数据库表中的名字)
      6. String columnName = metaData.getColumnLabel(i + 1);
      7. // 9. 利用反射获取属性
      8. Field declaredField = Order.class.getDeclaredField(columnName);
      9. declaredField.setAccessible(true); // 避免字段private导致报错
      10. declaredField.set(order, object);
      11. }
      12. return order;

      } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, resultSet); }

      return null; }

@Test public void testOrderForQuery() { String sql = “select order_id orderId, order_name orderName, order_date orderDate from order where order_id=?”; Order order = orderForQuery(sql, 4); System.out.println(order); }

  1. - **查 2.0 (使用泛型,不再固定使用某一个类)**
  2. ```sql
  3. /**
  4. * 通过泛型,不再固定使用某一个类
  5. * @param clazz
  6. * @param sql
  7. * @param args
  8. * @param <T>
  9. * @return
  10. */
  11. public <T> T getInstance(Class<T> clazz, String sql, Object ...args) {
  12. Connection conn = null;
  13. PreparedStatement ps = null;
  14. ResultSet resultSet = null;
  15. try {
  16. // 1. 连接数据库
  17. conn = JDBCUtils.getConnection();
  18. // 2. 预编译sql语句
  19. ps = conn.prepareStatement(sql);
  20. // 3. 配置占位符
  21. for (int i = 0; i < args.length; i++) {
  22. ps.setObject(i + 1, args[i]);
  23. }
  24. // 4. 结果集
  25. resultSet = ps.executeQuery();
  26. // 5. 结果集的元数据
  27. ResultSetMetaData metaData = resultSet.getMetaData();
  28. // 6. 通过元数据获取结果集中的列数
  29. int columnCount = metaData.getColumnCount();
  30. if (resultSet.next()) {
  31. // 7. 更改为使用泛型
  32. T t = clazz.newInstance();
  33. for (int i = 0; i < columnCount; i++) {
  34. // 8. 获取结果集中的属性
  35. Object object = resultSet.getObject(i + 1);
  36. // 9. 获取每个列的别名
  37. String columnName = metaData.getColumnLabel(i + 1);
  38. // 10. 给泛型对象指定的columnName属性,赋值为columnValue,通过反射
  39. Field declaredField = clazz.getDeclaredField(columnName);
  40. declaredField.setAccessible(true); // 给予权限,避免private报错
  41. declaredField.set(t, object);
  42. }
  43. return t;
  44. }
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. } finally {
  48. JDBCUtils.closeResource(conn, ps, resultSet);
  49. }
  50. return null;
  51. }
  52. @Test
  53. public void testGetInstance() {
  54. String sql = "select id,name,email from customers where id = ?";
  55. Customer instance = getInstance(Customer.class, sql, 12);
  56. System.out.println(instance);
  57. String sql1 = "select order_id orderId, order_name orderName from `order` where order_id = ?";
  58. Order instance1 = getInstance(Order.class, sql1, 2);
  59. System.out.println(instance1);
  60. }
  • 查 3.0 (查询多条数据) ```sql public List getForList(Class clazz, String sql, Object… args) { Connection conn = null; PreparedStatement ps = null; ResultSet resultSet = null; try { conn = JDBCUtils.getConnection();

    ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) {

    1. ps.setObject(i + 1, args[i]);

    }

    resultSet = ps.executeQuery(); // 获取结果集的元数据 ResultSetMetaData metaData = resultSet.getMetaData(); // 通过元数据获取结果集中的列数 int columnCount = metaData.getColumnCount();

    // 创建集合对象 ArrayList list = new ArrayList(); // 修改为while,这样看来,每一次next都是切换一条数据 while (resultSet.next()) {

    1. // 更改为使用泛型
    2. T t = clazz.newInstance();
    3. for (int i = 0; i < columnCount; i++) {
    4. Object object = resultSet.getObject(i + 1);
    5. // 获取每个列的列名
    6. String columnName = metaData.getColumnLabel(i + 1);
    7. // 给泛型对象指定的columnName属性,赋值为columnValue,通过反射
    8. Field declaredField = clazz.getDeclaredField(columnName);
    9. declaredField.setAccessible(true); // 给予权限,避免private报错
    10. declaredField.set(t, object);
    11. }
    12. // 向集合中添加元素
    13. list.add(t);

    }

    return list; } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, resultSet); } return null; }

@Test public void testQueryList() { String sql = “select id, name, email from customers where id < ?”; List forList = getForList(Customer.class, sql, 12); forList.forEach(System.out::println); // JDK8新特性:lambda表达式 }

  1. <a name="fMBEi"></a>
  2. ## 四、操作BLOB类型字段
  3. - BLOB类型分类:
  4. - TinyBlob 255
  5. - Blob 65K
  6. - MediumBlob 16M
  7. - LongBlob 4g
  8. <a name="QK0HW"></a>
  9. ## 五、批量插入
  10. - PreparedStatement 和 Statement 对比
  11. - 提高可读性和可维护性
  12. - PreparedStatement可防止SQL注入
  13. - PreparedStatement可以批处理,预编译
  14. - 代码
  15. ```sql
  16. @Test
  17. public void testInsert1() {
  18. Connection conn = null;
  19. PreparedStatement ps = null;
  20. try {
  21. long start = System.currentTimeMillis();
  22. conn = JDBCUtils.getConnection();
  23. ps = conn.prepareStatement("insert into goods(name) values (?)");
  24. for (int i = 0; i < 20000; i++) {
  25. ps.setObject(1, "name_" + i);
  26. ps.execute();
  27. }
  28. long end = System.currentTimeMillis();
  29. System.out.println("花费的时间为" + (end - start));
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. } finally {
  33. JDBCUtils.closeResource(conn, ps);
  34. }
  35. }
  36. /**
  37. * 批量插入方式三:
  38. * 1. addBatch()、executeBatch()、clearBatch()
  39. * 2. mysql默认情况下关闭批处理batch的,
  40. * 需要通过一个参数,来获得mysql批处理支持
  41. * 这个参数添加在 url 后面, ?rewriteBatchedStatements=true
  42. * 3. 使用更新的mysql驱动: mysql-connector-java-5.1.37-bin.jar
  43. */
  44. @Test
  45. public void testInsert2() {
  46. Connection conn = null;
  47. PreparedStatement ps = null;
  48. try {
  49. long start = System.currentTimeMillis();
  50. conn = JDBCUtils.getConnection();
  51. ps = conn.prepareStatement("insert into goods(name) values (?)");
  52. for (int i = 0; i < 1000000; i++) {
  53. ps.setObject(1, "name_" + i);
  54. // 1. “攒”sql
  55. ps.addBatch();
  56. if (i % 1000 == 0) {
  57. // 2. 执行batch
  58. ps.executeBatch();
  59. // 3. 清空batch
  60. ps.clearBatch();
  61. }
  62. }
  63. long end = System.currentTimeMillis();
  64. System.out.println("花费的时间为" + (end - start));
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. } finally {
  68. JDBCUtils.closeResource(conn, ps);
  69. }
  70. }
  71. /**
  72. * 批量插入方式四:
  73. * 设置不允许自动提交,待全部完成再提交
  74. */
  75. @Test
  76. public void testInsert3() {
  77. Connection conn = null;
  78. PreparedStatement ps = null;
  79. try {
  80. long start = System.currentTimeMillis();
  81. conn = JDBCUtils.getConnection();
  82. // 设置不允许自动提交数据
  83. conn.setAutoCommit(false);
  84. ps = conn.prepareStatement("insert into goods(name) values (?)");
  85. for (int i = 0; i < 1000000; i++) {
  86. ps.setObject(1, "name_" + i);
  87. // 1. “攒”sql
  88. ps.addBatch();
  89. if (i % 1000 == 0) {
  90. // 2. 执行batch
  91. ps.executeBatch();
  92. // 3. 清空batch
  93. ps.clearBatch();
  94. }
  95. }
  96. // 提交树
  97. conn.commit();
  98. long end = System.currentTimeMillis();
  99. System.out.println("花费的时间为" + (end - start));
  100. } catch (Exception e) {
  101. e.printStackTrace();
  102. } finally {
  103. JDBCUtils.closeResource(conn, ps);
  104. }
  105. }

六、数据库事务

  • 自动提交的情况
    • DDL 操作一旦执行,就会自动提交
      • set autocommit false 不起作用
    • DML 默认情况下,一旦执行就会默认提交
      • set autocommit false 管用
    • 默认在关闭连接时,自动提交数据

七、DAO及相关实现类

  • BaseDao

通用的基础父类,其他每个表都可以继承此表获得封装好的方法

  1. package jdbc.dao.daoPro;
  2. import jdbc.util.JDBCUtils;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.ParameterizedType;
  5. import java.lang.reflect.Type;
  6. import java.sql.*;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. /**
  10. * [进阶版] 进一步省去传入 Customer.class 操作,获取父类的类型,泛型
  11. * 封装了针对数据库的通用操作
  12. */
  13. public class BaseDao <T> {
  14. private Class<T> clazz = null;
  15. /**
  16. * 在父类中对 clazz 初始化
  17. * 避免每一个继承的子类都要给其初始化
  18. */
  19. {
  20. // 当前对象的父类的泛型,new 谁,这个this就指代 谁
  21. Type genericSuperclass = this.getClass().getGenericSuperclass();
  22. // 强转为带参泛型
  23. ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
  24. // 获取了父类的泛型参数,
  25. // 因为泛型可能有多个,所以这里返回是数组
  26. // 但是实际上我们知道只传递了一个参数,所以数组第一个参数就是我们要的参数
  27. Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
  28. // 泛型的第一个i参数,这里仍然需要一个强转
  29. clazz = (Class<T>)actualTypeArguments[0];
  30. }
  31. /**
  32. * 查询
  33. * 通过泛型,不再固定使用某一个类
  34. * version2, 考虑加入事务的情况
  35. * @param conn
  36. * @param sql
  37. * @param args
  38. * @param <T>
  39. * @return
  40. */
  41. public T getInstance(Connection conn, String sql, Object... args) {
  42. PreparedStatement ps = null;
  43. ResultSet resultSet = null;
  44. try {
  45. ps = conn.prepareStatement(sql);
  46. for (int i = 0; i < args.length; i++) {
  47. ps.setObject(i + 1, args[i]);
  48. }
  49. resultSet = ps.executeQuery();
  50. // 获取结果集的元数据
  51. ResultSetMetaData metaData = resultSet.getMetaData();
  52. // 通过元数据获取结果集中的列数
  53. int columnCount = metaData.getColumnCount();
  54. if (resultSet.next()) {
  55. // 更改为使用泛型
  56. T t = clazz.newInstance();
  57. for (int i = 0; i < columnCount; i++) {
  58. Object object = resultSet.getObject(i + 1);
  59. // 获取每个列的列名
  60. String columnName = metaData.getColumnLabel(i + 1);
  61. // 给泛型对象指定的columnName属性,赋值为columnValue,通过反射
  62. Field declaredField = clazz.getDeclaredField(columnName);
  63. declaredField.setAccessible(true); // 给予权限,避免private报错
  64. declaredField.set(t, object);
  65. }
  66. return t;
  67. }
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. } finally {
  71. JDBCUtils.closeResource(null, ps, resultSet);
  72. }
  73. return null;
  74. }
  75. /**
  76. * 通用的查询操作,返回多条记录的集合
  77. * @param conn
  78. * @param sql
  79. * @param args
  80. * @param <T>
  81. * @return
  82. */
  83. public List<T> getForList(Connection conn, String sql, Object... args) {
  84. PreparedStatement ps = null;
  85. ResultSet resultSet = null;
  86. try {
  87. ps = conn.prepareStatement(sql);
  88. for (int i = 0; i < args.length; i++) {
  89. ps.setObject(i + 1, args[i]);
  90. }
  91. resultSet = ps.executeQuery();
  92. // 获取结果集的元数据
  93. ResultSetMetaData metaData = resultSet.getMetaData();
  94. // 通过元数据获取结果集中的列数
  95. int columnCount = metaData.getColumnCount();
  96. // 创建集合对象
  97. ArrayList<T> list = new ArrayList<T>();
  98. while (resultSet.next()) {
  99. // 更改为使用泛型
  100. T t = clazz.newInstance();
  101. for (int i = 0; i < columnCount; i++) {
  102. Object object = resultSet.getObject(i + 1);
  103. // 获取每个列的列名
  104. String columnName = metaData.getColumnLabel(i + 1);
  105. // 给泛型对象指定的columnName属性,赋值为columnValue,通过反射
  106. Field declaredField = clazz.getDeclaredField(columnName);
  107. declaredField.setAccessible(true); // 给予权限,避免private报错
  108. declaredField.set(t, object);
  109. }
  110. // 向集合中添加元素
  111. list.add(t);
  112. }
  113. return list;
  114. } catch (Exception e) {
  115. e.printStackTrace();
  116. } finally {
  117. JDBCUtils.closeResource(null, ps, resultSet);
  118. }
  119. return null;
  120. }
  121. /**
  122. * 通用增删改操作
  123. * @param conn
  124. * @param sql
  125. * @param args
  126. * @return
  127. */
  128. public int update(Connection conn, String sql, Object... args) {
  129. PreparedStatement ps = null;
  130. try {
  131. // 1. 预编译sql语句
  132. ps = conn.prepareStatement(sql);
  133. // 2. 填充占位符
  134. for (int i = 0; i < args.length; i++) {
  135. ps.setObject(i + 1, args[i]);
  136. }
  137. // 3. 执行
  138. return ps.executeUpdate();
  139. } catch (Exception e) {
  140. e.printStackTrace();
  141. } finally {
  142. // 4. 关闭资源 (因为事务的原因,先不关连接 )
  143. JDBCUtils.closeResource(null, ps);
  144. }
  145. return 0;
  146. }
  147. /**
  148. * 用于查询特殊值的通用方法
  149. * @param conn
  150. * @param sql
  151. * @param args
  152. * @param <E>
  153. * @return
  154. */
  155. public <E> E getValue(Connection conn, String sql, Object ...args) {
  156. PreparedStatement ps = null;
  157. ResultSet rs = null;
  158. try {
  159. ps = conn.prepareStatement(sql);
  160. for (int i = 0; i < args.length; i++) {
  161. ps.setObject(i + 1, args[i]);
  162. }
  163. rs = ps.executeQuery();
  164. if (rs.next()) {
  165. return (E) rs.getObject(1);
  166. }
  167. } catch (SQLException throwables) {
  168. throwables.printStackTrace();
  169. } finally {
  170. JDBCUtils.closeResource(null, ps, rs);
  171. }
  172. return null;
  173. }
  174. }
  • CustomerDAO

Customer 接口,针对每一个表创建一个此接口以规范操作 customers 表的方法

  1. package jdbc.dao.daoPro;
  2. import jdbc.bean.Customer;
  3. import java.sql.Connection;
  4. import java.sql.Date;
  5. import java.util.List;
  6. /**
  7. * 此接口用于规范对于customerd表的常用操作
  8. */
  9. public interface CustomerDAO {
  10. /**
  11. * 将 customer 对象添加到数据库
  12. * @param conn
  13. * @param customer
  14. */
  15. void insert(Connection conn, Customer customer);
  16. /**
  17. * 删除表中指定id的数据
  18. * @param conn
  19. * @param id
  20. */
  21. void deleteById(Connection conn, int id);
  22. /**
  23. * 更新数据表中customer对象的数据
  24. * @param conn
  25. * @param customer
  26. */
  27. void update(Connection conn, Customer customer);
  28. /**
  29. * 通过id获得customer对象
  30. * @param conn
  31. * @param id
  32. * @return
  33. */
  34. Customer getCustomerById(Connection conn, int id);
  35. /**
  36. * 查询表中所有记录的集合
  37. * @param conn
  38. * @return
  39. */
  40. List<Customer> getAll(Connection conn);
  41. /**
  42. * 获取表中总记录数
  43. * @param conn
  44. * @return
  45. */
  46. Long getCount(Connection conn);
  47. /**
  48. * 获取数据表中最大生日
  49. * @param conn
  50. * @return
  51. */
  52. Date getMaxBirth(Connection conn);
  53. }
  • CustomerDAOImpl

上个接口的实现类,继承了 BaseDao 并实现了 CustomerDAO

  1. package jdbc.dao.daoPro;
  2. import jdbc.bean.Customer;
  3. import java.sql.Connection;
  4. import java.sql.Date;
  5. import java.util.List;
  6. public class CustomerDAOImpl extends BaseDao <Customer> implements CustomerDAO {
  7. // 获取当前类的父类的泛型,即获取 <Customer>
  8. @Override
  9. public void insert(Connection conn, Customer customer) {
  10. String sql = "insert into customers(name, email, birth) values(?, ?, ?)";
  11. update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth());
  12. }
  13. @Override
  14. public void deleteById(Connection conn, int id) {
  15. String sql = "delete from customers where id = ?";
  16. update(conn, sql, id);
  17. }
  18. @Override
  19. public void update(Connection conn, Customer customer) {
  20. String sql = "update customers set name=?, email=?, birth=? where id = ?";
  21. update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth(), customer.getId());
  22. }
  23. @Override
  24. public Customer getCustomerById(Connection conn, int id) {
  25. /**
  26. * 为什么不使用 * 代替?
  27. * 因为如果使用 * 的话会带出 photo 属性,这个属性目前是反射没办法处理的,所以就选择指定的属性返回。
  28. */
  29. String sql = "select id, name, email, birth from customers where id = ?";
  30. return getInstance(conn, sql, id);
  31. }
  32. @Override
  33. public List<Customer> getAll(Connection conn) {
  34. String sql = "select id, name, email, birth from customers";
  35. return getForList(conn, sql);
  36. }
  37. @Override
  38. public Long getCount(Connection conn) {
  39. // 因为返回的long类型,所以不需要做转换了,泛型
  40. return getValue(conn, "select count(*) from customers");
  41. }
  42. @Override
  43. public Date getMaxBirth(Connection conn) {
  44. // 因为返回Date类型,所以不需要做转换了,泛型
  45. return getValue(conn, "select max(birth) from customers");
  46. }
  47. }
  • Test

测试类,具体使用细节在这里

  1. package jdbc.dao.daoPro.junit;
  2. import jdbc.bean.Customer;
  3. import jdbc.util.JDBCUtils;
  4. import jdbc.dao.daoPro.CustomerDAOImpl;
  5. import org.junit.Test;
  6. import java.sql.Connection;
  7. import java.sql.Date;
  8. import java.text.SimpleDateFormat;
  9. import java.util.List;
  10. public class CustomerDAOImplTest {
  11. private CustomerDAOImpl dao = new CustomerDAOImpl();
  12. @Test
  13. public void insert() {
  14. Connection conn = null;
  15. try {
  16. conn = JDBCUtils.getConnection();
  17. SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
  18. Long timestamp = sdf.parse("2020-10-26").getTime();
  19. // 这里写id其实没什么用,因为是自增id
  20. Customer customer = new Customer(1, "天下", "tianxia@163.com", new Date(timestamp));
  21. dao.insert(conn, customer);
  22. System.out.println("添加成功");
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. } finally {
  26. JDBCUtils.closeResource(conn, null);
  27. }
  28. }
  29. @Test
  30. public void deleteById() {
  31. Connection conn = null;
  32. try {
  33. conn = JDBCUtils.getConnection();
  34. int id = 24;
  35. dao.deleteById(conn, id);
  36. System.out.println(id + " 删除成功");
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. } finally {
  40. JDBCUtils.closeResource(conn, null);
  41. }
  42. }
  43. @Test
  44. public void update() {
  45. Connection conn = null;
  46. try {
  47. conn = JDBCUtils.getConnection();
  48. SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
  49. Long timestamp = sdf.parse("2020-10-26").getTime();
  50. // 这里写id其实没什么用,因为是自增id
  51. Customer customer = new Customer(18, "贝少芬", null, new Date(timestamp));
  52. dao.update(conn, customer);
  53. System.out.println(customer.getId() + " 修改成功");
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. } finally {
  57. JDBCUtils.closeResource(conn, null);
  58. }
  59. }
  60. @Test
  61. public void getCustomerById() {
  62. Connection conn = null;
  63. try {
  64. conn = JDBCUtils.getConnection();
  65. Customer customerById = dao.getCustomerById(conn, 18);
  66. System.out.println(customerById);
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. } finally {
  70. JDBCUtils.closeResource(conn, null);
  71. }
  72. }
  73. @Test
  74. public void getAll() {
  75. Connection conn = null;
  76. try {
  77. conn = JDBCUtils.getConnection();
  78. List<Customer> list = dao.getAll(conn);
  79. list.forEach(System.out::println);
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. } finally {
  83. JDBCUtils.closeResource(conn, null);
  84. }
  85. }
  86. @Test
  87. public void getCount() {
  88. Connection conn = null;
  89. try {
  90. conn = JDBCUtils.getConnection();
  91. Long count = dao.getCount(conn);
  92. System.out.println(count);
  93. } catch (Exception e) {
  94. e.printStackTrace();
  95. } finally {
  96. JDBCUtils.closeResource(conn, null);
  97. }
  98. }
  99. @Test
  100. public void getMaxBirth() {
  101. Connection conn = null;
  102. try {
  103. conn = JDBCUtils.getConnection();
  104. Date maxBirth = dao.getMaxBirth(conn);
  105. System.out.println(maxBirth);
  106. } catch (Exception e) {
  107. e.printStackTrace();
  108. } finally {
  109. JDBCUtils.closeResource(conn, null);
  110. }
  111. }
  112. }

八、数据库连接池

  • 常用连接池集数:
    • DBCP
      • 速度比c3p0快,但是存在bug,不再提供支持
    • C3P0
      • 速度较慢,稳定性强,hibernate推荐
    • Proxool
    • BoneCP
    • Druid
      • 阿里提供
  • 代码 ```sql package jdbc.connection.util;

import com.alibaba.druid.pool.DruidDataSourceFactory; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.util.Properties;

public class JDBCUtils { /**

  1. * 避免每次新new对象,都创建一份新的连接池
  2. */
  3. private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
  4. /**
  5. * 使用 c3p0 获取数据库连接池技术
  6. * @return Connection 数据库连接
  7. */
  8. public static Connection getConnection1() throws Exception {
  9. // XML 文件应放置在直接命名的应用程序 CLASSPATH、WEB-INF/类或某些类似位置中的直接或 jar 文件中。
  10. // 文件名为:c3p0-config.xml
  11. // 文件路径:src/c3p0-config.xml
  12. Connection conn = cpds.getConnection();
  13. System.out.println(conn);
  14. return conn;
  15. }
  16. /**
  17. * DBCP为了避免每次new对象都创建一个数据库连接池,所以这里使用静态代码块的方式初始化
  18. */
  19. private static DataSource source;
  20. static {
  21. try {
  22. Properties pros = new Properties();
  23. FileInputStream fis = new FileInputStream(new File("src/jdbc/connection/dbcp.properties"));
  24. pros.load(fis);
  25. source = BasicDataSourceFactory.createDataSource(pros);
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. /**
  31. * 使用DBCP数据库连接技术获取数据库连接
  32. * @return
  33. * @throws Exception
  34. */
  35. public static Connection getConnection2() throws Exception {
  36. return source.getConnection();
  37. }
  38. private static DataSource sourceDruid;
  39. static {
  40. try {
  41. Properties pros = new Properties();
  42. // ClassLoader.getSystemClassLoader().getResourceAsStream 的首路径为 src/
  43. InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc/connection/druid.properties");
  44. pros.load(is);
  45. sourceDruid = DruidDataSourceFactory.createDataSource(pros);
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. /**
  51. * 使用 Druid 数据库连接技术获取数据库连接
  52. * @return
  53. * @throws Exception
  54. */
  55. public static Connection getConnection3() throws Exception {
  56. return sourceDruid.getConnection();
  57. }

}

  1. <a name="dDs3x"></a>
  2. ## 九、Apache-DBUtils实现CRUD操作
  3. - 代码
  4. ```java
  5. package jdbc.dbutils;
  6. import jdbc.bean.Customer;
  7. import jdbc.util.JDBCUtils;
  8. import org.apache.commons.dbutils.QueryRunner;
  9. import org.apache.commons.dbutils.ResultSetHandler;
  10. import org.apache.commons.dbutils.handlers.*;
  11. import org.junit.Test;
  12. import java.sql.Connection;
  13. import java.sql.Date;
  14. import java.sql.ResultSet;
  15. import java.sql.SQLException;
  16. import java.util.List;
  17. import java.util.Map;
  18. /**
  19. * 使用 apache-dbutils 封装的开源JDBC开源工具类,封装了增删改查
  20. */
  21. public class QueryRunnerTest {
  22. /**
  23. * 测试插入
  24. */
  25. @Test
  26. public void testInsert() {
  27. Connection conn = null;
  28. try {
  29. QueryRunner runner = new QueryRunner();
  30. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  31. String sql = "insert into customers(name, email, birth) values(?, ?, ?)";
  32. int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@163.com", "1997-02-23");
  33. System.out.println("添加了" + insertCount);
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. } finally {
  37. JDBCUtils.closeResource(conn, null);
  38. }
  39. }
  40. /**
  41. * 测试查询:
  42. * BeanHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录
  43. */
  44. @Test
  45. public void testQuery() throws Exception {
  46. QueryRunner runner = new QueryRunner();
  47. Connection conn = jdbc.connection.util.JDBCUtils.getConnection3();
  48. String sql = "select id, name, email, birth from customers where id = ?";
  49. BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
  50. Customer customer = runner.query(conn, sql, handler, 18);
  51. System.out.println(customer);
  52. }
  53. /**
  54. * 测试查询多条记录:
  55. * BeanListHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录
  56. */
  57. @Test
  58. public void testQueryList() {
  59. Connection conn = null;
  60. try {
  61. QueryRunner runner = new QueryRunner();
  62. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  63. String sql = "select id, name, email, birth from customers where id < ?";
  64. BeanListHandler<Customer> listHandler = new BeanListHandler<>(Customer.class);
  65. List<Customer> list = runner.query(conn, sql, listHandler, 18);
  66. list.forEach(System.out::println);
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. } finally {
  70. JDBCUtils.closeResource(conn, null);
  71. }
  72. }
  73. /**
  74. * MapHandler:对应表中的一条记录
  75. * 将字段及响应字段的值作为map中的key和value
  76. */
  77. @Test
  78. public void testQueryMap() {
  79. Connection conn = null;
  80. try {
  81. QueryRunner runner = new QueryRunner();
  82. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  83. String sql = "select id, name, email, birth from customers where id = ?";
  84. MapHandler handler = new MapHandler();
  85. Map<String, Object> map = runner.query(conn, sql, handler, 18);
  86. System.out.println(map);
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. } finally {
  90. JDBCUtils.closeResource(conn, null);
  91. }
  92. }
  93. /**
  94. * MapHandler:对应表中的一条记录
  95. * 将字段及响应字段的值作为map中的key和value
  96. * 将这些map添加到List
  97. */
  98. @Test
  99. public void testQueryMapList() {
  100. Connection conn = null;
  101. try {
  102. QueryRunner runner = new QueryRunner();
  103. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  104. String sql = "select id, name, email, birth from customers where id < ?";
  105. MapListHandler handler = new MapListHandler();
  106. List<Map<String, Object>> list = runner.query(conn, sql, handler, 18);
  107. list.forEach(System.out::println);
  108. } catch (Exception e) {
  109. e.printStackTrace();
  110. } finally {
  111. JDBCUtils.closeResource(conn, null);
  112. }
  113. }
  114. @Test
  115. public void testQueryCount() {
  116. Connection conn = null;
  117. try {
  118. QueryRunner runner = new QueryRunner();
  119. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  120. String sql = "select count(*) from customers";
  121. ScalarHandler handler = new ScalarHandler();
  122. Long count = (Long) runner.query(conn, sql, handler);
  123. System.out.println(count);
  124. } catch (Exception e) {
  125. e.printStackTrace();
  126. } finally {
  127. JDBCUtils.closeResource(conn, null);
  128. }
  129. }
  130. /**
  131. * ScalarHandler 用于查询特殊值
  132. */
  133. @Test
  134. public void testQueryMax() {
  135. Connection conn = null;
  136. try {
  137. QueryRunner runner = new QueryRunner();
  138. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  139. String sql = "select max(birth) from customers";
  140. ScalarHandler handler = new ScalarHandler();
  141. Date birthMax = (Date) runner.query(conn, sql, handler);
  142. System.out.println(birthMax);
  143. } catch (Exception e) {
  144. e.printStackTrace();
  145. } finally {
  146. JDBCUtils.closeResource(conn, null);
  147. }
  148. }
  149. /**
  150. * 自定义 ResultSetHandler 实现类
  151. *
  152. */
  153. @Test
  154. public void testQueryCustomer() {
  155. Connection conn = null;
  156. try {
  157. QueryRunner runner = new QueryRunner();
  158. conn = jdbc.connection.util.JDBCUtils.getConnection3();
  159. String sql = "select id,name,email,birth from customers where id = ?";
  160. /**
  161. * JDK8 中:匿名实现类的泛型还不能省略
  162. */
  163. ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
  164. @Override
  165. public Customer handle(ResultSet resultSet) throws SQLException {
  166. // 初始化
  167. // return null;
  168. // 制造虚拟
  169. // return new Customer(12, "陈龙", "chenglong@153.com", new Date(123123123123L));
  170. // 从数据库中获取
  171. if (resultSet.next()) {
  172. int id = resultSet.getInt("id");
  173. String name = resultSet.getString("name");
  174. String email = resultSet.getString("email");
  175. Date birth = resultSet.getDate("birth");
  176. Customer customer = new Customer(id, name, email, birth);
  177. return customer;
  178. }
  179. return null;
  180. }
  181. };
  182. Customer query = runner.query(conn, sql, handler, 18);
  183. System.out.println(query);
  184. } catch (Exception e) {
  185. e.printStackTrace();
  186. } finally {
  187. JDBCUtils.closeResource(conn, null);
  188. }
  189. }
  190. }