JDBC

https://github.com/sanshisi/JDBC_Study.git

MySql — 8.0.20

Java — 11.0.11

2021.12.12

  1. =====emp表的结构=====
  2. +---------+-------------+------+-----+---------+----------------+
  3. | Field | Type | Null | Key | Default | Extra |
  4. +---------+-------------+------+-----+---------+----------------+
  5. | id | int | NO | PRI | NULL | auto_increment |
  6. | name | varchar(10) | YES | | NULL | |
  7. | dept_id | int | YES | | NULL | |
  8. +---------+-------------+------+-----+---------+----------------+
  9. =====dept表的结构=====
  10. +-------+-------------+------+-----+---------+-------+
  11. | Field | Type | Null | Key | Default | Extra |
  12. +-------+-------------+------+-----+---------+-------+
  13. | id | int | YES | | NULL | |
  14. | name | varchar(10) | YES | | NULL | |
  15. | intro | varchar(10) | YES | | NULL | |
  16. +-------+-------------+------+-----+---------+-------+

1.JDBC介绍

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的 SQL 数据库存取和操作的公共接口(一组 API),定义了用来访问 数据库的标准Java 类库,(java.sql,javax.sql)使用这个类库可以以一种 标准的方法、方便地访问数据库资源

JDBC 为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题JDBC 的目标是使 Java 程序员使用 JDBC 可以连接任何提供了 JDBC 驱动程 序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过 多的了解,从而大大简化和加快了开发过程

2.链接数据库

先导包

  1. package com.blog;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. public class TestConnection {
  6. public static void main(String[] args) {
  7. try {
  8. // 1.加载驱动 [可以不写]
  9. Class.forName("com.mysql.cj.jdbc.Driver");
  10. // 2.通过DriverManager类来获取驱动
  11. // 2.1 提供Properties的对象,指明用户名和密码
  12. // 因为是本地链接,所以jdbc:mysql://localhost:3306/db_web...可以简写
  13. DriverManager.getConnection("jdbc:mysql:///db_web?user=root&password=root&serverTimezome=Asia/Shanghai");
  14. //Properties 该类主要用于读取Java的配置文件
  15. // Properties info = new Properties();
  16. // info.setProperty("user", "root");
  17. // info.setProperty("password", "root");
  18. // info.setProperty("serverTimezone", "Asia/Shanghai"); // 设置时区
  19. // DriverManager.getConnection("jdbc:mysql:///db_web", info);
  20. // 2.2 直接在获取驱动时传入
  21. Connection connection = DriverManager.getConnection("jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai",
  22. "root", "root");
  23. System.out.println(connection); // 输出获取的connection
  24. } catch (ClassNotFoundException e) {
  25. e.printStackTrace();
  26. } catch (SQLException throwables) {
  27. throwables.printStackTrace();
  28. }
  29. }
  30. }

通过配置文件链接数据库

  1. @Test
  2. public void testFile() {
  3. try {
  4. // 最终解决 硬编码问题
  5. // Properties 主要用于读取Java配置文件信息
  6. Properties properties = new Properties();
  7. properties.load(this.getClass().getClassLoader().getResourceAsStream("jdbc.properties"));
  8. String classDriver = properties.getProperty("jdbc.classDriver");
  9. String url = properties.getProperty("jdbc.url");
  10. String user = properties.getProperty("jdbc.user");
  11. String password = properties.getProperty("jdbc.password");
  12. // 加载驱动
  13. Class.forName(classDriver);
  14. Connection connection = null;
  15. // 传入参数
  16. connection = DriverManager.getConnection(url, user, password);
  17. System.out.println(connection);
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } catch (ClassNotFoundException e) {
  21. e.printStackTrace();
  22. } catch (SQLException throwables) {
  23. throwables.printStackTrace();
  24. }
  25. }

配置文件声明在工程的 src 目录下:【jdbc.properties】

  1. jdbc.classDriver=com.mysql.cj.jdbc.Driver
  2. jdbc.url=jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai
  3. jdbc.user=root
  4. jdbc.password=root
  1. // JDBC调用存储过程
  2. // CallableStatement 该对象是用来调用数据库的存储过程
  3. CallableStatement prepareCall = connection.prepareCall("call 存储过程名称");

3.PreparedStatement 实现 CRUD 操作

CRUD:增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)

🍕Statement:用于执行静态 SQL 语句并返回它所生成结果的对象

🍕PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句

🍕CallableStatement:用于执行 SQL 存储过程

3.1 Statement操作数据表

在 Java 早期使用的是Statement 接口来操作 SQL,但是因为这用字符串拼接 SQL 的方式完成 SQL 语句的,容易引发 SQL 注入漏洞问题,所以现在几乎都不再是用来,取而代之的则是使用 PreparedStatement 接口

