Java实现:
publicclass testForDB {
publicstaticvoid main(String[]args) throws UnsupportedEncodingException{
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String user = "root";
String password = "123456";
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user,password);
Statement statement = conn.createStatement();
String sql = "insert into tb_student (Id,StuName) value (22,'xxxxx')";
statement.execute(sql);
sql = "insert into tb_student (Id,StuName) value (22,'aaaaaa')";
statement.execute(sql);
} catch (Exception e) {
}
finally{
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {}
}
}
}
publicclass testForDB {
publicstaticvoid main(String[]args) throws UnsupportedEncodingException{
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String user = "root";
String password = "123456";
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user,password);
conn.setAutoCommit(false);
Statement statement = conn.createStatement();
String sql = "insert into tb_student (Id,StuName) value (24,'xxxxx')";
statement.execute(sql);
sql = "insert into tb_student (Id,StuName) value (24,'aaaaaa')";
statement.execute(sql);
conn.commit();
} catch (Exception e) {
if(conn!=null)
try {
conn.rollback();//实际上该语句可有可无,因为抛出异常时数据还没有commit
} catch (SQLException e1) {}
}
finally{
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {}
}
}
}
public class testForDB {
public static void main(String[]args) throws UnsupportedEncodingException{
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String user = "root";
String password = "123456";
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user,password);
conn.setAutoCommit(false);
Statement statement = conn.createStatement();
String sql = "insert into tb_student (Id,StuName) value (24,'xxxxx')";
statement.execute(sql);
conn.commit();
sql = "insert into tb_student (Id,StuName) value (25,'xxxxx')";
statement.execute(sql);
sql = "insert into tb_student (Id,StuName) value (25,'aaaaaa')";
statement.execute(sql);
conn.commit();
} catch (Exception e) {
if(conn!=null)
try {
conn.rollback();//实际上该语句可有可无,因为抛出异常时数据还没有commit
} catch (SQLException e1) {}
}
finally{
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {}
}
}
}
publicclass testForDB {
publicstaticvoid main(String[]args) throws UnsupportedEncodingException{
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String user = "root";
String password = "123456";
Connection conn = null;
Savepoint savepoint1 = null;
Savepoint savepoint2 = null;
Savepoint savepoint3 = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user,password);
conn.setAutoCommit(false);
Statement statement = conn.createStatement();
savepoint1 = conn.setSavepoint("point1");
String sql = "insert into tb_student (Id,StuName) value (29,'xxxxx')";
statement.execute(sql);
savepoint2 = conn.setSavepoint("point2");
sql = "insert into tb_student (Id,StuName) value (30,'xxxxx')";
statement.execute(sql);
savepoint3 = conn.setSavepoint("point3");
sql = "insert into tb_student (Id,StuName) value (31,'xxxxx')";
statement.execute(sql);
sql = "insert into tb_student (Id,StuName) value (31,'aaaaaa')";
statement.execute(sql);
conn.commit();
} catch (Exception e) {
if(conn!=null)
try {
/*
* 如果回滚至savepoint1,则数据库一条数据也没有新增,
* 回滚至savepoint2,则数据库新增一条Id等于29的数据,
* 回滚至savepoint3,则数据库新增两条数据(Id分别为29、30)
*/
conn.rollback(savepoint3);
conn.commit();
} catch (SQLException e1) {}
}
finally{
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {}
}
}
}
事务隔离
并发问题可归纳为以下几类:
A.丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖(A和B事务并发执行,A事务执行更新后,提交;B事务在A事务更新后,B事务结束前也做了对该行数据的更新操作,然后回滚,则两次更新操作都丢失了)。
B.脏读:一个事务读到另一个事务未提交的更新数据(A和B事务并发执行,B事务执行更新后,A事务查询B事务没有提交的数据,B事务回滚,则A事务得到的数据不是数据库中的真实数据。也就是脏数据,即和数据库中不一致的数据)。
C.不可重复读:一个事务读到另一个事务已提交的更新数据(A和B事务并发执行,A事务查询数据,然后B事务更新该数据,A再次查询该数据时,发现该数据变化了)。
D. 覆盖更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据(即A事务更新数据,然后B事务更新该数据,A事务查询发现自己更新的数据变了)。
E.虚读(幻读):一个事务读到另一个事务已提交的新插入的数据(A和B事务并发执行,A事务查询数据,B事务插入或者删除数据,A事务再次查询发现结果集中有以前没有的数据或者以前有的数据消失了)。
数据库系统提供了四种事务隔离级别供用户选择:
A.Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新(事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行。)。
B.Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
C.Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
D.Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
| 丢失更新 | 脏读 | 非重复读 | 覆盖更新 | 幻像读 |
未提交读 | Y | Y | Y | Y | Y |
已提交读 | N | N | Y | Y | Y |
可重复读 | N | N | N | N | Y |
串行化 | N | N | N | N | N |
隔离级别
数据库系统有四个隔离级别(大多数数据库默认级别为read commited)。对数据库使用何种隔离级别要审慎分析,因为
1. 维护一个最高的隔离级别虽然会防止数据的出错,但是却导致了并行度的损失,以及导致死锁出现的可能性增加。
2. 然而,降低隔离级别,却会引起一些难以发现的bug。
SERIALIZABLE(序列化)
添加范围锁(比如表锁,页锁等,关于range lock,我也没有很深入的研究),直到transaction A结束。以此阻止其它transaction B对此范围内的insert,update等操作。
幻读,脏读,不可重复读等问题都不会发生。
REPEATABLE READ(可重复读)
对于读出的记录,添加共享锁直到transaction A结束。其它transaction B对这个记录的试图修改会一直等待直到transaction A结束。
可能发生的问题:当执行一个范围查询时,可能会发生幻读。
READ COMMITTED(提交读)
在transaction A中读取数据时对记录添加共享锁,但读取结束立即释放。其它transaction B对这个记录的试图修改会一直等待直到A中的读取过程结束,而不需要整个transaction A的结束。所以,在transaction A的不同阶段对同一记录的读取结果可能是不同的。
可能发生的问题:不可重复读。
READ UNCOMMITTED(未提交读)
不添加共享锁。所以其它transaction B可以在transaction A对记录的读取过程中修改同一记录,可能会导致A读取的数据是一个被破坏的或者说不完整不正确的数据。
另外,在transaction A中可以读取到transaction B(未提交)中修改的数据。比如transaction B对R记录修改了,但未提交。此时,在transaction A中读取R记录,读出的是被B修改过的数据。
可能发生的问题:脏读。