一、JDBC概述

1.1 基本介绍

  1. JDBC 为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。
  2. Java 程序员使用 JDBC,可以连接任何提供了 JDBC 驱动程序的数据库系统,从而完成对数据库的各种操作
  3. JDBC 的基本原理图
    image.png

1.2 模拟JDBC

  1. package com.hspedu.jdbc.myjdbc;
  2. /**
  3. * @author 韩顺平
  4. * @version 1.0
  5. * 我们规定的 jdbc 接口(方法)
  6. */
  7. public interface JdbcInterface {
  8. //连接
  9. public Object getConnection() ;
  10. //crud
  11. public void crud();
  12. //关闭连接
  13. public void close();
  14. }
  1. package com.hspedu.jdbc.myjdbc;
  2. /**
  3. * @author 韩顺平
  4. * @version 1.0
  5. * mysql 数据库实现了 jdbc 接口 [模拟] 【mysql 厂商开发】
  6. */
  7. public class MysqlJdbcImpl implements JdbcInterface{
  8. @Override
  9. public Object getConnection() {
  10. System.out.println("得到 mysql 的连接");
  11. return null;
  12. }
  13. @Override
  14. public void crud() {
  15. System.out.println("完成 mysql 增删改查");
  16. }
  17. @Override
  18. public void close() {
  19. System.out.println("关闭 mysql 的连接");
  20. }
  21. }
  1. package com.hspedu.jdbc.myjdbc;
  2. /**
  3. * @author 韩顺平
  4. * @version 1.0
  5. * 模拟 oracle 数据库实现 jdbc
  6. */
  7. public class OracleJdbcImpl implements JdbcInterface {
  8. @Override
  9. public Object getConnection() {
  10. System.out.println("得到 oracle 的连接 升级");
  11. return null;
  12. }
  13. @Override
  14. public void crud() {
  15. System.out.println("完成 对 oracle 的增删改查");
  16. }
  17. @Override
  18. public void close() {
  19. System.out.println("关闭 oracle 的连接");
  20. }
  21. }
  1. package com.hspedu.jdbc.myjdbc;
  2. import org.junit.jupiter.api.Test;
  3. import java.io.FileInputStream;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.ResultSet;
  7. import java.sql.Statement;
  8. import java.util.Properties;
  9. import java.util.Scanner;
  10. /**
  11. * @author 韩顺平
  12. * @version 1.0
  13. */
  14. public class TestJDBC {
  15. public static void main(String[] args) throws Exception {
  16. //完成对 mysql 的操作
  17. JdbcInterface jdbcInterface = new MysqlJdbcImpl();
  18. jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定]
  19. jdbcInterface.crud();
  20. jdbcInterface.close();
  21. //完成对 oracle 的操作
  22. System.out.println("==============================");
  23. jdbcInterface = new OracleJdbcImpl();
  24. jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定]
  25. jdbcInterface.crud();
  26. jdbcInterface.close();
  27. }
  28. }

1.3 JDBC 带来的好处

  1. 如果Java 直接访问数据库,而数据库的种类又很多,这无疑增大了学习成本和操作难度
    image.png
  2. JDBC 是Java提供的一套用于数据库操作的接口 API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同的实现
    image.png

1.4 JDBC API

JDBC API 是一系列的接口,它统一和规范了应用程序与数据库的连接、执行 SQL 语,并得到返回结果等各类操作,相关类和接口在 java.sql 与 javax.sql 包中
image.png

二、JDBC快速入门

2.1 JDBC 程序编写步骤

  1. 注册驱动 - 加载 Driver 类
  2. 获取连接 - 得到 Connection
  3. 执行增删改查 - 发送 SQL 给 MySQL 执行
  4. 释放资源 - 关闭相关连接

2.2 快速上手

  1. package com.hspedu.jdbc;
  2. import com.mysql.jdbc.Driver;
  3. import java.sql.Connection;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import java.util.Properties;
  7. /**
  8. * @author HarborGao
  9. * @version 1.0
  10. * 这是第一个JDBC程序,完成简单的操作
  11. */
  12. public class Jdbc01 {
  13. public static void main(String[] args) throws SQLException {
  14. //前置工作: 在项目下创建一个文件夹比如 libs
  15. // 将 mysql.jar 拷贝到该目录下,点击 add to project ..加入到项目中
  16. //1. 注册驱动
  17. Driver driver = new Driver();
  18. //2. 建立连接
  19. // 解读
  20. //(1) jdbc:mysql:// 规定好表示协议,通过 jdbc 的方式连接 mysql
  21. //(2) localhost 主机,可以是 ip 地址
  22. //(3) 3306 表示 mysql 监听的端口
  23. //(4) hsp_db02 连接到 mysql dbms 的哪个数据库
  24. //(5) mysql 的连接本质就是前面学过的 socket 连接
  25. String url = "jdbc:mysql://localhost:3306/db01";
  26. //将 用户名和密码放入到 Properties 对象
  27. Properties properties = new Properties();
  28. //说明 user 和 password 是规定好,后面的值根据实际情况写
  29. properties.setProperty("user","root");
  30. properties.setProperty("password","111");
  31. Connection connect = driver.connect(url, properties);
  32. //3. 执行 sql
  33. //String sql = "insert into actor values(null,'黄蓉','女','1985-10-15','15636')";
  34. //String sql = "update actor set `name` = '周星驰' where id = 1";
  35. String sql = "delete from actor where id = 2";
  36. //statement 用于执行静态 SQL 语句并返回其生成的结果的对象
  37. Statement statement = connect.createStatement();
  38. int rows = statement.executeUpdate(sql); // 如果是 dml 语句,返回的就是影响行数
  39. System.out.println(rows > 0 ? "成功" : "失败");
  40. //4. 断开连接
  41. statement.close();
  42. connect.close();
  43. }
  44. }

