如何为 EntityManagerFactory 设置 Hibernate 拦截器
Posted
技术标签:
【中文标题】如何为 EntityManagerFactory 设置 Hibernate 拦截器【英文标题】:How to set a Hibernate interceptor for an EntityManagerFactory 【发布时间】:2016-01-07 12:44:04 【问题描述】:我正在使用 JPA,但我需要解开我的 EntityManagerFactory,所以我可以向 Session 添加一个拦截器。之后我想将 Session 包装回 EntityManager。
“为什么不直接使用 Session 而不是 EntityManager?”我们仍然希望减少可能的技术迁移的影响
我想用Interceptor做什么:
我可以通过以下方式恢复问题:该项目在警报数据库上运行查询。每个地方都有一个带有警报表的数据库,但客户希望拥有一个数据库,我们必须在其中创建多个“警报表”,每个地方一个(例如:Table_Alarm-Place1,Table_Alarm-Place2)。这意味着我们将为同一个实体有多个表,拦截器的目标是更改最终 SQL 中由 hibernate 生成的表名称
我如何假装使用拦截器:
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.unwrap(SessionFactory.class)”并用它打开一个会话。现在我需要将会话“转换”回 EntityManager 我使用术语“包装”因为 EntityManager 的“unwrap()”功能 @RafaelTeles 你能否指定再次包装它的原因以便更清楚。 我正在使用的项目使用 JPA,因此所有代码都使用了 EntityManager。我有一个创建我使用的 EntityManager 实例的工厂,如果我将其更改为 Session 我需要将项目的其余部分更改为 session 它真的必须是一个会话范围的拦截器吗?您可以改用 SessionFactory 范围的吗?第三,那个拦截器要做什么,也许还有其他更优雅的方法来实现这一点? 【参考方案1】:您可以在构建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();
【讨论】:
【参考方案2】:您似乎想要拥有一个多租户数据库。我之前遇到过类似的问题,并使用 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
当然,要使用过滤器而不是表名,您必须添加一列来区分表 - 我认为这比使用多个表名更好。
【讨论】:
"公共特权方面 MultitenantAspect ",这是 Java 吗?【参考方案3】:覆盖 Hibernate 的 EmptyInterceptor 的一个超级简单的方法就是在属性文件中使用它
spring.jpa.properties.hibernate.session_factory.interceptor=<fully-qualified-interceptor-class-name>
干杯:)
【讨论】:
【参考方案4】:您可以将所有必要的信息存储在静态ThreadLocal
实例中,然后再读取。
这样可以避免会话范围拦截器的复杂性,并且可以使用其他机制来实现相同的目标(例如使用更容易配置的会话工厂范围拦截器)。
【讨论】:
【参考方案5】:为什么不简单地做以下事情:
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
。
【讨论】:
我需要在 Session 中添加一个休眠拦截器。会话打开后,我没有找到这样做的方法。以上是关于如何为 EntityManagerFactory 设置 Hibernate 拦截器的主要内容,如果未能解决你的问题,请参考以下文章
Spring + EntityManagerFactory +Hibernate 监听器 + 注入
创建 EntityManagerFactory 时出现 ArrayIndexOutOfBoundsException
EntityManagerFactory 和 SessionFactory 的区别? [复制]