一.事务的定义
1.物理上的数据库事务
2.逻辑上事务与persistence context有关
3.应用程序上的事务指的是一个工作单元
二.物理事务
hibernate支持JDBC和JTA事务处理
TransactionFactory的两个功能:
1.让hibernate理解当前环境的事务语义,例如当时是一个JTA环境?当前物理事务是活跃的?
2.充当org.hibernate.Transaction工厂,允许应用程序去管理和检查事务状态org.hibernate.Transaction是hibernate中逻辑事务的概念,JPA中有类似的javax.persistence.EntityTransaction接口,但是只支持resource-local事务
3.物理事务JDBC
使用
java.sql.Connection.commit()
和 java.sql.Connection.rollback(),设置autocommit为false,对应的事务工厂类JdbcTransactionFactory
4.物理事务JTA
使用javax.transaction.UserTransaction从底层org.hibernate.service.jta.platform.spi.JtaPlatform,对应的事务工程类JtaTransactionFactory
5.物理事务CMT
使用JTAjavax.transaction.TransactionManager,从底层org.hibernate.service.jta.platform.spi.JtaPlatform,对应的事务工程类CMTTransactionFactory,在实际JEE CMT环境,获取javax.transaction.UserTransaction是受限的。这里的CMT简单说就是事务不由hibernate transcation api管理,而由容器管理
6.物理事务custom
自己实现接口org.hibernate.engine.transaction.spi.TransactionFactory,初始器hibernate.transaction.factory_class,通过传入实例或者类名(必须有无参构造函数)
7.物理事务遗留
transactionFactory已经移到其他目录,短暂支持一段时间
三.事务使用
hibernate没有任何内存加锁,直接使用JTA和JDBC连接,事务完全依赖数据库的隔离级别,session提供事务级别的cache。
避免数据库锁竞争,保持物理数据库事务尽量短。
四.事务模式和反模式
1.session-per-operation反模式
每个数据库操作都提交事务,开关session
2.session-per-request模式
最常见模式,从请求开始处打开session,开始事务,然后执行一系列操作,最后提交事务,关闭session
session和事务是一对一的关系
为避免在应用中传递session,SessionFactory提供getCurrentSession方法,‘current’取决于currentsessioncontext
2种sessioncontext范围
JTASESSIONCONTEXT,提供回调,当事务结束时可以关闭session,session在事务范围内第一次调用getCurrentSession时打开
ManagedSessionConext,由应用程序自己管理范围,在范围的开始调用bind方法,在结束处调用unbind,通常由一个外部组件来管理生命周期,例如AOP拦截器或者Filter
3.conversation
长会话跨多个数据库事务,要保持原子性只有是前面的事务是读,只有一个事务是写(通常是最后一个),自动版本检查可以检查并发修改
session-per-request-with-detachedobjects,在用户思考时间,所有实体对象处于detached状态,hibernate允许重新attach对象到一个新的session用于更新和持久化
session-per-conversation,hibernate允许session断开底层jdbc连接,在新请求到来时重新连接,而且是同一个session,但是session不允许设置自动flush,必须显示调用
五.对象同一性和数据库同一性
一个应用程序可以并发的访问一个持久化状态(即数据库行),但是同一个持久化实例不会在多个session中共享。
数据库同一性
foo.getId().equals( bar.getId() )
JVM同一性
foo==bar
在同一个session中,hibernate保证两种同一性是一致的,虽然不推荐,在同一个session中可以使用==比较对象
乐观锁自动版本或者时间戳检查并发访问
在session外,使用==会有问题,比如导致可以放置两个detached对象到一个set里,但是实际是代表的同一行。所以需要覆盖hashcode和equals方法,但是不要使用数据库标示符来实现,因为transient的对象在持久化后会改变数据库标示符,这破坏了set的contract
关于数据库的隔离级别和实现,可参考
关于丢失更新,网上有些解释不正确,丢失更新应该是第二个事务失败,而把第一个事务的更新也回滚了。通过排他写锁实现读取未提交隔离级别,可防止。