হাইবারনেট মানদণ্ড ফেচটাইপ.এগ্রেরের মাধ্যমে একাধিকবার বাচ্চাদের ফিরিয়ে দেয়


115

আমার একটি Orderক্লাস রয়েছে যার একটি তালিকা রয়েছে OrderTransactionsএবং আমি একে একে একাধিক হাইবারনেট ম্যাপিংয়ের সাথে ম্যাপ করেছি:

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

এইগুলির Orderএকটি ক্ষেত্রও রয়েছে orderStatus, যা নিম্নলিখিত মানদণ্ডের সাথে ফিল্টারিংয়ের জন্য ব্যবহৃত হয়:

public List<Order> getOrderForProduct(OrderFilter orderFilter) {
    Criteria criteria = getHibernateSession()
            .createCriteria(Order.class)
            .add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
    return criteria.list();
}

এটি কাজ করে এবং ফলাফল প্রত্যাশিত হিসাবে।

এখন এখানে আমার প্রশ্ন : কেন, যখন আমি আনার ধরণটি স্পষ্টভাবে সেট করি EAGER, Orderফলাফলগুলি তালিকায় একাধিক বার উপস্থিত হয় কেন?

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

নতুন সেটিংয়ের সাথে একই ফলাফলটিতে পৌঁছানোর জন্য আমার মানদণ্ডের কোডটি কীভাবে পরিবর্তন করতে হবে?


1
নীচে কী চলছে তা দেখার জন্য আপনি কি show_sql সক্ষম করার চেষ্টা করেছেন?
মিরকো এন।

অর্ডার ট্রান্সজেকশন এবং অর্ডার ক্লাসের কোডটিও যুক্ত করুন \
ইরান মেডান

উত্তর:


115

আমি যদি আপনার কনফিগারেশনটি সঠিকভাবে বুঝতে পারি তবে এটি আসলে প্রত্যাশিত আচরণ।

আপনি যে Orderকোনও ফলাফলের ক্ষেত্রে একই উদাহরণ পাবেন তবে এখন থেকে যেহেতু আপনি OrderTransactionএকটিতে যোগ দিচ্ছেন তাই এটি নিয়মিত স্কোএল যোগদানের একই পরিমাণে ফিরে আসতে হবে

সুতরাং আসলে এটি একাধিকবার প্রস্তুত করা উচিত । এটি লেখক (গ্যাভিন কিং) নিজেই এখানে খুব ভালভাবে ব্যাখ্যা করেছেন : এটি কেন এবং কীভাবে এখনও আলাদা ফলাফল পেতে পারে তা উভয়ই ব্যাখ্যা করে


এছাড়াও হাইবারনেট উল্লেখ প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী :

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

সাধারণ উদাহরণগুলি যা একই অর্ডার অবজেক্টের সদৃশ রেফারেন্সগুলি ফিরিয়ে দিতে পারে:

List result = session.createCriteria(Order.class)
                    .setFetchMode("lineItems", FetchMode.JOIN)
                    .list();

<class name="Order">
    ...
    <set name="lineItems" fetch="join">

List result = session.createCriteria(Order.class)
                       .list();
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();

এই সমস্ত উদাহরণ একই এসকিউএল বিবৃতি উত্পাদন করে:

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID

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

(কেন একটি বাম বাহির যোগদান করুন? যদি আপনার কোনও লাইন আইটেম ছাড়া অতিরিক্ত অর্ডার থাকে তবে ফলাফল সেটটি 16 টি সারি হবে যেখানে ডান পাশটি NULL পূরণ করা হবে, যেখানে লাইন আইটেমের ডেটা অন্য ক্রমের জন্য রয়েছে You আপনি আদেশ চান এমনকি যদি তাদের লাইন আইটেম নেই, তাই না? যদি না হয়, আপনার এইচকিউএল এর অভ্যন্তরীণ যোগদানের আনয়ন ব্যবহার করুন)।

হাইবারনেট এই সদৃশ উল্লেখগুলি ডিফল্টরূপে ফিল্টার করে না। কিছু লোক (আপনি না) আসলে এটি চান। আপনি কীভাবে এগুলি ফিল্টার করতে পারেন?

এটার মত:

Collection result = new LinkedHashSet( session.create*(...).list() );

121
আপনি নীচের ব্যাখ্যাটি বুঝতে না পারলেও, আপনি হাইবারনেট ফোরামে এই আচরণ সম্পর্কে ভালভাবে অভিযোগ করতে পারেন, কারণ এটি বোকা আচরণগুলি উল্টিয়ে দিচ্ছে!
টম অ্যান্ডারসন 12

