首页 文章

如何向Spring Data JPA添加自定义方法

提问于
浏览
120

我正在研究Spring Data JPA . 考虑下面的示例,我将默认使用所有crud和finder功能,如果我想自定义查找器,那么也可以在界面本身轻松完成 .

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {

  @Query("<JPQ statement here>")
  List<Account> findByCustomer(Customer customer);
}

我想知道如何为上述AccountRepository添加一个完整的自定义方法及其实现?由于它的接口我无法在那里实现该方法 .

9 回答

  • 64

    您需要为自定义方法创建单独的界面:

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

    并为该接口提供实现类:

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

    See also:

  • 3

    考虑到您的代码段,请注意您只能将Native对象传递给findBy ###方法,假设您要加载属于某些客户的帐户列表,一个解决方案就是这样做,

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

    make sue要查询的表的名称与Entity类相同 . 如需进一步实施,请查看this

  • 3

    我扩展了SimpleJpaRepository:

    public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
        implements ExtendedRepository<T> {
    
        private final JpaEntityInformation<T, ?> entityInformation;
    
        private final EntityManager em;
    
        public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
                                                          final EntityManager entityManager) {
           super(entityInformation, entityManager);
           this.entityInformation = entityInformation;
           this.em = entityManager;
        }
    }
    

    并将此类添加到@EnableJpaRepositoryries repositoryBaseClass .

  • 4

    我使用以下代码从我的自定义实现访问生成的查找方法 . 通过bean工厂实现实现可以防止循环bean创建问题 .

    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);
        }
    }
    
  • 5

    这在使用上受到限制,但对于简单的自定义方法,您可以使用 default 接口方法,例如:

    import demo.database.Customer;
    import org.springframework.data.repository.CrudRepository;
    
    public interface CustomerService extends CrudRepository<Customer, Long> {
    
    
        default void addSomeCustomers() {
            Customer[] customers = {
                new Customer("Józef", "Nowak", "nowakJ@o2.pl", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
                new Customer("Adrian", "Mularczyk", "adii333@wp.pl", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
                new Customer("Kazimierz", "Dejna", "sobieski22@weebly.com", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
                new Customer("Celina", "Dykiel", "celina.dykiel39@yahoo.org", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
            };
    
            for (Customer customer : customers) {
                save(customer);
            }
        }
    }
    

    编辑:

    this spring教程中写道:

    Spring Data JPA还允许您通过简单地声明其方法签名来定义其他查询方法 .

    所以甚至可以只声明方法:

    Customer findByHobby(Hobby personHobby);
    

    如果object Hobby 是Customer的属性,那么Spring将自动为您定义方法 .

  • 10

    如果您希望能够执行更复杂的操作,则可能需要访问Spring Data的内部,在这种情况下,以下工作(作为我对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);
            }
        }
    
    }
    
  • 3

    这里还有另一个问题需要考虑 . 有些人希望在您的存储库中添加自定义方法会在“/ search”链接下自动将它们公开为REST服务 . 遗憾的是,情况并非如此 . Spring目前不支持 .

    这是'按设计'功能,spring data rest显式检查方法是否为自定义方法,并且不将其公开为REST搜索链接:

    private boolean isQueryMethodCandidate(Method method) {    
      return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
    }
    

    这是Oliver Gierke的qoute:

    这是设计的 . 定制存储库方法不是查询方法,因为它们可以有效地实现任何行为 . 因此,我们目前无法决定公开该方法的HTTP方法 . POST将是最安全的选项,但这与通用查询方法(接收GET)不一致 .

    有关详细信息,请参阅此问题:https://jira.spring.io/browse/DATAREST-206

  • 0

    接受的答案有效,但有三个问题:

    • 在将自定义实现命名为 AccountRepositoryImpl 时,它使用未记录的Spring Data功能 . documentation明确指出它必须被称为 AccountRepositoryCustomImpl ,自定义接口名称加上 Impl

    • 你不能使用构造函数注入,只有 @Autowired ,这被认为是不好的做法

    • 在自定义实现中有一个循环依赖(这就是你不能使用构造函数注入的原因) .

    我发现了一种使其完美的方法,尽管不是没有使用另一个未记录的Spring Data功能:

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

    除了axtavt的answer之外,不要忘记如果需要它来构建查询,可以在自定义实现中注入Entity Manager:

    public class AccountRepositoryImpl implements AccountRepositoryCustom {
    
        @PersistenceContext
        private EntityManager em;
    
        public void customMethod() { 
            ...
            em.createQuery(yourCriteria);
            ...
        }
    }
    

相关问题