声明
我自己写博客更多的是为了让自己尝试总结和方便之后复习,有错误的地方并非有意误导,而是实力太弱。如果读者发现有错误了,可以进行评论,我好进行思考改正。非常感谢。
本篇博客继续总结JDBC剩下的内容:数据库的事务 | DAO及其子类 | 数据库连接池 | DBUtils实现CRUD操作 感兴趣的可以去看尚硅谷的视频
1、数据库的事务
事务:指一组逻辑操作单元(一个或多个DML操作),使数据从一种状态转变到另一种状态。 当在一个事务中执行多个操作时,要么所有的操作都被提交(commit),即这些修改就永久的保存下来;要么数据库管理系统将放弃所有的所有操作,整个事务回滚(rollback)到最初的状态
代码体现
- 取消数据的自动提交:
conn.setAutoCommit(false);
- 手动设置提交的位置(即事务的范围):
conn.commit();
- 回滚数据:
conn.rollback();
@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");
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2, "BB");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn, null);
}
}
问题:既然一个事务包含了多个DML操作,那为了内存的考虑,最合理的就是建立一个connection就可以了。因此在增删改查的方法体中不需要新建连接,而应该在调用的时候connection作为参数传入。
//通用的增删改操作(考虑上事务)
public int update(Connection conn,String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = 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.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4.资源的关闭
JDBCUtils.closeResource(null, ps);
}
return 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
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
DAO及其子类
我在学习这个的过程中觉得很有意思,规范编写代码真的很巧妙,但是在这里就不赘述了,因为课程的代码也是CRUD功能进行封装的,实际使用是不需要自己写的,可以用其他的框架。下一节就会介绍。
数据库连接池(重点)
连接池的好处其实很直观。就是先事先准备一定数量的连接放在连接池中,要用的就去取,不用就放回。这样就不需要频繁的去创建和销毁了。
实现的方式:
DBUtils实现CRUD操作
导入jar包
使用现成的jar中的QueryRunner测试增、删、改的操作
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
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 (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
使用现成的jar中的QueryRunner测试查询的操作
//测试查询
/*
* BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录。
*/
@Test
public void testQuery1(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
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, 23);
System.out.println(customer);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, null);
}
}
其实有多种类型的查询,一般的水平是不可能完全的自己编写,其实学会看和改就应该可以了。下面提供其他查询结果所用的方法,希望看到的时候能够知道是实现了什么功能,或者说知道用哪一种方法来实现,然后再去看手册。
/*
* BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合。
*/
/*
* MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录。
* 将字段及相应字段的值作为map中的key和value
*/
/*
* MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
* 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
*/
/*
* ScalarHandler:用于查询特殊值
*/
另外,使用dbutils.jar包中的DbUtils工具类实现连接等资源的关闭:
public static void closeResource1(Connection conn,Statement ps,ResultSet rs){
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
总结
JDBC的内容总结就是这样了,写完之后觉得写的很失败。最初的设想就是些一小部分就行了的,但是写着写着就不禁按照了课程的笔记走。最后挣扎一下,在这里补充:就JDBC的内容而言,实用点的话就只需要看具体数据库连接池是如何实现CRUD操作的就行了。其他博客总结的更加有针对性。Druid的操作。