Spring Data 2.x: добавление поведения клиентов во все репозитории - PullRequest
6 голосов
/ 03 августа 2020

У меня есть веб-приложение, созданное с помощью 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. Однако при запуске Jetty в Eclipse после перехода на Spring Data 2.3.2.RELEASE у меня возникло следующее исключение:

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$4(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$5(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$0(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;

}

1 Ответ

5 голосов
/ 05 августа 2020

Я просто установил приложение с 2.3.2.RELEASE, и оно выглядит упрощенным.

  1. Удалить MyRepositoryFactoryBean

  2. Заменить <repositories base-package="com.acme.repository... с

    <repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />
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
       }
     }
Убедитесь, что MyRepository находится вне упаковки base-package т.е. com.acme.repository.

Примечание

  • @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) в аннотированном классе @Configuration эквивалентно пункту 2. Пока пакет этого класса конфигурации com.acme.repository

  • @NoRepositoryBean, аннотация MyRepository эквивалентна точка 4

Пример приложения загрузки Spring с конфигурацией java для справки

  • https://github.com/kavi-kanap/stackoveflow-63223076

  • Импортируйте его как проект maven в свою среду IDE. Щелкните правой кнопкой мыши AccessingDataJpaApplication и запустите, он запустится, сохранит некоторые объекты и вызовет ваш собственный метод

...