JDBC事务测试
注:测试过程中出现了多处未主动释放链接的情况,这是不对的,仅仅是为了测试而这样做的。
测试目的:想测试事务的脏读
- 正确执行过程
- 先执行testTransactionSelect()
结果为:
com.mysql.cj.jdbc.ConnectionImpl@3c130745
4
User{user=‘CC’, password=‘abcd’, balance=5000}
- 再执行testTransactionUpdate()
在获取链接后如下图:
此时以获取链接更新完数据,但是因为链接还未断开,这时再次执行testTransactionSelect() 即可检测出脏读
结果为:
com.mysql.cj.jdbc.ConnectionImpl@3c130745
4
User{user=‘CC’, password=‘abcd’, balance=6600}
- 等待testTransactionUpdate() 方法执行结束后,再次查询testTransactionSelect()
结果为:
com.mysql.cj.jdbc.ConnectionImpl@3c130745
4
User{user=‘CC’, password=‘abcd’, balance=5000}
方法结束,事务也未提交,可看见数据“回滚”了。
- 正确执行过程
错误的执行过程即方式二未检测出脏读,具体就不予演示了。
testTransactionSelect()为什么获取了两次连接?
手动创建了一个链接,方法中又创建了一个链接。
隔离级别 “修改不成功” ?
可以修改,但是setTransactionIsolation()修改的是当前会话的隔离级别。
有两个原因导致我为什么判断修改不成功:
- 这个测试事务始终未提交
- 测试有Bug,也就是建了两个链接,两个链接的事务等级还不同,测试不出想要的结果。
两个JunitTest是两个事务连接还是一个事务连接?
是一个事务链接!(至少每个JunitTest获取的第一个链接是相同的)
事务的提交与连接的关闭?
testTransactionSelectUpdate()方法执行结束后,自动回滚数据?
可以理解为自动回滚吧(不清楚原理是否和自动回滚一样,但是结果是一样的)。
事务的一致性,要么提交,要么回滚,这里什么都没操作,而且autocommit是false,结果和回滚一样的,什么都没发生。
可以见以下代码分析:
//设置不自动提交直接断开链接,结果和自动回滚一样,也就是什么都没做
//设置不自动提交也不断开链接也就是什么都不操作,让方法直接结束,结果和自动回滚一样,也就是什么都没做
//其实就是事务的一致性
@Test
public void testUpdate() throws Exception {
String sql1 = "update user_table set balance = balance - 100 where user = ?";
Connection conn = JDBCUtils.getConnection();
boolean autoCommit = conn.getAutoCommit();
System.out.println(autoCommit);
conn.setAutoCommit(false);
update(conn,sql1, "AA");
//模拟网络异常
// System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2, "BB");
JDBCUtils.closeResource(conn,null);
System.out.println("转账成功!");
}
以上问题均已解决…
错误测试时的代码
//********************************************
@Test
public void testTransactionSelect() throws Exception {
Connection conn = JDBCUtils.getConnection();
//获取当前连接的隔离级别
System.out.println(conn.getTransactionIsolation());
//设置数据库的隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
//取消自动提交数据
conn.setAutoCommit(false);
String sql = "select * from user_table where user=?";
User user = getInstance(conn,User.class, sql, "CC");
System.out.println(user);
}
@Test
public void testTransactionUpdate() throws Exception {
Connection conn1 = JDBCUtils.getConnection();
//取消自动提交数据
conn1.setAutoCommit(false);
String sql = "update user_table set balance = ? where user = ?";
update(conn1,sql,6600,"CC");
Thread.sleep(15000);
System.out.println("修改结束");
}
//通用的查询操作,用于返回数据表中的一条记录(version 2.0 :考虑上事务)
public <T> T getInstance(Connection conn,Class<T> clazz,String sql,Object... args){
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();
if (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取每个列的列值:通过ResultSet
Object columnValue = rs.getObject(i + 1);
//通过ResultSetMetaDate
//获取每个列的列名:getColumnName() -- 不推荐使用
// String columnName = rsmd.getColumnName(i + 1);
//获取列的别名:getColumnLabel()
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射,将对象指定名columnName的属性值指定的值columnValue
Field field = t.getClass().getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
//通用的增删改操作 ---version 2.0(考虑上事务)
public int update(Connection conn, String sql, Object... args) {//sql中占位符的个数应与可变形参长度一致
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement的实例Connection conn = JDBCUtils.getConnection();
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 class JDBCUtils {
/**
* @return java.sql.Connection
* @Author
* @Description 获取数据库的连接 //TODO
* @Date 2020/11/6
* @Param []
*/
public static Connection getConnection() throws Exception {
//1.读取配置文件中4个基本信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String password = pros.getProperty("password");
String user = pros.getProperty("user");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
return connection;
}
}