一 为什么需要传播属性
service调用图
如上图所示 , service2 可能被单独调用,也有可能被service1调用 ,也有可能在其它service中调用, 因此不能使用 UserTransaction utx方式将
service2放入service1中 , 此刻就需要用到事务传播了,假设用service1用了required,会在service1中会创建一个事务,并将该事务传到service2中,
因此service1,service2都在同一个事务中了.
二 传播属性特性以及管理
1 Not Supportd - 不支持,如果当前有事务上下文,将挂起当前的事务
2 Support - 支持 , 如果有事务,将使用事务,如果没有事务,将不使用事务.
3 Required - 需要 , 如果当前有事务上下文,将使用当前的上下文的事务,
如果没有,则创建新的事务
4 Required New - 需要新的事务,如果当前有事务上下文,将挂起当前的事务,
并创建新的事务去执行任务,执行完成之后,在恢复到原来的事务.
5 Mandatory - 当前必须要有事务上下文,如果当前没有事务,将抛出异常
6 Nerver - 当前必须不能有事务上下文,如果有事务,将抛出异常.
三 ejb中使用传播属性示例
1 项目结构
Student.java : 学生实体类
StudentManager.java 学生管理接口类
StudentManagerImpl.java 学生管理实现类
StudentManagerClient.java Junit测试类,
测试1 : testAddStudent1 没有事务,增加学生失败效果
测试2 : testAddStudent2 有事务,增加用户成功
persistence.xml : 配置JTA的数据源
jndi.properties : 配置JNDI
client : 为JtaTest中testWithoutJta 和 testJta 提供客户端环境
jar包有 : JBOSS_HOME/client/*.jar
2 代码
Student.java
package leaning;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Student {
@Id
@GeneratedValue
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
StudentManager.java
package leaning;
public interface StudentManager {
public void addStudent(String name);
}
StudentManagerImpl.java
package leaning;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* 事务传播属性为 Supports
* **/
@Stateless(name="studentManager")
@Remote
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class StudentManagerImpl implements StudentManager {
@PersistenceContext(unitName="propagation_unit")
private EntityManager em;
// persist 需要事务支持
public void addStudent(String name) {
Student s = new Student();
s.setName(name);
em.persist(s);
}
}
StudentManagerClient.java
package test;
import static org.junit.Assert.*;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import leaning.StudentManager;
import org.junit.Test;
public class StudentManagerClient {
/**
* 没有事务,增加用户失败
* */
@Test
public void testAddStudent1() {
try{
InitialContext context = new InitialContext();
StudentManager studentManager = (StudentManager)context.lookup("studentManager/remote");
// 服务器端使用了事务传播特性为SUPPORTS即支持事务,如果没有事务,将不使用事务
// 现在客户端没有事务,但服务端执行更新又必须要有事务支持,所以本次操作将抛出异常,无法执行
studentManager.addStudent("张三");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 有事务,增加用户成功
* */
@Test
public void testAddStudent2() {
try{
InitialContext context = new InitialContext();
StudentManager studentManager = (StudentManager)context.lookup("studentManager/remote");
//JTA事物管理接口
UserTransaction utx = (UserTransaction)context.lookup("UserTransaction");
try{
utx.begin(); //开启事物
studentManager.addStudent("张三");
utx.commit(); //提交事物
}catch(Exception e){
utx.rollback();
e.printStackTrace();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost
java.naming.factory.url.pkgs=org.jboss.naming\:org.jnp.interfaces
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<!-- 采集JTA事物 -->
<persistence-unit name="propagation_unit" transaction-type="JTA">
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>