JPA Eclipselink - 返回相同会话的多个 EntityManagerFactory 实例
JPA Eclipselink - Multiple EntityManagerFactory instances returning same session
我了解 EntityManagerFactory(EMF1) 的一个实例有它自己的 EntityManager 和会话。如果我使用与 EMF1 相同的凭据创建 EntityManagerFactory(EMF2) 的另一个实例,那么它应该有自己的连接池和会话。但事实并非如此,由于这个原因,如果一切都相同,会话定制器也会被调用一次
package test.jpa.factory;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.SessionCustomizer;
import org.eclipse.persistence.sessions.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestEntityManagerFactory {
private static Logger logger = LoggerFactory.getLogger(TestEntityManagerFactory.class);
public static final String JDBC_URL = "javax.persistence.jdbc.url";
public static final String JDBC_USER = "javax.persistence.jdbc.user";
public static final String JDBC_PASSWD = "javax.persistence.jdbc.password";
public static final String WAREHOUSE_PERSISTENCE_UNIT = "etl-dw";
public static void main(String[] args) throws Exception {
String host = "xxxxcccc";
String port = "1521";
String user = "skipvpd";
String pwd = "abcd";
String service = "test_svc1";
System.out.println("Same user multiple EMF");
printSession(host, port, user, pwd, service);
printSession(host, port, user, pwd, service);
System.out.println("Different user multiple EMF");
user = "1032";
pwd = "abcd";
printSession(host, port, user, pwd, service);
user = "1033";
printSession(host, port, user, pwd, service);
}
private static void printSession(String host, String port, String user, String pwd, String serviceName)
throws Exception {
EntityManagerFactory emf = getEntityManagerFactory(host, port, user, pwd, serviceName);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = (Session) em.unwrap(org.eclipse.persistence.sessions.Session.class);
Connection connection = (Connection) em.unwrap(java.sql.Connection.class);
System.out.println("con_hashcode=" + connection.hashCode() + ",session_hashcode=" + session.hashCode()
+ ",em_hashcode=" + em.hashCode() + ",emf_hashcode=" + emf.hashCode());
em.getTransaction().commit();
em.close();
}
private static EntityManagerFactory getEntityManagerFactory(String host, String port, String user, String pwd,
String serviceName) {
Map<String, String> jpaConfig = new HashMap<String, String>();
try {
String jdbcURL = "jdbc:oracle:thin:@" + host + ":" + port + "/" + serviceName;
jpaConfig.put("javax.persistence.jdbc.url", jdbcURL);
jpaConfig.put(JDBC_USER, user);
jpaConfig.put(JDBC_PASSWD, pwd);
jpaConfig.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
TestEntityManagerFactory.ETLSessionCustomizer.class.getName());
} catch (Throwable t) {
logger.error("ERROR=\"Error occured in getJPAConfiguration\" EXCEPTION={}", t);
}
return Persistence.createEntityManagerFactory(WAREHOUSE_PERSISTENCE_UNIT, jpaConfig);
}
public static class ETLSessionCustomizer implements SessionCustomizer {
public void customize(Session session) {
System.out.println("customize is called for session_hash_code=" + session.hashCode());
}
}
}
同一用户多个EMF
为 session_hash_code=1193471756
调用自定义
con_hashcode=675100200,session_hashcode=1193471756,em_hashcode=627727856,emf_hashcode=179294202
con_hashcode=675100200,session_hashcode=1193471756,em_hashcode=166919726,emf_hashcode=1305777754
不同用户多个EMF
为 session_hash_code=1240796303 调用自定义
con_hashcode=738369543,session_hashcode=1240796303,em_hashcode=1017841629,emf_hashcode=1760715967
为 session_hash_code=1161255903
调用自定义
con_hashcode=92699135,session_hashcode=1161255903,em_hashcode=1535875885,emf_hashcode=2054926467
调试 JPA 代码后,我通过为每个实体管理器工厂提供唯一的会话名称解决了我的问题。
问题:
如果未提供 PersistenceUnitProperties.SESSION_NAME/eclipselink.session-name 则尝试创建 EntityManagerSetupImpl.getOrBuildSessionName 方法使用两个工厂相同的连接属性的会话名称。
第一次 EntityManagerSetupImpl 对象是使用会话名称作为键在 EntityManagerFactoryProvider 中创建和缓存的。
对于 EntityManagerFactory 的第二个实例(具有相同的会话名称),它发现该实例已在缓存中,因此不会创建新实例而是共享为第一个 EntityManagerFactory 创建的现有实例。
在我的示例中,如果您添加以下代码,您将看到不同的会话和哈希码,会话定制器被调用两次。
jpaConfig.put(PersistenceUnitProperties.SESSION_NAME, String.valueOf(System.nanoTime()));
这是更改后的输出
Same user multiple EMF
customize is called for session_hash_code=1173577197
con_hashcode=1091009906,session_hashcode=1173577197,em_hashcode=1569793443,emf_hashcode=1681793106
customize is called for session_hash_code=649715211
con_hashcode=1908828843,session_hashcode=649715211,em_hashcode=327304249,emf_hashcode=1296892976
我了解 EntityManagerFactory(EMF1) 的一个实例有它自己的 EntityManager 和会话。如果我使用与 EMF1 相同的凭据创建 EntityManagerFactory(EMF2) 的另一个实例,那么它应该有自己的连接池和会话。但事实并非如此,由于这个原因,如果一切都相同,会话定制器也会被调用一次
package test.jpa.factory;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.SessionCustomizer;
import org.eclipse.persistence.sessions.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestEntityManagerFactory {
private static Logger logger = LoggerFactory.getLogger(TestEntityManagerFactory.class);
public static final String JDBC_URL = "javax.persistence.jdbc.url";
public static final String JDBC_USER = "javax.persistence.jdbc.user";
public static final String JDBC_PASSWD = "javax.persistence.jdbc.password";
public static final String WAREHOUSE_PERSISTENCE_UNIT = "etl-dw";
public static void main(String[] args) throws Exception {
String host = "xxxxcccc";
String port = "1521";
String user = "skipvpd";
String pwd = "abcd";
String service = "test_svc1";
System.out.println("Same user multiple EMF");
printSession(host, port, user, pwd, service);
printSession(host, port, user, pwd, service);
System.out.println("Different user multiple EMF");
user = "1032";
pwd = "abcd";
printSession(host, port, user, pwd, service);
user = "1033";
printSession(host, port, user, pwd, service);
}
private static void printSession(String host, String port, String user, String pwd, String serviceName)
throws Exception {
EntityManagerFactory emf = getEntityManagerFactory(host, port, user, pwd, serviceName);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = (Session) em.unwrap(org.eclipse.persistence.sessions.Session.class);
Connection connection = (Connection) em.unwrap(java.sql.Connection.class);
System.out.println("con_hashcode=" + connection.hashCode() + ",session_hashcode=" + session.hashCode()
+ ",em_hashcode=" + em.hashCode() + ",emf_hashcode=" + emf.hashCode());
em.getTransaction().commit();
em.close();
}
private static EntityManagerFactory getEntityManagerFactory(String host, String port, String user, String pwd,
String serviceName) {
Map<String, String> jpaConfig = new HashMap<String, String>();
try {
String jdbcURL = "jdbc:oracle:thin:@" + host + ":" + port + "/" + serviceName;
jpaConfig.put("javax.persistence.jdbc.url", jdbcURL);
jpaConfig.put(JDBC_USER, user);
jpaConfig.put(JDBC_PASSWD, pwd);
jpaConfig.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
TestEntityManagerFactory.ETLSessionCustomizer.class.getName());
} catch (Throwable t) {
logger.error("ERROR=\"Error occured in getJPAConfiguration\" EXCEPTION={}", t);
}
return Persistence.createEntityManagerFactory(WAREHOUSE_PERSISTENCE_UNIT, jpaConfig);
}
public static class ETLSessionCustomizer implements SessionCustomizer {
public void customize(Session session) {
System.out.println("customize is called for session_hash_code=" + session.hashCode());
}
}
}
同一用户多个EMF
为 session_hash_code=1193471756
调用自定义con_hashcode=675100200,session_hashcode=1193471756,em_hashcode=627727856,emf_hashcode=179294202
con_hashcode=675100200,session_hashcode=1193471756,em_hashcode=166919726,emf_hashcode=1305777754
不同用户多个EMF
为 session_hash_code=1240796303 调用自定义 con_hashcode=738369543,session_hashcode=1240796303,em_hashcode=1017841629,emf_hashcode=1760715967
为 session_hash_code=1161255903
调用自定义con_hashcode=92699135,session_hashcode=1161255903,em_hashcode=1535875885,emf_hashcode=2054926467
调试 JPA 代码后,我通过为每个实体管理器工厂提供唯一的会话名称解决了我的问题。
问题:
如果未提供 PersistenceUnitProperties.SESSION_NAME/eclipselink.session-name 则尝试创建 EntityManagerSetupImpl.getOrBuildSessionName 方法使用两个工厂相同的连接属性的会话名称。
第一次 EntityManagerSetupImpl 对象是使用会话名称作为键在 EntityManagerFactoryProvider 中创建和缓存的。 对于 EntityManagerFactory 的第二个实例(具有相同的会话名称),它发现该实例已在缓存中,因此不会创建新实例而是共享为第一个 EntityManagerFactory 创建的现有实例。
在我的示例中,如果您添加以下代码,您将看到不同的会话和哈希码,会话定制器被调用两次。
jpaConfig.put(PersistenceUnitProperties.SESSION_NAME, String.valueOf(System.nanoTime()));
这是更改后的输出
Same user multiple EMF
customize is called for session_hash_code=1173577197
con_hashcode=1091009906,session_hashcode=1173577197,em_hashcode=1569793443,emf_hashcode=1681793106
customize is called for session_hash_code=649715211
con_hashcode=1908828843,session_hashcode=649715211,em_hashcode=327304249,emf_hashcode=1296892976