本文部分内容节选自Enterprise JavaBeans 3.0 by Bill Burke & Richard Monson-Haefel
4 Persistence
EJB支持声明性事务,事务传播行为可以由TransactionAttribute annotation或者XML配置文件指定。事务属性有下列可选值:NotSupported、Supports、Required、RequiresNew、Mandatory和Never(Srping多支持一个NESTED)。默认的事务属性是Required。
伴随着事务的传播,persistence context也有特定的传播规则:
- 如果一个EJB调用了具有不同事务范围的另一个EJB,那么persistence context不会被传播。
- 如果在事务范围之外调用transaction scoped entity manager,会创建一个与调用时间等长的persistence context。
- 如果在事务范围内调用transaction scoped entity manager,并且当前没有persistence context与事务关联,那么会创建一个persistence context,当事务被提交或者回滚后,persistence context也就自动结束,被EntityManager管理的所有entity都变成detached状态。
- 如果在事务范围内调用extended entity manager,那么不论是否在事务内,entity manager在其整个生命周期内维护同一个persistence context。
- Stateful session bean是唯一允许借助@PersistenceContext注入extended persistence context的EJB组件。当stateful session bean被移除时,extended persistence context也会被关闭。
- 如果使用transaction scoped persistence context的EJB调用了使用extended persistence context的stateful session bean,那么会导致异常。
- 如果使用extended persistence context的stateful session bean调用了另一个注入transaction scoped persistence context的EJB,那么extended persistence context会被传播。
- 如果使用extended persistence context的stateful session bean调用了另一个注入extended persistence context的stateful session bean,那么extended persistence context会被传播。
- Persistence context只是在注入本地接口时有效,如果被注入的是远程接口,则嵌套方和被嵌套方将不会共享persistence context。
以下是个简单的例子:
import javax.ejb.Local;
@Local
public interface StatelessLocal {
void doStatelessLocal(Movie m);
}
import javax.ejb.Remote;
@Remote
public interface StatelessRemote {
void doStatelessRemote(Movie m);
}
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import com.versus.ejb.injection.entitymanager.Movie;
@Stateless
public class StatelessImpl implements StatelessLocal, StatelessRemote {
//
@PersistenceContext(unitName = "ejb", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
public void doStatelessLocal(Movie movie) {
//
System.out.println("in StatelessImpl.doStatelessLocal()");
@SuppressWarnings("unused")
Movie m2 = entityManager.find(Movie.class, new Integer(2));
//
System.out.println(entityManager.contains(movie));
}
public void doStatelessRemote(Movie movie) {
//
System.out.println("in StatelessImpl.doStatelessRemote()");
@SuppressWarnings("unused")
Movie m2 = entityManager.find(Movie.class, new Integer(2));
//
System.out.println(entityManager.contains(movie));
}
}
import javax.ejb.Local;
@Local
public interface Stateful1Local {
void doStatefulLocal();
}
import javax.ejb.Remote;
@Remote
public interface Stateful1Remote {
void doStatefulRemote();
}
import javax.ejb.EJB;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
@Stateful
public class Stateful1Impl implements Stateful1Local, Stateful1Remote {
//
@PersistenceContext(unitName = "ejb", type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
//
@EJB
private StatelessLocal statelessLocal;
@EJB
private StatelessRemote statelessRemote;
@EJB
private Stateful2Local stateful2Local;
@EJB
private Stateful2Remote stateful2Remote;
public void doStatefulLocal() {
//
System.out.println("in Stateful1Impl.doStatefulLocal()");
Movie m1 = entityManager.find(Movie.class, new Integer(1));
System.out.println(entityManager.contains(m1));
//
statelessLocal.doStatelessLocal(m1);
statelessRemote.doStatelessRemote(m1);
//
stateful2Local.doStatefulLocal(m1);
stateful2Remote.doStatefulRemote(m1);
}
public void doStatefulRemote() {
//
System.out.println("in Stateful1Impl.doStatefulRemote()");
Movie m1 = entityManager.find(Movie.class, new Integer(1));
System.out.println(entityManager.contains(m1));
//
statelessLocal.doStatelessLocal(m1);
statelessRemote.doStatelessRemote(m1);
//
stateful2Local.doStatefulLocal(m1);
stateful2Remote.doStatefulRemote(m1);
}
}
import javax.ejb.Local;
@Local
public interface Stateful2Local {
void doStatefulLocal(Movie m);
}
import javax.ejb.Remote;
@Remote
public interface Stateful2Remote {
void doStatefulRemote(Movie m);
}
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
@Stateful
public class Stateful2Impl implements Stateful2Local, Stateful2Remote {
//
@PersistenceContext(unitName = "ejb", type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
public void doStatefulLocal(Movie movie) {
//
System.out.println("in Stateful2Impl.doStatefulLocal()");
@SuppressWarnings("unused")
Movie m3 = entityManager.find(Movie.class, new Integer(3));
//
System.out.println(entityManager.contains(movie));
}
public void doStatefulRemote(Movie movie) {
//
System.out.println("in Stateful2Impl.doStatefulRemote()");
@SuppressWarnings("unused")
Movie m3 = entityManager.find(Movie.class, new Integer(3));
//
System.out.println(entityManager.contains(movie));
}
}
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
public class PersistenceTest {
public static void main(String args[]) throws Exception {
//
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
InitialContext ctx = new InitialContext(properties);
//
if(true) {
Object ref1 = ctx.lookup("Stateful1ImplLocal");
Stateful1Local simpleStatefulLocal = (Stateful1Local)PortableRemoteObject.narrow(ref1, Stateful1Local.class);
simpleStatefulLocal.doStatefulLocal();
Object ref2 = ctx.lookup("Stateful1ImplRemote");
Stateful1Remote simpleStatefulRemote = (Stateful1Remote)PortableRemoteObject.narrow(ref2, Stateful1Remote.class);
simpleStatefulRemote.doStatefulRemote();
}
}
}
以上代码的输出是:
in Stateful1Impl.doStatefulLocal()
true
in StatelessImpl.doStatelessLocal()
true
in StatelessImpl.doStatelessRemote()
false
in Stateful2Impl.doStatefulLocal()
true
in Stateful2Impl.doStatefulRemote()
false
in Stateful1Impl.doStatefulRemote()
true
in StatelessImpl.doStatelessLocal()
true
in StatelessImpl.doStatelessRemote()
false
in Stateful2Impl.doStatefulLocal()
true
in Stateful2Impl.doStatefulRemote()
false