如何为 EntityManagerFactory 设置 Hibernate 拦截器
How to set a Hibernate interceptor for an EntityManagerFactory
我正在使用 JPA,但我需要打开我的 EntityManagerFactory,以便我可以向 Session 添加一个拦截器。之后我想将 Session 包装回 EntityManager。
"Why not just use Session instead of EntityManager?"我们仍然希望减少可能的技术迁移的影响
For what do I want to use Interceptor:
我可以通过以下方式恢复问题:该项目在警报数据库上运行 运行 查询。每个地方都有一个带有警报 Table 的数据库,但客户想要一个单一的数据库,我们必须在其中创建多个 "Alarm Table",每个地方一个(例如:Table_Alarm-Place1, Table_Alarm-Place2)。这意味着我们将有多个表用于同一实体,拦截器的目标是更改休眠在最终 SQL
中生成的 Table 名称
How I pretend to use the interceptor:
public class SqlInterceptor extends EmptyInterceptor {
private String tableSufix;
private static final Logger LOGGER = LoggerFactory.getLogger(SqlInterceptor.class);
public SqlInterceptor(String tableSufix) {...}
@Override
public String onPrepareStatement(String sql) {
String finalSql;
//Manipulated SQL (parsed by Hibernate)
return finalSql;
}
}
- 该项目使用 JPA 2.1 和 Hibernate 4.3。11.Final
为什么不简单地执行以下操作:
EntityManagerFactory entityManagerFactory = // created from somewhere.
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
// do whatever you need with the session factory here.
// Later in your code, initially use EntityManager and unwrap to Session.
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
基本上,不是尝试获取 Session
然后将其包装回 EntityManager
,而是简单地传递一个 JPA EntityManager
并将其解包到 Session
在 as-needed 的基础上。
您可以在构建 EntityManagerFactory
时提供 Interceptor
:
String persistenceUnitName = ...;
PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(persistenceUnitName);
Map<String, Object> configuration = new HashMap<>();
configuration.put(AvailableSettings.INTERCEPTOR, new SqlInterceptor());
EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration
);
EntityManagerFactory emf = entityManagerFactoryBuilder.build();
您似乎想要一个 multi-tenant 数据库。我之前遇到过类似的问题,并使用 aspectj 实现了一个拦截器来正确设置过滤器。即使您不选择过滤器选项,您也可以在每次使用 aspectj 创建会话时获取它,如下所示。
public privileged aspect MultitenantAspect {
after() returning (javax.persistence.EntityManager em): execution (javax.persistence.EntityManager javax.persistence.EntityManagerFactory.createEntityManager(..)) {
Session session = (Session) em.getDelegate();
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("ownerId", ownerId);
}
}
在下面的示例中,我只是在您需要过滤的实体上设置了需要配置的过滤器:
@Entity
@FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "ownerId", type = "long"))
@Filters({
@Filter(name = "tenantFilter", condition = "(owner=:ownerId or owner is null)")
})
public class Party {
}
当然,要使用过滤器而不是 table 名称,您必须添加一列来区分 tables - 我认为这比使用多个 table 名字。
您可以将所有必要的信息存储在静态 ThreadLocal
实例中,然后再读取它。
这样您就可以避免会话范围拦截器的复杂性,并且您可以使用其他机制来实现相同的目标(例如使用会话工厂范围拦截器,它更容易配置)。
覆盖 Hibernate 的 EmptyInterceptor 的一个超级简单的方法就是在属性文件中这样做
spring.jpa.properties.hibernate.session_factory.interceptor=<fully-qualified-interceptor-class-name>
干杯:)
我正在使用 JPA,但我需要打开我的 EntityManagerFactory,以便我可以向 Session 添加一个拦截器。之后我想将 Session 包装回 EntityManager。
"Why not just use Session instead of EntityManager?"我们仍然希望减少可能的技术迁移的影响
For what do I want to use Interceptor:
我可以通过以下方式恢复问题:该项目在警报数据库上运行 运行 查询。每个地方都有一个带有警报 Table 的数据库,但客户想要一个单一的数据库,我们必须在其中创建多个 "Alarm Table",每个地方一个(例如:Table_Alarm-Place1, Table_Alarm-Place2)。这意味着我们将有多个表用于同一实体,拦截器的目标是更改休眠在最终 SQL
中生成的 Table 名称How I pretend to use the interceptor:
public class SqlInterceptor extends EmptyInterceptor {
private String tableSufix;
private static final Logger LOGGER = LoggerFactory.getLogger(SqlInterceptor.class);
public SqlInterceptor(String tableSufix) {...}
@Override
public String onPrepareStatement(String sql) {
String finalSql;
//Manipulated SQL (parsed by Hibernate)
return finalSql;
}
}
- 该项目使用 JPA 2.1 和 Hibernate 4.3。11.Final
为什么不简单地执行以下操作:
EntityManagerFactory entityManagerFactory = // created from somewhere.
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
// do whatever you need with the session factory here.
// Later in your code, initially use EntityManager and unwrap to Session.
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
基本上,不是尝试获取 Session
然后将其包装回 EntityManager
,而是简单地传递一个 JPA EntityManager
并将其解包到 Session
在 as-needed 的基础上。
您可以在构建 EntityManagerFactory
时提供 Interceptor
:
String persistenceUnitName = ...;
PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(persistenceUnitName);
Map<String, Object> configuration = new HashMap<>();
configuration.put(AvailableSettings.INTERCEPTOR, new SqlInterceptor());
EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration
);
EntityManagerFactory emf = entityManagerFactoryBuilder.build();
您似乎想要一个 multi-tenant 数据库。我之前遇到过类似的问题,并使用 aspectj 实现了一个拦截器来正确设置过滤器。即使您不选择过滤器选项,您也可以在每次使用 aspectj 创建会话时获取它,如下所示。
public privileged aspect MultitenantAspect {
after() returning (javax.persistence.EntityManager em): execution (javax.persistence.EntityManager javax.persistence.EntityManagerFactory.createEntityManager(..)) {
Session session = (Session) em.getDelegate();
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("ownerId", ownerId);
}
}
在下面的示例中,我只是在您需要过滤的实体上设置了需要配置的过滤器:
@Entity
@FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "ownerId", type = "long"))
@Filters({
@Filter(name = "tenantFilter", condition = "(owner=:ownerId or owner is null)")
})
public class Party {
}
当然,要使用过滤器而不是 table 名称,您必须添加一列来区分 tables - 我认为这比使用多个 table 名字。
您可以将所有必要的信息存储在静态 ThreadLocal
实例中,然后再读取它。
这样您就可以避免会话范围拦截器的复杂性,并且您可以使用其他机制来实现相同的目标(例如使用会话工厂范围拦截器,它更容易配置)。
覆盖 Hibernate 的 EmptyInterceptor 的一个超级简单的方法就是在属性文件中这样做
spring.jpa.properties.hibernate.session_factory.interceptor=<fully-qualified-interceptor-class-name>
干杯:)