2.3 获取数据库连接5 种方式

  1. 方式一
    image.png
  2. 方式二
    image.png
  3. 方式三
    image.png
  4. 方式四
    image.png
  5. 方式五
    image.png
  1. package com.hspedu.jdbc;
  2. import com.mysql.jdbc.Driver;
  3. import org.junit.jupiter.api.Test;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. import java.sql.Connection;
  7. import java.sql.DriverManager;
  8. import java.sql.SQLException;
  9. import java.util.Properties;
  10. /**
  11. * @author HarborGao
  12. * @version 1.0
  13. * 分析Java连接MySQL的五种方式
  14. */
  15. public class JdbcConn {
  16. //方式一
  17. @Test
  18. public void connect01() throws SQLException {
  19. Driver driver = new Driver();
  20. String url = "jdbc:mysql://localhost:3306/db01";
  21. Properties properties = new Properties();
  22. properties.setProperty("user","root");
  23. properties.setProperty("password","111");
  24. Connection connect = driver.connect(url, properties);
  25. System.out.println("第一种方式" + connect);
  26. connect.close();
  27. }
  28. //方式二
  29. @Test
  30. public void connect02() throws Exception {
  31. //使用反射加载Driver类,动态加载,更加灵活,减少依赖性
  32. Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
  33. Driver driver = (Driver)aClass.newInstance();
  34. String url = "jdbc:mysql://localhost:3306/db01";
  35. Properties properties = new Properties();
  36. properties.setProperty("user","root");
  37. properties.setProperty("password","111");
  38. Connection connect = driver.connect(url, properties);
  39. System.out.println("第二种方式 " + connect);
  40. connect.close();
  41. }
  42. //方式三 使用 DriverManager 替代 Driver 进行统一管理
  43. @Test
  44. public void connect03() throws Exception {
  45. Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
  46. Driver driver = (Driver)aClass.newInstance();
  47. //创建 url 和 user 和 password
  48. String url = "jdbc:mysql://localhost:3306/db01";
  49. String user = "root";
  50. String password = "111";
  51. DriverManager.registerDriver(driver); //注册Driver驱动
  52. Connection connection = DriverManager.getConnection(url, user, password);
  53. System.out.println("第三种方式 " + connection);
  54. connection.close();
  55. }
  56. //方式四:使用 Class.forName 自动完成注册驱动,简化代码
  57. //这种方式在工作中使用最多,推荐使用
  58. @Test
  59. public void connect04() throws ClassNotFoundException, SQLException {
  60. //使用反射加载 Driver类
  61. //在加载 Driver类时,自动完成注册
  62. /*
  63. 源码:
  64. 1. 静态代码块,在类加载时,执行一次
  65. 2. DriverManager.registerDriver(new Driver());
  66. 3. 因此注册 Driver 的工作已经自动完成
  67. static {
  68. try {
  69. DriverManager.registerDriver(new Driver());
  70. } catch (SQLException var1) {
  71. throw new RuntimeException("Can't register driver!");
  72. }
  73. }
  74. */
  75. //Class.forName("com.mysql.jdbc.Driver"); //驱动5.1.6版之后可以省略不写,建议写上
  76. //创建 url 和 user 和 password
  77. String url = "jdbc:mysql://localhost:3306/db01";
  78. String user = "root";
  79. String password = "111";
  80. Connection connection = DriverManager.getConnection(url, user, password);
  81. System.out.println("第四种方式 " + connection);
  82. connection.close();
  83. }
  84. //方式五 在方式四的基础上改进,增加配置文件,让连接MySQL更加灵活
  85. @Test
  86. public void connect05() throws ClassNotFoundException, SQLException, IOException {
  87. //通过Properties对象获取配置文件信息
  88. Properties properties = new Properties();
  89. properties.load(new FileInputStream("src\\mysql.properties"));
  90. String user = properties.getProperty("user");
  91. String password = properties.getProperty("password");
  92. String driver = properties.getProperty("driver");
  93. String url = properties.getProperty("url");
  94. Class.forName(driver);
  95. Connection connection = DriverManager.getConnection(url, user, password);
  96. System.out.println("第五种方式 " + connection);
  97. connection.close();
  98. }
  99. }
  1. user=root
  2. password=123456
  3. url=jdbc:mysql://localhost:3306/db01
  4. driver=com.mysql.jdbc.Driver

三、ResultSet【结果集】

3.1 基本介绍

  1. 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
  2. ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
  3. next 方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回 false,因此可以在 while 循环中使用循环来遍历结果集
    image.png

3.2 应用案例

image.png

  1. package com.hspedu.jdbc;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.sql.*;
  5. import java.util.Properties;
  6. /**
  7. * @author HarborGao
  8. * @version 1.0
  9. */
  10. @SuppressWarnings({"all"})
  11. public class ResultSet_ {
  12. public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
  13. Properties properties = new Properties();
  14. properties.load(new FileInputStream("src\\mysql.properties"));
  15. String user = properties.getProperty("user");
  16. String password = properties.getProperty("password");
  17. String driver = properties.getProperty("driver");
  18. String url = properties.getProperty("url");
  19. //1. 注册驱动
  20. Class.forName(driver);
  21. //2. 得到连接
  22. Connection connection = DriverManager.getConnection(url, user, password);
  23. //3. 得到Statement
  24. Statement statement = connection.createStatement();
  25. //4. 组织sql
  26. String sql = "select id,`name`,sex,borndate from actor";
  27. //执行给定的 SQL语句,该语句返回单个ResultSet结果集
  28. ResultSet resultSet = statement.executeQuery(sql);
  29. //5. 使用while取出数据
  30. while (resultSet.next()) {
  31. int id = resultSet.getInt(1); //获取该行的第1列数据
  32. String name = resultSet.getString(2); //获取该行的第2列数据
  33. String sex = resultSet.getString(3); //获取该行的第3列数据
  34. Date borndate = resultSet.getDate(4); //获取该行的第4列数据
  35. System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate);
  36. }
  37. //6. 关闭连接
  38. resultSet.close();
  39. statement.close();
  40. connection.close();
  41. }
  42. }

四、Statement

4.1 基本介绍

  1. Statement对象 用于执行静态SQL语句并返回其生成的结果的对象
  2. 在连接建立后,需要对数据库进行访问,执行命令或是SQL语句,可以通过
    1. Statement 【存在SQL注入问题】
    2. PrepareStatement 【预处理】
    3. CallableStatement 【存储过程】
  3. Statement对象执行 SQL语句,存在SQL注入风险
  4. SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据种注入非法的SQL语句段或命令,恶意攻击数据库
  5. 要防范SQL注入,只要用 PrepareStatement(从Statement拓展而来)取代Statement 就可以了