通过调用Connection对象的 createStatement()方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果

  1. execute(sql) 执行SQL,返回值是个boolean值,表示是否成功,主要用来执行 create drop等等语句(主要用于初始化)
  2. executeBatch() 用于执行批量SQL
  3. statement.executeQuery(sql) 用于查询语句(DQL)
  4. statement.executeUpdate(sql) 主要用于DML语句 返回的是

Statement做 插入和查询

  1. package com.blog;
  2. import com.study001.Emp;
  3. import org.junit.Test;
  4. import java.sql.*;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class TestStatement {
  8. @Test
  9. public void InnerTest() {
  10. try {
  11. // 1.加载驱动
  12. Class.forName("com.mysql.cj.jdbc.Driver");
  13. // 2.通过DriverManager类获取数据库链接
  14. Connection connection = DriverManager.getConnection("jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai",
  15. "root", "root");
  16. // 3.获取Statement对象
  17. Statement statement = connection.createStatement();
  18. String sql = "insert emp(id,name,dept_id) values(10,'小鱼二',3)";
  19. int count = statement.executeUpdate(sql);
  20. System.out.println(sql);
  21. if (count > 0) {
  22. System.out.println("count=" + count + ",插入成功");
  23. } else {
  24. System.out.println("count=" + count + ",插入失败");
  25. }
  26. } catch (ClassNotFoundException e) {
  27. e.printStackTrace();
  28. } catch (SQLException throwables) {
  29. throwables.printStackTrace();
  30. }
  31. }
  32. @Test
  33. public void QueryAllTest() {
  34. Connection connection = null; // 获取数据库链接
  35. Statement statement = null; // 获取Statement对象进行CRUD操作
  36. ResultSet resultSet = null; // 接受 statement.executeQuery(sql) 查询对象
  37. List<com.study001.Emp> emps = new ArrayList<>();
  38. Emp emp = null;
  39. try {
  40. // 1.加载驱动
  41. Class.forName("com.mysql.cj.jdbc.Driver");
  42. // 2.通过DriverManager类获取数据库链接
  43. connection = DriverManager.getConnection("jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai",
  44. "root", "root");
  45. // 3.获取Statement对象
  46. statement = connection.createStatement();
  47. String sql = "select id,name,dept_id from emp"; // 在真正开发时不能使用 select *
  48. resultSet = statement.executeQuery(sql);
  49. // 因为可能存在多条数据,所以使用循环不断的取值,直到没有值为止
  50. while (resultSet.next()) {
  51. // 进入循环,意味着开始读取一行数据2211
  52. // sql中的顺序都是从1开始的
  53. // int id = resultSet.getInt(1);
  54. // String name = resultSet.getString(2);
  55. // int deptId = resultSet.getInt(3);
  56. int id = resultSet.getInt("id");
  57. String name = resultSet.getString("name");
  58. int deptId = resultSet.getInt("dept_Id");
  59. // System.out.println("id=" + id + ", name=" + name + ", dept_id=" + deptId);
  60. emp = new Emp(id,name,deptId);
  61. emps.add(emp);
  62. }
  63. } catch (ClassNotFoundException e) {
  64. e.printStackTrace();
  65. } catch (SQLException throwables) {
  66. throwables.printStackTrace();
  67. } finally {
  68. // 释放资源
  69. this.closeResource(connection, statement, resultSet);
  70. }
  71. // 使用方法引用对emps进行遍历
  72. emps.forEach(System.out::println);
  73. }
  74. // 做一个公共的关闭资源的工具
  75. public static void closeResource(Connection connection, Statement statement, ResultSet resultSet) {
  76. if (resultSet != null) {
  77. try {
  78. resultSet.close();
  79. } catch (SQLException e) {
  80. e.printStackTrace();
  81. }
  82. }
  83. if (statement != null) {
  84. try {
  85. statement.close();
  86. } catch (SQLException e) {
  87. e.printStackTrace();
  88. }
  89. }
  90. if (connection != null) {
  91. try {
  92. connection.close();
  93. } catch (SQLException e) {
  94. e.printStackTrace();
  95. }
  96. }
  97. }
  98. }

✨emp.java

  1. package com.blog;
  2. public class Emp {
  3. private Integer id;
  4. private String name;
  5. private Integer deptId;
  6. public Emp(Integer id, String name, Integer dept_id) {
  7. this.id = id;
  8. this.name = name;
  9. this.deptId = dept_id;
  10. }
  11. @Override
  12. public String toString() {
  13. return "Emp{" +
  14. "id=" + id +
  15. ", name='" + name + '\'' +
  16. ", dept_id=" + deptId +
  17. '}';
  18. }
  19. }

释放资源

释放 ResultSet, Statement,Connection

数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果 Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放

可以在 finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭


SQL 注入

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHEREuser='a' OR 1 = ' AND password = ' b'1' ='1') ,从而利用系统的 SQL 引擎完成恶意行为的做法。对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从 Statement 扩展而来) 取代 Statement 就可以了

3.2 PreparedStatement操作数据表

可 以 通 过 调 用 Connection 对 象 的 preparedStatement(String sql) 方 法 获 取PreparedStatement对象

PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句PreparedStatement 对象所 代 表 的 SQL 语 句 中 的 参 数 用 问 号 (?) 来 表 示 , 调 用PreparedStatement对象的setXxx()方法来设置这些参数.

setXxx()方法有两个参数,第一个参数是要设置的 SQL语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

PrepareStatement 做 插入

其它操作会在工具类中统一给出

  1. @Test
  2. public void testPs() {
  3. Connection connection = null;
  4. PreparedStatement ps = null;
  5. ResultSet rs = null;
  6. Scanner sc = new Scanner(System.in);
  7. try {
  8. connection = DriverManager
  9. .getConnection("jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai",
  10. "root", "root");
  11. System.out.print("请输入用户名:");
  12. String name = sc.nextLine().trim(); // 去除空格
  13. System.out.print("请输入密码:");
  14. String password = sc.nextLine().trim(); // 去除空格
  15. String sql = "select * from t_user where name = ? and password = ?";
  16. System.out.println(sql);
  17. ps = connection.prepareStatement(sql);
  18. ps.setString(1,name);
  19. ps.setString(2,password);
  20. rs = ps.executeQuery();
  21. if (rs.next()) {
  22. System.out.println("登录成功");
  23. } else {
  24. System.out.println("登录失败");
  25. }
  26. } catch (SQLException throwables) {
  27. throwables.printStackTrace();
  28. } finally {
  29. sc.close();
  30. closeResource(connection, ps, rs);
  31. }
  32. }

3.3 ResultSet 与 ResultSetMetaData

3.3.1 ResultSet

查询需要调用PreparedStatement 的 executeQuery()方法,查询结果是一个ResultSet对象

ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现’

ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。

ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator 对象的 hasNext()和 next()方法 的 结 合 体 。 当 指 针 指 向 一 行 时 , 可 以 通 过 调 用 getXxx(int index)或 getXxx(intcolumnName) 获取每一列的值。

例如: getInt(1), getString("name")

注意:Java 与数据库交互涉及到的相关 Java API 中的索引都从 1 开始。

✨ResultSet 接口的常用方法:

  1. boolean next()
  2. getString()

3.3.2 ResultSetMetaData

可用于获取关于 ResultSet 对象中列的类型和属性信息的对象

ResultSetMetaData meta = rs.getMetaData();

✨常用方法:

  1. ====✨
  2. getColumnName(int column) :获取指定列的名称
  3. getColumnLabel(int column) :获取指定列的别名
  4. getColumnCount() :返回当前 ResultSet 对象中的列数
  5. ====✨
  6. getColumnTypeName(int column):检索指定列的数据库特定的类型名称
  7. getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位
  8. isNullable(int column):指示指定列中的值是否可以为 null
  9. isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的
  1. @Test
  2. void testResultSetMetaData() {
  3. Connection connection = null;
  4. PreparedStatement ps = null;
  5. try {
  6. connection = DriverManager
  7. .getConnection("jdbc:mysql:///db_web?serverTimezone=Asia/Shanghai",
  8. "root", "root");
  9. // 获取PrepareStatement对象
  10. ps = connection.prepareStatement("select id, name userName, age, intros from t_user");
  11. // 获取ResultSetMetaData对象
  12. ResultSetMetaData metaData = ps.getMetaData();
  13. System.out.println("查询的字段的数量:"+ metaData.getColumnCount());
  14. System.out.println("查询的结果的列的名称:"+ metaData.getColumnName(2));
  15. System.out.println("查询的结果的字段的名称(别名):"+ metaData.getColumnLabel(2));
  16. } catch (SQLException e) {
  17. e.printStackTrace();
  18. } finally {
  19. TestConnection.closeResource(connection, ps, null);
  20. }
  21. }

4.JDBC通用工具类的实现

一般在开发中,我们会封装数据,实现一些好用的工具类

PropertieUtil.java

