4

我创建了一个使用 Spring Data + Hibernate + JPA + Quartz 的 Web 应用程序。在我的 Quartz 工作之一中,我需要刷新一个实体,因为该实体可能已被用户通过用户界面更改。基本上,实体包含 Quartz 作业的配置,如果实体在作业之外更改,则实体内的数据已过时,所以我想在持久化实体之前刷新,以免它覆盖更新的信息。

由于 Spring Data 确实在存储库上提供了刷新方法,因此我为 Spring Data 中的所有存储库创建了一个自定义实现。

这是我设置自定义实现的方式:

CustomRepository.java(接口)

@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {

    public void refresh(T entity);
}

CustomRepositoryImpl.java(实现)

@NoRepositoryBean
public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> {

    private EntityManager entityManager;

    public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager){
        super(domainClass, entityManager);

        this.entityManager = entityManager;
    }

    public void refresh(T entity) {
        this.entityManager.refresh(entity);

    }
}

CustomRepositoryFactoryBean.java(Spring 的工厂 Bean)

public class CustomRepositoryFactoryBean<T extends JpaRepository<S,ID>,S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager){
        return new CustomRepositoryFactory(entityManager);
    }

    private static class CustomRepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory{

        private EntityManager entityManager;

        public CustomRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }

        protected Object getTargetRepository(RepositoryMetadata metadata){
            return new CustomRepositoryImpl<T, ID>((Class<T>) metadata.getDomainType(), entityManager);
        }

        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata){
            return CustomRepository.class;
        }
    }
}

弹簧配置.xml

<jpa:repositories base-package="com.network.socially.data.repositories" 
  factory-class="com.network.socially.data.repositories.custom.CustomRepositoryFactoryBean">  
</jpa:repositories>

当应用程序通过 Tomcat 初始化时,自定义实现的设置似乎有效,但是当我尝试调用刷新方法时,我收到异常。

下面是调用刷新方法的代码:

public void execute(JobExecutionContext context) throws JobExecutionException {
    super.logExecution(context);
    super.initialize(context);

    this.extractObjects();

    MyJob myJob = this.myJobRepository.findOne(super.applicationJob.getJobId());
    this.myJobRepository.refresh(myJob);
        this.myJobRepository.save(myJob);
}

这会引发异常:

Caused by: java.lang.IllegalArgumentException: Entity not managed
    at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:914)
    at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:895)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
    at $Proxy25.refresh(Unknown Source)
    at com.network.socially.data.repositories.custom.CustomRepositoryImpl.refresh(CustomRepositoryImpl.java:22)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:333)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:318)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    ... 10 more

我不明白为什么不管理实体,因为从数据库中检索实体会导致它在持久性上下文中进行管理。

谁能指出我哪里出错了?我是否需要使用 JTA 而不是 RESOURCE-LOCAL?当我通过 JPA 检索实体时,为什么不管理实体?

4

2 回答 2

3

问题是您在调用时需要一个事务refresh()。Spring Data JPA 中的所有存储库方法都会在后台自动为您执行此操作。最简单的方法是使用 Spring 的@Transactional,假设您启用了 Spring 事务管理。例如:

@Transactional
public void execute(JobExecutionContext context) throws JobExecutionException {
  super.logExecution(context);
  super.initialize(context);

  this.extractObjects();

  MyJob myJob = this.myJobRepository.findOne(super.applicationJob.getJobId());
  this.myJobRepository.refresh(myJob);
  this.myJobRepository.save(myJob);
}

以上假设定义此方法的对象实例是 Spring 管理的 - 即 Spring bean。事务的边界延伸到整个方法。上述方法的缺点是,如果您refresh()在多个位置调用,则需要确保在每个位置的事务中调用它。

于 2013-08-06T01:15:19.157 回答
0

尝试在重新定义之前使用合并。

public void refresh(T entity) {
        this.entityManager.refresh(this.entityManager.merge(entity));

  }
于 2018-06-20T09:00:38.133 回答