4.2 演示SQL注入

  1. -- 演示 sql 注入
  2. -- 创建一张表
  3. CREATE TABLE admin ( -- 管理员表
  4. `user` VARCHAR(32) NOT NULL UNIQUE,
  5. `password` VARCHAR(32) NOT NULL DEFAULT '') CHARACTER SET utf8;
  6. -- 添加数据
  7. INSERT INTO admin VALUES('tom', '123');
  8. -- 查找某个管理是否存在
  9. SELECT *
  10. FROM admin
  11. WHERE `user` = 'tom' AND `password` = '123';
  12. -- SQL
  13. -- 输入用户名 1' or
  14. -- 输入万能密码 为 or '1'= '1
  15. SELECT *
  16. FROM admin
  17. WHERE `user` = '1' OR' AND `password` = 'OR '1'= '1';
  18. SELECT * FROM admin;
  1. package com.hspedu.jdbc.statement_;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.ResultSet;
  7. import java.sql.Statement;
  8. import java.util.Properties;
  9. import java.util.Scanner;
  10. /**
  11. * @author HarborGao
  12. * @version 1.0
  13. * 演示 Statement 的SQL注入问题
  14. */
  15. @SuppressWarnings({"all"})
  16. public class Statement_ {
  17. public static void main(String[] args) throws Exception {
  18. Properties properties = new Properties();
  19. properties.load(new FileInputStream("src\\mysql.properties"));
  20. String user = properties.getProperty("user");
  21. String password = properties.getProperty("password");
  22. String driver = properties.getProperty("driver");
  23. String url = properties.getProperty("url");
  24. Scanner scanner = new Scanner(System.in);
  25. System.out.print("请输入用户名:"); //next() 当接收到空格或者'就是表示结束
  26. //输入 1' or
  27. String admin_user = scanner.nextLine(); //如果希望看到SQL注入效果,这里需要用nextLine
  28. System.out.print("请输入密码:");
  29. //输入 or '1' = '1
  30. String admin_pwd = scanner.nextLine();
  31. Class.forName(driver);
  32. Connection connection = DriverManager.getConnection(url, user, password);
  33. Statement statement = connection.createStatement();
  34. String sql = "select `user`,`password` from `admin` where `user` = '"+ admin_user +"' " +
  35. "and `password` = '" + admin_pwd + "'";
  36. ResultSet resultSet = statement.executeQuery(sql);
  37. if (resultSet.next()) {
  38. System.out.println("恭喜,登陆成功!");
  39. } else {
  40. System.out.println("登陆失败");
  41. }
  42. resultSet.close();
  43. statement.close();
  44. connection.close();
  45. }
  46. }

五、 PreparedStatement

5.1 基本介绍

  1. PrepareStatement 执行的SQL 语句中的参数用问号( ? ) 来表示,调用 PrepareStatement 对象的 setXxx() 方法来设置这些参数。setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
  2. 调用 executeQuery(),返回 ResultSet 对象
  3. 调用 executeUpdate() ,执行更新,包括 增、删、修改

5.2 预处理的好处

  1. 不再使用+ 拼接sql语句,减少语法错误
  2. 有效的解决了 SQL 注入问题
  3. 大大减少了编译次数,效率较高

5.3 应用案例

  1. package com.hspedu.jdbc.preparestatement_;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.sql.*;
  5. import java.util.Properties;
  6. import java.util.Scanner;
  7. /**
  8. * @author HarborGao
  9. * @version 1.0
  10. * 演示 PrepareStatement 使用
  11. */
  12. public class PrepareStatement_ {
  13. public static void main(String[] args) throws Exception {
  14. Properties properties = new Properties();
  15. properties.load(new FileInputStream("src\\mysql.properties"));
  16. String user = properties.getProperty("user");
  17. String password = properties.getProperty("password");
  18. String driver = properties.getProperty("driver");
  19. String url = properties.getProperty("url");
  20. Scanner scanner = new Scanner(System.in);
  21. System.out.print("请输入用户名:"); //next() 当接收到空格或者'就是表示结束
  22. String admin_user = scanner.nextLine(); //如果希望看到SQL注入效果,这里需要用nextLine
  23. System.out.print("请输入密码:");
  24. String admin_pwd = scanner.nextLine();
  25. Class.forName(driver);
  26. Connection connection = DriverManager.getConnection(url, user, password);
  27. String sql = "select `user`,`password` from admin where `user` = ? and `password` = ?";
  28. // preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
  29. PreparedStatement preparedStatement = connection.prepareStatement(sql);
  30. preparedStatement.setString(1,admin_user);
  31. preparedStatement.setString(2,admin_pwd);
  32. ResultSet resultSet = preparedStatement.executeQuery();
  33. if (resultSet.next()) {
  34. System.out.println("恭喜,登陆成功!");
  35. } else {
  36. System.out.println("登陆失败");
  37. }
  38. resultSet.close();
  39. preparedStatement.close();
  40. connection.close();
  41. }
  42. }
  1. package com.hspedu.jdbc.preparedstatement_;
  2. import java.io.FileInputStream;
  3. import java.sql.Connection;
  4. import java.sql.DriverManager;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.util.Properties;
  8. import java.util.Scanner;
  9. /**
  10. * @author 韩顺平
  11. * @version 1.0
  12. * 演示 PreparedStatement 使用 dml 语句
  13. */
  14. @SuppressWarnings({"all"})
  15. public class PreparedStatementDML_ {
  16. public static void main(String[] args) throws Exception {
  17. //看 PreparedStatement 类图
  18. Scanner scanner = new Scanner(System.in);
  19. //让用户输入管理员名和密码
  20. System.out.print("请输删除管理员的名字: "); //next(): 当接收到 空格或者 '就是表示结束
  21. String admin_name = scanner.nextLine(); // 老师说明,如果希望看到 SQL 注入,这里需要用 nextLine
  22. // System.out.print("请输入管理员的新密码: ");
  23. // String admin_pwd = scanner.nextLine();
  24. //通过 Properties 对象获取配置文件的信息
  25. Properties properties = new Properties();
  26. properties.load(new FileInputStream("src\\mysql.properties"));
  27. //获取相关的值
  28. String user = properties.getProperty("user");
  29. String password = properties.getProperty("password");
  30. String driver = properties.getProperty("driver");
  31. String url = properties.getProperty("url");
  32. //1. 注册驱动
  33. Class.forName(driver);//建议写上
  34. //2. 得到连接
  35. Connection connection = DriverManager.getConnection(url, user, password);
  36. //3. 得到 PreparedStatement
  37. //3.1 组织 SqL , Sql 语句的 ? 就相当于占位符
  38. //添加记录
  39. //String sql = "insert into admin values(?, ?)";
  40. //String sql = "update admin set pwd = ? where name = ?";
  41. String sql = "delete from admin where name = ?";
  42. //3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
  43. PreparedStatement preparedStatement = connection.prepareStatement(sql);
  44. //3.3 给 ? 赋值
  45. preparedStatement.setString(1, admin_name);
  46. //preparedStatement.setString(2, admin_name);
  47. //4. 执行 dml 语句使用 executeUpdate
  48. int rows = preparedStatement.executeUpdate();
  49. System.out.println(rows > 0 ? "执行成功" : "执行失败");
  50. //关闭连接
  51. preparedStatement.close();
  52. connection.close();
  53. }
  54. }

5.4 小练习

  1. 创建admin表
  2. 使用PreparedStatement添加5条数据
  3. 修改tom的记录,将name改成king
  4. 删除一条的记录
  5. 查询全部记录,并显示在控制台

六、JDBC 的相关API 小结

JDBC 和数据库连接池 - 图12

七、封装 JDBCUtils

7.1 说明

在 JDBC 操作中,获取连接和释放资源是经常使用到的,可以将其封装成JDBC 连接的工具类JDBCUtils

