jdbc 系列文章列表, 请查看目录: 《jdbc学习笔记》
当多个事务并发运行时, 如果多个事务同时操作数据库中的相同数据,那么就容易产生并发问题. 笔者通过两个事务T1, T2 来举例:
脏读: 若事务T1 执行了更新字段, 但是还未执行提交操作; 此时如果T2 就读取到了T1 还未提交的字段, 那么就称之为脏读. 因为T1 不一定会执行提交操作, 也有可能执行回滚操作.不可重复读: 若T1 中读取了一个字段, 而T2对此字段进行了更新, 当T1中再次获取此字段时, 同一字段值就发生了变化.幻读: 在id唯一的情况下, 若事务T1中, 首先从数据表中查询id为10的数据不存在, 而此时T2 插入了id为10 的数据, 而T1认为id为10的数据并不存在, 插入id为10 的数据, 便会发生异常.数据库提供四种隔离级别,
mysql 支持四种事务隔离级别, 默认级别为 REPEATABLE READ(可重复读)oracle 只支持两种事务隔离级别: READ COMMITED 和 SERIALIZABLE, 默认级别为 READ COMMITED(读已提交) 隔离级别隔离级别描述READ UNCOMMITTED读未提交允许事务读取其它事务未提交的变更, 事务隔离性最低.READ COMMITED读已提交只允许事务读取已提交的数据变更, 可以避免脏读.REPEATABLE READ可重复读确保在一个事务中, 多次从一个字段中可获取相同的值, 也就是说在这个事务期间, 其它事务禁止对此字段进行变更.SERIALIZABLE串行化确保对同一张表, 事务只能一个一个执行, 不允许有并发事务运行. 也就是说在次事务期间, 禁止任何事务对此表进行增删改操作. 可避免并发问题, 但性能低下不同的隔离级别, 可避免部分并发问题. 隔离性越高, 性能就越差.
隔离级别脏读不可重复读幻读描述READ UNCOMMITTED√√√性能最高, 隔离性最差. 事务并发时脏读, 不可重复读, 幻读均有可能产生READ COMMITEDx√√应用最广泛, 可有效避免脏读, 但可能产生不可重复读, 幻读现象REPEATABLE READxx√可避免脏读, 不可重复读息现象. 但有可能产生幻读现象SERIALIZABLExxx性能最低, 隔离线最强. 事务并发时, 可有效避免脏读, 不可重复读, 幻读的产生首先, 封装一个对用户表t_user 进行操作的工具类.
public class UserDaoUtil { // 保存User 对象 public static void save(Connection connection, UserPO userPO) { String sql = "insert t_user(id, name, password) values (?, ?, ?)"; try { // 获取PreparedStatement, 并设置需要回填主键 PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 设置参数 preparedStatement.setInt(1, userPO.getId()); preparedStatement.setString(2, userPO.getName()); preparedStatement.setString(3, userPO.getPassword()); preparedStatement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } // 通过id 查询对象 public static UserPO findById(Connection connection, Integer id) { String sql = "select * from t_user where id = ?"; UserPO userPO = null; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, id); // 执行查询, 并解析结果 ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { userPO = new UserPO(); userPO.setId(resultSet.getObject("id", Integer.class)); userPO.setName(resultSet.getObject("name", String.class)); userPO.setPassword(resultSet.getObject("password", String.class)); } } catch (SQLException e) { e.printStackTrace(); } return userPO; } // 通过id更新密码 public static void updatePassword(Connection connection, Integer id) { String sql = "update t_user set password = ? where id = ?"; try { // 获取PreparedStatement 并设置参数 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, LocalTime.now().toString()); preparedStatement.setInt(2, id); preparedStatement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } }