web-dev-qa-db-ja.com

Spring Data JPAにカスタムメソッドを追加する方法

Spring Data JPAを検討しています。デフォルトですべてのcrudおよびFinder機能を動作させる以下の例を検討してください。Finderをカスタマイズしたい場合は、インターフェイス自体でも簡単に行うことができます。

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

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

上記のAccountRepositoryの実装で完全なカスタムメソッドを追加する方法を知りたいのですが?インターフェースなので、そこでメソッドを実装することはできません。

133
Sharad Yadav

カスタムメソッド用に別のインターフェイスを作成する必要があります。

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() { ... }
}

以下も参照してください:

240
axtavt

Axtavtの answer に加えて、クエリを作成する必要がある場合は、カスタム実装にEntity Managerを挿入できることを忘れないでください。

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    public void customMethod() { 
        ...
        em.createQuery(yourCriteria);
        ...
    }
}
67
jelies

これは使用が制限されていますが、単純なカスタムメソッドの場合は、次のような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", "[email protected]", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
            new Customer("Adrian", "Mularczyk", "[email protected]", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
            new Customer("Kazimierz", "Dejna", "[email protected]", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
            new Customer("Celina", "Dykiel", "[email protected]", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
        };

        for (Customer customer : customers) {
            save(customer);
        }
    }
}

編集:

この春 チュートリアルには次のように書かれています:

Spring Data JPAでは、メソッドシグネチャを宣言するだけで、他のクエリメソッドを定義することもできます。

そのため、次のようなメソッドを宣言することさえ可能です。

Customer findByHobby(Hobby personHobby);

オブジェクトHobbyがCustomerのプロパティである場合、Springは自動的にメソッドを定義します。

10

受け入れられた答えは機能しますが、3つの問題があります。

  • カスタム実装に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
    }
}
7
Danila Piatov

カスタム実装から生成されたfindメソッドにアクセスするために、次のコードを使用しています。 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
Peter Rietzler

コードスニペットを考慮すると、ネイティブオブジェクトのみをfindBy ###メソッドに渡すことができることに注意してください。特定の衣装に属するアカウントのリストをロードするとします。

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

クエリされるテーブルの名前がEntityクラスと同じであることを確認します。さらなる実装については this をご覧ください

4
samba

ここで考慮すべき別の問題があります。リポジトリにカスタムメソッドを追加すると、「/ search」リンクの下にRESTサービスとして自動的に公開されると期待する人もいます。残念ながらそうではありません。現在、Springはそれをサポートしていません。

これは「設計による」機能です。スプリングデータレストは、メソッドがカスタムメソッドであるかどうかを明示的にチェックし、REST検索リンクとして公開しません。

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

これは、オリバー・ジールケの一部です。

これは仕様です。カスタムリポジトリメソッドは、任意の動作を効果的に実装できるため、クエリメソッドではありません。したがって、現在、HTTPメソッドを決定してメソッドを公開することは不可能です。 POSTは最も安全なオプションですが、これは一般的なクエリメソッド(GETを受け取る)と一致しません。

詳細については、この問題を参照してください: https://jira.spring.io/browse/DATAREST-206

3
Lukasz Magiera

より洗練された操作を行えるようにしたい場合は、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
NealeU

ドキュメント化された機能 に明記されているように、Implプレフィックスを使用すると、非常にクリーンなソリューションが得られます。

  • @Repositoryインターフェイスで定義します。たとえば、MyEntityRepository、Spring Dataメソッドまたはカスタムメソッドのいずれかです。
  • クラスを作成しますMyEntityRepositoryImplImplサフィックスは魔法です)カスタムメソッドのみを実装する(同じパッケージにある必要はありません)およびannotate@Component **のようなクラス(@Repositoryは機能しません) 。
    • このクラスは、カスタムメソッドで使用する@Autowiredを介してMyEntityRepositoryを注入することもできます。


例:

エンティティクラス:

package myapp.domain.myentity;

@Entity
public class MyEntity {

    @Id
    private Long id;

    @Column
    private String comment;

}

リポジトリインターフェイス:

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);

}

カスタムメソッド実装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;
    }

}

私が特定した小さな欠点は次のとおりです。

  • Implクラスのカスタムメソッドは、コンパイラによって未使用としてマークされているため、@SuppressWarnings("unused")の提案です。
  • Implクラスは1つに制限されています。 (通常のフラグメントインターフェースの実装では、 ドキュメントが示唆しています 多数あります。)
1
acdcjunior

サイドノート:

Spring Dataリポジトリのカスタム実装を作成する場合:

フラグメントインターフェイスに対応するクラス名の最も重要な部分は、Impl postfixです。

0
Serhat Oz

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に追加します。

0
Devilluminati