194

SpringDataJPAを調べています。以下の例を考えてみましょう。ここでは、すべてのクラッドとファインダーの機能がデフォルトで機能します。ファインダーをカスタマイズしたい場合は、インターフェース自体でも簡単に実行できます。

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

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

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

4

14 に答える 14

327

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

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

参照:

于 2012-08-09T10:18:05.200 に答える
76

axtavtの回答に加えて、クエリを作成するためにエンティティマネージャーが必要な場合は、カスタム実装にエンティティマネージャーを挿入できることを忘れないでください。

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    public void customMethod() { 
        ...
        em.createQuery(yourCriteria);
        ...
    }
}
于 2013-03-11T14:51:40.543 に答える
27

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

  • カスタム実装に。という名前を付けるときに、文書化されていないSpringData機能を使用しAccountRepositoryImplます。ドキュメントAccountRepositoryCustomImplには、カスタムインターフェイス名に加えて呼び出す必要があることが明確に記載されていますImpl
  • コンストラクタインジェクションは使用できません@Autowired。これは、悪い習慣と見なされます。
  • カスタム実装内に循環依存関係があります(そのため、コンストラクターインジェクションを使用できません)。

文書化されていない別のSpringData機能を使用せずにではありませんが、それを完璧にする方法を見つけました。

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
    }
}
于 2018-07-30T22:38:03.370 に答える
24

追加のインターフェースを必要としないわずかに変更されたソリューションがあります。

文書化された機能で指定されているように、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);   // custom method, code at *Impl class below

    List<MyEntity> useTheRepo(Long id);  // custom method, code at *Impl class below

}

カスタムメソッド実装Bean:

package myapp.infrastructure.myentity;

@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the exact 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;
    }

}

使用法:

// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
@Service
public class SomeService {
    @Autowired
    private MyEntityRepository myEntityRepository;

    public void someMethod(String x, long y) {
        // call any method as usual
        myEntityRepository.findByCommentEndsWith(x);
        myEntityRepository.doSomeHql(y);
    }
}

これですべてです。既にお持ちのSpringDataリポジトリ以外のインターフェイスは必要ありません。


私が特定した唯一の考えられる欠点は次のとおりです。

  • クラス内のカスタムメソッドはImpl、コンパイラによって未使用としてマークされているため、@SuppressWarnings("unused")提案されます。
  • Implクラスは1つに制限されています。(通常のフラグメントインターフェイスの実装では、ドキュメントは多くの可能性があることを示唆しています。)
  • Implクラスを別のパッケージに配置し、テストで使用するのがのみ@DataJpaTestの場合は、テストに追加@ComponentScan("package.of.the.impl.clazz")する必要があるため、Springがクラスをロードします。
于 2019-04-06T00:56:22.720 に答える
16

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

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は自動的にメソッドを定義します。

于 2016-05-20T18:56:26.533 に答える
6

カスタム実装から生成された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);
    }
}
于 2015-04-24T05:13:47.080 に答える
5

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

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

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

于 2015-05-17T14:55:30.563 に答える
4

より高度な操作を実行できるようにする場合は、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);
        }
    }

}
于 2013-10-31T15:16:29.073 に答える
3

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

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

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

これはオリバー・ギールケの言葉です。

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

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

于 2015-07-21T08:35:03.790 に答える
2

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

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

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データリポジトリパートI.リファレンス

ここに画像の説明を入力してください

于 2020-06-19T17:39:49.577 に答える
2

私はDanilaのソリューションが好きで、それを使い始めましたが、チームの他の誰も、リポジトリごとに4つのクラスを作成する必要があることを好みませんでした。Danilaのソリューションは、ImplクラスでSpringDataメソッドを使用できる唯一のソリューションです。しかし、私はたった1つのクラスでそれを行う方法を見つけました。

public interface UserRepository extends MongoAccess, PagingAndSortingRepository<User> {

    List<User> getByUsername(String username);


    default List<User> getByUsernameCustom(String username) {
        // Can call Spring Data methods!
        findAll();

        // Can write your own!
        MongoOperations operations = getMongoOperations();
        return operations.find(new Query(Criteria.where("username").is(username)), User.class);
    }
}

db Bean(この例では、MongoOperations)にアクセスするための何らかの方法が必要です。MongoAccessは、Beanを直接取得することにより、すべてのリポジトリへのアクセスを提供します。

public interface MongoAccess {
    default MongoOperations getMongoOperations() {
        return BeanAccessor.getSingleton(MongoOperations.class);
    }
}

BeanAccessorの場所:

@Component
public class BeanAccessor implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static <T> T getSingleton(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }

    public static <T> T getSingleton(String beanName, Class<T> clazz){
        return applicationContext.getBean(beanName, clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanAccessor.applicationContext = applicationContext;
    }

}

残念ながら、インターフェイスで@Autowireを使用することはできません。BeanをMongoAccessImplに自動配線し、それにアクセスするためのメソッドをインターフェースに提供することもできますが、SpringDataは爆発します。ImplがPagingAndSortingRepositoryに間接的に関連付けられていることを期待しているとは思いません。

于 2020-12-09T02:07:16.290 に答える
1

私はmongoとspringを使用してこれに直面しました。したがって、MongoRepositoryを使用して基本クラッド操作を提供し、mongoTemplateを使用してカスタム基準クエリ操作を実装する必要があると仮定します。Crudとカスタムのリポジトリを挿入するための1つのインターフェイスを実現するには、次のように指定する必要があります。

カスタムインターフェイス:

public interface UserCustomRepository {
 List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest);
}

UserRepositoryインターフェースは、最初にUserCustomRepositoryを拡張し、次にMongoRepositoryを拡張する必要があります。

@Repository
public interface UserRepository extends UserCustomRepository, MongoRepository<User, ObjectId> {
}

UserRepositoryImplは、*Implサフィックスが付いたcrudインターフェースと同じ名前である必要があります。

@Component
@NoArgsConstructor
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class UserRepositoryImpl implements UserCustomRepository {

 private MongoTemplate mongoTemplate;

 @Override
 public List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest){
  //some impl
 }
}

いくつかのサービスを実装しましょう-ここでは、UserRepositoryインターフェースのみを注入し、crudリポジトリとカスタムクラスimplからのメソッドを使用します。

@Service
@NoArgsConstructor
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class UserService {

 private UserRepository userReposityry;

 public List<User> getUserByCriteria(UserCriteriaRequest request) {
   userRepository.findById(request.getUserId); // Crud repository method
   userRepository.findAllUsersBySomeCriteria(request); // custom method.
 }
}
于 2021-05-07T09:49:29.763 に答える
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;
    }
}

このクラスを@EnableJpaRepositoryriesrepositoryBaseClassに追加します。

于 2018-11-12T23:39:40.413 に答える
0

SimpleJpaRepositoryをリポジトリ実装の基本クラスとして使用し、インターフェイスにカスタムメソッドを追加します。例:

public interface UserRepository  {
    User FindOrInsert(int userId);
}

@Repository
public class UserRepositoryImpl extends SimpleJpaRepository implements UserRepository {

    private RedisClient redisClient;

    public UserRepositoryImpl(RedisClient redisClient, EntityManager em) {
        super(User.class, em);
        this.redisClient = redisClient;
    }


@Override
public User FindOrInsert(int userId) {

    User u = redisClient.getOrSet("test key.. User.class, () -> {
        Optional<User> ou = this.findById(Integer.valueOf(userId));
        return ou.get();
    });
    …………
    return u;
}
于 2020-09-08T16:53:43.523 に答える