স্প্রিং ডেটা জেপিএতে কীভাবে কাস্টম পদ্ধতি যুক্ত করা যায়


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
এই কাস্টম বাস্তবায়ন প্রকৃত সংগ্রহশালা ইনজেকশন করতে পারে, সুতরাং এটি সেখানে সংজ্ঞায়িত পদ্ধতিগুলি ব্যবহার করতে পারে? বিশেষত, আমি রেপোজিটরি ইন্টারফেসে বর্ণিত বিভিন্ন ফাইন্ড * ফাংশনগুলিকে একটি উচ্চ স্তরের প্রয়োগের সন্ধানের জন্য উল্লেখ করতে চাই। যেহেতু এইগুলি খুঁজে পাওয়া * () ফাংশনগুলির বাস্তবায়ন নেই, তাই আমি কাস্টম ইন্টারফেস বা ইমপ্লায় ক্লাসে এগুলি ঘোষণা করতে পারি না।
JBCP

18
আমি এই উত্তরটি অনুসরণ করেছি, দুর্ভাগ্যক্রমে এখন স্প্রিং ডেটা আমার "অ্যাকাউন্ট" অবজেক্টে সম্পত্তি "কাস্টমমেথড" সন্ধান করার চেষ্টা করছে কারণ এটি অ্যাকাউন্টরেপোসিটরিতে সংজ্ঞায়িত সমস্ত পদ্ধতির জন্য স্বয়ংক্রিয়ভাবে একটি প্রশ্ন উত্পন্ন করার চেষ্টা করছে। এটি বন্ধ করার কোনও উপায়?
নিক ফুয়েট

41
@ নিকফুট নোট করুন যে আপনি আপনার ভাণ্ডারটি যে শ্রেণীর দ্বারা প্রয়োগ করেছেন তার নামটি হওয়া উচিত: AccountRepositoryImplনয়: AccountRepositoryCustomImplইত্যাদি it's এটি নামকরণের কঠোর নাম।
জিওন

5
@ ওয়্যারড00 আমি মনে করি এটি একটি বিজ্ঞপ্তি রেফারেন্স তৈরি করে এবং @ জেবিসিপি এটি কীভাবে কাজ করে তা আমি দেখতে পাচ্ছি না। যখন আমি চেষ্টা করি এবং অনুরূপ কিছু করি তখন আমি একটি ব্যতিক্রম শেষ করি: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

অ্যাক্সাভেটের উত্তর ছাড়াও , আপনার প্রশ্নের তৈরির প্রয়োজন হলে আপনি নিজের কাস্টম বাস্তবায়নে সত্তা পরিচালককে ইনজেক্ট করতে পারেন তা ভুলে যাবেন না:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

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

10
ধন্যবাদ, তবে আমি কীভাবে কাস্টম বাস্তবায়নে পেজেবল এবং পৃষ্ঠা ব্যবহার করতে পারি তা জানতে চাই। কোন ইনপুট?
ওয়ান্ড মেকার

17

গৃহীত উত্তরটি কাজ করে তবে তিনটি সমস্যা রয়েছে:

  • এটি কাস্টম প্রয়োগের নামকরণ করার সময় একটি অননুমোদিত স্প্রিং ডেটা বৈশিষ্ট্য ব্যবহার করে 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 মটরশুটি পছন্দ সম্পর্কে অভিযোগ করেছিল ।
ছাগল


উভয় পক্ষ থেকে কল্পেশনি সোনাদির পদ্ধতিগুলি AccountRepositoryBasicএবং AccountRepositoryCustomএকটি ইনজেকশনের মাধ্যমে পাওয়া যাবেAccountRepository
31-

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

সম্পাদনা করুন:

ইন এই বসন্ত টিউটোরিয়াল শাস্ত্রে লেখা আছে:

স্প্রিং ডেটা জেপিএ আপনাকে অন্য কোয়েরি পদ্ধতিগুলি কেবল তাদের পদ্ধতির স্বাক্ষর ঘোষণা করে সংজ্ঞায়িত করতে দেয় allows

সুতরাং কেবল যেমন পদ্ধতি ঘোষণা করা এমনকি সম্ভব:

Customer findByHobby(Hobby personHobby);

এবং যদি অবজেক্ট Hobbyগ্রাহকের সম্পত্তি হয় তবে স্প্রিং আপনার জন্য স্বয়ংক্রিয়ভাবে পদ্ধতিটি সংজ্ঞায়িত করবে।


6

আমি আমার কাস্টম বাস্তবায়ন থেকে উত্পাদিত পদ্ধতিগুলি অ্যাক্সেস করতে নিম্নলিখিত কোডটি ব্যবহার করছি। শিম কারখানার মাধ্যমে বাস্তবায়ন পাওয়া বিজ্ঞপ্তি শিম তৈরির সমস্যাগুলি প্রতিরোধ করে।

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শ্রেণির সীমা রয়েছে । (নিয়মিত খণ্ড ইন্টারফেসে বাস্তবায়নের ক্ষেত্রে ডক্সটি পরামর্শ দেয় যে আপনার অনেকগুলি থাকতে পারে))

পরীক্ষার সময় একটি ছোট ক্যাভেট রয়েছে। আপনার যদি এটির প্রয়োজন হয় তবে আমাকে জানান এবং আমি উত্তরটি আপডেট করব।
acdcjunior

কীভাবে সঠিকভাবে MyEntityRepositoryImpl অনুমোদন করবেন?
কনস্ট্যান্টিন জিউবিন

@KonstantinZyubin আপনি autowire 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

আপনার কোড স্নিপেট বিবেচনা করে, দয়া করে নোট করুন যে আপনি কেবল ফাইন্ডবাই ### পদ্ধতিতে নেটিভ অবজেক্টগুলি পাস করতে পারবেন, আপনাকে বলতে দিন যে আপনি অ্যাকাউন্টের একটি তালিকা লোড করতে চান যা নির্দিষ্ট ব্যয়বহুলের অন্তর্ভুক্ত, একটি সমাধান হ'ল এটি করা,

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

সারণীটির নাম জিজ্ঞাসাবাদ করতে মামলা করতে সত্তা শ্রেণি হিসাবে একই ame আরও বাস্তবায়নের জন্য দয়া করে কটাক্ষপাত করা এই


1
এটি ক্যোয়ারির একটি টাইপ, এটি নামফাই এল ডি হওয়া উচিত , আমার এটি ঠিক করার যথাযথ অধিকার নেই।
BrunoJCM

3

এখানে আরও একটি বিষয় বিবেচনা করা দরকার। কিছু লোক আশা করে যে আপনার ভাণ্ডারে কাস্টম পদ্ধতি যুক্ত করলে তা '/ অনুসন্ধান' লিঙ্কের আওতায় স্বয়ংক্রিয়ভাবে তাদেরকে REST পরিষেবা হিসাবে প্রকাশ করবে। দুর্ভাগ্যক্রমে এটি হয় না। বসন্ত বর্তমানে সমর্থন করে না।

এটি 'নকশার দ্বারা' বৈশিষ্ট্যযুক্ত, বসন্তের ডেটাটি পদ্ধতিটি যদি কোনও কাস্টম পদ্ধতিতে থাকে এবং এটি কোনও বিশ্রাম অনুসন্ধান লিঙ্ক হিসাবে প্রকাশ না করে তা স্পষ্টভাবে পরীক্ষা করে:

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

এটি অলিভার গিয়েরকের একটি কউউট:

এটি নকশা দ্বারা। কাস্টম সংগ্রহস্থল পদ্ধতিগুলি কোনও প্রশ্নের পদ্ধতি নয় কারণ তারা কার্যকরভাবে কোনও আচরণ কার্যকর করতে পারে। সুতরাং, বর্তমানে এইচটিটিপি পদ্ধতিটির অধীনে পদ্ধতিটি প্রকাশ করার পক্ষে সিদ্ধান্ত নেওয়া আমাদের পক্ষে অসম্ভব। পোষ্টটি সবচেয়ে নিরাপদ বিকল্প হতে পারে তবে এটি জেনেরিক ক্যোয়ারী পদ্ধতির সাথে মিল নেই (যা জিইটি গ্রহণ করে)।

আরও বিশদের জন্য এই সমস্যাটি দেখুন: https://jira.spring.io/browse/DATAREST-206


এটি দুর্ভাগ্যজনক, আমি অনুসন্ধান করার জন্য অনেক সময় নষ্ট করেছি, আমি কী ভুল করেছি এবং শেষ পর্যন্ত, আমি বুঝতে পারি যে এরকম কোনও বৈশিষ্ট্য নেই। কেন তারা এমনকি সেই কার্যকারিতা বাস্তবায়ন করবে? কম শিম আছে? সমস্ত জায়গায় দাও পদ্ধতি এক জায়গায় রাখতে? আমি অন্য উপায়ে তা অর্জন করতে পারতাম। "একক ভাণ্ডারে আচরণ যুক্ত করার" বৈশিষ্ট্যটির লক্ষ্য কী তা কি কেউ জানেন?
স্কিভ

আপনি কেবলমাত্র @RestResource(path = "myQueryMethod")পদ্ধতিতে টীকা যুক্ত করে আরইএসটি-র মাধ্যমে যে কোনও সংগ্রহস্থল পদ্ধতি প্রকাশ করতে পারেন । উপরের উদ্ধৃতিটি কেবল উল্লেখ করছে যে স্প্রিং আপনাকে কীভাবে ম্যাপিং করতে চায় তা জানে না (যেমন জিইটি বনাম পোস্ট ইত্যাদি) সুতরাং এটি টীকা দ্বারা নির্দিষ্ট করে দেওয়া আপনার পক্ষে।
গ্রিন জায়ান্ট

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

আমি সিম্পলজপাআরপোসিটোরি বাড়িয়েছি:

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 ভান্ডারবেসক্লাসে যুক্ত করুন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.