通过传入配置文件的名称获取 Properties 对象

  1. package com.util;
  2. import java.io.IOException;
  3. import java.util.Properties;
  4. public class PropertieUtil {
  5. private static Properties properties;
  6. public static Properties getProperties(String name) {
  7. if (properties == null) {
  8. properties = new Properties();
  9. }
  10. try {
  11. // 获取配置文件 properties
  12. properties.load(PropertieUtil.class.getClassLoader().getResourceAsStream(name));
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. return properties;
  17. }
  18. }

DbUtil.java

进一步获取 Connection 对象

  1. package com.util;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. import java.util.Properties;
  6. public class DbUtil {
  7. public static Connection getConnectionByJdbc() {
  8. Properties prop = PropertieUtil.getProperties("jdbc.properties"); // 通过配置文件获取 properties
  9. String CalssDrive = prop.getProperty("jdbc.classDriver");
  10. String url = prop.getProperty("jdbc.url");
  11. String user = prop.getProperty("jdbc.user");
  12. String password = prop.getProperty("jdbc.password");
  13. // 加载驱动
  14. try {
  15. Class.forName(CalssDrive);
  16. Connection conn = DriverManager.getConnection(url,user,password);
  17. return conn;
  18. } catch (ClassNotFoundException e) {
  19. e.printStackTrace();
  20. } catch (SQLException throwables) {
  21. throwables.printStackTrace();
  22. }
  23. return null;
  24. }
  25. }

DBHepler.java

执行 DML 操作

  1. package com.util;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.sql.*;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class DBHepler {
  8. /**
  9. * 功能:更新工具方法DML语句 (增删改查操作)
  10. *
  11. * @param sql DML SQL
  12. * @param args 问号的参数
  13. * @return 影响的行数
  14. */
  15. public static int update(String sql, Object... args) {
  16. Connection connection = null;
  17. PreparedStatement ps = null;
  18. try {
  19. connection = DbUtil.getConnectionByJdbc();
  20. ps = connection.prepareStatement(sql);
  21. for (int i = 0; i < args.length; i++) {
  22. ps.setObject(i + 1, args[i]);
  23. }
  24. int count = ps.executeUpdate();
  25. return count;
  26. } catch (SQLException throwables) {
  27. throwables.printStackTrace();
  28. } finally {
  29. closeResource(connection, ps, null);
  30. }
  31. return 0;
  32. }
  33. /***
  34. * 封装查询所有对象的结果集,返回的泛型对象的List
  35. *
  36. * @param <T> 对象类型
  37. * @param clzz 对象的字节码文件
  38. * @param sql 查询SQL
  39. * @param args 问号的参数
  40. * @return 泛型对象的List, 表示符合条件的所有数据库记录(对象)
  41. */
  42. public static <T> T queryOne(Class<T> clzz, String sql, Object... args) {
  43. Connection connection = null;
  44. PreparedStatement ps = null;
  45. ResultSet rs = null;
  46. T t = null;
  47. connection = DbUtil.getConnectionByJdbc();
  48. try {
  49. ps = connection.prepareStatement(sql);
  50. // 为?赋值
  51. for (int i = 0; i < args.length; i++) {
  52. ps.setObject(i + 1, args[i]);
  53. }
  54. rs = ps.executeQuery();
  55. ResultSetMetaData metaData = ps.getMetaData();
  56. if (rs.next()) {
  57. // 通过反射获取对象
  58. t = clzz.getConstructor().newInstance();
  59. int columCount = metaData.getColumnCount();
  60. for (int i = 1; i <= columCount; i++) {
  61. // 通过下标获取列的别名
  62. // 此处应当注意 获取的是别名 而别名应当是 Emp类 中的属性
  63. String columnLabel = metaData.getColumnLabel(i);
  64. Object columValue = rs.getObject(columnLabel);
  65. // 通过反射获取属性
  66. Field field = clzz.getDeclaredField(columnLabel);
  67. field.setAccessible(true); // 解除属性私有性的影响
  68. field.set(t, columValue);
  69. }
  70. }
  71. } catch (SQLException throwables) {
  72. throwables.printStackTrace();
  73. } catch (InvocationTargetException e) {
  74. e.printStackTrace();
  75. } catch (InstantiationException e) {
  76. e.printStackTrace();
  77. } catch (IllegalAccessException e) {
  78. e.printStackTrace();
  79. } catch (NoSuchMethodException e) {
  80. e.printStackTrace();
  81. } catch (NoSuchFieldException e) {
  82. e.printStackTrace();
  83. }
  84. return t;
  85. }
  86. /***
  87. * 封装查询所有对象的结果集,返回的泛型对象的List
  88. *
  89. * @param <T> 对象类型
  90. * @param clzz 对象的字节码文件
  91. * @param sql 查询SQL
  92. * @param args 问号的参数
  93. * @return 泛型对象的List, 表示符合条件的所有数据库记录(对象)
  94. */
  95. public static <T> List<T> queryAll(Class<T> clzz, String sql, Object... args) {
  96. Connection connection = null;
  97. PreparedStatement ps = null;
  98. ResultSet rs = null;
  99. List<T> list = new ArrayList<>();
  100. T t = null;
  101. try {
  102. connection = DbUtil.getConnectionByJdbc();
  103. ps = connection.prepareStatement(sql);
  104. for (int i = 0; i < args.length; i++) {
  105. ps.setObject(i + 1, args[i]);
  106. }
  107. rs = ps.executeQuery();
  108. ResultSetMetaData metaData = ps.getMetaData();
  109. while (rs.next()) {
  110. t = clzz.getConstructor().newInstance();
  111. int columnCount = metaData.getColumnCount();
  112. for (int i = 1; i <= columnCount; i++) {
  113. String columnLabel = metaData.getColumnLabel(i);
  114. Object columnValue = rs.getObject(columnLabel);
  115. Field field = clzz.getDeclaredField(columnLabel);
  116. field.setAccessible(true);
  117. field.set(t, columnValue);
  118. }
  119. list.add(t);
  120. }
  121. } catch (SQLException e) {
  122. e.printStackTrace();
  123. } catch (NoSuchFieldException e) {
  124. e.printStackTrace();
  125. } catch (SecurityException e) {
  126. e.printStackTrace();
  127. } catch (IllegalArgumentException e) {
  128. e.printStackTrace();
  129. } catch (IllegalAccessException e) {
  130. e.printStackTrace();
  131. } catch (InstantiationException e) {
  132. e.printStackTrace();
  133. } catch (InvocationTargetException e) {
  134. e.printStackTrace();
  135. } catch (NoSuchMethodException e) {
  136. e.printStackTrace();
  137. } finally {
  138. closeResource(connection, ps, rs);
  139. }
  140. return list;
  141. }
  142. // 公共的关闭资源的工具
  143. private static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet) {
  144. if (resultSet != null) {
  145. try {
  146. resultSet.close();
  147. } catch (SQLException throwables) {
  148. throwables.printStackTrace();
  149. }
  150. }
  151. if (connection != null) {
  152. try {
  153. connection.close();
  154. } catch (SQLException throwables) {
  155. throwables.printStackTrace();
  156. }
  157. }
  158. if (ps != null) {
  159. try {
  160. ps.close();
  161. } catch (SQLException throwables) {
  162. throwables.printStackTrace();
  163. }
  164. }
  165. }
  166. }

