Spring Data JPA'ya özel yöntem nasıl eklenir


160

Spring Data JPA'yı inceliyorum. Tüm crud ve finder işlevlerini varsayılan olarak çalıştıracağım aşağıdaki örneği düşünün ve bir bulucuyu özelleştirmek istersem, arayüzün kendisinde de kolayca yapılabilir.

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

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

Yukarıdaki AccountRepository uygulaması ile nasıl tam bir özel yöntem ekleyebilirim bilmek istiyorum? Onun bir Arabirim beri orada yöntemi uygulayamıyorum.

Yanıtlar:


290

Özel yöntemleriniz için ayrı bir arayüz oluşturmanız gerekir:

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

public interface AccountRepositoryCustom {
    public void customMethod();
}

ve bu arayüz için bir uygulama sınıfı sağlayın:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

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

    public void customMethod() { ... }
}

Ayrıca bakınız:


21
Bu özel uygulama, gerçek depoyu enjekte edebilir, böylece orada tanımlanan yöntemleri kullanabilir mi? Özellikle, bir üst düzey bulma uygulamasında Havuz arabiriminde tanımlanan çeşitli find * işlevlerine başvurmak istiyorum. Bu find * () işlevlerinin bir uygulaması olmadığından, bunları Custom arabiriminde veya Impl sınıfında bildiremem.
JBCP

18
Ne yazık ki şimdi Spring Data otomatik olarak AccountRepository'de tanımlanan tüm yöntemler için bir sorgu oluşturmaya çalışıyor gibi benim "Hesap" nesnesi üzerinde "customMethod" özelliğini bulmaya çalışıyor. Bunu durdurmanın bir yolu var mý?
Nick Foote

41
@NickFoote, deponuzu uyguladığınız sınıfın adının: AccountRepositoryImplnot:, AccountRepositoryCustomImplvb. Olması gerektiğini unutmayın - çok katı bir adlandırma kuralı.
Xeon

5
@ wired00 Dairesel bir referans oluşturduğunu düşünüyorum ve @JBCP'nin nasıl çalıştığını göremiyorum. Benzer bir şey yapmaya çalıştığımda bir istisna 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.
Robert Hunt

6
Evet, eğer uzatıyorsanız işe yarayıp yaramadığına QueryDslRepositorySupportdair önceki yorumuma bakın Depoyu, yapıcı enjeksiyonu yerine alan veya ayarlayıcı enjeksiyonu yoluyla da enjekte etmeniz gerekir, aksi takdirde çekirdeği oluşturamaz. İşe yarıyor gibi görünüyor ama çözüm biraz 'kirli' hissediyor, bunun Bahar Veri ekibinden nasıl çalıştığını iyileştirmek için herhangi bir plan olup olmadığından emin değilim.
Robert Hunt

72

Axtavt en ilaveten cevap , size sorguları oluşturmak için gerekirse özel uygulanmasında Varlık Yöneticisi enjekte unutma:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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

10
Teşekkürler, ancak, özel uygulamada Parable ve Page nasıl kullanılacağını bilmek istiyorum. Herhangi bir girdi var mı?
Değnek Makinesi

17

Kabul edilen cevap işe yarıyor, ancak üç sorunu var:

  • Özel uygulamayı olarak adlandırırken belgelenmemiş bir Bahar Verisi özelliği kullanır AccountRepositoryImpl. Dokümantasyon açıkça çağrılacak bildiren AccountRepositoryCustomImpl, özel arayüz adı artıImpl
  • Sadece @Autowiredkötü uygulama olarak kabul edilen yapıcı enjeksiyonunu kullanamazsınız
  • Özel uygulamanın içinde dairesel bir bağımlılığınız var (bu yüzden yapıcı enjeksiyonunu kullanamazsınız).

Ben başka bir belgesiz Bahar Verisi özelliği kullanmadan olmasa da, mükemmel yapmak için bir yol buldum:

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

Bu işe yaradı. Oluşturucudaki parametre adının önemini vurgulamak istiyorum accountRepositoryBasic. Aksi takdirde bahar benim *Implyapıcı içine enjeksiyon için 2 fasulye seçeneklerinden şikayet etti .
keçi

Peki AccountRepository'nin kullanımı nedir
Soni

@KalpeshSoni her ikisinden de yöntemler AccountRepositoryBasicve AccountRepositoryCustombir enjekte yoluyla elde edilebilirAccountRepository
geg

1
Bağlamın oluşturulma şeklini verebilir misiniz? Hepsini bir araya getiremiyorum. Teşekkür ederim.
franta kocourek

12

Bu kullanımda sınırlıdır, ancak basit özel yöntemler için aşağıdaki gibi varsayılan arayüz yöntemlerini kullanabilirsiniz :

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

DÜZENLE:

Gelen bu bahar öğretici yazılıdır:

Bahar Verileri JPA, yalnızca yöntem imzasını bildirerek diğer sorgu yöntemlerini tanımlamanıza olanak tanır.

Bu nedenle, aşağıdaki gibi bir yöntem beyan etmek bile mümkündür:

Customer findByHobby(Hobby personHobby);

ve nesne HobbyMüşterinin mülkü ise, Spring sizin için yöntemi otomatik olarak tanımlayacaktır.


6

Im benim özel uygulama oluşturulan bulma yöntemlerine erişmek için aşağıdaki kodu kullanarak. Fasülye fabrikasından uygulama almak, dairesel fasülye yaratma problemlerini önler.

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