7.2 代码实现

  1. package com.hspedu.jdbc.utils;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.sql.*;
  5. import java.util.Properties;
  6. /**
  7. * @author HarborGao
  8. * @version 1.0
  9. */
  10. public class JDBCUtils {
  11. //定义相关属性(4个),因为只需要一份,因此,我们做成 static
  12. private static String user;
  13. private static String password;
  14. private static String driver;
  15. private static String url;
  16. //在static代码块初始化
  17. static {
  18. Properties properties = null;
  19. try {
  20. properties = new Properties();
  21. properties.load(new FileInputStream("src\\mysql.properties"));
  22. user = properties.getProperty("user");
  23. password = properties.getProperty("password");
  24. driver = properties.getProperty("driver");
  25. url = properties.getProperty("url");
  26. } catch (IOException e) {
  27. //在实际开发中,可以做以下处理
  28. //1. 将编译异常转成运行异常
  29. //2. 这时调用者可以选择捕获该异常,也可以选择默认处理该异常,比较方便
  30. throw new RuntimeException(e);
  31. }
  32. }
  33. public static Connection getConnection() throws IOException {
  34. try {
  35. return DriverManager.getConnection(url, user, password);
  36. } catch (SQLException e) {
  37. throw new RuntimeException(e);
  38. }
  39. }
  40. //关闭相关资源
  41. /*
  42. 1. ResultSet 结果集
  43. 2. Statement 或者 PreparedStatement
  44. 3. Connection
  45. 4. 如果需要关闭资源,就传入对象,否则传入 null
  46. */
  47. public static void close(ResultSet set, Statement statement, Connection connection) {
  48. try {
  49. if (set != null) {
  50. set.close();
  51. }
  52. if (statement != null) {
  53. statement.close();
  54. }
  55. if (connection != null) {
  56. connection.close();
  57. }
  58. } catch (SQLException e) {
  59. throw new RuntimeException(e);
  60. }
  61. }
  62. }

7.3 使用案例

  1. package com.hspedu.jdbc;
  2. import com.hspedu.jdbc.utils.JDBCUtils;
  3. import java.io.IOException;
  4. import java.sql.Connection;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.sql.SQLException;
  8. /**
  9. * @author HarborGao
  10. * @version 1.0
  11. * 演示如何使用JDBCUtils 工具类,完成DML和select操作
  12. */
  13. public class JDBCUtils_Use {
  14. public static void main(String[] args){
  15. //1. 得到连接
  16. Connection connection = JDBCUtils.getConnection();
  17. //2. DML操作
  18. //String sql = "insert into admin values(?,?)";
  19. //String sql = "update admin set `password` = ? where `user` = ?";
  20. /*String sql = "delete from admin where `user` = ?";
  21. PreparedStatement preparedStatement = connection.prepareStatement(sql);
  22. // preparedStatement.setString(1,"0000");
  23. // preparedStatement.setString(2,"bob");
  24. preparedStatement.setString(1,"xin");
  25. int rows = preparedStatement.executeUpdate();
  26. System.out.println(rows > 0 ? "成功" : "失败");*/
  27. //Query操作
  28. String sql = "select * from admin where `user` = ?";
  29. PreparedStatement preparedStatement = null;
  30. ResultSet resultSet = null;
  31. try {
  32. preparedStatement = connection.prepareStatement(sql);
  33. preparedStatement.setString(1,"jack");
  34. resultSet = preparedStatement.executeQuery();
  35. while (resultSet.next()) {
  36. String user = resultSet.getString("user");
  37. String password = resultSet.getString("password");
  38. System.out.println(user + "\t" + password);
  39. }
  40. } catch (SQLException e) {
  41. e.printStackTrace();
  42. } finally {
  43. //3. 关闭连接
  44. //JDBCUtils.close(null,preparedStatement,connection);
  45. JDBCUtils.close(resultSet,preparedStatement,connection);
  46. }
  47. }
  48. }

八、事务

8.1 基本介绍

  1. JDBC 程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚
  2. JDBC 程序中为了让多个SQL语句作为一个整体执行,需要使用事务
  3. 调用 Connection 的 setAutoCommit(false) 可以取消自动提交事务
  4. 在所有的SQL 语句都成功执行后,调用 Connection 的 commit() 方法提交事务
  5. 在其中某个操作失败或出现异常时,调用 Connection 的 rollback() 方法回滚事务

