1 Web技术概览

2 JDBC概述
2.1 数据持久化
把内存数据长久化保存。
2.2 Java中的数据存储
- JDBC直接访问
- JDO技术
- 第三方O/R工具,如Hibernate、Mybatis等
2.3 JDBC介绍
- JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口。定义了用于访问数据库的标准Java类库(java.sql javax.sql)使用这些类库可以以一种标准方法方便地访问数据库资源
- 统一的规范
- 目的是为使用JDBC连接任何提供了JDBC驱动程序的数据库,简化流程
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQNZtEO5-1645449806326)(C:\Users\Tao\AppData\Roaming\Typora\typora-user-images\image-20211229103928714.png)]](https://i-blog.csdnimg.cn/blog_migrate/18cbe01e114cfb9a2c8f8c2e07e1a39f.png)
2.4 JDBC体系结构
- 面向应用的API:程序员开发使用
- 面向数据库的API:供厂商开发驱动
2.5 JDBC编写步骤

3 数据库连接方式
Maven项目中首先要导入mysql-connector-java依赖。
3.1 方式一
public class ConnectionTest {
@Test
public void testConnection1() throws SQLException {
Driver driver = new com.mysql.cj.jdbc.Driver();
String url = "jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","");
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
}
jdbc:mysql:协议
localhost:ip地址
3306:mysql默认端口号
jdbc_learn:数据库名
?useUnicode=true&characterEncoding=utf-8:东八区添加
3.2 方式二-反射获取driver对象
Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
与方式一的区别是在第一步获取driver对象时使用反射。
这样具有可移植性。
3.3 方式三-使用DriverManager
public void testConnection3() throws Exception {
Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//2.提供基本信息
String url="jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "";
//注册驱动
DriverManager.registerDriver(driver);
//获取连接
DriverManager.getConnection(url,user,password);
}
3.4 方式四-省略注册驱动
public void testConnection4() throws Exception {
//提供基本信息
String url="jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "";
//加载Driver
Class.forName("com.mysql.cj.jdbc.Driver");
// Driver driver = (Driver) clazz.newInstance();
// //注册驱动
// DriverManager.registerDriver(driver);
//获取连接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
与方式三的区别是省略了注册驱动的代码。
原因:mysql的driver类中写了注册驱动的静态方法,随着driver类的加载自动注册了驱动。
3.5 方式五-最终版
将数据库连接需要的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接。
#连接数据库的四个基本信息
user=root
password=
url=jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8
driverClass=com.mysql.cj.jdbc.Driver
public void testConnection5() throws Exception {
InputStream is = ConnectionTest.class.getClassLoader().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");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
优点:
- 实现代码与数据的分离,实现了解耦。
- 如果需要修改配置文件信息,可以避免重新打包。
3 使用PreparedStatement实现CRUD操作
3.1 操作和访问数据库
-
数据库连接被用于向数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个socket连接。
-
在java.sql包中有3个接口分别定义了对数据库调用的不同方式:
- Statement:用于执行静态SQL语句并返回它所生成结果的对象
- PreparedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效的执行该语句
- CallableStatement:用于执行SQL存储过程

3.2 使用Statement的弊端
- 需要sql语句拼串,操作繁琐
- 且存在SQL注入问题:可以输入错误的用户名和密码但是正确连接数据库
3.3 PreparedStatement的使用
3.3.1 介绍
3.3.2 JDBCUtils:封装数据库连接和连接关闭
public class JDBCUtils {
/**
* @Description: 获取数据库连接
* @Param: []
* @return: java.sql.Connection
* @Author: CT
* @Date: 2021/12/29
*/
public static Connection getConnection() throws Exception {
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");
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* @Description: 关闭连接和Statement的操作
* @Param: [conn, ps]
* @return: void
* @Author: CT
* @Date: 2021/12/29
*/
public static void closeResource(Connection conn, Statement ps){
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.3.3 增改
public void testInsert() {
Connection conn = null;
PreparedStatement ps = null;
try {
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");
Class.forName(driverClass);
conn = DriverManager.getConnection(url, user, password);
// System.out.println(conn);
//4.预编译sql语句,返回PreparedStatement实例
String sql = "insert into customers(name,email,birth)value(?,?,?)";
ps = conn.prepareStatement(sql);
//5.填充占位符
ps.setString(1,"哪吒");
ps.setString(2,"nezha@gmail.com");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1000-01-01");
ps.setDate(3,new Date(date.getTime()));
//6.执行操作
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
//7.连接关闭
try {
if(ps!=null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void testUpdate() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "update customers set name = ? where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1,"莫扎特");
ps.setObject(2,18);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn,ps);
}
}
3.3.4 实现通用的增删改
通用方法
public void update(String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.关闭资源
JDBCUtils.closeResource(conn,ps);
}
}
测试
public void testCommonUpdate(){
String sql = "delete from customers where id = ?";
update(sql,3);
}
3.3.5 针对单个表的具体查询
public void testQuery1(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth from `customers` where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1,1);
resultSet = ps.executeQuery();
if(resultSet.next()){
//获取当前数据的各个字段
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
//将数据封装为一个对象
Customer customer = new Customer(id, name, email, birth);
System.out.println(customer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps,resultSet);
}
}
3.3.6 针对单个表的通用查询
public Customer queryForCustomers(String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0 ; i < args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if(rs.next()){
Customer customer = new Customer();
for(int i=0;i<columnCount;i++){
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给customer对象指定的columnName属性,赋值为columnValue,反射
Field field = Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(customer,columnValue);
}
return customer;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
测试用例
public void testQueryForCustomer(){
String sql = "select id,name,email,birth from `customers` where id = ?";
Customer customer = queryForCustomers(sql, 13);
System.out.println(customer);
}
3.3.7 表的字段名和类的属性名不一致
表的字段名和类的属性名不一致时,需要通过为结果集起别名的方式获得结果。别名与类的属性名相同。
此时将
String columnName = rsmd.getColumnName(i+1);//替换为以下方法
String columnName = rsmd.getColumnLabel(i+1);
public Order orderForQuery(String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if(rs.next()){
Order order = new Order();
for(int i = 0;i<columnCount;i++){
Object columnValue = rs.getObject(i + 1);
String columnName = rsmd.getColumnLabel(i+1);
Field field = Order.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(order,columnValue);
}
return order;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
测试用例
public void testOrderForQuery(){
String sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?";
Order order = orderForQuery(sql, 1);
System.out.println(order);
}
3.3.8 针对不同表的通用查询
public <T> List<T> getForList(Class<T> clazz,String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0 ; i < args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
ArrayList<T> list = new ArrayList<T>();
while(rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);
//给t对象指定的columnName属性,赋值为columnValue,反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps,rs);
}
return null;
}
测试用例
public void testGetForList(){
String sql="select id,name,email,birth from `customers` where id < ?";
List<Customer> list = getForList(Customer.class, sql, 12);
list.forEach(System.out::println);
}
3.3.9 PreparedStatement的优点
- 解决了拼串问题
- PreparedStatement使用了sql语句的预编译,从而使sql语句不会被语句陷阱修改原逻辑关系,解决了SQL注入问题
- PreparedStatement可以操作Blob的数据,而Statement不行
- 可以实现更高效的批量操作
3.4 Statement vs PreparedStatement
- 代码的可读性和可维护性
- PreparedStatement能最大可能提高性能:
- DBServer会对预编译语句提供性能优化。编译过的代码会缓存,后续只需要传参
- Statement没有缓存,所以没传一次语句需要重新编译一次
- (语法检查、语义检查、翻译成二进制命令,缓存)
- PreparedStatement可以防止SQL注入
3.5 小结
- 两种思想
- 面向接口编程思想
- ORM思想(object relational mapping)
- 一个数据表对应一个Java类
- 表中一条记录对应Java类的一个记录
- 表中一个字段对应Java类的属性
sql是需要结合列名和表的属性名来写,注意起别名
- 两种技术
- JDBC结果集的元数据:ResultSetMetaData
- 获取列数:getColumnCount()
- 获取列的别名:getColumnLabel()
- 通过反射,创建指定类的对象,获取指定的属性并赋值。
- JDBC结果集的元数据:ResultSetMetaData
4 操作BLOB类型字段
4.1 MySQL BLOB类型
-
MySQL中,BLOB是一个二进制的大型对象,是一个可以储存大量数据的容器,它能容纳不同大小的数据。
-
插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
-
MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)。

-
根据存入数据大小定义不同的BLOB类型。
-
需要注意的是:如果存储的文件过大,数据库的性能会下降。
-
如果在指定了相关的Blob类型后还报错:xxx too large,那么在mysql安装目录下找到my.ini文件加上如下配置:
max_allowed_packet=16M需要重启mysql服务。
4.2 向数据表插入BLOB类型数据
//向数据表customers中插入Blob型字段
@Test
public void testInsert() throws Exception {
Connection conn = JDBCUtils.getConnection();
String sql="insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"陈涛");
ps.setObject(2,"ct@qq.com");
ps.setObject(3,"1999-05-18");
FileInputStream is = new FileInputStream(new File("src/main/java/com/images/timg.jpg"));
ps.setObject(4,is);
ps.execute();
JDBCUtils.closeResource(conn,ps);
}
4.3 查询BLOB字段
BLOB字段采用IO流获取
@Test
public void testQuery(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
FileOutputStream fos = null;
try {
conn = JDBCUtils.getConnection();
String sql="select id,name,email,birth,photo from customers where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1,22);
rs = ps.executeQuery();
if(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
System.out.println(customer);
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos= new FileOutputStream("sds.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn,ps,rs);
}
}
5 批量插入
5.1 层次一:使用Statement
5.2 层次二:使用PreparedStatement
时间:52s
//批量插入方式二:使用PreparedStatement
@Test
public void testInsert(){
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i = 1;i<=20000;i++){
ps.setObject(1,"name_"+i);
ps.execute();
}
long end = System.currentTimeMillis();
System.out.println("time:"+(end-start));//time:52711
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps);
}
}
5.3 层次三:batch相关方法
时间:1488
/*批量插入方式三:
* 1.addBatch()、executeBatch()、clearBatch()
*
* */
@Test
public void testInsert2(){
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql="insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i = 1;i<=20000;i++){
ps.setObject(1,"name_"+i);
//1.攒sql
ps.addBatch();
if(i % 500 == 0){
//2.运行batch
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("time:"+(end-start));//time:52711
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,ps);
}
}
Tips:
mysql服务器默认关闭批处理,我们需要改参数,让mysql开启批处理支持,
rewriteBatchedStatements=true
写在配置文件的url后面
mysql8.0默认支持
5.4 层次四:设置不允许自动提交
时间:1399
在层次三的基础上,对Connection的自动提交设置为false,在预编译结束后统一提交。
conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);
...
...
conn.commit();
6 数据库事务
6.1 数据库事务介绍
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态
- 事务处理:保证所有事务作为一个工作单元来执行,即使出现故障也不会改变这种执行方式。当一个事务执行多个操作时,要么全部提交(commit),全部修改保留下来;要么放弃所有的更改,整个事务**回滚(rollback)**到最初状态。
- 为保证数据一致性
6.2 JDBC事务处理
首先明确一个关键点:数据一旦提交,就不可回滚
而哪些操作会导致数据自动提交?
- DDL操作一旦执行,都会自动提交
- DML操作默认执行后会提交
- 可以通过set autocommit = false方式取消DML操作的自动提交
- 默认在关闭连接时自动提交数据
JDBC程序为了让多个SQL语句作为一个事务执行,提供了:
- Connection对象的setAutoCommit(false)以取消自动提交事务
- 在所有SQL语句执行成功后,调用commit()方法统一提交事务
- 在出现异常时,调用rollback()方法回滚事务
若此时Connection未关闭,还可能被重复使用,则需要回复其自动提交状态:setAutoCommit(true)
尤其是在使用数据库连接池技术时,执行close()方法前,建议恢复自动提交
【实例:AA向BB转账100】
//****************************考虑数据库事务
@Test
public void testUpdateWithTx(){
Connection conn = null;
try {
//将连接放到事务操作的开始事务中关闭连接
conn = JDBCUtils.getConnection();
//1.取消数据自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql1,"AA");
// //模拟异常
// System.out.println(10/0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2,"BB");
System.out.println("转账成功");
//2.最后提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚操作
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
JDBCUtils.closeResource(conn,null);
}
}
//通用的增删改--version 2.0(应用于事务)
public int update(Connection conn,String sql,Object ...args){
PreparedStatement ps = null;
try {
//1.预编译sql语句
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.关闭资源
JDBCUtils.closeResource(null,ps);
}
return 0;
}
6.3 事务的ACID属性
-
原子性(Atomicity)
事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
-
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
即:事务按照预期生效,数据的状态是预期的状态。
-
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
-
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
6.3.1 数据库并发问题
- 对于同时运行的多个事务,当这些事务访问相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
- 脏读(dirty read): A事务读取了B事务尚未提交的更改数据,并且在这个数据基础上进行操作。如果此时恰巧B事务进行回滚,那么A事务读到的数据是根本不被承认的。
- 不可重复读(unrepeatable read):不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致。字段
- 幻读(phantom read): A事务读取B提交的新增数据,这时A事务将出现幻读的问题。幻读一般发生在计算统计数据的事务中。举个例子,假设银行系统在同一个事务中两次统计存款的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时两次统计的总金额将不一致。行
- 数据库事务的隔离性:数据库系统必须具有隔离并并发运行各个事务的能力,使他们不会互相影响,避免并发问题
- 一个事务与其他事务隔离的程度称为隔离级别。隔离级别越高,数据一致性就越好,并发性就越弱
6.3.2 四种隔离级别
- Oracle支持2种事务隔离级别:READ COMMITTED,SERIALIZABLE。Oracle默认事务隔离级别为:READ COMMITTED
- Mysql支持4种事务隔离级别,默认为:REPEATABLE READ
6.3.3 Mysql中设置隔离级别
- 每个mysql数据库连接都有一个全局变量@tx_isolation,表示事务隔离级别
- 查看
SELECT @@tx_isolation;
- 设置当前数据库连接的隔离级别
set transaction isolation level read committed;
- 设置全局
set global transaction isolation level read committed;
-
补充
- 创建用户
create user 用户名 identified by '密码';- 授予权限
#赋予用户对表的增删改查的权限 grant select,insert,update,delete on 数据库.* to 用户名@localhost identified by '密码';
7 DAO及其实现类
-
DAO(Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道,夹在业务逻辑与数据库资源中间。包括了对数据的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息
-
作用:为了实现功能的模块化,更利于代码的维护和升级
-
流程:
BaseDAO写出通用的方法——>针对特定表的DAO接口,继承了BaseDAO,规范一系列针对特定表的操作——>特定表接口 DAO的实现类DAOImpl里面,编写具体的方法
【BaseDAO】类
/**
* @author CT
* @Description 封装了针对于数据表的通用操作
* @create 2022-01-12-22:19
*/
public abstract class BaseDAO {
//通用的增删改--v.2.0(应用于事务)
public int update(Connection conn, String sql, Object ...args){
PreparedStatement ps = null;
try {
//1.预编译sql语句
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.关闭资源
JDBCUtils.closeResource(null,ps);
}
return 0;
}
//通用查询操作:返回数据表一条数据--v.2.0(事务)
public <T> T getInstance(Connection conn,Class<T> clazz,String sql,Object ...args){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0 ; i < args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if(rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);
//给customer对象指定的columnName属性,赋值为columnValue,反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null,ps,rs);
}
return null;
}
//查询返回多条数据--v.2.0(事务)
public <T> List<T> getForList(Connection conn,Class<T> clazz, String sql, Object ...args){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
//填充占位符
for(int i = 0 ; i < args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
ArrayList<T> list = new ArrayList<T>();
while(rs.next()){
T t = clazz.newInstance();
for(int i=0;i<columnCount;i++){
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名
String columnLabel = rsmd.getColumnLabel(i+1);
//给t对象指定的columnName属性,赋值为columnValue,反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null,ps,rs);
}
return null;
}
//查询特殊值的通用方法
public <E> E getValue(Connection conn,String sql,Object...args){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i = 0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery();
if(rs.next()){
return (E) rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null,ps,rs);
}
return null;
}
}
【CustomerDAO】类
/**
* @author CT
* @Description 规范针对于customers表的常用操作
* @create 2022-01-12-22:32
*/
public interface CustomerDAO {
/**
* @Description: 将customer对象添加到数据库中
* @Param: [conn, customer]
* @return: void
* @Author: CT
* @Date: 2022/1/12 22:37
*/
void insert(Connection conn, Customer customer);
/**
* @Description: 根据id删除表中一条记录
* @Param: [conn, id]
* @return: void
* @Author: CT
* @Date: 2022/1/12 22:40
*/
void deleteByID(Connection conn,int id);
/**
* @Description: 根据内存中的customer对象修改表中对应的记录
* @Param: [conn, customer]
* @return: void
* @Author: CT
* @Date: 2022/1/12 22:41
*/
void update(Connection conn,Customer customer);
/**
* @Description: 根据id查询Customer对象
* @Param: [conn, id]
* @return: com.ct2.bean.Customer
* @Author: CT
* @Date: 2022/1/12 22:42
*/
Customer getCustomerById(Connection conn,int id);
/**
* @Description: 查询数据表中的所有记录,返回集合
* @Param: [conn]
* @return: java.util.List<com.ct2.bean.Customer>
* @Author: CT
* @Date: 2022/1/12 22:44
*/
List<Customer> getAll(Connection conn);
/**
* @Description: 获取数据表中记录数
* @Param: [conn]
* @return: long
* @Author: CT
* @Date: 2022/1/12 22:45
*/
long getCount(Connection conn);
/**
* @Description: 返回数据表最大的生日
* @Param: [conn]
* @return: java.sql.Date
* @Author: CT
* @Date: 2022/1/12 22:46
*/
Date getMaxBirth(Connection conn);
}
【CustomerDAOImpl】类
/**
* @author CT
* @Description Customer表DAO的实现类
* @create 2022-01-12-22:47
*/
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {
@Override
public void insert(Connection conn, Customer customer) {
String sql = "insert into customers(name,email,birth)values(?,?,?)";
update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth());
}
@Override
public void deleteByID(Connection conn, int id) {
String sql = "delete from customers where id = ?";
update(conn,sql,id);
}
@Override
public void update(Connection conn, Customer customer) {
String sql = "update customers set name = ?,email = ?,birth = ? where id = ?";
update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth(),customer.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql = "select id,name,email,birth from customers where id = ?";
Customer customer = getInstance(conn, Customer.class, sql, id);
return customer;
}
@Override
public List<Customer> getAll(Connection conn) {
String sql ="select id,name,email,birth from customers";
List<Customer> list = getForList(conn, Customer.class, sql);
return list;
}
@Override
public long getCount(Connection conn) {
String sql = "select count(*) from customers";
return getValue(conn, sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customers";
return getValue(conn,sql);
}
}
测试用例
/**
* @author CT
* @Description 测试DAO及其实现类功能
* @create 2022-01-12-23:09
*/
public class CustomerDAOImplTest {
private CustomerDAOImpl dao = new CustomerDAOImpl();
@Test
public void insert() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(1, "ct", "1827323@qq.com", new Date(972323213L));
dao.insert(conn,customer);
System.out.println("添加成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void deleteByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
dao.deleteByID(conn,23);
System.out.println("删除成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void update() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(22, "ct", "1827323@qq.com", new Date(972323213L));
dao.update(conn,customer);
System.out.println("修改成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void getCustomerById() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = dao.getCustomerById(conn, 10);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void getAll() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
List<Customer> list = dao.getAll(conn);
for(Customer c : list){
System.out.println(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void getCount() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println("记录数:"+dao.getCount(conn));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
@Test
public void getMaxBirth() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Date maxBirth = dao.getMaxBirth(conn);
System.out.println("最大的生日(数值上):"+maxBirth);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
}
8 数据库连接池
8.1 JDBC数据库连接池的必要性
- 开发基于数据库的web程序时,传统模式基本是按以下步骤:
- 在主程序(如servlet、beans)中建立数据库连接
- 进行sql连接
- 断开数据库连接
- 传统模式存在以下问题:
- 普通数据库连接使用DriverManager来获取,每次建立连接都需要将Connection加载到内存中,再验证用户名和密码(需要花费0.05s~1s时间)。需要连接时建立一个连接,执行完成后断开,这种方式会耗费大量的资源和时间。**数据库连接资源没有得到很好的重复利用。**无法应对高负载情况
- 对于每一次数据库连接,使用完之后都得关闭。如果因为程序异常而未能关闭,将会导致数据库系统内存泄漏,最终导致重启数据库
- 不能控制被创建的连接对象数,系统资源会被毫无顾忌地分配出去,连接过多也可能导致内存泄露,服务器崩溃
8.2 数据库连接池技术
-
基本思想:为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从缓冲池中取出一个,使用完再放回去。
-
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
-
最小数据库连接数:初始化创建。数据库连接池至少有的连接数
最大数据库连接数:限定最大的连接数。超过的话,之后的请求会被加入到等待队列中
-
工作原理:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWCPeT4y-1645449806331)(C:\Users\Tao\AppData\Roaming\Typora\typora-user-images\image-20220113135614756.png)]
- 优点:
- 资源重用
- 更快的系统反应速度
- 新的资源分配手段
- 统一的连接管理,避免数据库连接泄露
8.3 多种开源的数据库连接池
- JDBC数据库连接使用javax.sql.DataSource来表示,DataSource只是一个接口,通常由服务器(Weblogic、WebSphere、Tomcat)实现,也有开源的:
- DBCP:由Apache提供。速度相对C3P0快,但自身存在bug
- C3P0:速度相对慢但稳定。hibernate官方推荐使用
- Proxool
- BoneCP
- Druid:阿里提供的
- DataSource用来取代DriverManager来获取Connection,获取速度较快,同时可以大幅度提高数据库访问速度
8.3.1 C3P0数据库连接池
- 依赖
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
- 方式一:显式创建
//方式一:
@Test
public void testGetConnection() throws Exception {
//获取C3P0数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8" );
cpds.setUser("root");
cpds.setPassword("");
//设置初始化时数据库连接池的连接数
cpds.setInitialPoolSize(10);
Connection conn = cpds.getConnection();
System.out.println(conn);
}
-
方式二:配置文件xml创建
放在resources包下
<?xml version="1.0" encoding="utf-8" ?>
<c3p0-config>
<named-config name="helloc3p0">
<!--提供获取连接的4个基本信息-->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password"></property>
<!--数据库连接池管理的基本信息-->
<!--单次增加的连接数-->
<property name="acquireIncrement">5</property>
<!--初始化连接数-->
<property name="initialPoolSize">10</property>
<!--最少连接数-->
<property name="minPoolSize">10</property>
<!--最大连接数-->
<property name="maxPoolSize">100</property>
<!--维护的最多的Statement个数-->
<property name="maxStatements">50</property>
<!--每个连接可使用的最多的Statement个数-->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
测试
//方式二:使用配置文件
@Test
public void testGetConnection1() throws Exception {
ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
8.3.2 DBCP数据库连接池
- 依赖
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.5.5</version>
</dependency>
- 方式一:显式创建
//方式一
@Test
public void testGetConnection() throws SQLException {
//创建连接池
BasicDataSource source = new BasicDataSource();
//设置基本信息
source.setDriverClassName("com.mysql.cj.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8");
source.setUsername("root");
source.setPassword("");
//设置连接池管理属性
source.setInitialSize(10);
source.setMaxActive(10);
//...
Connection conn = source.getConnection();
System.out.println(conn);
}
- 方式二:配置文件
//方式二:使用配置文件
@Test
public void testGetConnection1() throws Exception {
Properties pros = new Properties();
//方式一:
// InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
//方式二:
FileInputStream is = new FileInputStream(new File("src/main/resources/dbcp.properties"));
pros.load(is);
DataSource source = BasicDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
username=root
password=
8.3.3 Druid数据库连接池
-
Druid是阿里巴巴开源平台上的一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一
-
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.0</version>
</dependency>
- 使用方式:使用配置文件
@Test
public void getConnection() throws Exception {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_learn?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
username=root
password=
initialSize=10
maxActive=10
8.4 使用三种连接池技术实现的JDBCUtils
public class JDBCUtils {
/**
* @Description: 使用C3P0数据库连接池技术获取连接
*/
static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection1() throws Exception {
Connection conn = cpds.getConnection();
return conn;
}
/**
* @Description: 使用DBCP数据库连接池技术获取连接
*/
private static DataSource source;
static {
try {
Properties pros = new Properties();
FileInputStream is = new FileInputStream(new File("src/main/resources/dbcp.properties"));
pros.load(is);
source = BasicDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection2() throws Exception {
Connection conn = source.getConnection();
return conn;
}
/**
* @Description: 使用Druid数据库连接池技术获取连接
*/
private static DataSource source1;
static {
try {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
source1 = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection3() throws Exception {
Connection conn = source1.getConnection();
return conn;
}
}
9 Apache-DBUtils实现CRUD操作
9.1 Apache-DBUtils简介
- common-dbutils是Apache组织提供的一个开源JDBC工具类库,是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能
- API:
- org.apache.commons.dbutils.QueryRunner
- org.apache.commons.dbutils.ResultSetHandler
- 工具类:org.apache.commons.dbutils.Dbutils
- Maven依赖
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.3</version>
</dependency>
9.2 Apache-DBUtils使用
在使用QueryRunner的方法中,需要提供对应的ResultSetHandler,即结果的返回方式。ResultSetHandler是一个规范结果返回方式的接口。
其实现类包括:
| 实现类 | 功能 |
|---|---|
| ResultSetHandler(接口) | 以下所有实现类的实现的接口 |
| BeanHandler | 用于封装表中的一条记录,返回一个对象 |
| BeanListHandler | 用于封装表中的多条记录的集合List,返回对象的集合List |
| MapHandler | 用于封装表中的一条记录,返回一个集合Map;该Map中将字段及其值设定key=value |
| BeanListHandler | 用于封装表中的多条记录的集合,返回Map的集合List |
| ScalarHandler | 用于查询特殊值,返回一个对象 |
- 增删改
@Test
public void testInsert(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "insert into customers(name,email,birth)values(?,?,?)";
int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@126.com", "1997-09-08");
System.out.println("添加了"+insertCount+"条记录");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
- 查询
//查询
//BeanHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录
@Test
public void testQuery1() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
Customer customer = runner.query(conn, sql, handler, 24);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
//BeanListHandler:ResultSetHandler接口的实现类,用于封装表中的多条记录的集合
@Test
public void testQuery2() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
List<Customer> list = runner.query(conn, sql, handler, 15);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
//MapHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录,返回Map
@Test
public void testQuery3() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
MapHandler handler = new MapHandler();
Map<String, Object> map = runner.query(conn, sql, handler, 24);
System.out.println(map);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
//MapListHandler:ResultSetHandler接口的实现类,用于封装表中的多条记录,返回Map的集合List
@Test
public void testQuery4() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler, 15);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
//ScalarHandler:ResultSetHandler接口的实现类,用于查询特殊值
@Test
public void testQuery5(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select count(*) from customers";
ScalarHandler handler = new ScalarHandler();
long count = (long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
9.3 自定义ResultSetHandler实现类
正常Apache-DBUtils中提供的ResultSetHandler实现类已经足够,以下自定义只是演示方法。
//自定义ResultSetHandler实现类
@Test
public void testQuery7(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
@Override
public Customer handle(ResultSet resultSet) throws SQLException {
if(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
Date birth = resultSet.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
return customer;
}
return null;
}
};
Customer customer = runner.query(conn, sql, handler, 20);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
9.4 Dbutils工具类实现资源关闭
/**
* @Description: 使用Dbutils工具类实现资源关闭
*/
public static void closeResource1(Connection conn, Statement ps, ResultSet rs){
//closeQuietly()内封装了try-catch可以省去
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
ng) runner.query(conn, sql, handler);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
## 9.3 自定义ResultSetHandler实现类
正常Apache-DBUtils中提供的ResultSetHandler实现类已经足够,以下自定义只是演示方法。
```java
//自定义ResultSetHandler实现类
@Test
public void testQuery7(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {
@Override
public Customer handle(ResultSet resultSet) throws SQLException {
if(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
Date birth = resultSet.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
return customer;
}
return null;
}
};
Customer customer = runner.query(conn, sql, handler, 20);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn,null);
}
}
9.4 Dbutils工具类实现资源关闭
/**
* @Description: 使用Dbutils工具类实现资源关闭
*/
public static void closeResource1(Connection conn, Statement ps, ResultSet rs){
//closeQuietly()内封装了try-catch可以省去
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
本文详细介绍了Java JDBC与数据库的交互,涵盖了JDBC概述、数据持久化、JDBC API、连接数据库的多种方式、PreparedStatement的使用、批量操作、数据库事务的ACID属性、DAO设计模式及其实现类,以及C3P0、DBCP、Druid等数据库连接池的使用。此外,还讨论了Apache-DBUtils在简化CRUD操作中的应用,以及如何自定义ResultSetHandler。最后,文章提到了资源关闭的Dbutils工具类方法。
210

被折叠的 条评论
为什么被折叠?



