![](/img/trans.png)
[英]Injecting the application TransactionManager into a JPA EntityListener
[英]JPA @EntityListener does not work as expected
我正在集成 Spring4 和 Hibernate5,但是有一個我無法解決的問題。 我在 BaseEntity 類上使用 @EntityListener 注釋,該類是其他業務模型的超類。 我也在 BaseEntity 上使用 @MappedSuperclass。 但它不起作用!
使用 Spring 基礎注解並成功運行應用程序。 我還向數據庫插入了一條記錄。 所以我認為我的項目配置是最新的。
任何機構讓我知道為什么? 非常感謝。
這是 BaseEntity 類。
@MappedSuperclass
@EntityListeners(EntityListener.class)
public class BaseEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, updatable = false)
private Date createDate;
@Column(nullable = false)
private Date modifyDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
這是 EntityListener 類。
public class EntityListener {
@PrePersist
public void prePersist(BaseEntity entity) {
entity.setCreateDate(new Date());
entity.setModifyDate(new Date());
}
@PreUpdate
public void preUpdate(BaseEntity entity) {
entity.setModifyDate(new Date());
}
}
以下是我基於Spring注解的項目配置。
@Configuration
@EnableWebMvc
//@ImportResource({ "classpath:xxxxx.xml" })
@PropertySources({
@PropertySource("classpath:application.properties")
})
@ComponentScan({"com.yeager.admin.persistence","com.yeager.admin.web","com.yeager.admin.service","com.yeager.admin.common"})
@EnableAspectJAutoProxy
//@EnableRetry
public class AppConfig {
@Bean(name = "multipartResolver")
public CommonsMultipartResolver getResolver() throws IOException {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
return resolver;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public static SpringContext springContext() {
return new SpringContext();
}
}
DAL的主要配置是這樣的,
@Configuration
@EnableTransactionManagement
@PropertySource({"classpath:persistence-mysql.properties"})
public class PersistenceConfig {
@Autowired
private Environment env;
public PersistenceConfig() {
super();
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.yeager.admin.persistence.entity");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean
public DataSource dataSource() {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
try {
comboPooledDataSource.setDriverClass(env.getProperty("jdbc.driver"));
} catch (PropertyVetoException e) {
e.printStackTrace();
}
comboPooledDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
comboPooledDataSource.setUser(env.getProperty("jdbc.username"));
comboPooledDataSource.setPassword(env.getProperty("jdbc.password"));
comboPooledDataSource.setInitialPoolSize(Integer.valueOf(env.getProperty("datasource.pool.initialPoolSize")));
return comboPooledDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.generate_statistics",env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("hibernate.jdbc.fetch_size", env.getProperty("hibernate.jdbc.fetch_size"));
hibernateProperties.setProperty("hibernate.jdbc.batch_size", env.getProperty("hibernate.jdbc.batch_size"));
hibernateProperties.setProperty("hibernate.max_fetch_depth", env.getProperty("hibernate.max_fetch_depth"));
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache",env.getProperty("hibernate.cache.use_second_level_cache"));
hibernateProperties.setProperty("hibernate.cache.use_query_cache",env.getProperty("hibernate.cache.use_query_cache"));
// hibernateProperties.setProperty("hibernate.cache.provider_class",env.getProperty("hibernate.cache.provider_class"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "update");
return hibernateProperties;
}
}
我使用 Hibernate 的 LocalSessionFactoryBean 類而不是 JPA 的 EntityManager 類。 請問這是什么原因?
--------------- 6.19 --------------
我錯了。 我不應該使用基於 Spring LocalSessionFactoryBean 類的 @EntityListener 注釋。 對於hibernate5,有一種特殊的配置方式。 http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#annotations-jpa-entitylisteners現在,我修改我的代碼如下,
@Component
public class EntityEventListener {
@Autowired
private SessionFactory sessionFactory;
@PostConstruct
public void registerListeners(){
EventListenerRegistry eventListenerRegistry = ((SessionFactoryImplementor) sessionFactory).getServiceRegistry().getService(EventListenerRegistry.class);
eventListenerRegistry.prependListeners(EventType.PRE_INSERT, PreInsertEntityListener.class);
}
}
預插入實體監聽器
public class PreInsertEntityListener implements PreInsertEventListener {
@Override
public boolean onPreInsert(PreInsertEvent event) {
// if (event.getEntity() instanceof AdminUser){
// ((AdminUser) event.getEntity()).setCreateDate(new Date());
// ((AdminUser) event.getEntity()).setModifyDate(new Date());
// }
BaseEntity baseEntity = (BaseEntity) event.getEntity();
baseEntity.setCreateDate(new Date());
baseEntity.setModifyDate(new Date());
return false;
}
}
但是,我還有一個問題。 我閱讀了 hibernate doc 並搜索了許多關於此的信息。 當我插入實體數據時,我的代碼已經不起作用。
請幫幫我,謝謝!
盡管您既沒有發布具體/派生實體也沒有發布業務代碼來持久化它,但您發布的代碼似乎是正確的。
為了給它一個小測試,我向超類添加了一個生成的 UID 並創建了一個具體的實體:
import javax.persistence.Entity;
@Entity
public class DerivedEntity extends BaseEntity {
private static final long serialVersionUID = -6441043639437893962L;
}
既然你提到了 Spring,這里有一個 Spring Data JPA 存儲庫來保存它:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DerivedEntityRepository extends CrudRepository<DerivedEntity, Long> {
}
這個小測試應該表明( @PrePersist
)監聽器有效:
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringRunner.class)
@Transactional
@SpringBootTest
public class DerivedEntityRepositoryTests {
@Autowired
private DerivedEntityRepository derivedEntityRepository;
@Test
public void insertDerivedEntity() {
DerivedEntity entity = new DerivedEntity();
entity = derivedEntityRepository.save(entity);
assertThat(entity.getCreateDate()).isNotNull();
}
}
順便提一下,如果您以后不想增強您的自定義偵聽器,現有的 Spring Data JPA AuditingEntityListener完全可以完成您目前正在做的事情(甚至更多)。 在這種情況下,您可以使用@EnableJpaAuditing
增強@Configuration
類並修改您的BaseEntity
如下:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
// ...
@CreatedDate
@Column(nullable = false, updatable = false)
private Date createDate;
@LastModifiedDate
@Column(nullable = false)
private Date modifyDate;
// ...
}
這將使您的自定義EntityListener
可有可無。
只需查看Spring JPA 審計以獲取更多信息。 如果您想使用 Hibernate 增強審計,請嘗試Hibernate Envers 。
非常感謝大家。 我已經解決了這個問題。 我將分享我的解決方案,如果您正在做同樣的事情,希望對您有所幫助。
首先,我的出發點是錯誤的。 因為之前用的是JPA,所以在集成Spring4和Hibernate5的時候默認使用@EntityListener注解。 然后,我閱讀了 Hibernate doc 和許多相關文章,發現有一種實現實體偵聽器的新方法。 查看休眠文檔
最后,我的解決方案如下。
這是我的 BaseEntity 類。
@MappedSuperclass
public class BaseEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, updatable = false)
private Date createDate;
@Column(nullable = false)
private Date modifyDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
首先,您需要定義 EntityListener 類。
public class EntityListener implements PreInsertEventListener, PreUpdateEventListener {
private static final String CREATE_DATE_PROPERTY = "createDate";
private static final String MODIFY_DATE_PROPERTY = "modifyDate";
@Override
public boolean onPreInsert(PreInsertEvent event) {
if (event.getEntity() instanceof BaseEntity){
//property name of entity
String[] propertyNames = event.getPersister().getEntityMetamodel().getPropertyNames();
//property value of entity
Object[] state = event.getState();
for (int i = 0; i < propertyNames.length ; i ++) {
if (CREATE_DATE_PROPERTY.equals(propertyNames[i]) || MODIFY_DATE_PROPERTY.equals(propertyNames[i])){
state[i] = new Date();
}
}
}
return false;
}
@Override
public boolean onPreUpdate(PreUpdateEvent event) {
if (event.getEntity() instanceof BaseEntity){
//property name of entity
String[] propertyNames = event.getPersister().getEntityMetamodel().getPropertyNames();
//property value of entity
Object[] state = event.getState();
for (int i = 0; i < propertyNames.length ; i ++) {
if (MODIFY_DATE_PROPERTY.equals(propertyNames[i])){
state[i] = new Date();
}
}
}
return false;
}
}
最后,您應該注冊實體事件偵聽器。
@SuppressWarnings("unchecked")
@Component
public class EntityEventListenerRegistry {
@Autowired
private SessionFactory sessionFactory;
/**
* EventListenerRegistry:http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#annotations-jpa-entitylisteners
*/
@PostConstruct
public void registerListeners(){
EventListenerRegistry eventListenerRegistry = ((SessionFactoryImplementor) sessionFactory).getServiceRegistry().getService(EventListenerRegistry.class);
eventListenerRegistry.prependListeners(EventType.PRE_INSERT, EntityListener.class);
eventListenerRegistry.prependListeners(EventType.PRE_UPDATE, EntityListener.class);
}
}
我遇到了同樣的問題,在我的情況下,用 @EntityListeners 定義的偵聽器指的是另一個包中的類(不在同一個類加載器中),並且沒有被掃描。 將類添加到我的持久性上下文后,它開始按預期工作。
因此,請始終確保將與持久性相關的任何類添加到持久性上下文中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.