8.2 应用实例

  1. 不使用事务时模拟转账出现的问题 ``sql CREATE table account ( id INT PRIMARY KEY AUTO_INCREMENT,name` VARCHAR(32) NOT NULL DEFAULT ‘’, balance DOUBLE NOT NULL DEFAULT 0 ) CHARACTER SET utf8;

INSERT INTO account VALUES(null,’马云’,3000),(null,’马化腾’,10000);

SELECT * FROM account;

  1. ```java
  2. package com.hspedu.jdbc.transaction_;
  3. import com.hspedu.jdbc.utils.JDBCUtils;
  4. import java.sql.Connection;
  5. import java.sql.PreparedStatement;
  6. import java.sql.SQLException;
  7. /**
  8. * @author HarborGao
  9. * @version 1.0
  10. * 模拟不使用事务出现的问题 - 转账
  11. */
  12. public class Transaction_ {
  13. public static void main(String[] args) {
  14. Connection connection = JDBCUtils.getConnection();
  15. PreparedStatement preparedStatement = null;
  16. try {
  17. String sql1 = "update account set `balance` = balance - 1000 where `name` = '马云'";
  18. preparedStatement = connection.prepareStatement(sql1);
  19. int rows = preparedStatement.executeUpdate();
  20. System.out.println(rows > 0 ? "扣款成功" : "扣款失败");
  21. int i = 10/0;
  22. String sql2 = "update account set `balance` = balance + 1000 where `name` = '马化腾'";
  23. preparedStatement = connection.prepareStatement(sql2);
  24. int rows2 = preparedStatement.executeUpdate();
  25. System.out.println(rows2 > 0 ? "成功到账" : "没有到账");
  26. } catch (SQLException e) {
  27. e.printStackTrace();
  28. } finally {
  29. JDBCUtils.close(null,preparedStatement,connection);
  30. }
  31. }
  32. }

image.png
image.png

  1. 使用事务解决上述问题 ```java package com.hspedu.jdbc.transaction_;

import com.hspedu.jdbc.utils.JDBCUtils;

import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException;

/**

  • @author HarborGao
  • @version 1.0
  • 模拟使用事务解决问题 - 转账 */ public class Transaction_ { public static void main(String[] args) {

    1. Connection connection = JDBCUtils.getConnection();
    2. String sql1 = "update account set `balance` = balance - 1000 where `name` = '马化腾'";
    3. String sql2 = "update account set `balance` = balance + 1000 where `name` = '马云'";
    4. PreparedStatement preparedStatement = null;
    5. try {
    6. connection.setAutoCommit(false);
    7. preparedStatement = connection.prepareStatement(sql1);
    8. preparedStatement.executeUpdate();
    9. int i = 1 / 0;
    10. preparedStatement = connection.prepareStatement(sql2);
    11. preparedStatement.executeUpdate();
    12. } catch (Exception e) {
    13. try {
    14. System.out.println("发生了异常,回滚...");
    15. connection.rollback();
    16. } catch (SQLException ex) {
    17. ex.printStackTrace();
    18. }
    19. System.out.println(e.getMessage());
    20. } finally {
    21. try {
    22. connection.commit();
    23. } catch (SQLException e) {
    24. e.printStackTrace();
    25. }
    26. JDBCUtils.close(null,preparedStatement,connection);
    27. }

    } } ```

九、批处理

9.1 基本介绍

  1. 当需要成批插入或者更新记录时,可以采用 Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理,通常情况下比单独提交处理更有效率
  2. JDBC 的批量处理语句包括下面方法:
    1. addBatch():添加需要批量处理的SQL语句或参数
    2. executeBatch():执行批量处理语句
    3. clearBatch():清空批量处理包的语句
  3. JDBC 连接 MySQL时,如果要使用批处理功能,请在 url 中加参数 ?rewriteBatchedStatements=true
  4. 批处理往往和 PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高

9.2 应用实例

  1. 演示向 admin2 表中添加 5000 条数据,看看使用批处理耗时多久
  2. 注意:需要修改配置文件,在url中加参数 ```java package com.hspedu.jdbc.batch_;

import com.hspedu.jdbc.utils.JDBCUtils; import org.junit.jupiter.api.Test;

import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException;

/**

  • @author HarborGao
  • @version 1.0
  • 演示Java的批处理 */ public class Batch_ { //传统方法,添加5000条数据到admin2 @Test public void noBatch() throws Exception {

    1. long start = System.currentTimeMillis();
    2. Connection connection = JDBCUtils.getConnection();
    3. String sql = "insert into admin2 values(null,?,?)";
    4. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    5. for (int i = 0; i < 5000; i++) {
    6. preparedStatement.setString(1,"jack" + i);
    7. preparedStatement.setString(2,"666");
    8. preparedStatement.executeUpdate();
    9. }
    10. //关闭连接
    11. JDBCUtils.close(null,preparedStatement,connection);
    12. long stop = System.currentTimeMillis();
    13. System.out.println("传统方式耗时 " + (stop - start)); //传统方式耗时 16725

    }

    //使用批量处理添加数据 @Test public void useBatch() throws Exception {

    1. long start = System.currentTimeMillis();
    2. Connection connection = JDBCUtils.getConnection();
    3. String sql = "insert into admin2 values(null,?,?)";
    4. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    5. for (int i = 0; i < 5000; i++) {
    6. preparedStatement.setString(1,"jack" + i);
    7. preparedStatement.setString(2,"666");
    8. //将SQL语句加入到批量处理包中
    9. preparedStatement.addBatch();
    10. //当有1000条记录时,再批量执行
    11. if ((i + 1) % 1000 == 0) {
    12. preparedStatement.executeBatch();
    13. //清空批量处理包,以备下次使用
    14. preparedStatement.clearBatch();
    15. }
    16. }
    17. //关闭连接
    18. JDBCUtils.close(null,preparedStatement,connection);
    19. long stop = System.currentTimeMillis();
    20. System.out.println("批量方式耗时 " + (stop - start)); //批量方式耗时 273

    } } ```

十、数据库连接池

10.1 5k次连接数据库问题

  1. 编写程序完成连接MySQL数据库5000次的操作
  2. 看看有什么问题,耗时多久? ```java package com.hspedu.jdbc.datasource;

import com.hspedu.jdbc.utils.JDBCUtils; import org.junit.jupiter.api.Test;

import java.sql.Connection;

/**

  • @author HarborGao
  • @version 1.0 */ public class ConQuestion { //连接5000次 MySQL @Test public void testCon() {
    1. for (int i = 0; i < 5000; i++) {
    2. //使用传统的JDBC方式,得到连接
    3. Connection connection = JDBCUtils.getConnection();
    4. //做一些工作,比如得到PreparedStatement,发送 sql
    5. //.....
    6. //关闭
    7. JDBCUtils.close(null,null,connection);
    8. }
    } } ```

10.2 传统获取 Connection问题分析

  1. 传统的JDBC数据库连接使用 DriverManager来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证IP地址,用户名和密码(0.05~1s时间)。需要数据库连接的时候,就像数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
  2. 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
  3. 传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。
  4. 解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)。

10.3 数据库连接池基本介绍

  1. 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需要从“缓冲池”中取出一个,使用完毕后再放回去
  2. 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
  3. 当应用程序向连接池请求的连接数量超过最大连接数量时,这些请求将会被加入到等待队列中
  4. 示意图
    image.png

10.4 数据库连接池种类

  1. JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DateSource 只是一个接口,该接口通常由第三方提供实现 【提供 .jar】
  2. C3P0 数据库连接池,速度相对较慢,稳定性不错
  3. DBCP 数据连接池,速度相对 C3P0较快,但不稳定
  4. Proxool 数据库连接池,有监控连接池状态的功能,稳定性教 C3P0 差一点
  5. BoneCP 数据库连接池,速度快
  6. Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池

10.5 C3P0 应用实例

  1. package com.hspedu.jdbc.datasource;
  2. import com.mchange.v2.c3p0.ComboPooledDataSource;
  3. import org.junit.jupiter.api.Test;
  4. import java.io.FileInputStream;
  5. import java.sql.Connection;
  6. import java.sql.SQLException;
  7. import java.util.Properties;
  8. /**
  9. * @author HarborGao
  10. * @version 1.0
  11. * 演示 C3P0的使用
  12. */
  13. public class C3P0_ {
  14. //方式一 相关的参数再程序中指定 user,url,password 等
  15. @Test
  16. public void useC3P0_01() throws Exception {
  17. //1. 创建一个数据源对象
  18. ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
  19. //2. 通过配置文件 mysql.properties 获取相关连接的信息
  20. Properties properties = new Properties();
  21. properties.load(new FileInputStream("src\\mysql.properties"));
  22. //读取相关属性值
  23. String url = properties.getProperty("url");
  24. String user = properties.getProperty("user");
  25. String password = properties.getProperty("password");
  26. String driver = properties.getProperty("driver");
  27. //给数据源 comboPooledDataSource 设置相关的参数
  28. //注意:连接管理是由 comboPooledDataSource 负责
  29. comboPooledDataSource.setDriverClass(driver);
  30. comboPooledDataSource.setJdbcUrl(url);
  31. comboPooledDataSource.setUser(user);
  32. comboPooledDataSource.setPassword(password);
  33. //设置初始化连接数
  34. comboPooledDataSource.setInitialPoolSize(10);
  35. //设置最大连接数
  36. comboPooledDataSource.setMaxPoolSize(50);
  37. for (int i = 0; i < 5000; i++) {
  38. Connection connection = comboPooledDataSource.getConnection();
  39. connection.close();
  40. }
  41. }
  42. //方式二 使用配置文件模板来完成
  43. //1. 将C3P0 提供的配置文件 复制到src下
  44. //2. 该文件指定了连接数据库和连接池的相关参数
  45. @Test
  46. public void testC3P0_02() throws SQLException {
  47. ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("my_sql");
  48. for (int i = 0; i < 5000; i++) {
  49. Connection connection = comboPooledDataSource.getConnection();
  50. connection.close();
  51. }
  52. }
  53. }

