一、JDBC实现数据库的连接
1、IEDA引入JDBC.jar包
在工程目录下,创建一个lib文件夹,将jar包复制到该文件夹下,然后如图进行引入。
2、连接方式
首先我们先写一个配置文件jdbc.properties
使用配置文件的好处:
实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
如果修改了 配置信息,省去重新编译的过程。
user=root password=sm1208 url=jdbc:mysql://localhost:3306/javaweb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true driverClass=com.mysql.cj.jdbc.Driver
package com.sun.jdbc; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * @author JumperSun * @date 2022-09-08-19:57 */ public class ConnectionTest { // 非读取配置文件方式 @Test public void testConnection1(){ try { // 1.数据库连接的4个基本要素 String url = "jdbc:mysql://localhost:3306/javaweb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"; String driverName = "com.mysql.cj.jdbc.Driver"; String user = "root"; String password = "sm1208"; // 2.加载驱动(实例化Driver 注册驱动) Class.forName(driverName); // 3.获取连接 Connection conn = DriverManager.getConnection(url, user, password); System.out.println(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } // 读取配置文件方式 @Test public void testConnection2() throws Exception { // 1.加载配置文件 InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); // 2.读取配置信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 3.加载驱动 Class.forName(driverClass); // 4.获取连接 Connection conn = DriverManager.getConnection(url, user, password); System.out.println(conn); } }
3、JDBCUtils工具类封装数据库连接和关闭操作
package com.sun.jdbc; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; /** * @author JumperSun * @date 2022-09-08-20:53 * 封装的JDBC工具类 */ public class JDBCUtils { public static Connection getConnection() throws Exception { // 1.读取配置文件的信息 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 2.加载驱动 Class.forName(driverClass); // 3.返回获取连接 return DriverManager.getConnection(url, user, password); } public static void closeResources(Connection conn, PreparedStatement ps) { try { if (ps != null) { ps.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } public static void closeResources(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs != null) { rs.close(); } if (ps != null) { ps.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
二、使用PreparedStatement实现CRUD操作
PreparedStatement是什么?
PreparedStatement用来执行SQL查询语句的API之一,数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。
数据库操作对象都有哪些?
Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
CallableStatement:用于执行 SQL 存储过程
为什么使用PreparedStatement?
使用Statement操作数据表存在弊端
存在拼串操作、繁琐
存在SQL注入问题
PreparedStatement表示一条预编译过的 SQL 语句
能最大可能提高性能
能最大可能提高性能:
综上:
1、PreparedStatement实现增、删、改操作
// 通用的增、删、改操作(体现一:增、删、改 体现二:针对不同的表) public boolean update(String sql,Object ...args) { Connection conn = null; PreparedStatement ps = null; boolean flag = false; try { // 1.获取数据库的连接 conn = com.sun.jdbc.JDBCUtils.getConnection(); // 2.获取PreparedStatement实例 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行sql语句 flag = ps.execute(); } catch (Exception e) { e.printStackTrace(); } finally { // 5.关闭资源 JDBCUtils.closeResources(conn,ps); return flag; } }
2、PreparedStatement实现查询操作
// 通用的针对于不同表的查询:返回一个对象 public <T> T getBean(Class<T> clazz,String sql,Object ...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1.获取数据库连接 conn = JDBCUtils.getConnection(); // 2.预编译sql语句,得到PrepareStatement对象 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行executeQuery(),得到结果:ResultSet rs = ps.executeQuery(); // 5.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); if (rs.next()) { // 通过反射,创建指定类的对象,获取指定的属性并赋值 T t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { // 遍历每一个列 // 获取列值 Object columnval = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel((Integer) columnval); // 使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t,columnval); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 7.关闭资源 JDBCUtils.closeResources(conn,ps,rs); } return null; }
// 通用的针对于不同表的查询:返回对象集合 public <T> List<T> getBeanList(Class<T> clazz, String sql, Object ...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1.获取数据库连接 conn = com.sun.jdbc.JDBCUtils.getConnection(); // 2.预编译sql语句,得到PrepareStatement对象 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行executeQuery(),得到结果:ResultSet rs = ps.executeQuery(); // 5.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); // 创建集合对象 ArrayList<T> list = new ArrayList<>(); while (rs.next()) { T t = clazz.newInstance(); // 处理结果集一行数据中的每一列:给 for (int i = 0; i < columnCount; i++) { // 获取列值 Object columnval = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel((Integer) columnval); // 使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t,columnval); } list.add(t); } return list; } catch (Exception e) { e.printStackTrace(); } finally { // 7.关闭资源 JDBCUtils.closeResources(conn,ps,rs); } return null; }
3、ResultSet与ResultSetMetaData
我们进行查询操作需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象。
ResultSet
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现
ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象 的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。 相当于Iterator对象的 hasNext() 和 next() 方法的结合体。
当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值。
例如: getInt(1), getString(“name”)
注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始。
ResultSet 接口的常用方法: boolean next()、getString()
ResultSetMetaData
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
ResultSetMetaData meta = rs.getMetaData();
getColumnName(int column):获取指定列的名称
getColumnLabel(int column):获取指定列的别名
getColumnCount():返回当前 ResultSet 对象中的列数。
getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
isNullable(int column):指示指定列中的值是否可以为 null。
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
如何获取 ResultSetMetaData: 调用 ResultSet 的 getMetaData() 方法即可
获取 ResultSet 中有多少列:调用 ResultSetMetaData 的 getColumnCount() 方法
获取 ResultSet 每一列的列的别名是什么:调用 ResultSetMetaData 的getColumnLabel() 方法
4、ORM思想(object relational mapping)
一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性
5、资源的释放
释放ResultSet, Statement,Connection。
数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将 导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
6、PsUtils封装PrepareStatement操作
package com.sun.util;
import com.sun.util.JDBCUtils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; /** * @author JumperSun * @date 2022-09-08-20:29 */ public class PsUtils { // 通用的增、删、改操作(体现一:增、删、改 体现二:针对不同的表) public boolean update(String sql,Object ...args) { Connection conn = null; PreparedStatement ps = null; boolean flag = false; try { // 1.获取数据库的连接 conn = com.sun.jdbc.JDBCUtils.getConnection(); // 2.获取PreparedStatement实例 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行sql语句 flag = ps.execute(); } catch (Exception e) { e.printStackTrace(); } finally { // 5.关闭资源 JDBCUtils.closeResources(conn,ps); return flag; } } // 通用的针对于不同表的查询:返回一个对象 public <T> T getBean(Class<T> clazz,String sql,Object ...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1.获取数据库连接 conn = com.sun.jdbc.JDBCUtils.getConnection(); // 2.预编译sql语句,得到PrepareStatement对象 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行executeQuery(),得到结果:ResultSet rs = ps.executeQuery(); // 5.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { // 遍历每一个列 // 获取列值 Object columnval = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel((Integer) columnval); // 使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t,columnval); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 7.关闭资源 JDBCUtils.closeResources(conn,ps,rs); } return null; } // 通用的针对于不同表的查询:返回对象集合 public <T> List<T> getBeanList(Class<T> clazz, String sql, Object ...args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1.获取数据库连接 conn = com.sun.jdbc.JDBCUtils.getConnection(); // 2.预编译sql语句,得到PrepareStatement对象 ps = conn.prepareStatement(sql); // 3.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } // 4.执行executeQuery(),得到结果:ResultSet rs = ps.executeQuery(); // 5.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); // 创建集合对象 ArrayList<T> list = new ArrayList<>(); while (rs.next()) { T t = clazz.newInstance(); // 处理结果集一行数据中的每一列:给 for (int i = 0; i < columnCount; i++) { // 获取列值 Object columnval = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel((Integer) columnval); // 使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t,columnval); } list.add(t); } return list; } catch (Exception e) { e.printStackTrace(); } finally { // 7.关闭资源 JDBCUtils.closeResources(conn,ps,rs); } return null; } }