17
বেশ ডান টম, আইডি গ্যাভিন কিংসকে অহঙ্কারী মনোভাবটি ভুলে গিয়েছিল। তিনি আরও বলেছিলেন 'হাইবারনেট এই সদৃশ উল্লেখগুলি ডিফল্টরূপে ফিল্টার করে না। কিছু লোক (আপনি না) আসলে এই 'আইডিটি আগ্রহী হন যখন লোকেরা আসলে এটি পিঁপড়ে দেয়।
পল টেলর

16
@ টমএন্ডারসন হ্যাঁ ঠিক কারও কেন এই নকলের প্রয়োজন হবে? আমি নির্ভেজাল কৌতূহল নিয়ে জিজ্ঞাসা করছি, যেহেতু আমার কোনও ধারণা নেই ... আপনি নিজের মতো করে নকল তৈরি করতে পারেন .. ;-)
পরোবে

13
দীর্ঘশ্বাস. এটি আসলে হাইবারনেট ত্রুটি, আইএমএইচও। আমি আমার প্রশ্নগুলি অনুকূল করতে চাই, তাই আমি আমার ম্যাপিং ফাইলটিতে "যোগ" করতে "নির্বাচন করুন" থেকে যাই। হঠাৎ আমার কোড BREAKS পুরো জায়গা জুড়ে। তারপরে আমি প্রায় দৌড়াতে এবং ফলাফল ট্রান্সফর্মারগুলি এবং হোয়াট নোট যোগ করে আমার সমস্ত ডিএওগুলি ঠিক করেছি। ব্যবহারকারীর অভিজ্ঞতা == খুব নেতিবাচক। আমি বুঝতে পারি যে কিছু লোক উদ্ভট কারণে ডুপ্লিকেট থাকা একেবারে পছন্দ করে, তবে কেন আমি বলতে পারি না "এই বস্তুগুলিকে দ্রুততর আনুন তবে আমাকে" ডুপ্লিকেট দিয়ে বাগ করবেন না "ফেচ =" জাস্ট ওয়ার্ল্ড প্লিজ "উল্লেখ করে?
রোমান জেনকা

@ ইরান: আমি একই ধরণের সমস্যার মুখোমুখি হয়েছি। আমি সদৃশ পিতামাত্ত বস্তুগুলি পাচ্ছি না, তবে প্রতিক্রিয়াতে পিতামাতার সংখ্যা সংখ্যা হওয়ায় আমি প্রতিটি প্যারেন্ট অবজেক্টে বহুবার পুনরাবৃত্তি করছি। কোন ধারণা কেন এই সমস্যা?
মন্ত্রি

93

ইরান যা উল্লেখ করেছেন তা ছাড়াও, আপনি যে আচরণ চান তা পাওয়ার আরেকটি উপায়, ফলাফল ট্রান্সফর্মার সেট করা:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

8
এটি বেশিরভাগ ক্ষেত্রেই কাজ করবে .... আপনি যখন 2 সংগ্রহ / সংস্থানগুলি আনার জন্য ক্রেটারিয়া ব্যবহার করার চেষ্টা করবেন তখন ব্যতীত
জেমসডি

42

চেষ্টা

@Fetch (FetchMode.SELECT) 

উদাহরণ স্বরূপ

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;

}


11
ফ্যাচমোড.জেট হাইবারনেট দ্বারা চালিত এসকিউএল ক্যোয়ারীর সংখ্যা বাড়িয়ে দেয় তবে রুট সত্তার রেকর্ডে প্রতি একটি উদাহরণ নিশ্চিত করে। হাইবারনেট এই ক্ষেত্রে প্রতিটি শিশুর রেকর্ডের জন্য একটি নির্বাচন বরখাস্ত করবে। পারফরম্যান্স বিবেচনার ক্ষেত্রে আপনার এটির জন্য অ্যাকাউন্ট করা উচিত।
বিপুল

1
@ বিপুলকুমার হ্যাঁ, তবে এই বিকল্পটি আমরা যখন অলস আনয়ন করতে পারি না কারণ সাব অবজেক্টগুলিতে অ্যাক্সেসের জন্য অলস আনার জন্য আমাদের একটি অধিবেশন বজায় রাখা দরকার।
ম্যাথি

18

তালিকা এবং অ্যারেলিস্ট ব্যবহার না করে সেট এবং হ্যাশসেট ব্যবহার করুন।

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

2
এটি কি হাইবারনেট সেরা অনুশীলনের ঘটনাগত উল্লেখ বা ওপি থেকে বহু-শিশু পুনরুদ্ধার প্রশ্নের সাথে সম্পর্কিত?
জ্যাকব জুইয়ার্স