c3p0-config.xml

10.6 Druid(德鲁伊)应用实例

  1. package com.hspedu.jdbc.datasource;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import org.junit.jupiter.api.Test;
  4. import javax.sql.DataSource;
  5. import java.io.FileInputStream;
  6. import java.sql.Connection;
  7. import java.util.Properties;
  8. /**
  9. * @author HarborGao
  10. * @version 1.0
  11. * 演示 Druid 德鲁伊 数据库连接池的使用
  12. */
  13. public class Druid_ {
  14. @Test
  15. public void testDruid() throws Exception {
  16. //1. 加入 Druid jar包
  17. //2. 加入配置文件 druid.properties ,将该文件拷贝到项目 src 目录下
  18. //3. 读取配置文件
  19. Properties properties = new Properties();
  20. properties.load(new FileInputStream("src\\druid.properties"));
  21. //4. 创建一个指定参数的数据库连接池
  22. DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
  23. for (int i = 0; i < 5000; i++) {
  24. Connection connection = dataSource.getConnection();
  25. connection.close();
  26. }
  27. }
  28. }
  1. # druid.properties 配置文件
  2. #key=value
  3. driverClassName=com.mysql.jdbc.Driver
  4. url=jdbc:mysql://localhost:3306/db01?rewriteBatchedStatements=true
  5. #url=jdbc:mysql://localhost:3306/girls
  6. username=root
  7. password=111
  8. #initial connection Size
  9. initialSize=10
  10. #min idle connection size
  11. minIdle=5
  12. #max active connection size
  13. maxActive=50
  14. #max wait time (5000 mil seconds)
  15. maxWait=5000

10.7 将JDBCUtils 工具类改成Druid(德鲁伊)实现

  1. package com.hspedu.jdbc.datasource;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import javax.sql.DataSource;
  4. import java.io.FileInputStream;
  5. import java.sql.Connection;
  6. import java.sql.ResultSet;
  7. import java.sql.SQLException;
  8. import java.sql.Statement;
  9. import java.util.Properties;
  10. /**
  11. * @author HarborGao
  12. * @version 1.0
  13. * 基于Druid数据库连接池的工具类
  14. */
  15. public class JDBCUtilsByDruid {
  16. private static DataSource ds;
  17. //在静态代码块中完成初始化
  18. static {
  19. Properties properties = new Properties();
  20. try {
  21. properties.load(new FileInputStream("src\\druid.properties"));
  22. ds = DruidDataSourceFactory.createDataSource(properties);
  23. } catch (Exception e) {
  24. throw new RuntimeException(e);
  25. }
  26. }
  27. public static Connection getConnection() {
  28. try {
  29. return ds.getConnection();
  30. } catch (SQLException e) {
  31. throw new RuntimeException(e);
  32. }
  33. }
  34. public static void close(ResultSet resultSet, Statement statement, Connection connection) {
  35. try {
  36. if (resultSet != null) {
  37. resultSet.close();
  38. }
  39. if (statement != null) {
  40. statement.close();
  41. }
  42. if (connection != null) {
  43. connection.close();
  44. }
  45. } catch (SQLException e) {
  46. throw new RuntimeException(e);
  47. }
  48. }
  49. }

十一、Apache - DBUtils

11.1 先分析一个问题

  1. 关闭 connection后,resultSet 结果集无法使用
  2. resultSet 不利于数据的管理
  3. 示意图
    image.png

11.2 用已学知识自己尝试写一个工具类实现

  1. @Test
  2. public void testSelectToArrayList() {
  3. Connection connection = null;
  4. String sql = "select * from actor";
  5. PreparedStatement preparedStatement = null;
  6. ResultSet resultSet = null;
  7. ArrayList<Actor> list = new ArrayList<>();
  8. try {
  9. connection = JDBCUtilsByDruid.getConnection();
  10. preparedStatement = connection.prepareStatement(sql);
  11. resultSet = preparedStatement.executeQuery();
  12. while (resultSet.next()) {
  13. int id = resultSet.getInt("id");
  14. String name = resultSet.getString("name");
  15. String sex = resultSet.getString("sex");
  16. Date borndate = resultSet.getDate("borndate");
  17. String phone = resultSet.getString("phone");
  18. //把得到的 resultSet 记录,封装到一个Actor对象,再放入到ArrayList
  19. list.add(new Actor(id,name,sex,borndate,phone));
  20. }
  21. } catch (SQLException e) {
  22. e.printStackTrace();
  23. } finally {
  24. JDBCUtilsByDruid.close(resultSet,preparedStatement,connection);
  25. }
  26. System.out.println("集合中的数据:" + list);
  27. }
  1. package com.hspedu.jdbc.datasource;
  2. import java.util.Date;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. public class Actor {
  8. private Integer id;
  9. private String name;
  10. private String sex;
  11. private Date borndate;
  12. private String phone;
  13. public Actor() {} //一定要给一个无参构造器,反射需要
  14. public Actor(Integer id, String name, String sex, Date borndate, String phone) {
  15. this.id = id;
  16. this.name = name;
  17. this.sex = sex;
  18. this.borndate = borndate;
  19. this.phone = phone;
  20. }
  21. public Integer getId() {
  22. return id;
  23. }
  24. public void setId(Integer id) {
  25. this.id = id;
  26. }
  27. public String getName() {
  28. return name;
  29. }
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33. public String getSex() {
  34. return sex;
  35. }
  36. public void setSex(String sex) {
  37. this.sex = sex;
  38. }
  39. public Date getBorndate() {
  40. return borndate;
  41. }
  42. public void setBorndate(Date borndate) {
  43. this.borndate = borndate;
  44. }
  45. public String getPhone() {
  46. return phone;
  47. }
  48. public void setPhone(String phone) {
  49. this.phone = phone;
  50. }
  51. @Override
  52. public String toString() {
  53. return "\nActor{" +
  54. "id=" + id +
  55. ", name='" + name + '\'' +
  56. ", sex='" + sex + '\'' +
  57. ", borndate=" + borndate +
  58. ", phone='" + phone + '\'' +
  59. '}';
  60. }
  61. }

