Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

spring-data

I have a web app that was built with Spring Data 1.6.1.RELEASE. It adds custom behavior too all repositories by following the example in the Spring Data' online documentation here:

1.3.2 Adding custom behavior to all repositories https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

Basically, it extends JpaRepository with custom behavior shown below:

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

  void sharedCustomMethod(ID id);
}

Here is the code for implementation of this new interface:

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
  }
}

Custom repository factory 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;
    }
  }
}

Here is the declaration of the factory bean:

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

Everything works well with Spring Data 1.6.1.RELEASE. However, I got the following exception when starting Jetty in Eclipse after I switched to 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)

What can I try to fix this?

UPDATE

I added the following constructor but still have the same problem:

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

}

like image 671
curious1 Avatar asked Aug 03 '20 02:08

curious1


People also ask

What is the difference between CrudRepository and JpaRepository?

CrudRepository mainly provides CRUD operations. PagingAndSortingRepository provide methods to perform pagination and sorting of records. JpaRepository provides JPA related methods such as flushing the persistence context and deleting of records in batch.

What does @repository do in Spring?

Spring @Repository annotation is used to indicate that the class provides the mechanism for storage, retrieval, search, update and delete operation on objects.

What is difference between JpaRepository and PagingAndSortingRepository?

PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.

How to add custom functionality to a spring data repository?

In order to add this functionality to our Spring Data Repository, we have to add an interface. It is mandatory for our interface to follow the naming convention of $ {Original Repository name}Custom. Therefore, the interface describing our custom functionality should be * Created by gkatzioura on 6/3/16. * Created by gkatzioura on 6/3/16.

How to add custom methods to all spring data JPA repositories?

When we want to add custom methods into all Spring Data JPA repositories, the first thing that we have to is to create a base interface which declares the custom methods. We can create the base repository interface by following these steps: The T type parameter is the type of the managed entity.

What is spring data repository abstraction?

The central interface in Spring Data repository abstraction is Repository (probably not that much of a surprise). It is typeable to the domain class to manage as well as the id type of the domain class. This interface mainly acts as marker interface to capture the types to deal with and help us when discovering interfaces that extend this one.

What is spring data?

Overview Spring Data makes the process of working with entities a lot easier by merely defining repository interfaces. These come with a set of pre-defined methods and allow the possibility of adding custom methods in each interface.


1 Answers

I just setup an application with 2.3.2.RELEASE and its look like it has been simplified.

  1. Delete MyRepositoryFactoryBean

  2. Replace <repositories base-package="com.acme.repository... with

    <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. Make sure MyRepository is outside the package base-package i.e com.acme.repository.

Note

  • @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) on a class annotated @Configuration is equivalent to point 2. As long as the package of that configuration class is com.acme.repository

  • @NoRepositoryBean annotation on MyRepository is equivalent to point 4

Sample Spring boot app with java config for reference

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

  • Import it as maven project to your IDE. Right click on AccessingDataJpaApplication and run, it will start, save some entities and call your custom method

like image 175
Kavithakaran Kanapathippillai Avatar answered Jun 24 '23 04:06

Kavithakaran Kanapathippillai