स्प्रिंग डेटा JPA में कस्टम विधि कैसे जोड़ें


160

मैं स्प्रिंग डेटा जेपीए में देख रहा हूं। नीचे दिए गए उदाहरण पर विचार करें जहां मुझे डिफ़ॉल्ट रूप से काम करने वाले सभी क्रूड और खोजक की कार्यक्षमता मिलेगी और अगर मैं एक खोजक को अनुकूलित करना चाहता हूं तो वह भी इंटरफ़ेस में आसानी से किया जा सकता है।

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

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

मैं जानना चाहूंगा कि मैं उपरोक्त खाताप्रमाण पत्र के कार्यान्वयन के साथ एक पूर्ण कस्टम विधि कैसे जोड़ सकता हूं? इसके इंटरफ़ेस के बाद से मैं वहां विधि लागू नहीं कर सकता।

जवाबों:


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
क्या यह कस्टम कार्यान्वयन वास्तविक रिपॉजिटरी को इंजेक्ट कर सकता है, इसलिए यह वहां परिभाषित तरीकों का उपयोग कर सकता है? विशेष रूप से, मैं उच्च स्तरीय खोज कार्यान्वयन में रिपॉजिटरी इंटरफ़ेस में परिभाषित विभिन्न फ़ंड * फ़ंक्शन को संदर्भित करना चाहूंगा। चूँकि उन लोगों को * () कार्यों का कार्यान्वयन नहीं मिलता है, इसलिए मैं उन्हें कस्टम इंटरफ़ेस या इम्प्ले क्लास में घोषित नहीं कर सकता।
जेबीसीपी

18
मैंने इस उत्तर का अनुसरण किया है, दुर्भाग्य से अब स्प्रिंग डेटा मेरे "खाता" ऑब्जेक्ट पर संपत्ति "customMethod" खोजने की कोशिश कर रहा है क्योंकि यह स्वचालित रूप से AccountRepository पर परिभाषित सभी विधियों के लिए एक क्वेरी उत्पन्न करने की कोशिश कर रहा है। इसे रोकने का कोई तरीका?
निक फुटे

41
@NickFoote ध्यान दें कि जिस कक्षा को आप अपने भंडार को लागू करते हैं उसका नाम होना चाहिए: AccountRepositoryImplनहीं: 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रिपॉजिटरी को कंस्ट्रक्टर इंजेक्शन के बजाय फ़ील्ड या सेटर इंजेक्शन के माध्यम से भी इंजेक्ट करना होगा अन्यथा यह बीन बनाने में सक्षम नहीं होगा। यह काम करने लगता है, लेकिन समाधान थोड़ा 'गंदा' लगता है, मुझे यकीन नहीं है कि यह कैसे सुधारने की कोई योजना है, यह स्प्रिंग कंपनी टीम से कैसे काम करता है।
रॉबर्ट हंट

72

Axtavt के जवाब के अलावा , आप अपने कस्टम कार्यान्वयन में Entity Manager को इंजेक्ट नहीं कर सकते हैं यदि आपको अपने प्रश्नों को बनाने की आवश्यकता है:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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

10
धन्यवाद, हालांकि, मैं जानना चाहता हूं कि कस्टम कार्यान्वयन में पेजेबल और पेज का उपयोग कैसे करें। कोई इनपुट?
वैंड मेकर

17

स्वीकृत उत्तर काम करता है, लेकिन इसमें तीन समस्याएं हैं:

  • कस्टम कार्यान्वयन के रूप में नामकरण करते समय यह एक undocumented स्प्रिंग डेटा सुविधा का उपयोग करता है AccountRepositoryImplप्रलेखन स्पष्ट रूप से कहा गया है कि यह कहा जा चुका है AccountRepositoryCustomImpl, कस्टम इंटरफ़ेस नाम के साथ साथImpl
  • आप कंस्ट्रक्टर इंजेक्शन का उपयोग नहीं कर सकते हैं, केवल @Autowired, जिसे बुरा अभ्यास माना जाता है
  • आपके पास कस्टम कार्यान्वयन के अंदर एक परिपत्र निर्भरता है (यही कारण है कि आप कंस्ट्रक्टर इंजेक्शन का उपयोग नहीं कर सकते हैं)।

मुझे इसे पूर्ण बनाने का एक तरीका मिल गया, हालांकि बिना किसी अन्य अवांछित स्प्रिंग डेटा सुविधा का उपयोग किए बिना:

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)। अन्यथा वसंत ने शिकायत की कि मेरे *Implनिर्माता में इंजेक्शन के लिए 2 बीन पसंद हैं ।
बकरी

तो क्या खाताप्रमाण पत्र का उपयोग होता है
कल्पेश सोनी

@KalpeshSoni दोनों से तरीके AccountRepositoryBasicऔर AccountRepositoryCustomएक इंजेक्शन के माध्यम से उपलब्ध हो जाएगाAccountRepository
geg

1
क्या आप कृपया संदर्भ प्रदान करने का तरीका प्रदान कर सकते हैं? मैं यह सब एक साथ नहीं कर पा रहा हूं। धन्यवाद।
फ्रांता कोकरेक

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

संपादित करें:

में इस वसंत ट्यूटोरियल लिखा है:

स्प्रिंग डेटा JPA आपको केवल उनके विधि हस्ताक्षर की घोषणा करके अन्य क्वेरी विधियों को परिभाषित करने की अनुमति देता है।