TestUtil.java

  1. package com.util;
  2. import com.study001.Emp;
  3. import org.junit.Test;
  4. import java.util.List;
  5. public class TestUtil {
  6. @Test
  7. public void test00() {
  8. String sql = "insert emp(id,name,dept_id) values(?,?,?)";
  9. int count = DBHepler.update(sql, 21, "小小鱼", 3);
  10. if (count>0) {
  11. System.out.println("插入成功");
  12. } else {
  13. System.out.println("插入失败");
  14. }
  15. }
  16. @Test
  17. public void test01() {
  18. String sql = "select id,name,dept_id deptId from emp";
  19. Emp emp = DBHepler.queryOne(Emp.class, sql);
  20. System.out.println(emp);
  21. }
  22. @Test
  23. public void test02() {
  24. String sql = "select id,name,dept_id deptId from emp";
  25. List<Emp> emps = DBHepler.queryAll(Emp.class, sql);
  26. emps.forEach(System.out::println);
  27. }
  28. }

工具类小结

✨解释一下关于别名的问题

  1. ……
  2. rs = ps.executeQuery();// 获取了 ResultSet 对象
  3. ResultSetMetaData metaData = ps.getMetaData();// 获取了 ResultSetMetaData 对象
  4. if (rs.next()) {
  5. // 通过反射获取对象
  6. // getConstructor() 获取构造方法 newInstance() 实例化改对象[该类必须已经加载过]
  7. t = clzz.getConstructor().newInstance(); // 最终获得的是 Emp 对象
  8. int columCount = metaData.getColumnCount(); // 获取列数
  9. for (int i = 1; i <= columCount; i++) {
  10. // 通过下标获取列的别名
  11. // 此处是为了保证后面通过反射获取属性能获取成功,即保证 获取到的别名和Emp对象中对应的属性名相同
  12. String columnLabel = metaData.getColumnLabel(i);
  13. Object columValue = rs.getObject(columnLabel); // 通过别名获取对应的值 [通过下标也可以]
  14. // Object columValue = rs.getObject(i);
  15. // 通过反射获取属性
  16. // 如果传入的别名与Emp中对应的属性名不同,该处就会获取不到属性
  17. Field field = clzz.getDeclaredField(columnLabel);
  18. field.setAccessible(true); // 解除属性私有性的影响
  19. field.set(t, columValue); // 将对应的值传入Emp对象中
  20. }
  21. }
  22. ……

5.JDBC API小结

两种思想:

  • 面向接口编程的思想
  • ORM 思想(object relational mapping)

ORM思想:

这就是为什么要查询语句中字段对应的别名要与其对应的对象的属性一样

一个数据表对应一个 java 类