11.3 DBUtils 基本介绍

  1. commons-dbutils 是 Apache组织提供的一个开源 JDBC工具类库,它是对JDBC的封装,使用dbutils 能极大简化 JDBC 编码的工作量
  2. DBUtils 类
    1. QueryRunner类:该类封装了SQL的执行,是线程安全的,可以实现增、删、改、查、批处理
    2. 使用QueryRunner类实现查询
    3. ResultSetHandler接口:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式

ArrayHandler:把结果集中的第一行数据转成对象数组
ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里
ColumnListHandler:将结果集中的某一列数据存放到List中
KeyedHandler(name):将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List

11.4 应用实例

使用DBUtils + 数据连接池(德鲁伊)方式,完成对表actor的crud

  1. package com.hspedu.jdbc.dbutis_;
  2. import com.hspedu.jdbc.datasource.Actor;
  3. import com.hspedu.jdbc.datasource.JDBCUtilsByDruid;
  4. import org.apache.commons.dbutils.QueryRunner;
  5. import org.apache.commons.dbutils.ResultSetHandler;
  6. import org.apache.commons.dbutils.handlers.BeanListHandler;
  7. import org.junit.jupiter.api.Test;
  8. import java.sql.Connection;
  9. import java.sql.SQLException;
  10. import java.util.List;
  11. /**
  12. * @author HarborGao
  13. * @version 1.0
  14. * 使用apache-DBUtils 工具类+druid完成对表的crud
  15. */
  16. public class DBUtils_USE {
  17. @Test
  18. public void testQueryMany() throws SQLException {
  19. //1. 得到连接
  20. Connection connection = JDBCUtilsByDruid.getConnection();
  21. //2. 使用 DBUtils 类和接口,先引入相关的 jar包
  22. //3. 创建 QueryRunner
  23. QueryRunner queryRunner = new QueryRunner();
  24. //4. 就可以执行相关方法,返回ArrayList结果集
  25. //String sql = "select * from actor where id >= ?";
  26. String sql = "select id,name from actor where id > ?";
  27. //说明:
  28. //(1) query方法 执行一个sql语句,得到resultSet --封装到--> ArrayList集合中
  29. //(2) 返回集合
  30. //(3) connection: 连接
  31. //(4) sql: 执行的sql语句
  32. //(5) new BeanListHandler<>(Actor.class): 在将resultSet -> Actor 对象 -> 封装到 ArrayList
  33. // 底层使用反射机制 去获取Actor类的属性,然后进行封装
  34. //(6) 1: 可变形参,可以有多个,作用是给sql语句中的 ? 赋值
  35. //(7) 底层得到的resultSet, 会在query 关闭,并关闭 preparedStatement
  36. List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
  37. System.out.println("输出集合的信息");
  38. for (Actor actor : list) {
  39. System.out.print(actor);
  40. }
  41. JDBCUtilsByDruid.close(null,null,connection);
  42. }
  43. @Test
  44. public void testScaler() throws SQLException {
  45. Connection connection = JDBCUtilsByDruid.getConnection();
  46. QueryRunner queryRunner = new QueryRunner();
  47. String sql = "select name from actor where id = ?";
  48. //说明:返回单行单列记录 使用的是ScalarHandler()
  49. Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 2);
  50. System.out.println(obj);
  51. JDBCUtilsByDruid.close(null,null,connection);
  52. }
  53. //演示 apache-dbutils + druid 完成DML操作 (update,delete,insert)
  54. @Test
  55. public void testDML() throws SQLException {
  56. Connection connection = JDBCUtilsByDruid.getConnection();
  57. QueryRunner queryRunner = new QueryRunner();
  58. //这里组织sql语句完成相应的 DML操作
  59. //String sql = "insert into actor values(null,?,?,?,?)";
  60. //String sql = "update actor set `name` = ? where id = ?";
  61. String sql = "delete from actor where id = ?";
  62. //int rows = queryRunner.update(connection, sql,"张三丰","男","1956-08-19","133");
  63. //int rows = queryRunner.update(connection, sql,"爱丽丝",3);
  64. int affectedRow = queryRunner.update(connection, sql,4);
  65. System.out.println(affectedRow > 0 ? "成功" : "失败");
  66. JDBCUtilsByDruid.close(null,null,connection);
  67. }
  68. }

11.5 表和JavaBean 的类型映射关系

image.png

十二、DAO 和增删改查通用方法-BasicDao

12.1 先分析一个问题

apache-dbutils + Druid 简化了JDBC开发,但还有不足:

  1. SQL 语句是固定的,不能通过参数传入,通用性不好,需要进行改进,更方便的执行 增删改查
  2. 对于 select操作,如果有返回值,返回类型不能固定,需要使用泛型
  3. 将来的表很多,业务需求复杂,不可能只靠一个Java类完成

BasicDAO 示意图
img01.jpg

12.2 基本说明

  1. DAO:data access object 数据访问对象
  2. BasicDAO,是专门和数据库交互的,即完成对数据库(表)的CRUD操作
  3. 在BasicDAO的基础上,实现一张表对应一个DAO,更好的完成功能,比如Customer表 - Customer.java类 - CunsomerDAO.java

12.3 应用实例

完成一个简单设计

  1. com.hspedu.dao_.utils 工具类
  2. com.hspedu.dao_.domain javaBean
  3. com.hspedu.dao_.dao 存放XxxDao 和 BasicDao
  4. com.hspedu.dao_.test 写测试类

image.png
JDBCUtilsByDruid.java 【工具类】

  1. package com.hspedu.dao_.utils;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import javax.sql.DataSource;
  4. import java.io.FileInputStream;
  5. import java.sql.Connection;
  6. import java.sql.ResultSet;
  7. import java.sql.SQLException;
  8. import java.sql.Statement;
  9. import java.util.Properties;
  10. /**
  11. * @author HarborGao
  12. * @version 1.0
  13. * 基于Druid数据库连接池的工具类
  14. */
  15. public class JDBCUtilsByDruid {
  16. private static DataSource ds;
  17. //在静态代码块中完成初始化
  18. static {
  19. Properties properties = new Properties();
  20. try {
  21. properties.load(new FileInputStream("src\\druid.properties"));
  22. ds = DruidDataSourceFactory.createDataSource(properties);
  23. } catch (Exception e) {
  24. throw new RuntimeException(e);
  25. }
  26. }
  27. public static Connection getConnection() {
  28. try {
  29. return ds.getConnection();
  30. } catch (SQLException e) {
  31. throw new RuntimeException(e);
  32. }
  33. }
  34. public static void close(ResultSet resultSet, Statement statement, Connection connection) {
  35. try {
  36. if (resultSet != null) {
  37. resultSet.close();
  38. }
  39. if (statement != null) {
  40. statement.close();
  41. }
  42. if (connection != null) {
  43. connection.close();
  44. }
  45. } catch (SQLException e) {
  46. throw new RuntimeException(e);
  47. }
  48. }
  49. }

