스프링 부트에서의 스프링 관리 휴지 상태 인터셉터 사용 방법
스프링 관리 휴지 상태 인터셉터(http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch14.html)를 Spring Boot에 통합할 수 있습니까?
Spring Data JPA 및 Spring Data REST를 사용하고 있으며 엔티티에서 특정 필드를 업데이트하기 위해 휴지 상태 인터셉터가 필요합니다.
표준 JPA 이벤트에서는 이전 값을 얻을 수 없기 때문에 Hibernate 인터셉터를 사용해야 한다고 생각합니다.
Spring Bean이기도 한 하이버네이트 인터셉터를 추가하는 방법이 특별히 쉬운 방법은 없지만, 하이버네이트가 완전히 관리하고 있다면 쉽게 인터셉터를 추가할 수 있습니다. 위해서는 다음 .application.properties
:
spring.jpa.properties.hibernate.ejb.interceptor=my.package.MyInterceptorClassName
할 할 수 .LocalContainerEntityManagerFactoryBean
. 。EntityManagerFactoryBuilder
.1에 Spring Boot 1.1.4에서 Spring Boot 1.1.4로 .(Map)
1.2.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;
}
};
}
스프링 부트 2를 사용한 솔루션
@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);
}
}
- Spring Boot 2.1.7을 사용하고 있습니다.풀어주다.
hibernate.session_factory.interceptor
하면 .hibernate.ejb.interceptor
양쪽 속성이 동작하는 것은 아마도 하위 호환성 요건 때문일 것입니다.
application.properties가 아닌 hibernatePropertiesCustomizer를 사용하는 이유
되는 답변은 를 '알다'에입니다.spring.jpa.properties.hibernate.ejb.interceptor
application.properties/yml의 속성.인터셉터가 여러 응용 프로그램에서 사용되는 lib에 있는 경우 이 아이디어가 작동하지 않을 수 있습니다.각 어플리케이션의 application.properties를 변경할 필요 없이 lib에 의존관계를 추가하는 것만으로 대행 수신기를 활성화할 수 있습니다.
몇 개의 스레드를 참조해, 다음과 같은 솔루션을 얻을 수 있었습니다.
Spring-Boot 1.2.3을 사용하고 있습니다.릴리스(현재 ga)
이 버그(DATAREST-373)에 기재되어 있는 것이 사용 예입니다.
호호 i of of of of of 의 할 수 .User
@Entity
생성 시, 저장 시 특별한 논리를 갖습니다.작성은 매우 간단합니다.@HandleBeforeCreate
.@Entity
입니다.0L
★★★★★★ 。
저장을 위해 빈 공간을 확장하는 휴지 상태 인터셉터를 구현했습니다.가로채기
@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;
}
}
스프링 부트를 사용한 문서에는 다음과 같이 기재되어 있습니다.
spring.jpa.properties의 모든 속성.*는 로컬 EntityManagerFactory 작성 시 일반 JPA 속성(프리픽스가 제거됨)으로 통과됩니다.
자료에서 바와 같이 할 수 .spring.jpa.properties.hibernate.ejb.interceptor
스프링 부츠 나는 그 말을 @Autowire PasswordEncoder
일하기 위해.
그래서 저는 HibernateJpa를 사용하기로 했습니다.자동 설정 및 덮어쓰기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);
}
}
Interceptor
Hibernate를 인스턴스화하는 대신, 그것이 작동하기 위한 열쇠였습니다.
지금 신경이 쓰이는 것은 논리가 두 개로 나뉘어져 있다는 것입니다만, 일단 DATAREST-373이 해결되면, 이것이 필요하게 될 것입니다.
스프링 부트용 휴지 상태의 청취자(spring-boot-starter 1.2.4)의 간단한 파일 예시입니다.릴리스)
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;
}
}
안녕하세요.
다음 문서를 참조하십시오.https://github.com/spring-projects/spring-boot/commit/59d5ed58428d8cb6c6d9fb723d0e334fe3e7d9be (사용: Hibernate Properties Customizer 인터페이스)
또는
단순 가로채기의 경우:
어플리케이션에서 이를 설정하려면 spring.jpa.properties를 추가해야 합니다.hibernate.ejb.displor 또는 = path.to.displor(application.properties에 있습니다).인터셉터 자체는 @Component여야 합니다.
요격범이 실제로 콩을 사용하지 않는 한.그렇지 않으면 조금 더 복잡하지만 기꺼이 해결책을 제시하겠습니다.
application-test.properties에 빈칸을 추가하는 것을 잊지 마십시오.테스트에서 로깅 시스템(또는 로깅 시스템 사용 목적)을 사용하지 않는 인터셉터입니다(이는 그다지 도움이 되지 않습니다).
이것이 당신에게 도움이 되었기를 바랍니다.
마지막으로 Spring/Hibernate 버전을 항상 갱신합니다(최신 버전 사용).새로운 버전에서는 설정을 가능한 한 줄이려고 하면 대부분의 코드가 용장화됩니다.
Spring Boot이 아닌 Spring 4.1.1, Hibernate 4.3.11 어플리케이션에서도 같은 문제가 발생하였습니다.
Hibernate Entity Factory Builder 코드를 후) 빈를 (Hibernate Entity Manager Factory Builder Impl 코드)에 입니다.hibernate.ejb.interceptor
엔티티 매니저 정의의 속성, 휴지 상태에서는 이미 인스턴스화된 빈이 사용됩니다.
애플리케이션 컨텍스트의 entityManager 정의에는 다음과 같은 내용이 있습니다.
<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>
감사요격기는 스프링에 의해 관리되므로 자동 배선 및 기타 스프링성 동작을 사용할 수 있습니다.
같은 문제에 부딪혀 모든 셋업을 처리할 수 있는 작은 스프링 라이브러리를 만들었습니다.
https://github.com/teastman/spring-data-hibernate-event
Spring Boot을 사용하는 경우 종속성을 추가합니다.
<dependency>
<groupId>io.github.teastman</groupId>
<artifactId>spring-data-hibernate-event</artifactId>
<version>1.0.0</version>
</dependency>
그런 다음 주석 @HibernateEventListener를 임의의 메서드에 추가합니다.첫 번째 파라미터는 리슨하는 엔티티이고 두 번째 파라미터는 리슨하는 하이버네이트이벤트입니다static util 함수 getProperty도 추가했습니다.체크할 특정 속성에 보다 쉽게 액세스할 수 있도록 인덱스를 작성하지만 원시 휴지 상태 이벤트만 볼 수도 있습니다.
@HibernateEventListener
public void onUpdate(MyEntity entity, PreUpdateEvent event) {
int index = getPropertyIndex(event, "name");
if (event.getOldState()[index] != event.getState()[index]) {
// The name changed.
}
}
Hibernate Interceptors와 Spring Data JPA의 통합 방법에 대해 이틀 동안 조사한 결과, 저의 솔루션은 Java 구성과 xml 구성 간의 하이브리드이지만 이 게시물은 매우 유용했습니다.최종 해결책은 다음과 같습니다.
AuditLogInterceptor 클래스:
public class AuditLogInterceptor extends EmptyInterceptor{
private int updates;
//interceptor for updates
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
currentState[i] = new Date();
return true;
}
}
}
return false;
}
}
데이터 소스 Java 구성:
@Bean
DataSource dataSource() {
//Use JDBC Datasource
DataSource dataSource = new DriverManagerDataSource();
((DriverManagerDataSource)dataSource).setDriverClassName(jdbcDriver);
((DriverManagerDataSource)dataSource).setUrl(jdbcUrl);
((DriverManagerDataSource)dataSource).setUsername(jdbcUsername);
((DriverManagerDataSource)dataSource).setPassword(jdbcPassword);
return dataSource;
}
대행 수신기를 추가하는 엔티티 및 트랜잭션 매니저
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceUnitName="InterceptorPersistentUnit" p:persistenceXmlLocation="classpath:audit/persistence.xml"
p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="ORACLE" p:showSql="true" />
지속성 컨피규레이션파일
<persistence-unit name="InterceptorPersistentUnit">
<class>com.app.CLASSTOINTERCEPT</class>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.ejb.interceptor"
value="com.app.audit.AuditLogInterceptor" />
</properties>
</persistence-unit>
표준 JPA 이벤트에서는 이전 값을 얻을 수 없기 때문에 Hibernate 인터셉터를 사용해야 한다고 생각합니다.
아니요, 가로채기를 사용하지 않고 JPA를 사용해야만 이전 값을 얻을 수 있습니다.
감사하는 엔티티의 기본 클래스는 다음과 같습니다.Auditable<T>
즉, 선언할 수 있습니다.@Transient
유형 변수Auditable<T>
내면에Auditable<T>
엔티티가 오래된 값으로 영속적인 컨텍스트에 로드되었을 때 및 업데이트되기 전에 오래된 값의 복사(아래 참조)로 엔티티를 채울 수 있습니다.
/**
* Extend this class if you want your entities to be audited.
*/
@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditListener.class)
public abstract class Auditable implements Serializable {
@JsonIgnore
@Transient
private Auditable oldState;
}
당신은 가질 수 있다@PostLoad
내부Auditable
기본 엔티티 또는 청취자 내부에 있는 것을 선호합니다.AuditListener
@EntityListeners
.
public class AuditListener {
/**
* Triggered when an entity is loaded to the persistent.
*
* @param entity the one which is loaded
*/
@PostLoad
public void onPostLoad(final Auditable entity) {
//Here, you have access to the entity before it gets updated and
//after it's loaded to the context, so now you can have a new copy
//and set it to that Transient variable so you make sure it not
//gets persisted by JPA.
entity.setOldState(SerializationUtils.clone(entity));
}
/**
* Triggered when an entity updated and before committed the
* transaction.
*
* @param entity the one which is updated
*/
@PostUpdate
public void onPostUpdate(final Auditable entity) {
//Here, you have both copies the old and the new, thus you can
//track the changes and save or log them where ever you would like.
}
}
음음음음음음음음음음음음음음음음음음 because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because because 를 사용할 수 있습니다.ApplicationContext
예를 들어 다음과 같습니다.
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtil.applicationContext=applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
그러면 이렇게 대행 수신기에 있는 서비스를 호출할 수 있습니다.
public class SimpleInterceptor extends EmptyInterceptor {
@Override
public String onPrepareStatement(String sql) {
MyService myService=SpringContextUtil.getApplicationContext().getBean(MyService.class);
myService.print();
return super.onPrepareStatement(sql);
}
}
Merson의하면, 또은 Paulo Merson을 입니다.HibernatePropertiesCustomizer
)FunctionalInterface
의 내부에서는,@Configuration
다다을을 、 을 、 용 、 용 、 。
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(MyInterceptor interceptor) {
return props -> props.put("hibernate.session_factory.interceptor", interceptor);
}
Spring Boot 에서는, 하이버네이트 커스텀 인터셉터를 간단하게 등록할 수 있습니다.HibernatePropertiesCustomizer
및 override " " " " interface interface interface "customize
의 방법custum inteceptor
로로 합니다.hibernateProperties
:
@Component
public class MyCustomInterceptor extends EmptyInterceptor implements HibernatePropertiesCustomizer {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.session_factory.interceptor", this);
}
@Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, org.hibernate.type.Type[] types) {
System.out.println("onSave");
return super.onSave(entity, id, state, propertyNames, types);
}
}
언급URL : https://stackoverflow.com/questions/25283767/how-to-use-spring-managed-hibernate-interceptors-in-spring-boot
'source' 카테고리의 다른 글
Angularjs는 상위 범위의 변화를 감시합니다. (0) | 2023.03.23 |
---|---|
동적 python 개체를 json으로 변환 (0) | 2023.03.18 |
Oracle의 인덱스 구성 테이블을 사용해야 하는 경우아니면, 언제 하면 안 될까요? (0) | 2023.03.18 |
초보자용 XSLT 레퍼런스 (0) | 2023.03.18 |
logback.xml에서 스프링 속성 자리 표시자를 사용할 수 없습니다. (0) | 2023.03.18 |