事务:
指操作数据库的一个程序执行单无,这些操作要么全部成功,要么全部失败以保证数据的完成性和统一性.
多事务并发引起的问题
a : 第一类丢失更新
撤销一个事务时把其它事务更新的数据也覆盖了。
for example : 事务A 和B 同时访问数 据Data 如果事务B 更新了数据,但事务A执行了回滚操作那么数据就回到当初
如: 银行帐户 Account_A 中有200元 甲执行取100元的操作 乙执行转入100元 如果甲成功取出100 但乙方操作失败那么帐户中还有200元 这样银行就丢 了100块
b: 脏读
指: 一个事务读取了另一个事务未提交的数据.
例如:
甲取款100元未提交,乙进行转帐查到帐户内剩有100元,这是甲放弃操作回滚,乙正常操作提交,帐户内最终为0元,乙读取了甲的脏数据,客户损失100元
c: 不可重复读
指: 一个事务在两次读同一行数据时得到不一样的结果
实际上是指读取了以更新数据
甲乙同时开始都查到帐户内为200元,甲先开始取款100元提交,这时乙在准备最后更新时候又进行了一次查询,发现结果是100元,这时乙就会很困惑,不知道 该将帐户改为100还是0。
d: 第二类丢失更新
指不可重复读的特例:
如上:如乙没有进行第二次查询那么 取出100后还有100 这时银行就丢了100
事务的隔离级别:
1 允许脏读取但不允许丢失更新的系统,要在读取未提交(read uncommitted)的隔离性中操作。如果一个为提交事务已经写到一个行,
另一个事务就不可能再写到这个行。但任何事务都可以读取任何行。这个隔离性级别可以在数据库管理系统中通过专门的写锁来实现。
2 允许不可重复读取但不允许脏读取系统,要实现读取提交(read committed)的事务隔离性。这可以用共享的读锁和专门的写锁来实现。
读取事务不会阻塞其他事务访问行。但是为提交的鞋事务隔赛所有其他的事务访问改行。
3 在可重复读(repeatable read)隔离性模式中操作的系统既不允许不可重复读取,也不允许脏读取。幻读可能发生。
4 可序列化 (serializable)提供最严格的事务隔离性。这个隔离性级别模拟连续的事务执行,好像事务是连续的一个接一个的执行,而不是
并发的执行。
1: Serilalizable 串行化 [隔离级别最高]
2: reapetable read 可重复读
3: read commmited 读取已提交数据 [通常采用]
4: read uncommited 读取未提交数据 [隔离级别最差]
那么对于多数的数据库系统可采用 第三种 read Commited 读取以提交数据.这样可以避免脏读,时性能上面也有一定优势.
每个数据库连接都有一个全局变量来表示当前的隔离级别 @@tx_isolation JDBC采用的是数据库默认的隔离级别。
在Hibernate.cfg.xml中我们可以显示的设置隔离级别
Read uncommited 1
read commmited 2
reapetable read 4
Serilazable 8
在sesssionFacotry中加入属性 hibernate.connection.isolation 即可发控制:
当数据库系统采用Red Committed隔离级别时,会导致不可重复读和第二类丢失更新的并发问题,在可能出现这种问题的场合。可以在应用程序中采用悲观锁或乐观锁来避免这类问题。
乐观锁:
乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源,因此不作数据库层次上的锁定。为了维护正确的数据,乐观锁使用应用程序上的版本控制(由程序逻辑来实现的)来避免可能出现的并发问题。
唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、 或者时间戳来检测更新冲突(并且防止更新丢失)
悲观锁:
悲观锁假定当前事务操纵数据资源时,肯定还会有其他事务同时访问该数据资源,为了免当前事务的操作受到干扰,先锁定资源。尽管悲观锁能够防止丢失更新和不可重复读这类并发问题,但是它影响并发性能,因此应该很谨慎地使用悲观锁
Hibernate配置选项:
hibernate.connection.isolation = 4
1-读取为提交隔离性
2-读取提交隔离性
3-可重复读取隔离性
4-可序列化隔离性
实现乐观锁
1 在hibernate中启用版本控制:
hibernate提供自动的版本控制。每个实体实例都有一个版本,它可以是一个数字或者是一个时间戳。当对象修改时,hibernate就增加
他的版本号,自动比较版本,如果侦测到冲突就抛出异常。因此,你给所有持久化的实体类都添加这个版本属性,来启动乐观锁:
version:
- public class Item implements Serializable {
- private Integer itemId;
- private String itemName;
- /** 版本控制 */
- private int version;
- <class name="Item" table="ITEM">
- <id name="itemId" column="ITEM_ID" type="integer">
- <generator class="native"/>
- </id>
- <!-- XML格式的version属性映射必须立即放在标示符属性映射之后 -->
- <version name="version" access="field" column="OBJ_VERSION" />
- <property name="itemName" type="string" column="ITEM_NAME"/>
- </class>
- /**
- * 报错:
- * org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
- * @param itemName
- */
- private static void update1(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- // 一样会报错
- // item1.setItemName(itemName);
- item1.setItemName(itemName+"1");
- tr.commit();
- session.close();
- //org.hibernate.StaleObjectStateException:
- //Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
- tr1.commit();
- session1.close();
- sessionFactory.close();
- }
- /**
- * 提交成功
- * @param itemName
- */
- private static void update2(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- tr.commit();
- session.close();
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- item1.setItemName(itemName+"1");
- tr1.commit();
- session1.close();
- sessionFactory.close();
- }
- public class Item implements Serializable {
- private Integer itemId;
- private String itemName;
- /** 版本控制 */
- private Date lastUpdated;
- <class name="Item" table="ITEM">
- <id name="itemId" column="ITEM_ID" type="integer">
- <generator class="native"/>
- </id>
- <timestamp name="lastUpdated" access="field" column="LAST_UPDATED"></timestamp>
- <property name="itemName" type="string" column="ITEM_NAME"/>
- </class>
- /**
- * 报错:
- * org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
- * @param itemName
- */
- private static void update1(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- // 一样会报错
- // item1.setItemName(itemName);
- item1.setItemName(itemName+"1");
- tr.commit();
- session.close();
- //org.hibernate.StaleObjectStateException:
- //Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
- tr1.commit();
- session1.close();
- sessionFactory.close();
- }
- /**
- * 提交成功
- * @param itemName
- */
- private static void update2(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- tr.commit();
- session.close();
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- item1.setItemName(itemName+"1");
- tr1.commit();
- session1.close();
- sessionFactory.close();
- }
org.hibernate.persister.entity.AbstractEntityPersister.check:
- protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException {
- try {
- expectation.verifyOutcome( rows, statement, -1 );
- }
- catch( StaleStateException e ) {
- if ( !isNullableTable( tableNumber ) ) {
- if ( getFactory().getStatistics().isStatisticsEnabled() ) {
- getFactory().getStatisticsImplementor()
- .optimisticFailure( getEntityName() );
- }
- throw new StaleObjectStateException( getEntityName(), id );
- }
- return false;
- }
- catch( TooManyRowsAffectedException e ) {
- throw new HibernateException(
- "Duplicate identifier in table for: " +
- MessageHelper.infoString( this, id, getFactory() )
- );
- }
- catch ( Throwable t ) {
- return false;
- }
- return true;
- }
- /**
- * Perform verification of the outcome of the RDBMS operation based on
- * the type of expectation defined.
- *
- * @param rowCount The RDBMS reported "number of rows affected".
- * @param statement The statement representing the operation
- * @param batchPosition The position in the batch (if batching)
- * @throws SQLException Exception from the JDBC driver
- * @throws HibernateException Problem processing the outcome.
- */
- public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) throws SQLException, HibernateException;
- /**
- * 我用了两个factory,也是可以提交成功的
- * 你想想,这证明了什么
- * @param itemName
- */
- private static void update3(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- tr.commit();
- session.close();
- sessionFactory.close();
- Configuration configuration1 = new Configuration().configure();
- SessionFactory sessionFactory1 = configuration1.buildSessionFactory();
- Session session1 = sessionFactory1.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- item1.setItemName(itemName+"11");
- tr1.commit();
- session1.close();
- sessionFactory1.close();
- }
- <!-- org.hibernate.MappingException: optimistic-lock=all|dirty requires dynamic-update="true" -->
- <!-- dirty的区别在于,只有当某个属性共同修改时,才报错;all是只要有修改就报错 -->
- <class name="Item" table="ITEM" optimistic-lock="all" dynamic-update="true">
- <id name="itemId" column="ITEM_ID" type="integer">
- <generator class="native"/>
- </id>
- <property name="itemName" type="string" column="ITEM_NAME"/>
- </class>
1、显示的悲观锁
当我们关注应用程序的课伸缩性时。例如,对标量查询可能需要可重复读取。
不是把所有的数据库事务转换为一个更高的、不可伸缩的隔离性级别,而是在必要时,在Hibernate Session中使用lock()方法获得
更高的隔离性:
- /**
- * session.lock(item, LockMode.UPGRADE);
- * @param itemName
- */
- private static void update1(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- //指定锁级别
- session.lock(item, LockMode.UPGRADE);
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 1);
- item1.setItemName(itemName+"1");
- //停在这步,因为碰到for update的锁了,他要等到tr提交了之后才会执行,但是,我们这个demo当然无法执行到这步
- tr1.commit();
- session1.close();
- tr.commit();
- session.close();
- sessionFactory.close();
- }
- /**
- * 只锁定指定列的数据,不会锁定表
- * 但是,以前这种方式是悲观锁,会锁定整个表,如果指定锁定某一行,如下:
- * Item item = (Item) session.get(Item.class, 1, LockMode.UPGRADE);
- * @param itemName
- */
- private static void update2(String itemName) {
- Configuration configuration = new Configuration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item = (Item) session.get(Item.class, 1);
- item.setItemName(itemName);
- //指定锁级别
- session.lock(item, LockMode.UPGRADE);
- Session session1 = sessionFactory.openSession();
- Transaction tr1 = session1.beginTransaction();
- Item item1 = (Item) session1.get(Item.class, 2);
- item1.setItemName(itemName+"1");
- //执行通过
- tr1.commit();
- session1.close();
- tr.commit();
- session.close();
- sessionFactory.close();
- }
LockMode.NONE--别到数据库中去,除非对象不处于任何高速缓存中。
LockMode.READ--绕过所有高速缓存,并执行版本检查,来验证内存中的对象是否与当前数据中村中的版本相同。
LockMode.UPGRADE--绕过所有高速缓存,做一个版本检查,如果支持的话,就获得数据库级的悲观升级锁。相当于Java Persistence中的LockModeType.READ。如果数据库方言不支持
select...for update选项,这个模式就透明的退回到LockMode.READ。
LockMode.UPGRADE_NOWAIT--与LockMode.UPGRADE相同,但如果支持的话,就使用select...for update nowait。他禁用了等待并发所释放,因为如果无法获得锁,就立即抛出锁异常。
如果数据库SQL方言不支持nowait选择,这个模式就透明的退回到LockMode.UPGRADE。
LockMode.FORCE--在数据库中强制增加对象的版本,来表明他已经被当前事务修改。相当于Java Persistence中的LockModeType.WRITE。
LockMode.WRITE--当hibernate已经在当前事务中写到一个行时,就自动获得他。(内部模式)
默认情况下load()和get()使用NONE。