Mar*_*ijk 32 hibernate spring-data spring-data-jpa spring-data-rest spring-boot
是否有可能在Spring Boot中集成Spring管理的Hibernate拦截器(http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch14.html)?
我正在使用Spring Data JPA和Spring Data REST,并且需要一个Hibernate拦截器来对实体上的特定字段进行更新.
使用标准JPA事件,不可能获得旧值,因此我认为我需要使用Hibernate拦截器.
Phi*_*ebb 41
没有一种特别简单的方法来添加一个也是Spring Bean的Hibernate拦截器,但如果它完全由Hibernate管理,你可以轻松添加一个拦截器.为此,请将以下内容添加到您的application.properties
:
spring.jpa.properties.hibernate.ejb.interceptor=my.package.MyInterceptorClassName
Run Code Online (Sandbox Code Playgroud)
如果你需要Interceptor也是一个bean,你可以创建自己的LocalContainerEntityManagerFactoryBean
.在EntityManagerFactoryBuilder
从春天引导1.1.4是有点过于严格与通用的,所以你需要转换为属性(Map)
,我们将看到固定,对1.2.
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder factory, DataSource dataSource,
JpaProperties properties) {
Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.putAll(properties.getHibernateProperties(dataSource));
jpaProperties.put("hibernate.ejb.interceptor", hibernateInterceptor());
return factory.dataSource(dataSource).packages("sample.data.jpa")
.properties((Map) jpaProperties).build();
}
@Bean
public EmptyInterceptor hibernateInterceptor() {
return new EmptyInterceptor() {
@Override
public boolean onLoad(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
System.out.println("Loaded " + id);
return false;
}
};
}
Run Code Online (Sandbox Code Playgroud)
Fot*_*los 19
以几个线程为参考,我得到了以下解决方案:
我正在使用Spring-Boot 1.2.3.RELEASE(这是目前的ga)
我的用例就是这个bug(DATAREST-373)中描述的.
我需要能够User
@Entity
在创建时对密码进行编码,并在保存时具有特殊逻辑.创建非常简单,使用@HandleBeforeCreate
和检查@Entity
id是否0L
相等.
为了保存,我实现了一个扩展EmptyInterceptor的Hibernate Interceptor
@Component
class UserInterceptor extends EmptyInterceptor{
@Autowired
PasswordEncoder passwordEncoder;
@Override
boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
if(!(entity instanceof User)){
return false;
}
def passwordIndex = propertyNames.findIndexOf { it == "password"};
if(entity.password == null && previousState[passwordIndex] !=null){
currentState[passwordIndex] = previousState[passwordIndex];
}else{
currentState[passwordIndex] = passwordEncoder.encode(currentState[passwordIndex]);
}
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
使用spring boot文档说明了这一点
当创建本地EntityManagerFactory时,spring.jpa.properties.*中的所有属性都作为普通的JPA属性(带有前缀剥离)传递.
正如许多参考文献所述,我们可以spring.jpa.properties.hibernate.ejb.interceptor
在Spring-Boot配置中定义我们的拦截器.但是我无法@Autowire PasswordEncoder
上班.
所以我使用了HibernateJpaAutoConfiguration并覆盖了protected void customizeVendorProperties(Map<String, Object> vendorProperties)
.这是我的配置.
@Configuration
public class HibernateConfiguration extends HibernateJpaAutoConfiguration{
@Autowired
Interceptor userInterceptor;
@Override
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
vendorProperties.put("hibernate.ejb.interceptor",userInterceptor);
}
}
Run Code Online (Sandbox Code Playgroud)
自动装配Interceptor
而不是允许Hibernate实例化它是使其工作的关键.
现在困扰我的是逻辑被分成两部分,但希望一旦DATAREST-373被解决,那么这就不必要了.
我简单的一个用于spring boot的hibernate监听器的文件示例(spring-boot-starter 1.2.4.RELEASE)
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.*;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.jpa.HibernateEntityManagerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.persistence.EntityManagerFactory;
@Component
public class UiDateListener implements PostLoadEventListener, PreUpdateEventListener {
@Inject EntityManagerFactory entityManagerFactory;
@PostConstruct
private void init() {
HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.appendListeners(EventType.POST_LOAD, this);
registry.appendListeners(EventType.PRE_UPDATE, this);
}
@Override
public void onPostLoad(PostLoadEvent event) {
final Object entity = event.getEntity();
if (entity == null) return;
// some logic after entity loaded
}
@Override
public boolean onPreUpdate(PreUpdateEvent event) {
final Object entity = event.getEntity();
if (entity == null) return false;
// some logic before entity persist
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
@Component
public class MyInterceptorRegistration implements HibernatePropertiesCustomizer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.session_factory.interceptor", myInterceptor);
}
}
Run Code Online (Sandbox Code Playgroud)
hibernate.session_factory.interceptor
您可以使用hibernate.ejb.interceptor
。这两个属性都可能由于向后兼容性要求而起作用。一个建议的答案是spring.jpa.properties.hibernate.ejb.interceptor
在application.properties/yml 中的属性中指示您的拦截器。如果您的拦截器位于将由多个应用程序使用的库中,则此想法可能行不通。您希望通过仅向lib添加依赖项来激活拦截器,而无需每个应用程序更改其application.properties。
我在 Spring 4.1.1、Hibernate 4.3.11 应用程序中遇到了类似的问题 - 而不是 Spring Boot。
我发现的解决方案(在阅读 Hibernate EntityManagerFactoryBuilderImpl 代码之后)是,如果您将 bean 引用而不是类名传递给hibernate.ejb.interceptor
实体管理器定义的属性,Hibernate 将使用已经实例化的 bean。
因此,在应用程序上下文中的实体管理器定义中,我有这样的内容:
<bean id="auditInterceptor" class="com.something.AuditInterceptor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
...>
<property name="jpaProperties">
<map>
...
<entry key="hibernate.ejb.interceptor">
<ref bean="auditInterceptor" />
</entry>
...
</map>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
auditInterceptor 由 Spring 管理,因此自动装配和其他 Spring 特性的行为将可供它使用。
你好,
读一下:https://github.com/spring-projects/spring-boot/commit/59d5ed58428d8cb6c6d9fb723d0e334fe3e7d9be(使用:HibernatePropertiesCustomizer接口)
或者
对于简单的拦截器:
为了在您的应用程序中配置它,您只需添加:spring.jpa.properties.hibernate.ejb.interceptor = path.to.interceptor(在 application.properties 中)。拦截器本身应该是@Component。
只要拦截器实际上不使用任何 beans。否则会有点复杂,但我很乐意提供解决方案。
不要忘记在 application-test.properties 中添加一个EmptyInterceptor ,以便在测试中不使用日志系统(或任何您想要使用它的东西)(这不会很有帮助)。
希望这对您有用。
最后一点:始终更新您的 Spring / Hibernate 版本(尽可能使用最新版本),您会发现大多数代码将变得多余,因为新版本会尝试尽可能减少配置。
归档时间: |
|
查看次数: |
37016 次 |
最近记录: |