事务可以用以下方式管理:
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager();
Transaction transaction = entityManager.getTransaction()
try
{
transaction.begin();
someBusinessCode();
transaction.commit();
}
catch(Exception ex)
{
transaction.rollback();
throw ex;
}
优点:
缺点:
Spring支持两类事务管理
强烈建议使用声明式事务。如果想知道其原因,请阅读下面的内容,否则,可以直接跳转到声明式事务管理实现的部分。
现在,让我们细致的分析每一种事务管理方法。
Spring Framework提供了两种编程式事务管理方法。 a. 使用TransactionTemplate (Spring推荐这种实现): Context Xml file:
<!-- Initialization for data source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Definition for ServiceImpl bean -->
<bean id="serviceImpl" class="com.service.ServiceImpl">
<constructor-arg ref="transactionManager"/>
</bean>
Service类:
public class ServiceImpl implements Service
{
private final TransactionTemplate transactionTemplate;
// 使用构造器注入来使用PlatfromTransactionManager
public ServiceImpl(PlatformTransactionManager transactionManager)
{
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod()
{
return transactionTemplate.execute(new TransactionCallback()
{
//这段代码在事务上下文中执行
public Object doInTransaction(TransactionStatus status)
{
updateOperation1();
return resultOfUpdateOperation2();
}
});
}}
如果没有返回值,就使用TransactionCallbackWithoutResult
匿名类。
transactionTemplate.execute(new TransactionCallbackWithoutResult()
{
protected void doInTransactionWithoutResult(TransactionStatus status)
{
updateOperation1();
updateOperation2();
}
});
TransactionTemplate
类的实例是线程安全的,这些实例不包含任何会话状态。TransactionTemplate
实例确实会维持配置信息状态,所以即使多个类共享同一个TransactionTemplate
实例,如果一个类需要使用另一种配置的TransactionTemplate
(比如不同的隔离级别),那么就需要配置两个不同的实例。b. 直接使用PlatformTransactionManager
实现
<!-- Initialization for data source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- Initialization for TransactionManager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
public class ServiceImpl implements Service
{
private PlatformTransactionManager transactionManager;
public void setTransactionManager( PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try
{
// execute your business logic here
}
catch (Exception ex)
{
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
}
在进入声明式事务管理之前,让我们看看如何选择事务管理方式:
第一步:在spring应用程序上下文xml文件中定义事务管理器。
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
第二步:通过在spring应用程序上下文XML文件中添加以下条目,打开对事务注释的支持。
<tx:annotation-driven transaction-manager="txManager"/>
或是在配置类中添加@EnableTransactionManagement
@Configuration
@EnableTransactionManagement
public class AppConfig
{
...
}
Spring建议只使用@Transactional来注解具体类(以及具体类的方法),而不是接口。
原因是如果在接口上注解,并且使用基于类的代理(proxy-target-class="true")或是aop(mode="aspectj"),那么事务注解将无法被识别。
第三步:将注解添加在类(或是类的方法)或是接口(或是接口的方法上)
<tx:annotation-driven proxy-target-class="true">
默认配置为proxy-target-class="false"
@Transactional
注解可以放在接口,接口方法,类或是类方法上现在让我们了解一下@Transactional
的属性:
@Transactional (isolation=Isolation.READ_COMMITTED)
Isolation.DEFAULT
READ_COMMITTED 防止脏读;会发生不可重复的读取和幻读。
READ_UNCOMMITTED 会出现脏读,不可重复读和幻读。即可以看到事务尚未提交的数据
REPEATABLE_READ 可重复读。会出现幻读
序列化 防止脏读,幻读和不可重复读
@Transactional(timeout=60)
默认为底层事务系统的默认超时。
当事务超过该时间没有响应时,则会对底层系统发出回滚请求
@Transactional(propagation=Propagation.REQUIRED)
默认的传播级别为Required
。其它的选项如REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER, 和NESTED
REQUIRED 表示如果当前没有活跃的事务上下文,目标方法将无法运行。如果在调用此方法之前已经启动了事务管理,那么它将在相同的事务中继续,或者在调用此方法时将立即开始新的事务。
REQUIRES_NEW 表示每次调用目标方法时都必须启动新的事务。如果已有事务,它将暂停。
MANDATORY 表示目标方法需要运行中的事务。如果没有事务,它将抛出异常。
SUPPORTS 无论是否有事务上下文,目标方法可以执行。如果当前有事务上下文,它将在同一个上下文中运行。如果没有,它仍将执行。这个选项适合获取数据的方法。
NOT_SUPPORTED 目标方法无需传播事务上下文。
NEVER 如果在事务上下文中执行目标方法,则抛出异常
@Transactional (rollbackFor=Exception.class)
rollbackFor=RunTimeException.class
RuntimeException
,事务就会回滚。@Transactional (noRollbackFor=IllegalStateException.class)
如果该异常出现时,则不进行回滚
最后,也是最重要的一个问题,@Transactional
注解究竟应该放在哪一层?Service层还是Dao层?
LazyInitializationException