How to add custom method to Spring Data JPA

后端 未结 12 1904
盖世英雄少女心
盖世英雄少女心 2020-11-22 12:58

I am looking into Spring Data JPA. Consider the below example where I will get all the crud and finder functionality working by default and if I want to customize a finder t

相关标签:
12条回答
  • 2020-11-22 13:26

    If you want to be able to do more sophisticated operations you might need access to Spring Data's internals, in which case the following works (as my interim solution to DATAJPA-422):

    public class AccountRepositoryImpl implements AccountRepositoryCustom {
    
        @PersistenceContext
        private EntityManager entityManager;
    
        private JpaEntityInformation<Account, ?> entityInformation;
    
        @PostConstruct
        public void postConstruct() {
            this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);
        }
    
        @Override
        @Transactional
        public Account saveWithReferenceToOrganisation(Account entity, long referralId) {
            entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
            return save(entity);
        }
    
        private Account save(Account entity) {
            // save in same way as SimpleJpaRepository
            if (entityInformation.isNew(entity)) {
                entityManager.persist(entity);
                return entity;
            } else {
                return entityManager.merge(entity);
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 13:27

    You need to create a separate interface for your custom methods:

    public interface AccountRepository 
        extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }
    
    public interface AccountRepositoryCustom {
        public void customMethod();
    }
    

    and provide an implementation class for that interface:

    public class AccountRepositoryImpl implements AccountRepositoryCustom {
    
        @Autowired
        @Lazy
        AccountRepository accountRepository;  /* Optional - if you need it */
    
        public void customMethod() { ... }
    }
    

    See also:

    • 4.6 Custom Implementations for Spring Data Repositories

    • Note that the naming scheme has changed between versions. See https://stackoverflow.com/a/52624752/66686 for details.

    0 讨论(0)
  • 2020-11-22 13:27

    The accepted answer works, but has three problems:

    • It uses an undocumented Spring Data feature when naming the custom implementation as AccountRepositoryImpl. The documentation clearly states that it has to be called AccountRepositoryCustomImpl, the custom interface name plus Impl
    • You cannot use constructor injection, only @Autowired, that are considered bad practice
    • You have a circular dependency inside of the custom implementation (that's why you cannot use constructor injection).

    I found a way to make it perfect, though not without using another undocumented Spring Data feature:

    public interface AccountRepository extends AccountRepositoryBasic,
                                               AccountRepositoryCustom 
    { 
    }
    
    public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
    {
        // standard Spring Data methods, like findByLogin
    }
    
    public interface AccountRepositoryCustom 
    {
        public void customMethod();
    }
    
    public class AccountRepositoryCustomImpl implements AccountRepositoryCustom 
    {
        private final AccountRepositoryBasic accountRepositoryBasic;
    
        // constructor-based injection
        public AccountRepositoryCustomImpl(
            AccountRepositoryBasic accountRepositoryBasic)
        {
            this.accountRepositoryBasic = accountRepositoryBasic;
        }
    
        public void customMethod() 
        {
            // we can call all basic Spring Data methods using
            // accountRepositoryBasic
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:31

    Considering your code snippet, please note that you can only pass Native objects to the findBy### method, lets say you want to load a list of accounts that belongs certain costumers, one solution is to do this,

    @Query("Select a from Account a where a."#nameoffield"=?1")
    List<Account> findByCustomer(String "#nameoffield");
    

    Make sue the name of the table to be queried is thesame as the Entity class. For further implementations please take a look at this

    0 讨论(0)
  • 2020-11-22 13:34

    Im using the following code in order to access generated find methods from my custom implementation. Getting the implementation through the bean factory prevents circular bean creation problems.

    public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {
    
        private BrandRepository myRepository;
    
        public MyBean findOne(int first, int second) {
            return myRepository.findOne(new Id(first, second));
        }
    
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            myRepository = beanFactory.getBean(MyRepository.class);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:37

    As specificed in the documented functionality, the Impl suffix allows us to have a clean solution:

    • Define in the @Repository interface, say MyEntityRepository, either Spring Data methods or custom methods
    • Create a class MyEntityRepositoryImpl (the Impl suffix is the magic) anywhere (doesn't even need to be in the same package) that implements the custom methods only and annotate such class with @Component** (@Repository will not work).
      • This class can even inject MyEntityRepository via @Autowired for use in the custom methods.


    Example:

    Entity class:

    package myapp.domain.myentity;
    
    @Entity
    public class MyEntity {
    
        @Id
        private Long id;
    
        @Column
        private String comment;
    
    }
    

    Repository interface:

    package myapp.domain.myentity;
    
    @Repository
    public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
    
        // EXAMPLE SPRING DATA METHOD
        List<MyEntity> findByCommentEndsWith(String x);
    
        List<MyEntity> doSomeHql(Long id);
    
        List<MyEntity> useTheRepo(Long id);
    
    }
    

    Custom methods implementation bean:

    package myapp.infrastructure.myentity;
    
    @Component // Must be @Component !!
    public class MyEntityRepositoryImpl { // must have the repo name + Impl !!
    
        @PersistenceContext
        private EntityManager entityManager;
    
        @Autowired
        private MyEntityRepository myEntityRepository;
    
        @SuppressWarnings("unused")
        public List<MyEntity> doSomeHql(Long id) {
            String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
            TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
            query.setParameter("id", id);
            return query.getResultList();
        }
    
        @SuppressWarnings("unused")
        public List<MyEntity> useTheRepo(Long id) {
            List<MyEntity> es = doSomeHql(id);
            es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
            es.add(myEntityRepository.findById(2L).get());
            return es;
        }
    
    }
    

    The small drawbacks I identified are:

    • The custom methods in the Impl class are marked as unused by the compiler, thus the @SuppressWarnings("unused") suggestion.
    • You have a limit of one Impl class. (Whereas in the regular fragment interfaces implementation the docs suggest you could have many.)
    0 讨论(0)
提交回复
热议问题