Spring 数据 2.x:将客户行为添加到所有存储库

Spring Data 2.x: adding customer behavior to all repositories

我有一个使用 Spring Data 1.6.1.RELEASE 构建的网络应用程序。它按照 Spring Data 在线文档中的示例在此处为所有存储库添加自定义行为:

1.3.2 向所有存储库添加自定义行为 https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

基本上,它使用如下所示的自定义行为扩展 JpaRepository:

public interface MyRepository<T, ID extends Serializable> 
  extends JpaRepository<T, ID> {

  void sharedCustomMethod(ID id);
}

下面是这个新接口的实现代码:

public class MyRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {

  private EntityManager entityManager;

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

    this.entityManager = entityManager;
  }

  public void sharedCustomMethod(ID id) {
    // implementation goes here
  }
}

自定义存储库工厂 bean:

public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
  extends JpaRepositoryFactoryBean<R, T, I> {

  protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

    return new MyRepositoryFactory(entityManager);
  }

  private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

    private EntityManager entityManager;

    public MyRepositoryFactory(EntityManager entityManager) {
      super(entityManager);

      this.entityManager = entityManager;
    }

    protected Object getTargetRepository(RepositoryMetadata metadata) {

      return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager);
    }

    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {

      // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory
      //to check for QueryDslJpaRepository's which is out of scope.
      return MyRepository.class;
    }
  }
}

这里是工厂 bean 的声明:

<repositories base-package="com.acme.repository"
  factory-class="com.acme.MyRepositoryFactoryBean" />

一切都适用于 Spring Data 1.6.1.RELEASE。但是,在切换到 Spring Data 2.3.2.RELEASE:

后,在 Eclipse 中启动 Jetty 时出现以下异常
java.lang.IllegalStateException: No suitable constructor found on interface com.acme.repository.MyRepository to match the given arguments: [class org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation, class com.sun.proxy.$Proxy52]. Make sure you implement a constructor taking these
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.lambda$getTargetRepositoryViaReflection(RepositoryFactorySupport.java:522)
    at java.util.Optional.orElseThrow(Optional.java:290)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepositoryViaReflection(RepositoryFactorySupport.java:522)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getTargetRepositoryViaReflection(RepositoryFactorySupport.java:506)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:180)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:162)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:72)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:309)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet(RepositoryFactoryBeanSupport.java:297)
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:212)
    at org.springframework.data.util.Lazy.get(Lazy.java:94)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:144)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=16=](AbstractBeanFactory.java:324)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:878)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:401)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:292)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
    at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:843)
    at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:533)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:816)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:345)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1406)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1368)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:778)
    at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:262)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:522)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
    at org.eclipse.jetty.server.Server.start(Server.java:427)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:105)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:394)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9Adapter.start(Jetty9Adapter.java:68)
    at net.sourceforge.eclipsejetty.starter.common.AbstractJettyLauncherMain.launch(AbstractJettyLauncherMain.java:84)
    at net.sourceforge.eclipsejetty.starter.jetty9.Jetty9LauncherMain.main(Jetty9LauncherMain.java:42)

我该如何解决这个问题?

更新

我添加了以下构造函数,但仍然有同样的问题:

public MyRepositoryImpl(
        JpaMetamodelEntityInformation<T, ID> entityInformation,
        EntityManager entityManager) {
   super(entityInformation, entityManager);
   this.em = entityManager;

}

我刚刚使用 2.3.2.RELEASE 设置了一个应用程序,它看起来已经被简化了。

  1. 删除MyRepositoryFactoryBean

  2. <repositories base-package="com.acme.repository...替换为

    <repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />
  1. MyRepositoryImpl
    public class MyRepositoryImpl<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {

       private EntityManager entityManager;

       MyRepositoryImpl(JpaEntityInformation entityInformation,
                     EntityManager entityManager) {
        super(entityInformation, entityManager);

        // Keep the EntityManager around to used from the newly introduced 

        this.entityManager = entityManager;
       }

       public void sharedCustomMethod(ID id) {
        // implementation goes here
       }
     }
  1. 确保 MyRepository 在包裹外 base-packagecom.acme.repository.

备注

  • @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)上一个class注解@Configuration相当于第2点,只要那个配置class的包是com.acme.repository

  • @NoRepositoryBean MyRepository 上的注释等同于点 4

示例 Spring 启动应用程序 java 配置供参考