简体   繁体   中英

Spring Data - How to reuse repository in repository fragment?

Update : the following is using Spring Boot 2.1.0

I have a Spring Data repository and I am trying to provide some custom functionality to it, following the fragments example from the documentation .

So I've added an extra interface to the repository:

public interface UserRepository extends JpaRepository<User, Long>, UserExtraLogic {
    User findByFirstNameAndLastName(String firstName, String lastName);
}

with this custom interface:

interface UserExtraLogic {
    void ensureHasAccess();
}

and its implementation:

class UserExtraLogicImpl implements UserExtraLogic {
    public void ensureHasAccess() {
    }
}

The problem is that I would like to be able to use my repositories inside UserExtraLogicImpl , so that I can reuse query methods like findByFirstNameAndLastName without having to write them by myself with EntityManager . So I tried this:

class UserExrtaLogicImpl implements UserExtraLogic {
    @Autowired
    private UserRepository userRepository;
}

But then the application does not start. I get a NullPointerException, but I think it's just Spring getting into a cycle trying to resolve these dependencies.

Is what I'm trying to do possible? Is there another way to do this?

You can lazily load your repository with ObjectFactory<T> (Spring concept) or Provider<T> (standard java api).

class UserExrtaLogicImpl implements UserExtraLogic {
    @Autowired
    private ObjectFactory<UserRepository> userRepository;

    public void soSomething() {
         userRepository().getObject().findById(xxx);
    }
}

I have the exactly the same code patterns and encounter the same problem in a recent project and I finally solve it by using @Lazy to lazy initialise UserRepository :

class UserExrtaLogicImpl implements UserExtraLogic {

    @Lazy
    @Autowired
    private UserRepository userRepository;

}

Repositories enablement

From documentation

If you're using Spring XML configuration, you should have this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <jpa:repositories base-package="com.acme.repositories" />

</beans>

If you're using Java configuration instead, you should have this:

@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    // …
  }
}

Repositories configuration

Also, you need to add @Repository annotation on your repositories:

@Repository
public interface UserRepository extends JpaRepository<User, Long>, UserExtraLogic {
    User findByFirstNameAndLastName(String firstName, String lastName);
}

Explanation

From documentation

Using the repositories element looks up Spring Data repositories as described in “Creating Repository Instances” . Beyond that, it activates persistence exception translation for all beans annotated with @Repository , to let exceptions being thrown by the JPA persistence providers be converted into Spring's DataAccessException hierarchy.

Pay attention to this in documentation.

Extending the fragment interface with your repository interface combines the CRUD and custom functionality and makes it available to clients.

When you extend your fragment interface, your final repository will include that as well. That is the benift of it. If you want to access original repository logic I can suggest you 3 methods.

  1. Add your query in your fragment and extend it in your repository. Then in your service method include an aggregated method. In that method write your logic.
  2. In your custom interface, inlude your repository and do not extend your custom interface in repository.
  3. Use a decorator class. Delegate all the methods to repository and combine custom logic with repository.

Do not include circular dependencies as it is not a good practise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM