在我的一个JBOSS应用服务器里,有这样三个EJB
@Stateless
@Local({A.class})
@Remote({A.class})
public ABean implements A{
@EJB
B b;
@Override
public void a(){
T entity = b.find(@Stateless id);b.delete(entity);
b.delete(entity);
}
}
@Local({B.class})
@Remote({B.class})
public BImpl implements B{
@PersistenceContext
protected EntityManager entityManager;
public T find(Long id){
return entityManager.find(T.class,id);
}
public void delete(T entity){
system.out.println(entityManager.contains(entity));
entityManager.remove(entity);
}
}
其中ABean,BImpl是无状态会话Bean,都有业务接口分别为A和B。另外T为实体Bean。使用了CMT, 只使用了一个数据源。
远程客户端通过JNDI获取A的引用后,调用A.a()方法,最后是报出了异常的,异常原因是“试图删除一个脱管(detached)实体”,而且system.out.println(entityManager.contains(entity))输出是false,即证明了所传给B.delete方法的实体是一个已经脱管的EntityBean;
后来对ABean进行了修改,对于ABean中的B接口不利用@EJB来进行容器注入,而是通过JNDI来查找本地接口引用,这个时候,执行A.a()方法是可行的,没有报错,而且system.out.println(entityManager.contains(entity))输出是true,即证明了所传给B.delete方法的实体是一个还受容器管理的受管EntityBean;
但是如果ABean中的B接口通过JNDI来查找的不是本地接口,而是远程接口的时候,执行A.a()方法还是会报错的,错误原因还是因为“试图删除一个脱管(detached)实体”,system.out.println(entityManager.contains(entity))输出还是false。
通过对上面的实验,可以得出的结论是通过JNDI查找的远程接口中方法所返回的EntityBean是脱管的,而通过JNDI查找的本地接口中方法所返回的EntityBean是受管的。这个可以理解。但是为什么通过@EJB利用容器注入的接口所返回的EntityBean是脱管的?这个真的是无法解释,我也不清楚为什么
下面是从网上找到的一些概念
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
一个持久化上下文与一个事务上下文是松耦合的;严格的讲,它隐含的与一个事务会话共存。
EntityManager
实体对象由实体管理器进行管理,通过EntityManager和持久化上下文进行交互
实体管理器有两种:
容器类:容器型的实体管理器由容器负责试题管理器之间的协作,Java EE应用服务器提供的就是管理型的实体管理器。
应用程序型:实体管理器的生命周期由应用程序控制,应用程序通过javax.persistence.EntityManagerFactoty的creaeEntityManager创建EntityManager实例
如果使用事务范围的容器管理的实体管理器,那么提交事务、回滚事务、清除持久化上下文、关闭实体管理器、以及序列化实体或按值传递一个实体(如在分布式应用中,通过远程接口传递等)都会产生脱管实体。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
其实依赖注入只工作在本地命名服务中,不能注入远程服务器的对象。在实际应用中,EJB可能会使用到其他EJB或资源,这时必须通过JNDI查找或注入注释。因些上面通过@EJB来注入接口存根的话,应该是本地的才对,但是结果却跟通过JNDI获取到的本地接口执行结果不一样,真是不清楚怎么回事了。