বুঝেছি. ওপির প্রশ্নে মাধ্যমিক। যদিও, জোন নিবন্ধটি সম্ভবত নুনের দানা সহ নেওয়া উচিত ... মন্তব্যে লেখকের নিজস্ব ভর্তির উপর ভিত্তি করে।
জ্যাকব জুইয়ার্স

2
এটি একটি খুব ভাল উত্তর আইএমও। আপনি যদি ডুপ্লিকেটগুলি না চান, তবে এটি সম্ভবত সম্ভাব্য যে আপনি একটি তালিকা-এর চেয়ে একটি সেট ব্যবহার করুন a একটি সেট ব্যবহার করে (এবং সঠিক সমান / হ্যাশকোড পদ্ধতিগুলি অবশ্যই প্রয়োগ করা) আমার সমস্যার সমাধান করেছে। রেডহাট ডক হিসাবে বর্ণিত হ্যাশকোড / সমতুল্য প্রয়োগ করার সময় আইডি ক্ষেত্রটি ব্যবহার না করার বিষয়ে সাবধান হন।
মাদুর

1
আপনার আইএমও জন্য ধন্যবাদ। তদুপরি, সমান () এবং হ্যাশকোড () পদ্ধতি তৈরি করতে সমস্যায় পড়বেন না। আপনার আইডিই বা লম্বোক আপনার জন্য এগুলি তৈরি করতে দিন।
Αλέκος

3

জাভা 8 এবং স্ট্রিম ব্যবহার করে আমি আমার ইউটিলিটি পদ্ধতিতে এই রিটার্নের স্টেটমেন্ট যুক্ত করেছি:

return results.stream().distinct().collect(Collectors.toList());

স্ট্রিমগুলি খুব দ্রুত নকলকে সরিয়ে দেয়। আমি আমার সত্তা শ্রেণিতে এটিকে টোটালভাবে ব্যবহার করি:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;

আমি মনে করি যে পদ্ধতিতে সেশনটি ব্যবহার করতে আমার অ্যাপ্লিকেশনটি আরও ভাল is ক্লোজস সেশন যখন করলাম। অবাস্তব ফাঁসির সংগ্রহের ধরণটি ব্যবহার করতে আমার সত্তা শ্রেণি সেট করুন। আমি রিফ্যাক্টরে যাই


3

2 সম্পর্কিত সংগ্রহ আনতে আমার একই সমস্যা: ব্যবহারকারীর 2 টি ভূমিকা (সেট) এবং 2 টি খাবার (তালিকা) থাকে এবং খাবারটি নকল হয়।

@Table(name = "users")
public class User extends AbstractNamedEntity {

   @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
   @Column(name = "role")
   @ElementCollection(fetch = FetchType.EAGER)
   @BatchSize(size = 200)
   private Set<Role> roles;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
   @OrderBy("dateTime DESC")
   protected List<Meal> meals;
   ...
}

DISTINCT সহায়তা করে না (ডেটা-জেপিএ কোয়েরি):

@EntityGraph(attributePaths={"meals", "roles"})
@QueryHints({@QueryHint(name= org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")}) // remove unnecessary distinct from select
@Query("SELECT DISTINCT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

শেষ পর্যন্ত আমি 2 টি সমাধান পেয়েছি:

  1. লিঙ্কডহ্যাশসেটে তালিকা পরিবর্তন করুন
  2. কেবল ক্ষেত্র "খাবার" সহ অ্যান্টিগ্রাফ ব্যবহার করুন এবং LOAD টাইপ করুন, যা তারা ঘোষণার সাথে সাথে ভূমিকাগুলি লোড করে (এন + 1 সমস্যা রোধ করতে ইগ্রার এবং ব্যাচসাইজ = 200 দ্বারা):

চূড়ান্ত সমাধান:

@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

1

হ্যাক ব্যবহারের পরিবর্তে:

  • Set পরিবর্তে List
  • criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

যা আপনার বর্গ কোয়েরিটি পরিবর্তন করে না, আমরা ব্যবহার করতে পারি (জেপিএ স্পেসের উদ্ধৃতি)

q.select(emp).distinct(true);

যা ফলস্বরূপ এসকিএল কোয়েরিটি সংশোধন করে, এর DISTINCTমধ্যে এটি একটি থাকে।


0

এটি বাহ্যিক সংযুক্তি প্রয়োগ করে এবং সদৃশ ফলাফল আনতে দুর্দান্ত আচরণ বলে মনে হচ্ছে না। স্ট্রিম ব্যবহার করে আমাদের ফলাফল ফিল্টার করা একমাত্র সমাধান। ধন্যবাদ java8 ফিল্টার করার সহজ উপায় প্রদান।

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