तो यह केवल विधि घोषित करने के लिए संभव है जैसे:

Customer findByHobby(Hobby personHobby);

और यदि ऑब्जेक्ट Hobbyग्राहक की संपत्ति है तो स्प्रिंग स्वचालित रूप से आपके लिए विधि को परिभाषित करेगा।


6

मेरे कस्टम कार्यान्वयन से उत्पन्न तरीकों को खोजने के लिए Im निम्नलिखित कोड का उपयोग कर रहा है। बीन कारखाने के माध्यम से कार्यान्वयन प्राप्त करना परिपत्र बीन निर्माण समस्याओं को रोकता है।

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, या तो स्प्रिंग डेटा तरीके या कस्टम तरीके
  • एक वर्ग बनाएं MyEntityRepositoryImpl( Implप्रत्यय जादू है) कहीं भी (एक ही पैकेज में होने की आवश्यकता नहीं है) जो केवल कस्टम विधियों को लागू करता है और ** ( काम नहीं करेगा ) के साथ ऐसी कक्षा को एनोटेट करता है । @Component@Repository
    • यह वर्ग भी इंजेक्षन कर सकते हैं MyEntityRepositoryके माध्यम से @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);

}

कस्टम तरीके कार्यान्वयन बीन:

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वर्ग की सीमा है । (जबकि नियमित रूप से टुकड़े के इंटरफेस को लागू करने में डॉक्स का सुझाव है कि आपके पास कई हो सकते हैं।)

परीक्षण के दौरान एक छोटा सा चेतावनी है। यदि आपको इसकी आवश्यकता है, तो मुझे बताएं और मैं उत्तर को अपडेट करूंगा।
एकड्यूजन जूनियर

ठीक से कैसे करें Autowire MyEntityRepositoryImpl?
कॉन्सटेंटिन ज़ायुबिन

@KonstantinZyubin आप ऑटोवेयर करते हैं MyEntityRepository, नहीं *Impl
एकड्यूजनियोर

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

अपने कोड स्निपेट को ध्यान में रखते हुए, कृपया ध्यान दें कि आप केवल मूल वस्तुओं को खोजे ### विधि से पास कर सकते हैं, जिससे आप कह सकते हैं कि आप उन खातों की एक सूची लोड करना चाहते हैं जो कुछ निश्चित लागतों से संबंधित हैं, एक समाधान यह करना है,

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

मुकदमा करने के लिए तालिका के नाम पर मुकदमा करें जिसे इकाई वर्ग कहा जाता है। आगे के कार्यान्वयन के लिए कृपया इस पर एक नज़र डालें


1
यह क्वेरी पर एक टाइपो है, यह nameoffie l d होना चाहिए , मुझे इसे ठीक करने का उचित अधिकार नहीं है।
ब्रूनो जेसीएम

3

यहां एक और मुद्दे पर विचार किया जाना है। कुछ लोग उम्मीद करते हैं कि आपके रिपॉजिटरी में कस्टम विधि जोड़ने से वे स्वतः ही REST सेवाओं के रूप में '/ खोज' लिंक के तहत उजागर हो जाएंगे। यह दुर्भाग्य से मामला नहीं है। वसंत वर्तमान में समर्थन नहीं करता है।

यह 'डिज़ाइन द्वारा' सुविधा है, यदि कोई कस्टम विधि है, तो स्प्रिंग डेटा बाकी स्पष्ट रूप से जाँच करता है और इसे REST खोज लिंक के रूप में उजागर नहीं करता है:

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

यह ओलिवर गिएके का एक qoute है:

यह डिजाइन द्वारा है। कस्टम रिपॉजिटरी विधि कोई क्वेरी विधि नहीं है क्योंकि वे किसी भी व्यवहार को प्रभावी ढंग से लागू कर सकते हैं। इस प्रकार, वर्तमान में HTTP विधि के बारे में निर्णय लेना असंभव है। POST सबसे सुरक्षित विकल्प होगा लेकिन यह सामान्य क्वेरी विधियों (जो GET प्राप्त करता है) के अनुरूप नहीं है।

अधिक जानकारी के लिए इस मुद्दे को देखें: https://jira.spring.io/browse/DATAREST-206


यह दुर्भाग्यपूर्ण है, मैंने यह जानने में बहुत समय बर्बाद किया है कि मैंने क्या गलत किया है और आखिरकार, मैं समझता हूं कि ऐसी कोई सुविधा नहीं है। वे उस कार्यक्षमता को क्यों लागू करेंगे? कम फलियाँ होने के लिए? एक ही स्थान पर सभी डाओ तरीके हैं? मैं इसे अन्य तरीकों से हासिल कर सकता था। क्या किसी को पता है कि "एकल रिपॉजिटरी में व्यवहार को जोड़ना" सुविधा का लक्ष्य क्या है?
स्किव

आप केवल @RestResource(path = "myQueryMethod")विधि में एनोटेशन जोड़कर REST के माध्यम से किसी भी रिपॉजिटरी विधियों को उजागर कर सकते हैं । ऊपर दिए गए उद्धरण में कहा गया है कि वसंत यह नहीं जानता है कि आप इसे कैसे मैप करना चाहते हैं (यानी जीएसटी बनाम पोस्ट इत्यादि) तो इसे एनोटेशन के माध्यम से निर्दिष्ट करना आपके ऊपर है।
ग्रीनजाइंट

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

स्प्रिंग डेटा रिपॉजिटरी भाग 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 में जोड़ता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.