表中的一条记录对应 java 类的一个对象

表中的一个字段对应 java 类的一个属性

注意:sql 是需要结合列名和表的属性名来写。注意起别名。


两种技术

JDBC 结果集的元数据:ResultSetMetaData

DatabaseMetaData 类中提供了许多方法用于获得数据源的各种信息,通过这些方法可以非常详细的了解数据库的信息

获取列数:getColumnCount()

获取列的别名:getColumnLabel()

通过反射,创建指定类的对象,获取指定的属性并赋值

6.DAO

DAO:Data Access Object 访问数据信息的类和接口,包括了对数据的 CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。作用:为了实现功能的模块化,更有利于代码的维护和升级。


首先看一下项目结构

  1. 📒dao
  2. 📒impl
  3. [接口实现类]
  4. BaseDaoImpl.java
  5. DeptDaoImpl.java
  6. EmpDaoImpl.java
  7. [接口]
  8. BaseDao.java
  9. DeptDao.java
  10. EmpDao.java
  11. 📒daoTest
  12. dao测试类
  13. 📒entity
  14. [实体类]
  15. Dept.java
  16. Emp.java

JDBC - 图1


1.📒entity

📒entity
[实体类]
Dept.java
Emp.java

JDBC - 图2


2.📒dao

📒dao
📒impl
[接口实现类]
BaseDaoImpl.java
DeptDaoImpl.java
EmpDaoImpl.java
[接口]
BaseDao.java
DeptDao.java
EmpDao.java

2.1接口

JDBC - 图3


BaseDao.java

  1. package com.dao;
  2. import java.util.List;
  3. public interface BaseDao<T> {
  4. /**
  5. * 增删改操作
  6. * @param sql
  7. * @param args
  8. * @return
  9. */
  10. int update(String sql, Object... args);
  11. /**
  12. * 封装查询一个对象
  13. *
  14. * @param sql 查询SQL
  15. * @param args 问号的参数
  16. * @return 一个clzz对应的对象T
  17. */
  18. T queryOne(String sql, Object... args);
  19. /***
  20. * 封装查询所有对象的结果集,返回的泛型对象的List
  21. * @param sql 查询SQL
  22. * @param args 问号的参数
  23. * @return 泛型对象的List, 表示符合条件的所有数据库记录(对象)
  24. */
  25. List<T> queryAll(String sql, Object... args);
  26. }

2.2接口实现类

