数据库的并发问题
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种 并发问题:
脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的 内容就是临时且无效的。
不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字 段, 值就不同了。
幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如 果 T1 再次读取同一个表, 就会多出几行。
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问 题。 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰 程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
数据库的四种隔离级别:
以user_table表为例,表的结构如下:
下面代码演示读未提交数据和读已提交数据 :
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 org.junit.Test;
import com1jdbcConnection.jdbcConnection;
public class TransactionTest {
//修改操作
public void update(Connection conn,String sql,Object ...args){//sql中占位符的个数与可变形参的长度一致
//1、编译sql语句
PreparedStatement ps=null;
try {
ps = conn.prepareStatement(sql);
//2、填充占位符
for(int i=0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
//3、执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally{
//4、关闭资源
jdbcConnection.CloseResource(null, ps);
}
}
//查询操作
@Test
public void testTransactionSelect() throws Exception{
Connection conn=jdbcConnection.connection();
conn.setAutoCommit(false);
//设置数据库的隔离级别
/*TRANSACTION_READ_UNCOMMITTED:未解决脏读问题
*TRANSACTION_READ_COMMITTED:解决了脏读问题
*/
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//当前是读已提交数据
//查看当前的隔离级别
System.out.println("隔离级别:"+conn.getTransactionIsolation());
String sql="select user,password,balance from user_table where user=?";
User user=update(conn,User.class,sql,"CC");
System.out.println(user.toString());
conn.commit();
conn.setAutoCommit(true);
jdbcConnection.CloseResource(conn, null);
}
//修改操作
@Test
public void testTransactionUpdata() throws Exception{
Connection conn=jdbcConnection.connection();
conn.setAutoCommit(false);
String sql="update user_table set balance=? where user=?";
update(conn,sql,5000,"CC");
Thread.sleep(5000);
System.out.println("修改结束!");
jdbcConnection.CloseResource(conn, null);
}
//通用查询操作,返回一条数据
public <T> T update(Connection conn,Class<T> clazz,String sql,Object ...args){
//1、实例化PreparedStatement对象
PreparedStatement ps=null;
ResultSet rs=null;
try {
ps = conn.prepareStatement(sql);
//2、填充占位符
for(int i=0;i<args.length;i++){
ps.setObject(i+1, args[i]);
}
//3、执行,获取结果集
rs = ps.executeQuery();
//4、获取元数据
ResultSetMetaData rsmd=rs.getMetaData();
//5、获取列数
int columnValue=rsmd.getColumnCount();
if(rs.next()){
T t=clazz.newInstance();
for(int i=0;i<columnValue;i++){
//6、获取列名
String columnName=rsmd.getColumnLabel(i+1);
//7、获取当前列的值
Object value=rs.getObject(i+1);
//8、给t对象指定的columnName属性,赋值为columnValue:通过反射
Field field=clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, value);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//9、关闭资源
jdbcConnection.CloseResource(null, ps,rs);
}
return null;
}
}
代码中User类如下:
public class User {
private String user;
private String password;
private int balance;
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + ", balance=" + balance + "]";
}
}
代码中jdbcConnection类的构造查看:封装数据库的连接关闭操作_qq_46053741的博客-CSDN博客