此问题具体关于以编程方式创建由Hibernate 5支持的JPA EntityManagerFactory
,意味着没有配置xml文件和< strong>不使用Spring 。此外,这个问题具体关于使用Hibernate Interceptor创建EntityManagerFactory
。
我知道如何按照我想要的方式创建一个Hibernate SessionFactory
,但我不想要一个Hibernate SessionFactory
,我想要一个由Hibernate {{1}支持的JPA EntityManagerFactory
}。给定SessionFactory
有一种获取基础EntityManagerFactory
的方法,但如果您拥有的是SessionFactory
并且您想要的只是一个SessionFactory
包装,它会出现你运气不好。
使用Hibernate版本4.2.2 EntityManagerFactory
已被弃用,但似乎没有其他方式以编程方式创建Ejb3Configuration
,所以我做了类似的事情:
EntityManagerFactory
Hibernate 4.3.0 @SuppressWarnings( "deprecation" )
EntityManagerFactory buildEntityManagerFactory(
UnmodifiableMap<String,String> properties,
UnmodifiableCollection<Class<?>> annotatedClasses,
Interceptor interceptor )
{
Ejb3Configuration cfg = new Ejb3Configuration();
for( Binding<String,String> binding : properties )
cfg.setProperty( binding.key, binding.value );
for( Class<?> annotatedClass : annotatedClasses )
cfg.addAnnotatedClass( annotatedClass );
cfg.setInterceptor( interceptor );
return cfg.buildEntityManagerFactory();
}
被删除了,所以我不得不利用这个hack:
Ejb3Configuration
(这是一个黑客,因为我正在实例化包EntityManagerFactory buildEntityManagerFactory(
UnmodifiableMap<String,String> properties,
UnmodifiableCollection<Class<?>> annotatedClasses,
Interceptor interceptor )
{
Configuration cfg = new Configuration();
for( Binding<String,String> binding : properties )
cfg.setProperty( binding.key, binding.value );
for( Class<?> annotatedClass : annotatedClasses )
cfg.addAnnotatedClass( annotatedClass );
cfg.setInterceptor( interceptor );
StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder();
ssrb.applySettings( cfg.getProperties() ); //??? why again?
ServiceRegistry serviceRegistry = ssrb.build();
return new EntityManagerFactoryImpl( PersistenceUnitTransactionType.RESOURCE_LOCAL, /**/
/*discardOnClose=*/true, /*sessionInterceptorClass=*/null, /**/
cfg, serviceRegistry, null );
}
中的EntityManagerFactoryImpl
。)
现在,使用Hibernate 5,他们已经更改了org.hibernate.jpa.internal
的构造函数,因此上面的代码不起作用。我可以浪费几个小时试图弄清楚如何设置,以便我可以调用该构造函数,但我确信在经过几个Hibernate版本之后,它们也不再有用了。
所以,这是我的问题:
是否有人知道实现此功能的一种干净利落的方式
EntityManagerFactoryImpl
以便以编程方式创建Hibernate EntityManagerFactory buildEntityManagerFactory(
UnmodifiableMap<String,String> properties,
UnmodifiableCollection<Class<?>> annotatedClasses,
Interceptor interceptor )
,这意味着 没有配置xml文件 和 不使用Spring 但 使用Hibernate Interceptor ?
有一个老问题:Hibernate create JPA EntityManagerFactory with out persistence.xml但它有一个旧版本的Hibernate的答案,这已经在这个问题中得到了预期。这是不可能的,因为我希望它能够与Hibernate 5一起使用,理想情况下,它不会使用任何弃用或内部的东西,以便有一段时间可以工作的机会。
答案 0 :(得分:0)
最简单的方法是传递org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor
引用,这是对“持久性单元”信息的抽象。在正常的JPA引导中,Hibernate将在persistence.xml上构建PersistenceUnitDescriptor
(对于JPA所谓的“SE引导”)或在javax.persistence.spi.PersistenceUnitInfo
上构建(对于JPA所谓的“EE引导”)。
但它是一个抽象的原因:)你可以创建自己的并传递你希望Hibernate使用的东西。这种工作的预期方式是从org.hibernate.jpa.boot.spi.Bootstrap
开始,例如:
EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder(
new CustomPersistenceUnitDescriptor(),
Collections.emptyMap()
).build();
...
class CustomPersistenceUnitDescriptor implements PersistenceUnitDescriptor {
@Override
public Properties getProperties() {
final Properties properties = new Properties();
properties.put( AvailableSettngs.INTERCEPTOR, new MyInterceptor( ... );
return properties;
}
...
}
答案 1 :(得分:0)
经过大量研究,我发现此解决方案有效:
创建一个注入拦截器的PersistenceProvider:
import org.hibernate.Interceptor;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;
import java.util.Map;
public class InterceptorAwareHibernatePersistenceProvider extends HibernatePersistenceProvider {
@Autowired
private Interceptor interceptor;
/**
* 2017-05-24 · reworked from SpringHibernateJpaPersistenceProvider so that we can inject a custom
* {@link EntityManagerFactoryBuilderImpl}; previous implementation that overrides
* {@link InterceptorAwareHibernatePersistenceProvider#getEntityManagerFactoryBuilder} no longer works
* as there are several paths with various arguments and the overloaded one was no longer called.
*/
@Override
@SuppressWarnings("rawtypes")
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
return new EntityManagerFactoryBuilderImpl(new PersistenceUnitInfoDescriptor(info), properties) {
@Override
protected void populate(SessionFactoryBuilder sfBuilder, StandardServiceRegistry ssr) {
super.populate(sfBuilder, ssr);
if (InterceptorAwareHibernatePersistenceProvider.this.interceptor == null) {
throw new IllegalStateException("Interceptor must not be null");
} else {
sfBuilder.applyInterceptor(InterceptorAwareHibernatePersistenceProvider.this.interceptor);
}
}
}.build();
}
}
创建拦截器:
public class TableNameInterceptor extends EmptyInterceptor {
@Override
public String onPrepareStatement(String sql) {
String mandant = ThreadLocalContextHolder.get(ThreadLocalContextHolder.KEY_MANDANT);
sql = sql.replaceAll(TABLE_NAME_MANDANT_PLACEHOLDER, mandant);
String prepedStatement = super.onPrepareStatement(sql);
return prepedStatement;
}
}
就我而言,我正在使用拦截器来动态更改表名 在运行时带有一个我之前设置为ThreadLocal的值 执行任何数据库访问。
ThreadLocalContextHolder.put(ThreadLocalContextHolder.KEY_MANDANT, "EWI");
return this.transactionStatusRepository.findOne(id);
为了方便起见,ThreadLocalContextHolder:
public class ThreadLocalContextHolder {
public static String KEY_MANDANT;
private static final ThreadLocal<Map<String,String>> THREAD_WITH_CONTEXT = new ThreadLocal<>();
private ThreadLocalContextHolder() {}
public static void put(String key, String payload) {
if(THREAD_WITH_CONTEXT.get() == null){
THREAD_WITH_CONTEXT.set(new HashMap<String, String>());
}
THREAD_WITH_CONTEXT.get().put(key, payload);
}
public static String get(String key) {
return THREAD_WITH_CONTEXT.get().get(key);
}
public static void cleanupThread(){
THREAD_WITH_CONTEXT.remove();
}
}
使用Spring Bean配置将所有内容连接在一起:
@Primary
@Bean
public EntityManagerFactory entityManagerFactory(DataSource dataSource,
PersistenceProvider persistenceProvider,
Properties hibernateProperties) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setJpaProperties(hibernateProperties);
factory.setPackagesToScan("com.mypackage");
factory.setDataSource(dataSource);
factory.setPersistenceProvider(persistenceProvider);
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public Properties hibernateProperties() {
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
jpaProperties.put("hibernate.hbm2ddl.auto",
environment.getRequiredProperty("spring.jpa.hibernate.ddl-auto")
);
jpaProperties.put("hibernate.ejb.naming_strategy",
environment.getRequiredProperty("spring.jpa.properties.hibernate.ejb.naming_strategy")
);
jpaProperties.put("hibernate.show_sql",
environment.getRequiredProperty("spring.jpa.properties.hibernate.show_sql")
);
jpaProperties.put("hibernate.format_sql",
environment.getRequiredProperty("spring.jpa.properties.hibernate.format_sql")
);
return jpaProperties;
}
@Primary
@Bean
public PersistenceProvider persistenceProvider() {
return new InterceptorAwareHibernatePersistenceProvider();
}
@Bean
public Interceptor interceptor() {
return new TableNameInterceptor();
}