Actor.java 【JavaBean】

  1. package com.hspedu.dao_.domain;
  2. import java.util.Date;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. public class Actor {
  8. private Integer id;
  9. private String name;
  10. private String sex;
  11. private Date borndate;
  12. private String phone;
  13. public Actor() {} //一定要给一个无参构造器,反射需要
  14. public Actor(Integer id, String name, String sex, Date borndate, String phone) {
  15. this.id = id;
  16. this.name = name;
  17. this.sex = sex;
  18. this.borndate = borndate;
  19. this.phone = phone;
  20. }
  21. public Integer getId() {
  22. return id;
  23. }
  24. public void setId(Integer id) {
  25. this.id = id;
  26. }
  27. public String getName() {
  28. return name;
  29. }
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33. public String getSex() {
  34. return sex;
  35. }
  36. public void setSex(String sex) {
  37. this.sex = sex;
  38. }
  39. public Date getBorndate() {
  40. return borndate;
  41. }
  42. public void setBorndate(Date borndate) {
  43. this.borndate = borndate;
  44. }
  45. public String getPhone() {
  46. return phone;
  47. }
  48. public void setPhone(String phone) {
  49. this.phone = phone;
  50. }
  51. @Override
  52. public String toString() {
  53. return "\nActor{" +
  54. "id=" + id +
  55. ", name='" + name + '\'' +
  56. ", sex='" + sex + '\'' +
  57. ", borndate=" + borndate +
  58. ", phone='" + phone + '\'' +
  59. '}';
  60. }
  61. }

BasicDAO.java 【DAO】

  1. package com.hspedu.dao_.dao;
  2. import com.hspedu.dao_.utils.JDBCUtilsByDruid;
  3. import org.apache.commons.dbutils.QueryRunner;
  4. import org.apache.commons.dbutils.handlers.BeanHandler;
  5. import org.apache.commons.dbutils.handlers.BeanListHandler;
  6. import org.apache.commons.dbutils.handlers.ScalarHandler;
  7. import java.sql.Connection;
  8. import java.util.List;
  9. /**
  10. * @author HarborGao
  11. * @version 1.0
  12. * 开发 BasicDAO,是其他DAO的父类
  13. */
  14. public class BasicDAO<T> {
  15. private QueryRunner qr = new QueryRunner();
  16. //开发通用的 DML方法,针对任意表
  17. public int update(String sql,Object... parameters) {
  18. Connection connection = null;
  19. try {
  20. connection = JDBCUtilsByDruid.getConnection();
  21. int update = qr.update(connection, sql, parameters);
  22. return update;
  23. } catch (Exception e) {
  24. throw new RuntimeException(e);
  25. } finally {
  26. JDBCUtilsByDruid.close(null,null,connection);
  27. }
  28. }
  29. /**
  30. *
  31. * @param sql sql语句,可以有 ?
  32. * @param clazz 传入一个类的Class对象 比如 Actor.class
  33. * @param parameters 传入 ? 的具体的值,可以是多个
  34. * @return 根据 Actor.class 返回对应的 ArrayList
  35. */
  36. //返回多个对象(即查询的结果是多行),针对任意表
  37. public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {
  38. Connection connection = null;
  39. try {
  40. connection = JDBCUtilsByDruid.getConnection();
  41. List<T> query = qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
  42. return query;
  43. } catch (Exception e) {
  44. throw new RuntimeException(e);
  45. } finally {
  46. JDBCUtilsByDruid.close(null,null,connection);
  47. }
  48. }
  49. //查询单行结果的通用方法
  50. public T querySingle(String sql,Class<T> clazz,Object... parameters) {
  51. Connection connection = null;
  52. try {
  53. connection = JDBCUtilsByDruid.getConnection();
  54. return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
  55. } catch (Exception e) {
  56. throw new RuntimeException(e);
  57. } finally {
  58. JDBCUtilsByDruid.close(null,null,connection);
  59. }
  60. }
  61. //查询单行单列 即单值的方法
  62. public Object queryScalar(String sql, Object... parameters) {
  63. Connection connection = null;
  64. try {
  65. connection = JDBCUtilsByDruid.getConnection();
  66. return qr.query(connection, sql, new ScalarHandler(), parameters);
  67. } catch (Exception e) {
  68. throw new RuntimeException(e);
  69. } finally {
  70. JDBCUtilsByDruid.close(null,null,connection);
  71. }
  72. }
  73. }

ActorDAO.java 【DAO】

  1. package com.hspedu.dao_.dao;
  2. import com.hspedu.dao_.domain.Actor;
  3. /**
  4. * @author HarborGao
  5. * @version 1.0
  6. */
  7. public class ActorDAO extends BasicDAO<Actor>{
  8. //1. 自动继承BasicDAO的方法
  9. //2. 根据业务需求,编写特有的方法
  10. }

TestDAO.java 【测试】

  1. package com.hspedu.dao_.test;
  2. import com.hspedu.dao_.dao.ActorDAO;
  3. import com.hspedu.dao_.dao.GoodsDAO;
  4. import com.hspedu.dao_.domain.Actor;
  5. import com.hspedu.dao_.domain.Goods;
  6. import org.junit.jupiter.api.Test;
  7. import java.util.List;
  8. /**
  9. * @author HarborGao
  10. * @version 1.0
  11. */
  12. public class TestDAO {
  13. //测试 ActorDAO 对 actor 表的 crud 操作
  14. @Test
  15. public void testActorDAO() {
  16. ActorDAO actorDAO = new ActorDAO();
  17. //1. 查询
  18. List<Actor> actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 1);
  19. System.out.println("===查询多行结果===");
  20. for (Actor actor : actors) {
  21. System.out.println(actor);
  22. }
  23. //2. 查询单行记录
  24. Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 1);
  25. System.out.println("===查询单行结果===");
  26. System.out.println(actor);
  27. //3. 查询单值
  28. Object o = actorDAO.queryScalar("select name from actor where id = ?", 1);
  29. System.out.println("===查询单行单列值===");
  30. System.out.println(o);
  31. //4. DML操作
  32. int update = actorDAO.update("insert into actor values (null,?,?,?,?)", "宋祖儿", "女", "1997-06-19", "120");
  33. System.out.println(update > 0 ? "成功" : "失败");
  34. }
  35. }

 

学习参考(致谢):

  1. B站 @程序员鱼皮 Java学习一条龙
  2. B站 @韩顺平 零基础30天学会Java