İçinde specificed gibi belgelenmiş işlevsellik , Impleki bize temiz bir çözüm olmasını sağlar:

  • @RepositoryArabirimde, örneğin MyEntityRepositoryBahar Verisi yöntemlerini veya özel yöntemleri tanımlayın
  • Bir sınıf oluşturun MyEntityRepositoryImpl( Implher yerde (hatta aynı paket içinde olması gerekmez) eki sihirli) olduğu uygular özel yöntemler sadece ve Annotatesekmesindeki ile böyle sınıf @Component** ( @Repository olmaz çalışması).
    • Bu sınıf , özel yöntemlerde kullanılmak üzere MyEntityRepositoryüzerinden de enjekte edilebilir @Autowired.


Misal:

Varlık sınıfı:

package myapp.domain.myentity;

@Entity
public class MyEntity {

    @Id
    private Long id;

    @Column
    private String comment;

}

Havuz arayüzü:

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

}

Özel yöntemler uygulama fasulye:

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

}

Tespit ettiğim küçük dezavantajlar:

  • ImplSınıftaki özel yöntemler derleyici tarafından kullanılmamış olarak işaretlenir, bu nedenle @SuppressWarnings("unused")öneri.
  • Bir Implsınıf sınırınız var . (Normal parça arayüzleri uygulamasında ise dokümanlar çok fazla seçeneğiniz olabileceğini gösterir.)

Test sırasında küçük bir uyarı var. İhtiyacınız olursa bana bildirin, yanıtı güncelleyeceğim.
1919'da

düzgün Autowire MyEntityRepositoryImpl nasıl?
Konstantin Zyubin

@KonstantinZyubin Autowire MyEntityRepositorydeğil, değil *Impl.
acdcjunior

4

Daha karmaşık işlemler yapabilmek istiyorsanız Spring Data'nın iç kısımlarına erişmeniz gerekebilir, bu durumda aşağıdakiler çalışır ( DATAJPA-422'ye geçici çözümüm olarak ):

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

Kod snippet'inizi göz önünde bulundurarak, yalnızca Yerel nesneleri findBy ### yöntemine aktarabileceğinizi, belirli müşterilere ait hesapların bir listesini yüklemek istediğinizi varsayalım, bunun bir çözümü,

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

Sorgulanacak tablonun adını Entity sınıfı olarak adlandırın. Ayrıca uygulamalar için bakmak lütfen bu


1
Sorguda bir yazım hatası, nameoffie l d olmalıdır , düzeltmek için doğru hakkım yok.
BrunoJCM

3

Burada dikkate alınması gereken bir konu daha var. Bazı kişiler, deponuza özel yöntem eklemenin, bunları '/ arama' bağlantısı altında otomatik olarak REST hizmetleri olarak göstermesini bekler. Maalesef durum böyle değil. Spring şu anda bunu desteklemiyor.

Bu, 'tasarım gereği' özelliğidir; yay verileri geri kalanı, yöntemin özel bir yöntem olup olmadığını ve REST arama bağlantısı olarak gösterip göstermediğini açıkça kontrol eder:

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

Bu bir Oliver Gierke qoute'u:

Bu tasarım gereğidir. Özel havuz yöntemleri, herhangi bir davranışı etkin bir şekilde uygulayabildikleri için sorgu yöntemi değildir. Bu nedenle, şu anda altındaki yöntemi ortaya çıkarmak için HTTP yöntemine karar vermemiz imkansızdır. POST en güvenli seçenek olurdu, ancak bu genel sorgu yöntemleriyle (GET alan) uyumlu değildir.

Daha fazla ayrıntı için bu konuya bakın: https://jira.spring.io/browse/DATAREST-206


Bu talihsiz bir durum, neyi yanlış yaptığımı öğrenmeye çalışmak için çok fazla zaman harcadım ve son olarak böyle bir özellik olmadığını anlıyorum. Neden bu işlevselliği uygulasınlar ki? Daha az fasulyeye sahip olmak? Tüm dao yöntemlerini tek bir yerde tutmak mı? Bunu başka şekillerde başarabilirdim. Herkes "tek depolara davranış ekleme" özelliğinin ne olduğunu biliyor mu?
Skeeve

Herhangi bir havuz yöntemini REST yoluyla @RestResource(path = "myQueryMethod")açıklamaya yalnızca yönteme ekleyerek ekleyebilirsiniz . Yukarıdaki alıntı sadece Spring'in nasıl eşleştirilmesini istediğinizi bilmediğini (yani GET vs POST vb.) Belirtmek içindir, bu yüzden ek açıklama ile belirtmek size kalmıştır.
GreenGiant

1

Tüm depolara özel davranış ekleme:

Tüm depolara özel davranış eklemek için, önce paylaşılan davranışı bildirmek üzere bir ara arabirim eklersiniz.

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

    void sharedCustomMethod( ID id );
}

Artık bireysel depo arabirimleriniz, bildirilen işlevselliği içerecek şekilde Depo arabirimi yerine bu ara arabirimi genişletecektir.

Ardından, ara teknolojinin kalıcılık teknolojisine özgü havuz taban sınıfını genişleten bir uygulama oluşturun. Bu sınıf daha sonra depo vekil sunucuları için özel bir temel sınıf görevi görür.

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

Kaynak Veri Depoları Bölüm I. Referans resim açıklamasını buraya girin


0

Ben SimpleJpaRepository genişletir:

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

ve bu sınıfı @EnableJpaRepositoryries repositoryBaseClass öğesine ekler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.