1、JDBC概述
1.1、数据的持久化
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
1.2、JDBC的理解
JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)
JDBC,是SUN公司提供的一套API,使用这套API可以实现对具体数据库的操作(获取连接,关闭连接,DML,DDL,DCL,以及事务的管理等等)
好处:两个层次:
- 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
- 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
- 从开发程序员的角度:不需要关注具体的数据库的细节
- 数据库厂商:只需要提供标准的具体实现
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。 ————面向接口编程
2、JDBC连接
public static Connection getConnection() throws Exception {
// 1.读取配置文件中的4个基本信息
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
String url = prop.getProperty("url");
String driver = prop.getProperty("driver");
String user = prop.getProperty("user");
String pad = prop.getProperty("password");
//2.加载驱动
Class.forName(driver);
//3.获取连接
Connection connection = DriverManager.getConnection(url, user, pad);
return connection;
}
配置文件 db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/web
user=root
password=123456
JDBCUtils.java
package com.ice.util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBCUtil {
public static Connection getConnection() throws Exception {
// 1.读取配置文件中的4个基本信息
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
String url = prop.getProperty("url");
String driver = prop.getProperty("driver");
String user = prop.getProperty("user");
String pad = prop.getProperty("password");
//2.加载驱动
Class.forName(driver);
//3.获取连接
Connection connection = DriverManager.getConnection(url, user, pad);
return connection;
}
public static void closeResource(Connection con, PreparedStatement pre, ResultSet rs){
try {
if (rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pre!=null){
pre.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (con!=null){
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3、PreparedStatement接口
3.1、Statement使用的弊端
使用Statement操作数据表存在弊端:
- 问题一:存在拼串操作,繁琐
- 问题二:存在SQL注入问题
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法。
Statement没办法操作Blob类型变量
Statement实现批量插入时,效率较低
3.2、PreparedStatement接口实现CRUD操作
PreparedStatement的理解
- PreparedStatement是Statement的子接口
- An object that represents a precompiled SQL statement. 预编译SQL语句
- 可以解决Statement的SQL注入问题,拼串问题
使用PreparedStatement实现通用的增、删、改的方法
//通用的增删改操作
public int update(String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
int resultCount = 0;
try {
//1.获取数据库的连接
conn = JDBCUtil.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//4.执行
resultCount = ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
return resultCount;
}finally {
//5.资源关闭
JDBCUtil.closeResource(conn,ps,null);
}
return resultCount;
}
使用PreparedStatement实现通用的查询操作
// 通用的针对于不同表的查询:返回一个对象 (version 1.0)
public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.预编译sql语句,得到PreparedStatement对象
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.1通过ResultSetMetaData得到columnCount,columnLabel;通过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(i + 1);
// 6.2使用反射,给对象的相应属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.关闭资源
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
// 通用的针对于不同表的查询:返回多个对象 (version 1.0)
public <T> List<T> getForList(Class<T> clazz, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.获取数据库连接
conn = JDBCUtil.getConnection();
// 2.预编译sql语句,得到PreparedStatement对象
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.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
while (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
for (int i = 0; i < columnCount; i++) {// 遍历每一个列
// 获取列值
Object columnVal = rs.getObject(i + 1);
// 获取列的别名:列的别名,使用类的属性名充当
String columnLabel = rsmd.getColumnLabel(i + 1);
// 6.2使用反射,给对象的相应属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnVal);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.关闭资源
JDBCUtil.closeResource(conn, ps, rs);
}
return null;
}
两种思想:
面向接口编程的思想
ORM编程思想:(object relational mapping)
- 一个数据表对应一个java类
- 表中的一条记录对应Java类的一个对象
- 表中的一个字段对应java类的一个属性
两种技术:
使用结果集的元数据:ResultSetMetaDate
- getColumnCount():获取列数
- getColumnLabel():获取列的别名
- 说明:如果sql中没有给字段起别名,getColumnLabel()获取的就是列名
反射的使用:
- 创建对应的运行类的对象
- 在运行时,动态的调用指定的运行时类的属性、方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wje8kB5F-1634700545980)(JDBC.assets/image-20211013185557646.png)]
3.3、PreparedStatement可以操作Blob类型的变量
写入操作:setBlob(InputStream is);
读取操作:
Blob blob = getBlob(int index);
InputStream is = blob.getBinaryStream();
//获取连接
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setString(1, "徐海强");
ps.setString(2, "xhq@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("xhq.png");
ps.setBlob(4, fis);
//执行
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);
//读取数据
String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?";
conn = getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 8);
rs = ps.executeQuery();
if(rs.next()){
Integer id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//读取Blob类型的字段
Blob photo = rs.getBlob(5);
InputStream is = photo.getBinaryStream();
OutputStream os = new FileOutputStream("c.jpg");
byte [] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
JDBCUtils.closeResource(conn, ps, rs);
if(is != null){
is.close();
}
if(os != null){
os.close();
}
}
3.4、PreparedStatement实现高效的批量插入
- addBatch()、executeBatch()、clearBatch()
- mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理支持,?rewriteBatchedStatements=true 写在配置文件的url后
- 使用更新的mysql驱动:mysql-connector-java-5.1.37-bin.jar
- 设置连接时不允许自动提交数据
/** 层次四:在层次三的基础上操作* 使用Connection 的 setAutoCommit(false) / commit()*/@Testpublic void testInsert2() throws Exception{ long start = System.currentTimeMillis(); Connection conn = JDBCUtils.getConnection(); //1.设置为不自动提交数据 conn.setAutoCommit(false); String sql = "insert into goods(name)values(?)"; PreparedStatement ps = conn.prepareStatement(sql); for(int i = 1;i <= 1000000;i++){ ps.setString(1, "name_" + i); //1.“攒”sql ps.addBatch(); if(i % 500 == 0){ //2.执行 ps.executeBatch(); //3.清空 ps.clearBatch(); } } //2.提交数据 conn.commit(); long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start));//1000000条:4978 JDBCUtils.closeResource(conn, ps);}
PreparedStatement与Statement的异同
- 接口与子接口的关系
- 开发中,使用PreparedStatement,不使用Statement
- An object that represents a precompiled SQL statement.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXVdhKK2-1634700545983)(JDBC.assets/image-20211013203500690.png)]
4、事务
4.1、概念
什么较数据库事务?
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
一组逻辑单元:一个或多个DML操作。
事务处理的原则:***保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务被提交(commit)*,那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)**到最初状态。
说明:
- 数据一旦提交,就不可回滚
- 那些操作会导致数据的自动提交?
- DDL操作一旦执行,都会自动提交。
- set autocommit = false 对DDL操作失效。
- DML默认情况下,一旦执行,就会自动提交。
- 我们可以通过set autocommit = false 的方式取消DML操作的自动提交
- 默认在关闭连接时,会自动的提交数据
- DDL操作一旦执行,都会自动提交。
考虑上事务后的数据库查询方法
public int update(Connection conn,String sql,Object...args){ PreparedStatement ps = null; int resultCount = 0; try { //1.预编译sql语句,返回PreparedStatement的实例 ps = conn.prepareStatement(sql); //2.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } //3.执行 resultCount = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); return resultCount; }finally { //4.资源关闭 JDBCUtil.closeResource(null,ps,null); } return resultCount; }
public <T> T getInstance(Connection conn,Class<T> clazz, String sql, Object... args) { PreparedStatement ps = null; ResultSet rs = null; try { // 1.预编译sql语句,得到PreparedStatement对象 ps = conn.prepareStatement(sql); // 2.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } // 3.执行executeQuery(),得到结果集:ResultSet rs = ps.executeQuery(); // 4.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 5.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.getDeclaredConstructor().newInstance(); for (int i = 0; i < columnCount; i++) {// 遍历每一个列 // 获取列值 Object columnVal = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel(i + 1); // 5.2使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 6.关闭资源 JDBCUtil.closeResource(null, ps, rs); } return null;}
4.2、事务的ACID属性
-
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 -
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 -
隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 -
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
4.3、数据操作过程中可能出现的问题:(针对隔离性)
- 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
隔离级别越高, 数据一致性就越好, 但并发性越弱。
4.4、数据库提供的4种隔离级别
- Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED 。
- Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。
设置隔离级别
查看当前的隔离级别:SELECT @@tx_isolation;
设置当前 mySQL 连接的隔离级别:set transaction isolation level read committed;
设置数据库系统的全局的隔离级别:set global transaction isolation level read committed;
5、DAO
DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。有时也称作:BaseDAO
作用:为了实现功能的模块化,更有利于代码的维护和升级。
5.1、BeanDAO
package com.ice.DAO;import com.ice.util.JDBCUtil;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.*;import java.util.ArrayList;import java.util.List;/** * @author: zph * @date: 2021/10/14 10:08 * @description:封装了针对于数据表的通用的操作 */public abstract class BaseDao<T> { private Class<T> clazz = null; { Type genericSuperclass = this.getClass().getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = paramType.getActualTypeArguments(); clazz = (Class<T>) actualTypeArguments[0]; } public int update(Connection conn, String sql, Object...args){ PreparedStatement ps = null; int resultCount = 0; try { //1.预编译sql语句,返回PreparedStatement的实例 ps = conn.prepareStatement(sql); //2.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i+1,args[i]); } //3.执行 resultCount = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); return resultCount; }finally { //4.资源关闭 JDBCUtil.closeResource(null,ps,null); } return resultCount; } public T getInstance(Connection conn, String sql, Object... args) { PreparedStatement ps = null; ResultSet rs = null; try { // 1.预编译sql语句,得到PreparedStatement对象 ps = conn.prepareStatement(sql); // 2.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } // 3.执行executeQuery(),得到结果集:ResultSet rs = ps.executeQuery(); // 4.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 5.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.getDeclaredConstructor().newInstance(); for (int i = 0; i < columnCount; i++) {// 遍历每一个列 // 获取列值 Object columnVal = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel(i + 1); // 5.2使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 6.关闭资源 JDBCUtil.closeResource(null, ps, rs); } return null; } public List<T> getForList(Connection conn,String sql, Object... args) { PreparedStatement ps = null; ResultSet rs = null; try { // 1. 预编译sql语句,得到PreparedStatement对象 ps = conn.prepareStatement(sql); // 2.填充占位符 for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } // 3.执行executeQuery(),得到结果集:ResultSet rs = ps.executeQuery(); // 4.得到结果集的元数据:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 5.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值 int columnCount = rsmd.getColumnCount(); //创建集合对象 ArrayList<T> list = new ArrayList<>(); while (rs.next()) { T t = clazz.getDeclaredConstructor().newInstance(); for (int i = 0; i < columnCount; i++) {// 遍历每一个列 // 获取列值 Object columnVal = rs.getObject(i + 1); // 获取列的别名:列的别名,使用类的属性名充当 String columnLabel = rsmd.getColumnLabel(i + 1); // 5.2使用反射,给对象的相应属性赋值 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, columnVal); } list.add(t); } return list; } catch (Exception e) { e.printStackTrace(); } finally { // 6.关闭资源 JDBCUtil.closeResource(null, ps, rs); } return null; } public <E> E getValue(Connection conn,String sql,String...args){ PreparedStatement pre = null; ResultSet rs = null; try { pre = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { pre.setObject(i+1,args[i]); } rs = pre.executeQuery(); if (rs.next()){ return (E) rs.getObject(1); } } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtil.closeResource(null,pre,rs); } return null; }}
5.2、UserDao
public interface UserDao { //将user对象放到数据库中。 void insert(Connection conn, User user); //针对指定的id,删除表中的一条记录 void deleteById(Connection conn,int id); //针对内存中的user对象,去修改数据表中指定的记录 void update(Connection conn,User user); //针对指定的id查询得到对应的User对象 User getUserById(Connection conn,int id); //查询表中的所有记录构成的集合 List<User> getAll(Connection conn); //返回数据表中的数据的条目数 Long getCount(Connection conn);}
5.3、UserDaoImpl
package com.ice.DAO;import com.ice.pojo.User;import java.sql.Connection;import java.util.List;/** * @author: zph * @date: 2021/10/14 11:01 * @description: */public class UserDaoImpl extends BaseDao<User> implements UserDao { @Override public void insert(Connection conn, User user) { String sql = "insert into user(username,usercode,password,sex,age,email)values(?,?,?,?,?,?)"; update(conn,sql,user.getUsername(),user.getUsercode(),user.getPassword(),user.getSex(),user.getAge(),user.getEmail()); } @Override public void deleteById(Connection conn, int id) { String sql = "delete from user where id = ?"; update(conn,sql,id); } @Override public void update(Connection conn, User user) { String sql = "update user set username=?,usercode=?,password=?,sex=?,age=?,email=? where id = ?"; update(conn,sql,user.getUsername(),user.getUsercode(),user.getPassword(),user.getSex(),user.getAge(),user.getEmail(),user.getId()); } @Override public User getUserById(Connection conn, int id) { String sql = "select * from user where id = ?"; User user = getInstance(conn, sql, id); return user; } @Override public List<User> getAll(Connection conn) { String sql = "select * from user"; List<User> users = getForList(conn, sql); return users; } @Override public Long getCount(Connection conn) { String sql = "select Count(*) from user"; return getValue(conn,sql); }}
6、数据库连接池
6.1、使用JDBC数据库连接池的必要性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9H3Bvvs-1634700545986)(JDBC.assets/image-20211013212925460.png)]
- 为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
- 数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
- 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0w3hsiT1-1634700545989)(JDBC.assets/image-20211013213223507.png)]
6.2、Druid(德鲁伊)数据库连接池
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
public class druidTest { @Test public void test() throws Exception { Properties pro = new Properties(); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); pro.load(is); DataSource dataSource = DruidDataSourceFactory.createDataSource(pro); Connection connection = dataSource.getConnection(); System.out.println(connection); }}
url=jdbc:mysql://localhost:3306/web?rewriteBatchedStatements=trueusername=rootpassword=123456driverClassName=com.mysql.jdbc.DriverinitialSize=10maxActive=20maxWait=1000filters=wall
//JDBCUtils使用public static DataSource source = null;static { try { Properties pro = new Properties(); InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); pro.load(is); source = DruidDataSourceFactory.createDataSource(pro); } catch (Exception e) { e.printStackTrace(); }}public static Connection getConnection1() throws SQLException { Connection connection = source.getConnection(); return connection;}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rw44FO2b-1634700545991)(JDBC.assets/image-20211014141957931.png)]
7、DBUtils提供的jar包实现CRUD操作
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
API介绍:
- org.apache.commons.dbutils.QueryRunner
- org.apache.commons.dbutils.ResultSetHandler
- 工具类:org.apache.commons.dbutils.DbUtils
文档:
file:///D:/file%20folder/jar%E5%8C%85/Apache-DBUtils/commons-dbutils-1.7/apidocs/index.html
7.1、主要API的使用
DbUtils
DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
- public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
- public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
- public static void commitAndClose(Connection conn)throws SQLException: 用来提交连接的事务,然后关闭连接
- public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
- public static void rollback(Connection conn)throws SQLException:允许conn为null,因为方法内部做了判断
- public static void rollbackAndClose(Connection conn)throws SQLException
- rollbackAndCloseQuietly(Connection)
- public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
QueryRunner类
-
该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
-
QueryRunner类提供了两个构造器:
- 默认的构造器
- 需要一个 javax.sql.DataSource 来作参数的构造器
QueryRunner类的主要方法:
-
更新
- public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
…
- public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
-
插入
- public T insert(Connection conn,String sql,ResultSetHandler rsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
…
- public T insert(Connection conn,String sql,ResultSetHandler rsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
-
批处理
- public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
- public T insertBatch(Connection conn,String sql,ResultSetHandler rsh,Object[][] params)throws SQLException:只支持INSERT语句
…
-
查询
- public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
…
- public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
ResultSetHandler接口及实现类
-
该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。
-
ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。
-
接口的主要实现类:
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- **BeanHandler:**将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- **BeanListHandler:**将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- **MapHandler:**将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- **MapListHandler:**将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- **ScalarHandler:**查询单个值对象