2.2.1 BaseDaoImpl.java
  1. package com.dao.Impl;
  2. import com.dao.BaseDao;
  3. import com.study002.DbUtil;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.lang.reflect.ParameterizedType;
  7. import java.lang.reflect.Type;
  8. import java.sql.*;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. @SuppressWarnings("unchecked")
  12. public class BaseDaoImpl<T> implements BaseDao<T> {
  13. // 定义一个变量来接收泛型的类型
  14. private Class<T> clzz;
  15. {
  16. //获取当前BaseDAO的子类继承的父类中的泛型
  17. Type genericSuperclass = this.getClass().getGenericSuperclass();
  18. ParameterizedType paramType = (ParameterizedType) genericSuperclass;
  19. //获取了父类的泛型参数
  20. Type[] typeArguments = paramType.getActualTypeArguments();
  21. //泛型的第一个参数
  22. clzz = (Class<T>) typeArguments[0];
  23. }
  24. @Override
  25. public int update(String sql, Object... args) {
  26. Connection connection = null;
  27. PreparedStatement ps = null;
  28. try {
  29. connection = DbUtil.getConnectionByJdbc();
  30. ps = connection.prepareStatement(sql);
  31. for (int i = 0; i < args.length; i++) {
  32. ps.setObject(i + 1, args[i]);
  33. }
  34. int count = ps.executeUpdate();
  35. return count;
  36. } catch (SQLException e) {
  37. e.printStackTrace();
  38. } finally {
  39. closeResource(connection, ps, null);
  40. }
  41. return 0;
  42. }
  43. @Override
  44. /**
  45. * 封装查询一个对象
  46. * @param <T> 对象类型
  47. * @param clzz 对象的字节码文件
  48. * @param sql 查询SQL
  49. * @param args 问号的参数
  50. * @return 一个clzz对应的对象T
  51. */
  52. public T queryOne(String sql, Object...args) {
  53. Connection connection = null;
  54. PreparedStatement ps = null;
  55. ResultSet rs = null;
  56. T t = null;
  57. try {
  58. connection = DbUtil.getConnectionByJdbc();
  59. ps = connection.prepareStatement(sql);
  60. // 为?赋值
  61. for (int i = 0; i < args.length; i++) {
  62. ps.setObject(i + 1, args[i]);
  63. }
  64. rs = ps.executeQuery();
  65. ResultSetMetaData metaData = ps.getMetaData();
  66. if (rs.next()) {
  67. // 通过反射获取对象
  68. t = (T) clzz.getConstructor().newInstance();
  69. int columnCount = metaData.getColumnCount();
  70. for (int i = 1; i <= columnCount; i++) {
  71. // 通过下标获取列的名称
  72. // String colunmName = metaData.getColumnName(i);
  73. // 通4RFC 4RC过下标获取列的别名(就是对象的属性的名称)
  74. String columnLabel = metaData.getColumnLabel(i);
  75. Object columnValue = rs.getObject(columnLabel);
  76. // 通过反射获取属性
  77. Field field = clzz.getDeclaredField(columnLabel);
  78. field.setAccessible(true);
  79. field.set(t, columnValue);
  80. }
  81. }
  82. } catch (SQLException e) {
  83. e.printStackTrace();
  84. } catch (NoSuchFieldException e) {
  85. e.printStackTrace();
  86. } catch (SecurityException e) {
  87. e.printStackTrace();
  88. } catch (IllegalArgumentException e) {
  89. e.printStackTrace();
  90. } catch (IllegalAccessException e) {
  91. e.printStackTrace();
  92. } catch (InstantiationException e) {
  93. e.printStackTrace();
  94. } catch (InvocationTargetException e) {
  95. e.printStackTrace();
  96. } catch (NoSuchMethodException e) {
  97. e.printStackTrace();
  98. } finally {
  99. closeResource(connection, ps, rs);
  100. }
  101. return t;
  102. }
  103. @Override
  104. /***
  105. * 封装查询所有对象的结果集,返回的泛型对象的List
  106. * @param <T> 对象类型
  107. * @param clzz 对象的字节码文件
  108. * @param sql 查询SQL
  109. * @param args 问号的参数
  110. * @return 泛型对象的List,表示符合条件的所有数据库记录(对象)
  111. */
  112. public List<T> queryAll(String sql, Object...args) {
  113. Connection connection = null;
  114. PreparedStatement ps = null;
  115. ResultSet rs = null;
  116. List<T> list = new ArrayList<>();
  117. T t = null;
  118. try {
  119. connection = DbUtil.getConnectionByJdbc();
  120. ps = connection.prepareStatement(sql);
  121. for (int i = 0; i < args.length; i++) {
  122. ps.setObject(i + 1, args[i]);
  123. }
  124. rs = ps.executeQuery();
  125. ResultSetMetaData metaData = ps.getMetaData();
  126. while (rs.next()) {
  127. t = clzz.getConstructor().newInstance();
  128. int columnCount = metaData.getColumnCount();
  129. for (int i = 1; i <= columnCount; i++) {
  130. // 通过下标获取列的名称
  131. // String colunmName = metaData.getColumnName(i);
  132. // 通过下标获取列的别名(就是对象的属性的名称)
  133. String columnLabel = metaData.getColumnLabel(i);
  134. Object columnValue = rs.getObject(columnLabel);
  135. Field field = clzz.getDeclaredField(columnLabel);
  136. // 通过反射获取属性
  137. field.setAccessible(true);
  138. field.set(t, columnValue);
  139. }
  140. list.add(t);
  141. }
  142. } catch (SQLException e) {
  143. e.printStackTrace();
  144. } catch (NoSuchFieldException e) {
  145. e.printStackTrace();
  146. } catch (SecurityException e) {
  147. e.printStackTrace();
  148. } catch (IllegalArgumentException e) {
  149. e.printStackTrace();
  150. } catch (IllegalAccessException e) {
  151. e.printStackTrace();
  152. } catch (InstantiationException e) {
  153. e.printStackTrace();
  154. } catch (InvocationTargetException e) {
  155. e.printStackTrace();
  156. } catch (NoSuchMethodException e) {
  157. e.printStackTrace();
  158. } finally {
  159. closeResource(connection, ps, rs);
  160. }
  161. return list;
  162. }
  163. // 做一个公共的关闭资源的工具
  164. public void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet) {
  165. if (resultSet != null) {
  166. try {
  167. resultSet.close();
  168. } catch (SQLException e) {
  169. e.printStackTrace();
  170. }
  171. }
  172. if (ps != null) {
  173. try {
  174. ps.close();
  175. } catch (SQLException e) {
  176. e.printStackTrace();
  177. }
  178. }
  179. if (connection != null) {
  180. try {
  181. connection.close();
  182. } catch (SQLException e) {
  183. e.printStackTrace();
  184. }
  185. }
  186. }
  187. }

