我的问题实际上很简单,但我真的找不到一个好的解决方案。
我目前在我的应用程序中管理多个数据库:
我正在使用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。
答案 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。这样可以使代码更容易和更易读。