JDBC学习笔记——2
六、事务
概念及属性
-
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
-
事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务**回滚(rollback)**到最初状态。
事务的ACID属性:
-
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 -
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 -
隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 -
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
数据库的并发问题:
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
- 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
package jdbc_2.transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
import jdbc_1.myutil.JDBCUtils;
public class TransactionTest {
/*
* 在user_table表中,演示AA向BB转账100元
* update user_table set balance = balance - 100 where user = 'AA';
* update user_table set balance = balance + 100 where user = 'BB';
*/
@Test
public void testTransaction_1() {
/*
* 1. 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
* 逻辑操作单元:一个或多个DML操作
*
* 2.事务处理原则:
* 保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,
* 要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,
* 整个事务回滚(rollback)到最初状态。
*
* 3. 数据一旦提交,就不可以回滚
*
* 4.哪些情况会导致数据的自动提交?
* (1)DDL一旦执行,就会被自动提交
* (2)DML默认情况下会自动提交,可以通过set autocommit = false; 取消DML的自动提交,但该操作对DDL不起作用
* (3)在关闭连接时,数据会自动提交
*/
String sql1 = "update user_table set balance = balance - 100 where user = ?";
String sql2 = "update user_table set balance = balance + 100 where user = ?";
CUD(sql1, "AA");
CUD(sql2, "BB");
}
public int CUD(String sql, Object ...obj) {
//sql中占位符的个数应该与可变形参的长度相同
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,获取PreparedStatement对象
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
}
//4.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//5.关闭资源
JDBCUtils.closeResource(ps, conn);
return 0;
}
@Test
public void testTransaction_2() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
//1. 关闭自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
CUDForTransaction(conn, sql1, "AA");
String sql2 = "update user_table set balance = balance + 100 where user = ?";
CUDForTransaction(conn, sql2, "BB");
//2. 提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3. 如果发生异常,则回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
//4. 修改回原状态(主要使用在数据库连接池中)
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(null, conn);
}
}
/*
* 考虑数据库事务的CUD操作
*/
public int CUDForTransaction(Connection conn, String sql, Object ...obj) {
//sql中占位符的个数应该与可变形参的长度相同
PreparedStatement ps = null;
try {
//1.预编译sql语句,获取PreparedStatement对象
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//4.关闭资源
JDBCUtils.closeResource(ps, null);//注意:不关闭连接
return 0;
}
}
四种隔离级别
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
package jdbc_2.transaction;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import org.junit.Test;
import jdbc_1.bean.User;
import jdbc_1.myutil.JDBCUtils;
/**
* 演示事务的隔离级别
* @author MCC
*
*/
public class IsolationTest {
@Test
public void testIsolationSelect() throws Exception {
Connection conn = JDBCUtils.getConnection();
//获取当前连接的隔离级别
int isolation = conn.getTransactionIsolation();//四种隔离级别分别对应1, 2, 4, 8
//设置数据库的隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
System.out.println(isolation);
//取消自动提交
conn.setAutoCommit(false);
String sql = "select user, password, balance from user_table where user = ?";
User user = generalOneSelectForTx(conn, User.class, sql, "CC");
System.out.println(user);
}
@Test
public void testIsolationUpdate() throws Exception {
Connection conn = JDBCUtils.getConnection();
//取消自动提交
conn.setAutoCommit(false);
String sql = "update user_table set balance = ? where user = ?";
CUDForTransaction(conn, sql, 5000, "CC");
//睡眠20s之后再提交数据
Thread.sleep(20000);
conn.commit();
}
//添加事务的通用查询操作
public <T> T generalOneSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.建立连接
// conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
//4.执行sql,获取结果集对象以及结果集元数据对象
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
if(rs.next()) {//如果有数据,则取出数据
T t = clazz.getConstructor().newInstance();
//根据数据的字段数,判断取出次数
int columnCount = rsmd.getColumnCount();
for(int i=0; i<columnCount; i++) {
//取出数值以及列名
/*
* 获取列的列名:rsmd.getColumnName()——不推荐使用
* 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
*/
Object columnValue = rs.getObject(i+1);
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将该值赋值给t对象的对应属性
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
//考虑事务的CUD
public int CUDForTransaction(Connection conn, String sql, Object ...obj) {
//sql中占位符的个数应该与可变形参的长度相同
PreparedStatement ps = null;
try {
//1.预编译sql语句,获取PreparedStatement对象
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//4.关闭资源
JDBCUtils.closeResource(ps, null);//注意:不关闭连接
return 0;
}
}
七、DAO
- 将每个表封装成一个类,给出每个类的规范(即接口)
- 定义BaseDAO类,提供所有基本功能
- 定义Implement类,继承BaseDAO类并实现各自规范,即为一个DAO
版本1
BaseDAO类
package jdbc_2.DAO;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import jdbc_1.myutil.JDBCUtils;
/**
* 封装了关于数据表的通用操作
* DAO:Data(base) Access Object
* @author MCC
*
*/
public abstract class BaseDAO {
//添加事务的CUD
public int CUDForTx(Connection conn, String sql, Object ...obj) {
//sql中占位符的个数应该与可变形参的长度相同
PreparedStatement ps = null;
try {
//1.预编译sql语句,获取PreparedStatement对象
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//4.关闭资源
JDBCUtils.closeResource(ps, null);//注意:不关闭连接
return 0;
}
//添加事务的通用查询操作
public <T> T generalOneSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.建立连接
// conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
//4.执行sql,获取结果集对象以及结果集元数据对象
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
if(rs.next()) {//如果有数据,则取出数据
T t = clazz.getConstructor().newInstance();
//根据数据的字段数,判断取出次数
int columnCount = rsmd.getColumnCount();
for(int i=0; i<columnCount; i++) {
//取出数值以及列名
/*
* 获取列的列名:rsmd.getColumnName()——不推荐使用
* 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
*/
Object columnValue = rs.getObject(i+1);
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将该值赋值给t对象的对应属性
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
//添加事务的查询
public <T> List<T> generalMoreSelectForTx(Connection conn, Class<T> clazz, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// //1.建立连接
// conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
//4.执行sql,获取结果集对象以及结果集元数据对象
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
//创建集合对象
List<T> list = new ArrayList<T>();
while(rs.next()) {//如果有数据,则取出数据
T t = clazz.getConstructor().newInstance();
//根据数据的字段数,判断取出次数
int columnCount = rsmd.getColumnCount();
//给t对象指定属性赋值
for(int i=0; i<columnCount; i++) {
//取出数值以及列名
/*
* 获取列的列名:rsmd.getColumnName()——不推荐使用
* 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
*/
Object columnValue = rs.getObject(i+1);
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将该值赋值给t对象的对应属性
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
//将t对象添加到集合中
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
//针对于分组函数的查询操作
public <T> T getGroupValues(Connection conn, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
rs = ps.executeQuery();
if(rs.next()) {
Object value = rs.getObject(1);
return (T)value;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
}
CustomerDAO接口
package jdbc_2.DAO;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import jdbc_1.bean.Customer;
/**
* 此接口用于规范针对于customers表的CRUD操作
* @author MCC
*
*/
public interface CustomerDAO {
/**
* 将cust对象添加到数据库中
* @param conn
* @param cust
*/
void insert(Connection conn, Customer cust);
/**
* 根据指定的ID删除表中的记录
* @param conn
* @param id
*/
void deleteByID(Connection conn, int id);
/**
* 根据cust对象的id修改数据库中对应id的信息为cust的信息
* @param conn
* @param cust
*/
void updateByID(Connection conn, Customer cust);
/**
* 根据指定的id查询对应的Customer对象
* @param conn
* @param id
*/
Customer getCustomerByID(Connection conn, int id);
/**
* 查询满足条件的所有Customer对象
* @param conn
* @return
*/
List<Customer> getAllCustomerByID(Connection conn);
/**
* 获取表中记录的总数
* @param conn
* @return
*/
Long getCount(Connection conn);
/**
* 获取表中的最大生日
* @param conn
* @return
*/
Date getMaxBirth(Connection conn);
}
CustomerDAOImpl类
package jdbc_2.DAO;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import jdbc_1.bean.Customer;
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {
@Override
public void insert(Connection conn, Customer cust) {
String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth());
}
@Override
public void deleteByID(Connection conn, int id) {
String sql = "delete from customers where id = ?";
CUDForTx(conn, sql, id);
}
@Override
public void updateByID(Connection conn, Customer cust) {
String sql = "update customers set name = ?, email = ?, birth = ? where id = ? ";
CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
}
@Override
public Customer getCustomerByID(Connection conn, int id) {
String sql = "select id, name, email, birth from customers where id = ?";
Customer customer = generalOneSelectForTx(conn, Customer.class, sql, id);
return customer;
}
@Override
public List<Customer> getAllCustomerByID(Connection conn) {
String sql = "select id, name, email, birth from customers";
List<Customer> list = generalMoreSelectForTx(conn, Customer.class, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customers";
Object values = getGroupValues(conn, sql);
return (Long)values;
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customers";
Object values = getGroupValues(conn, sql);
return (Date)values;
}
}
测试类
package jdbc_2.DAO.junit;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import org.junit.Test;
import jdbc_1.bean.Customer;
import jdbc_1.myutil.JDBCUtils;
/**
* 测试CustomerDAOImplTest
*/
import jdbc_2.DAO.CustomerDAOImpl;
public class CustomerDAOImplTest {
//每个方法都要用,定义为成员属性
private CustomerDAOImpl dao = new CustomerDAOImpl();
@Test
public void testInsert() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(0, "宇智波鼬", "yuzhiboyou@126.com", new Date(19930607));
dao.insert(conn, customer);
System.out.println("添加成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testDeleteByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
dao.deleteByID(conn, 13);
System.out.println("删除成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testUpdateByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(18, "贝多芬", "beiduofen@126.com", new Date(19930607L));
dao.updateByID(conn, customer);
System.out.println("修改成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetCustomerByID() {
Connection conn = null;
try {
//单独演示使用c3p0获取链接
conn = jdbc_2.myutil.JDBCUtils.getConnection_c3p0();
Customer customer = dao.getCustomerByID(conn, 19);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetAllCustomerByID() {
Connection conn = null;
try {
conn = jdbc_2.myutil.JDBCUtils.getConnection_DBCP();
List<Customer> list = dao.getAllCustomerByID(conn);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetCount() {
Connection conn = null;
try {
conn = jdbc_2.myutil.JDBCUtils.getConnection_druid();
Long count = dao.getCount(conn);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetMaxBirth() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Date maxBirth = dao.getMaxBirth(conn);
System.out.println(maxBirth);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
}
版本2
BaseDAO类
package jdbc_2.DAO_pro;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import jdbc_1.myutil.JDBCUtils;
/**
* 封装了关于数据表的通用操作
* DAO:Data(base) Access Object
* @author MCC
*
*/
public abstract class BaseDAO<T> {
private Class<T> clazz = null;
/*
* 所有继承BaseDAO的子类在使用BaseDAO中的方法时,都会先执行代码块,
* 代码块中的操作是获取子类所继承的父类的泛型参数,之后将该参数(实际上是一个类)赋值给clazz,
* 而clazz被声明成成员属性,因此可以被每个成员方法调用,这样就不需要在函数中传入Class<T> clazz参数了。
* 例如CustomerDAOImpl extends BaseDAO implements CustomerDAO,因为父类BaseDAO有泛型,所以声明为
* CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO
* 代码块中的方法,可以获取子类CustomerDAOImpl中带泛型的父类BaseDAO<Customer>中的泛型参数Customer,
* 之后将其赋值给clazz,因此,成员方法中的所有clazz在被CustomerDAOImpl调用时不需要传入clazz = Customer.class了
*/
{
//获取当前正在调用BaseDAO的子类所继承的父类的泛型
//获取该子类所继承的泛型父类
Type genericSuperclass = this.getClass().getGenericSuperclass();
//类型转换
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取父类的泛型参数
Type[] typeArguments = paramType.getActualTypeArguments();
//因为只有一个泛型,所以取出数组中的第一个参数即可
Type type = typeArguments[0];
//赋值给clazz
clazz = (Class<T>) type;
}
//添加事务的CUD
public int CUDForTx(Connection conn, String sql, Object ...obj) {
//sql中占位符的个数应该与可变形参的长度相同
PreparedStatement ps = null;
try {
//1.预编译sql语句,获取PreparedStatement对象
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);//sql从1开始,obj从0开始
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
//4.关闭资源
JDBCUtils.closeResource(ps, null);//注意:不关闭连接
return 0;
}
//添加事务的通用查询操作
public T generalOneSelectForTx(Connection conn, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.建立连接
// conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
//4.执行sql,获取结果集对象以及结果集元数据对象
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
if(rs.next()) {//如果有数据,则取出数据
T t = clazz.getConstructor().newInstance();
//根据数据的字段数,判断取出次数
int columnCount = rsmd.getColumnCount();
for(int i=0; i<columnCount; i++) {
//取出数值以及列名
/*
* 获取列的列名:rsmd.getColumnName()——不推荐使用
* 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
*/
Object columnValue = rs.getObject(i+1);
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将该值赋值给t对象的对应属性
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
//添加事务的查询
public List<T> generalMoreSelectForTx(Connection conn, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// //1.建立连接
// conn = JDBCUtils.getConnection();
//2.预编译sql语句
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
//4.执行sql,获取结果集对象以及结果集元数据对象
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
//创建集合对象
List<T> list = new ArrayList<T>();
while(rs.next()) {//如果有数据,则取出数据
T t = clazz.getConstructor().newInstance();
//根据数据的字段数,判断取出次数
int columnCount = rsmd.getColumnCount();
//给t对象指定属性赋值
for(int i=0; i<columnCount; i++) {
//取出数值以及列名
/*
* 获取列的列名:rsmd.getColumnName()——不推荐使用
* 获取列的别名:rsmd.getColumnLabel()——没有起别名时,默认返回列的列名
*/
Object columnValue = rs.getObject(i+1);
String columnLabel = rsmd.getColumnLabel(i+1);
//通过反射,将该值赋值给t对象的对应属性
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
//将t对象添加到集合中
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
//针对于分组函数的查询操作
//需要声明为泛型方法,返回值类型与泛型类T不一定相同
public <E> E getGroupValues(Connection conn, String sql, Object ...obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i=0; i<obj.length; i++) {
ps.setObject(i+1, obj[i]);
}
rs = ps.executeQuery();
if(rs.next()) {
Object value = rs.getObject(1);
return (E)value;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(rs, ps, null);
}
return null;
}
}
CustomerDAO接口
package jdbc_2.DAO_pro;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import jdbc_1.bean.Customer;
/**
* 此接口用于规范针对于customers表的CRUD操作
* @author MCC
*
*/
public interface CustomerDAO {
/**
* 将cust对象添加到数据库中
* @param conn
* @param cust
*/
void insert(Connection conn, Customer cust);
/**
* 根据指定的ID删除表中的记录
* @param conn
* @param id
*/
void deleteByID(Connection conn, int id);
/**
* 根据cust对象的id修改数据库中对应id的信息为cust的信息
* @param conn
* @param cust
*/
void updateByID(Connection conn, Customer cust);
/**
* 根据指定的id查询对应的Customer对象
* @param conn
* @param id
*/
Customer getCustomerByID(Connection conn, int id);
/**
* 查询满足条件的所有Customer对象
* @param conn
* @return
*/
List<Customer> getAllCustomerByID(Connection conn);
/**
* 获取表中记录的总数
* @param conn
* @return
*/
Long getCount(Connection conn);
/**
* 获取表中的最大生日
* @param conn
* @return
*/
Date getMaxBirth(Connection conn);
}
CustomerDAOImpl类
package jdbc_2.DAO_pro;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import jdbc_1.bean.Customer;
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {
@Override
public void insert(Connection conn, Customer cust) {
String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth());
}
@Override
public void deleteByID(Connection conn, int id) {
String sql = "delete from customers where id = ?";
CUDForTx(conn, sql, id);
}
@Override
public void updateByID(Connection conn, Customer cust) {
String sql = "update customers set name = ?, email = ?, birth = ? where id = ? ";
CUDForTx(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
}
@Override
public Customer getCustomerByID(Connection conn, int id) {
String sql = "select id, name, email, birth from customers where id = ?";
Customer customer = generalOneSelectForTx(conn, sql, id);
return customer;
}
@Override
public List<Customer> getAllCustomerByID(Connection conn) {
String sql = "select id, name, email, birth from customers";
List<Customer> list = generalMoreSelectForTx(conn, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customers";
Object values = getGroupValues(conn, sql);
return (Long)values;
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customers";
Object values = getGroupValues(conn, sql);
return (Date)values;
}
}
测试类
package jdbc_2.DAO_pro.junit;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import org.junit.Test;
import jdbc_1.bean.Customer;
import jdbc_1.myutil.JDBCUtils;
import jdbc_2.DAO_pro.CustomerDAOImpl;
public class CustomerDAOImplTest {
//每个方法都要用,定义为成员属性
private CustomerDAOImpl dao = new CustomerDAOImpl();
@Test
public void testInsert() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(0, "宇智波鼬", "yuzhiboyou@126.com", new Date(19930607));
dao.insert(conn, customer);
System.out.println("添加成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testDeleteByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
dao.deleteByID(conn, 13);
System.out.println("删除成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testUpdateByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = new Customer(18, "贝多芬", "beiduofen@126.com", new Date(19930607L));
dao.updateByID(conn, customer);
System.out.println("修改成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetCustomerByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = dao.getCustomerByID(conn, 19);
System.out.println(customer);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetAllCustomerByID() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
List<Customer> list = dao.getAllCustomerByID(conn);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetCount() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Long count = dao.getCount(conn);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
@Test
public void testGetMaxBirth() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Date maxBirth = dao.getMaxBirth(conn);
System.out.println(maxBirth);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
}
八、数据库连接池
- JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
- C3P0 是一个开源组织提供的一个数据库连接池,**速度相对较慢,稳定性还可以。**hibernate官方推荐使用
- Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
- BoneCP 是一个开源组织提供的数据库连接池,速度快
- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
- DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
- DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
- 特别注意:
- 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
- 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
C3P0
配置文件:文件名必须为c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="c3p0Config">
<!-- 提供获取连接的4个基本信息 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test?serverTimezone=UTC&rewriteBatchedStatements=true</property>
<property name="user">root</property>
<property name="password">macheng_0213</property>
<!-- 数据库连接池管理的基本信息 -->
<!-- 当数据库连接池中的连接不够时,c3p0可以向数据库服务器申请的额外连接数 -->
<property name="acquireIncrement">5</property>
<!-- c3p0初始时的连接数 -->
<property name="initialPoolSize">10</property>
<!-- c3p0维护的最小连接数 -->
<property name="minPoolSize">10</property>
<!-- c3p0维护的最大连接数 -->
<property name="maxPoolSize">100</property>
<!-- c3p0维护的最大Statement的个数 -->
<property name="maxStatements">50</property>
<!-- 每个连接可以使用的最大Statement个数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
package jdbc_2.connectionpool;
import java.sql.Connection;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 演示C3P0数据库连接池技术
* @author MCC
*
*/
public class C3P0Test {
/*
* 方式1:
*/
@Test
public void testGetConnection_1() throws Exception {
//获取c3p0数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.cj.jdbc.Driver"); //loads the jdbc driver
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&rewriteBatchedStatements=true");
cpds.setUser("root");
cpds.setPassword("macheng_0213");
//设置数据库连接池的属性,初始连接数,最大连接数等
cpds.setInitialPoolSize(10);//初始时池中有10个连接
//获取连接
Connection conn = cpds.getConnection();
}
/*
* 方式2:使用xml文件
*/
@Test
public void testGetConnection_2() throws Exception {
//创建数据库连接池的同时,加载指定xml文件
ComboPooledDataSource cpds = new ComboPooledDataSource("c3p0Config");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
}
DBCP
dbcp连接池常用基本配置属性:
-
initialSize:连接池启动时创建的初始化连接数量(默认值为0)
-
maxActive:连接池中可同时连接的最大的连接数(默认值为8,调整为20,高峰单机器在20并发左右,自己根据应用场景定)
-
maxIdle:连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)
-
minIdle:连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,调整为5,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接,类似于jvm参数中的Xmn设置)
-
maxWait:最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限,调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
-
poolPreparedStatements:开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)
-
maxOpenPreparedStatements:开启池的prepared 后的同时最大连接数(默认无限制,同上,未配置)
-
minEvictableIdleTimeMillis:连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
-
removeAbandonedTimeout:超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180)
-
removeAbandoned:超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为false,调整为true)
package jdbc_2.connectionpool;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
/**
* 演示DBCP数据库连接池技术
* @author MCC
*
*/
public class DBCPTest {
/*
* 方式1:
*/
@Test
public void testGetConnection1() throws SQLException {
//创建DBCP数据库连接池
BasicDataSource ds = new BasicDataSource();
//设置基本信息
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&rewriteBatchedStatements=true");
ds.setUsername("root");
ds.setPassword("macheng_0213");
//设置涉及DBCP管理的属性
ds.setInitialSize(10);
ds.setMaxActive(8);
//获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
/**
* 方式2:使用配置文件
* @throws Exception
*/
@Test
public void testGetConnection2() throws Exception {
Properties prop = new Properties();
InputStream in = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
prop.load(in);
DataSource ds = BasicDataSourceFactory.createDataSource(prop);
//获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
Druid
package jdbc_2.connectionpool;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* 演示druid数据库连接池技术
* @author MCC
*
*/
public class DruidTest {
@Test
public void getConnection() throws Exception {
//与DBCP创建对象方式类似
Properties prop = new Properties();
InputStream in = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
prop.load(in);
DataSource ds = DruidDataSourceFactory.createDataSource(prop);
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
使用数据库连接池的工具类
package jdbc_2.myutil;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import jdbc_2.connectionpool.DBCPTest;
import jdbc_2.connectionpool.DruidTest;
/**
* 使用数据库连接池获取连接
* @author MCC
*
*/
public class JDBCUtils {
/**
* 使用c3p0数据库连接池获取链接
* @return Connection conn
* @throws SQLException
*/
//声明为静态成员属性,保证c3p0只有一个,所有对象共享一个数据库连接池
private static ComboPooledDataSource cpds = new ComboPooledDataSource("c3p0Config");
public static Connection getConnection_c3p0() throws SQLException {
Connection conn = cpds.getConnection();
return conn;
}
/**
* 使用DBCP数据库连接池获取链接
* @return Connection conn
* @throws SQLException
*/
//声明静态代码块,使静态成员属性在类加载时就被赋值,且只赋值一次
private static DataSource ds;
static {
try {
Properties prop = new Properties();
InputStream in = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
prop.load(in);
ds = BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection_DBCP() throws SQLException {
//获取连接
Connection conn = ds.getConnection();
return conn;
}
/**
* 使用druid数据库连接池获取链接
* @return Connection conn
* @throws SQLException
*/
//声明静态代码块,使静态成员属性在类加载时就被赋值,且只赋值一次
private static DataSource dso;
static {
try {
Properties prop = new Properties();
InputStream in = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
prop.load(in);
dso = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection_druid() throws SQLException {
//与DBCP创建对象方式类似
Connection conn = dso.getConnection();
return conn;
}
/**
* 关闭statement和Connection
* @param conn
* @param s
*/
public static void closeResource(Statement s, Connection conn) {
try {
if(s != null) {
s.close();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//重载
public static void closeResource(ResultSet rs, Statement s, Connection conn) {
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
if(s != null) {
s.close();
}
} catch(SQLException e2) {
e2.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
九、Apache-DBUtils
-
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
-
API介绍:
- org.apache.commons.dbutils.QueryRunner
- org.apache.commons.dbutils.ResultSetHandler
- 工具类:org.apache.commons.dbutils.DbUtils
QueryRunner
使用QueryRunner对象进行增删改查
update:增删改
query:查
package jdbc_2.Apache_DBUtils;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import jdbc_1.bean.Customer;
import jdbc_2.myutil.JDBCUtils;
/*
* commons-dbutils是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,
* 并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
*/
/**
* 使用QueryRunner进行插入
* @author MCC
*
*/
public class QueryRunnerTest {
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "insert into customers (name, email, birth) values (?, ?, ?)";
int updateCount = qr.update(conn, sql, "赤砂之蝎", "xie@qq.com", "1999-2-1");
System.out.println("添加成功 " + updateCount + " 条数据!");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* BeanHandler:ResultSetHandler接口的实现类,接收查询结果是一个对象的数据
* @throws Exception
*/
@Test
public void testSelect_bean() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select id, name, email, birth from customers where id = ?";
BeanHandler<Customer> bh = new BeanHandler<Customer>(Customer.class);
Customer customer = qr.query(conn, sql, bh, 24);
System.out.println(customer);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* BeanListHandler:接收查询结果是多个对象的数据
* @throws Exception
*/
@Test
public void testSelect_beanList() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select id, name, email, birth from customers where id > ?";
BeanListHandler<Customer> blh = new BeanListHandler<Customer>(Customer.class);
List<Customer> customerList = qr.query(conn, sql, blh, 19);
customerList.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* MapHandler:将查询结果(只有一条)保存为map,键为列名,值为列值
* @throws Exception
*/
@Test
public void testSelect_map() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select id, name, email, birth from customers where id = ?";
MapHandler mh = new MapHandler();
Map<String, Object> map = qr.query(conn, sql, mh, 25);
Set<Entry<String, Object>> es = map.entrySet();
Iterator<Entry<String, Object>> iter = es.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
/*
* id=25
name=赤砂之蝎
email=xie@qq.com
birth=1999-02-01
*/
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* MapListHandler:将查询结果(多条)保存为map,键为列名,值为列值
* @throws Exception
*/
@Test
public void testSelect_mapList() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select id, name, email, birth from customers where id > ?";
MapListHandler mlh = new MapListHandler();
List<Map<String, Object>> customerList = qr.query(conn, sql, mlh, 19);
customerList.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* SaclarHandler:保存分组查询的数据
* 示例1:查询记录总数
* @throws Exception
*/
@Test
public void testSelect_scalar1() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select count(*) from customers";
//分组查询的结果类型不确定,要指定泛型
ScalarHandler<Long> sh = new ScalarHandler<Long>();
Long count = qr.query(conn, sql, sh);
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
/**
* ScalarHandler:保存分组查询的数据,即保存特殊值的数据
* 示例2:查询最大生日
* @throws Exception
*/
@Test
public void testSelect_scalar2() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select max(birth) from customers";
//分组查询的结果类型不确定,要指定泛型
ScalarHandler<Date> sh = new ScalarHandler<Date>();
Date date = qr.query(conn, sql, sh);
System.out.println(date);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
}
ResetSetHandler
-
该接口用于处理 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: 查询单个值对象
复现针对于customer表的BeanHandler
package jdbc_2.Apache_DBUtils;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Test;
import jdbc_1.bean.Customer;
import jdbc_2.myutil.JDBCUtils;
public class MyResultSetHandler {
@Test
public void testResultSetHandler() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection_druid();
String sql = "select id, name, email, birth from customers where id > ?";
/*
* 通过匿名内部类实现ResultSetHandler接口,以返回多条记录为例
*/
ResultSetHandler<List<Customer>> handler = new ResultSetHandler<List<Customer>>() {
@Override
public List<Customer> handle(ResultSet rs) throws SQLException {
List<Customer> list = new ArrayList<Customer>();
while(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);
list.add(customer);
}
return list;
}
};
List<Customer> list = runner.query(conn, sql, handler, 20);
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, conn);
}
}
}
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。
package jdbc_2.Apache_DBUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbutils.DbUtils;
/**
* 演示DbUtils工具类提供的方法
* @author MCC
*
*/
public class DbUtilsTest {
public void closeResource(Connection conn, Statement s, ResultSet rs) {
// try {
// DbUtils.close(conn);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(s);
// } catch (SQLException e) {
// e.printStackTrace();
// }
// try {
// DbUtils.close(rs);
// } catch (SQLException e) {
// e.printStackTrace();
// }
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(s);
DbUtils.closeQuietly(rs);
}
}
十、总结
- 使用数据库连接池(druid)获取链接
- 使用Apache-DBUtils下的QueryRunner进行增删改查
- (可选)使用Apache-DBUtils下的DbUtils关闭连接