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の実装で完全なカスタムメソッドを追加する方法を知りたいのですが?インターフェースなので、そこでメソッドを実装することはできません。
カスタムメソッド用に別のインターフェイスを作成する必要があります。
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() { ... }
}
以下も参照してください:
命名スキームはバージョン間で変更されていることに注意してください。詳細については、 https://stackoverflow.com/a/52624752/66686 を参照してください。
Axtavtの answer に加えて、クエリを作成する必要がある場合は、カスタム実装にEntity Managerを挿入できることを忘れないでください。
public class AccountRepositoryImpl implements AccountRepositoryCustom {
@PersistenceContext
private EntityManager em;
public void customMethod() {
...
em.createQuery(yourCriteria);
...
}
}
これは使用が制限されていますが、単純なカスタムメソッドの場合は、次のような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は自動的にメソッドを定義します。
受け入れられた答えは機能しますが、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
}
}
カスタム実装から生成された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);
}
}
コードスニペットを考慮すると、ネイティブオブジェクトのみをfindBy ###メソッドに渡すことができることに注意してください。特定の衣装に属するアカウントのリストをロードするとします。
@Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");
クエリされるテーブルの名前がEntityクラスと同じであることを確認します。さらなる実装については this をご覧ください
ここで考慮すべき別の問題があります。リポジトリにカスタムメソッドを追加すると、「/ 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
より洗練された操作を行えるようにしたい場合は、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);
}
}
}
ドキュメント化された機能 に明記されているように、Impl
プレフィックスを使用すると、非常にクリーンなソリューションが得られます。
@Repository
インターフェイスで定義します。たとえば、MyEntityRepository
、Spring Dataメソッドまたはカスタムメソッドのいずれかです。MyEntityRepositoryImpl
(Impl
サフィックスは魔法です)カスタムメソッドのみを実装する(同じパッケージにある必要はありません)および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つに制限されています。 (通常のフラグメントインターフェースの実装では、 ドキュメントが示唆しています 多数あります。)サイドノート:
Spring Dataリポジトリのカスタム実装を作成する場合:
フラグメントインターフェイスに対応するクラス名の最も重要な部分は、Impl postfixです。
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に追加します。