动态创建EntityManager

时间:2014-07-04 10:53:44

标签: java jpa ejb

我的问题实际上很简单,但我真的找不到一个好的解决方案。

我目前在我的应用程序中管理多个数据库:

  • 一个UNIQUE admin DB(带有静态名称);
  • 一个客户端数据库,其名称取决于客户端。

我正在使用JPA,我想为客户端数据库动态创建EntityManagers。但是当我以编程方式创建它时,我收到此错误: javax.persistence.TransactionRequiredException:已在资源本地的EntityManager上调用joinTransaction,该EntityManager无法注册JTA事务。

以下是代码:

@Stateful
public class ServiceImpl implements Service{

private EntityManagerFactory emf;
private EntityManager em;

@PostConstruct      // automatically called when EJB constructed and session starts
public void init() {
    emf = Persistence.createEntityManagerFactory("punit");
    em = emf.createEntityManager();
}
...

并且

@Stateful(mappedName = "CustomerService")
public class CustomerServiceImpl extends ServiceImpl implements CustomerService {

@Override
public void create(Customer cust) {
    getEm().joinTransaction();
    getEm().persist(cust);
}

更一般地说,我遇到了JPA的问题。我只想连接两个数据库,对它们进行一些CRUD操作。但我真的不知道如何管理交易(我的第一种方法是让容器管理它......)。

如果有人可以帮助我,可能会很棒!

注意:我正在使用Glassfish Java EE服务器和PGSql DB。

2 个答案:

答案 0 :(得分:1)

在jpa中,您可以在persistence.xml文件中声明多个prsistenceunits。

在注射时,您可以执行以下操作:

@PersistenceContext(unitName = "unitName0", properties={@PersistenceProperty(...)}
EntityManager emClient;

@PersistenceContext(unitName = "unitName1", properties={@PersistenceProperty(...)}
EntityManager emAdmin;

这样,您不必手动创建实体管理器,因此您可以获得容器事务管理。

未经测试:

如果您有动态数据库名称,则会注入EntityManagerFactory

@PersistenceContext(unitName =" name") EntityManagerFactory emf;

//在您想要EntityManager的位置

Map<String, String> props; //put the connection property for the EM here
EntityManager em = emf.createEntityManager(props);

基于以下事实:在J2EE环境中我们使用DataSources和ConnectionPooling的概念,实现这种动态数据源几乎是不可能的,而不需要手动创建entitymanagerfactory。

这是我的理由: 服务器管理连接池,jpa提供程序(例如eclipselink)使用jndi来确定与数据库的连接。这意味着如果要更改数据库名称,则它还必须具有连接池资源和关联的jdbc资源。这样做会否定你想要做的事情。

基本解决方案:手动创建EntityManagerFactory并手动管理事务。 在持久性xml中指定单元为非jta,以使其工作。

然后,您可以基于用户会话以编程方式提供连接数据:

这类事:

//这必须是特定于会话的。

class PersistenceSession{
  static Map<String, String> clientSessionProps;

  //When new session starts and a new client has logged in.
  static void setClientConnectionProperties(Client client){
    .....
  }

  static Map<String, String> getClientSessionProps(){
   return clientSessionProps;
  }
}

在ejb级别。

@Stateless
   public class TestEntityFacade extends AbstractFacade<TestEntity> {


    private EntityManagerFactory emf;

    @PostConstruct
    void init(){
      emf = Persistence.createEntityManagerFactory("name");
    }

    @Override
    protected EntityManager getEntityManager() {
            return emf.createEntityManager(PersistenceSession.getClientSessionProps());
    }

    public TestEntityFacade() {
        super(TestEntity.class);
    }

     void add(Entity e){

        EntityManager em = getEntityManager();
        em.getTransaction().begin();
        .....
        em.getTransaction().commit();
    }

}

答案 1 :(得分:0)

实际上非常简洁的方法是使用CDI Producers,您可以定义一个将为您的任意数量的自定义实体管理器生成的bean,请参阅示例。

@SessionScoped
public class EntityManagerProducer {

     @Produces
     @AdminDB
     @PersistenceContext(unitName = "adminDB")
     public EntityManaged adminDB;

     @Produces
     @UserDB
     @PersistenceContext(unitName = "userDB")
     public EntityManaged userDB;
}

这意味着这两个EntityManagers会在每个用户会话中创建,而不是将它们注入任何bean

@Inject
@UserDB
private EntityManager em; //if you want to use UserDB now

@UserDB@AdminDB是您自己定义的Qualifiers。这样可以使代码更容易和更易读。