2.2.2 DeptDaoImpl.java
  1. package com.dao.Impl;
  2. import com.dao.DeptDao;
  3. import com.entity.Dept;
  4. import java.util.List;
  5. public class DeptDaoImpl extends BaseDaoImpl<Dept> implements DeptDao {
  6. @Override
  7. public void update(Dept dept) {
  8. super.update("update dept set name=?,intro=? where id = ? ",dept.getName(),dept.getName(),dept.getInfo());
  9. }
  10. @Override
  11. public void delete(int id) {
  12. super.update("delete from dept where id = ? ",id);
  13. }
  14. @Override
  15. public void delete(Dept dept) {
  16. // 必须要所有属性全对上才能删除
  17. super.update("delete from dept where id = ? and name = ? and intro = ?",dept.getId(),dept.getName(),dept.getInfo());
  18. }
  19. @Override
  20. public void add(Dept dept) {
  21. super.update("insert into dept(id,name,intro) values(?,?,?)",dept.getId(),dept.getName(),dept.getInfo());
  22. }
  23. @Override
  24. public Dept findById(int id) {
  25. Dept dept = super.queryOne("select id,name,intro from dept where id = ?", id);
  26. return dept;
  27. }
  28. @Override
  29. public List<Dept> findAll() {
  30. List<Dept> depts = super.queryAll("select id,name,intro from dept ");
  31. return depts;
  32. }
  33. }

2.2.3 EmpDaoImpl.java
  1. package com.dao.Impl;
  2. import com.dao.EmpDao;
  3. import com.entity.Emp;
  4. import java.util.List;
  5. public class EmpDaoImpl extends BaseDaoImpl<Emp> implements EmpDao {
  6. @Override
  7. public void update(Emp emp) {
  8. super.update("update emp set name=?,dept_id=? where id = ? ",emp.getName(),emp.getDeptId(),emp.getId());
  9. }
  10. @Override
  11. public void delete(int id) {
  12. super.update("delete from emp where id = ? ",id);
  13. }
  14. @Override
  15. public void delete(Emp emp) {
  16. // 必须要所有属性全对上才能删除
  17. super.update("delete from emp where id = ? and name = ? and dept_id = ?",emp.getId(),emp.getName(),emp.getDeptId());
  18. }
  19. @Override
  20. public void add(Emp emp) {
  21. super.update("insert into emp(id,name,dept_id) values(?,?,?)",emp.getId(),emp.getName(),emp.getDeptId());
  22. }
  23. @Override
  24. public Emp findById(int id) {
  25. Emp emp = super.queryOne("select id,name,dept_id deptId from emp where id = ?", id);
  26. return emp;
  27. }
  28. @Override
  29. public List<Emp> findAll() {
  30. // List<Emp> emps = super.queryAll("select id,name,dept_id from emp");
  31. List<Emp> emps = super.queryAll("select id,name,dept_id deptId from emp where dept_id = ?",2);
  32. return emps;
  33. }
  34. }

2.2测试类

  1. package com.daoTest;
  2. import com.dao.Impl.EmpDaoImpl;
  3. import com.entity.Emp;
  4. import org.junit.Test;
  5. import java.util.List;
  6. public class DaoTest {
  7. @Test
  8. public void test01() {
  9. // 测试插入
  10. Emp e1 = new Emp(8, "小周", 3);
  11. // Emp e2 = new Emp(9, "小黄", 4);
  12. EmpDaoImpl daoEmp = new EmpDaoImpl();
  13. daoEmp.add(e1);
  14. // daoEmp.add(e2);
  15. }
  16. @Test
  17. public void test02() {
  18. // 测试更新
  19. // Emp e = new Emp(8, "小周", 3);
  20. Emp e = new Emp(8, "小周来啦", 2);
  21. EmpDaoImpl daoEmp = new EmpDaoImpl();
  22. daoEmp.update(e);
  23. }
  24. @Test
  25. public void test03() {
  26. // 测试删除
  27. Emp e = new Emp(8, "小周", 3);
  28. EmpDaoImpl daoEmp = new EmpDaoImpl();
  29. daoEmp.delete(e);
  30. // daoEmp.delete(1);
  31. }
  32. @Test
  33. public void test04() {
  34. // 测试查询
  35. Emp e = new Emp(8, "小周", 3);
  36. EmpDaoImpl daoEmp = new EmpDaoImpl();
  37. // Emp byId = daoEmp.findById(e.getId());
  38. // System.out.println(byId);
  39. List<Emp> emps = daoEmp.findAll();
  40. emps.forEach(System.out::println);
  41. }
  42. public static void main(String[] args) {
  43. // 测试查询
  44. Emp e = new Emp(8, "小周", 3);
  45. EmpDaoImpl daoEmp = new EmpDaoImpl();
  46. // Emp byId = daoEmp.findById(e.getId());
  47. // System.out.println(byId);
  48. List<Emp> emps = daoEmp.findAll();
  49. emps.forEach(System.out::println);
  50. }
  51. }

[

](https://github.com/sanshisi/JDBC_Study.git)