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


160

Spring Data JPAを調査しています。デフォルトですべてのクラッドおよびファインダー機能を動作させる以下の例を考えます。ファインダーをカスタマイズしたい場合は、インターフェース自体でも簡単に実行できます。

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

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

上記のAccountRepositoryの実装に完全なカスタムメソッドを追加するにはどうすればよいですか?インターフェースなので、そこでメソッドを実装することはできません。

回答:


290

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

public interface AccountRepository 
    extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }

public interface AccountRepositoryCustom {
    public void customMethod();
}

そのインターフェースの実装クラスを提供します:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @Autowired
    @Lazy
    AccountRepository accountRepository;  /* Optional - if you need it */

    public void customMethod() { ... }
}

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


21
このカスタム実装は実際のリポジトリを注入できるので、そこで定義されたメソッドを使用できますか?具体的には、上位レベルのfind実装で、Repositoryインターフェースで定義されているさまざまなfind *関数を参照したいと思います。これらのfind *()関数には実装がないため、カスタムインターフェイスまたはImplクラスで宣言することはできません。
JBCP 2014年

18
私はこの回答に従いましたが、残念ながら、Spring DataはAccountRepositoryで定義されたすべてのメソッドのクエリを自動的に生成しようとしているため、「Account」オブジェクトでプロパティ「customMethod」を見つけようとしています。これを止める方法はありますか?
Nick Foote

41
@NickFooteは、リポジトリを実装するクラスの名前は次のようにする必要があることに注意してください:AccountRepositoryImplnot:AccountRepositoryCustomImplなど。これは非常に厳密な命名規則です。
Xeon

5
@ wired00循環参照を作成すると思いますが、@ JBCPがどのように機能するのかわかりません。私が試してみて、似た何かをするとき、私は例外で終わる:Error creating bean with name 'accountRepositoryImpl': Bean with name 'accountRepositoryImpl' has been injected into other beans [accountRepository] in its raw version as part of a circular reference, but has eventually been wrapped.
ロバート・ハント

6
ええ、拡張QueryDslRepositorySupportする場合は機能しないという前のコメントを参照してください。コンストラクターインジェクションではなく、フィールドまたはセッターインジェクションを介してリポジトリをインジェクトする必要があります。そうしないと、Beanを作成できません。それは機能しているように見えますが、ソリューションは少し「汚い」と感じています。これがSpring Dataチームからどのように機能するかを改善する計画があるかどうかはわかりません。
ロバートハント

72

axtavtの回答に加えて、クエリの構築に必要な場合は、カスタム実装にEntity Managerを注入できることを忘れないでください。

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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

10
ただし、ありがとうございます。カスタム実装でPageableとPageを使用する方法を知りたいのですが。入力はありますか?
ワンドメーカー

17

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

  • カスタム実装にと名前を付ける場合、ドキュメントに記載されていないSpring Data機能を使用しAccountRepositoryImplます。ドキュメントは明確にそれが呼ばれるようにしていると述べ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
    }
}

これはうまくいきました。コンストラクターのパラメーターの名前の重要性を強調したいのですが、この回答の規則に従う必要があります(必ずでなければなりませんaccountRepositoryBasic)。それ以外の場合、Springは、私の*Implコンストラクターに注入するための2つのBeanの選択肢があることについて不満を述べました。
ヤギ

それで、AccountRepositoryの使用は何
ですか

双方から@KalpeshSoni方法AccountRepositoryBasic及びAccountRepositoryCustom注入を介して利用できるようになりますAccountRepository
GEG

1
コンテキストの作成方法を教えてください。すべてをまとめることはできません。ありがとうございました。
franta kocourek

12

これは使用法に制限がありますが、単純なカスタムメソッドの場合は、次のようなデフォルトのインターフェースメソッドを使用できます。

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

編集:

では、この春のチュートリアルそれが書かれています:

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

したがって、次のようなメソッドを宣言することも可能です。

Customer findByHobby(Hobby personHobby);

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


6

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

文書化された機能で指定されているように、Implサフィックスを使用すると、クリーンなソリューションを使用できます。

  • @Repositoryインターフェースで、たとえばMyEntityRepository、Spring Dataメソッドまたはカスタムメソッドを定義します。
  • カスタムメソッドのみを実装し、そのようなクラスに**(注釈が付けられない)の注釈を付けるクラスを作成しますMyEntityRepositoryImplImplサフィックスは魔法です)。 @Component@Repository
    • このクラスは、カスタムメソッドで使用するためにMyEntityRepositoryvia @Autowiredを注入することもできます。


例:

エンティティークラス:

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つに制限されています。(通常のフラグメントインターフェイスの実装では、ドキュメントには多くのインターフェイスが存在する可能性があることが示されています)。

テスト中の小さな注意点があります。必要な場合はお知らせください。回答を更新します。
acdcjunior

MyEntityRepositoryImplを適切にAutowireする方法
コンスタンティンジュビン

@KonstantinZyubinオートワイヤーMyEntityRepositoryではなく、オートワイヤー*Impl
acdcjunior

4

あなたはより洗練された操作を行うことができるようにしたい場合は、春データの内部にアクセスする必要があるかもしれません、その場合には(への私の暫定的な解決策として、次の作品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);
        }
    }

}

4

コードスニペットを考慮して、ネイティブオブジェクトはfindBy ###メソッドにしか渡せないことに注意してください。特定のコスチューマーに属するアカウントのリストをロードしたいとします。1つの解決策はこれを行うことです。

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

問い合わせるテーブルの名前はEntityクラスと同じです。さらなる実装については、これを見てください


1
クエリのタイプミスです、それはnameoffieあるべきリットルの dは、私はそれを修正するための適切な権利を持っていません。
BrunoJCM 2017

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


それは残念なことに、私が何を間違えたかを知るために非常に多くの時間を費やして無駄にしました、そして最後に、そのような特徴がないことを理解します。なぜ彼らはその機能を実装するのですか?豆を減らすには?すべてのdaoメソッドを1つの場所に置くには?他の方法でそれを達成できたはずです。「単一のリポジトリに動作を追加する」機能の目的は何か知っていますか?
16

メソッドに@RestResource(path = "myQueryMethod")アノテーションを追加するだけで、RESTを介してリポジトリメソッドを公開できます。上記の引用は、Springがマッピングする方法(GETとPOSTなど)を知らないことを示しているだけなので、アノテーションを使用して指定する必要があります。
GreenGiant

1

すべてのリポジトリにカスタム動作を追加する:

すべてのリポジトリにカスタム動作を追加するには、最初に中間インターフェースを追加して、共有動作を宣言します。

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

    void sharedCustomMethod( ID id );
}

これで、個々のリポジトリインターフェイスが、宣言された機能を含めるために、リポジトリインターフェイスではなくこの中間インターフェイスを拡張します。

次に、永続化テクノロジ固有のリポジ​​トリ基本クラスを拡張する中間インターフェースの実装を作成します。このクラスは、リポジトリプロキシのカスタム基本クラスとして機能します。

public class MyRepositoryImpl <T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID>
{

    private EntityManager entityManager;

       // There are two constructors to choose from, either can be used.
    public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager)
    {
        super( domainClass, entityManager );

        // This is the recommended method for accessing inherited class dependencies.
        this.entityManager = entityManager;
    }


    public void sharedCustomMethod( ID id )
    {
        // implementation goes here
    }
}

Spring Data RepositoriesパートI.リファレンス ここに画像の説明を入力してください


0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.