আমি কীভাবে কোনও জেপিএ ওয়ানটোন সম্পর্ককে অলস করতে পারি


212

এই অ্যাপ্লিকেশনটিতে আমরা বিকাশ করছি, আমরা লক্ষ্য করেছি যে একটি দৃশ্য বিশেষভাবে ধীর ছিল। আমি এই ভিউটির প্রোফাইল দিয়েছি এবং লক্ষ্য করেছি যে হাইবারনেট দ্বারা সম্পাদিত একটি কোয়েরি ছিল যা 10 সেকেন্ড সময় নিয়েছিল এমনকি ডাটাবেসে আনার জন্য কেবল দুটি বস্তু থাকলেও। সমস্ত OneToManyএবং ManyToManyসম্পর্ক অলস ছিল তাই সমস্যা ছিল না। প্রকৃত এসকিউএল কার্যকর করা হচ্ছে তা পরিদর্শন করার সময়, আমি লক্ষ্য করেছি যে ক্যোয়ারিতে 80 জনেরও বেশি যোগ দিয়েছে।

বিষয়টি আরও তদন্ত করে, আমি লক্ষ্য করেছি যে সমস্যাটি সত্তা শ্রেণির মধ্যে গভীর শ্রেণিবিন্যাস OneToOneএবং ManyToOneসম্পর্কের কারণে হয়েছিল। সুতরাং, আমি ভেবেছিলাম, আমি কেবল তাদের অলস করে তুলব, যা সমস্যার সমাধান করা উচিত। তবে হয় @OneToOne(fetch=FetchType.LAZY)বা এ্যানোটেট @ManyToOne(fetch=FetchType.LAZY)করা কাজ করে বলে মনে হচ্ছে না। হয় আমি একটি ব্যতিক্রম পাই বা তারপরে সেগুলি প্রকৃতপক্ষে কোনও প্রক্সি অবজেক্টের সাথে প্রতিস্থাপিত হয় না এবং এভাবে অলস হয়।

কোনও ধারণা কীভাবে আমি এটি কাজ করব? নোট করুন যে আমি persistence.xmlসম্পর্কগুলি বা কনফিগারেশন সম্পর্কিত বিশদগুলি সংজ্ঞায়িত করতে ব্যবহার করি না , সবকিছু জাভা কোডে সম্পন্ন হয়েছে।

উত্তর:


218

প্রথমে কেএলই এর জবাব সম্পর্কে কিছু স্পষ্টতা :

  1. আন-কন্ট্রাইন্ড (অবিচ্ছিন্ন) ওয়ান-টু-ওয়ান সমিতি কেবলমাত্র বাইটকোড উপকরণ ছাড়া প্রক্সি করা যায় না। এর কারণ হ'ল মালিক সত্তা জেনে রাখা উচিত যে অ্যাসোসিয়েশন সম্পত্তিটিতে প্রক্সি অবজেক্ট বা NULL থাকা উচিত কিনা এবং এটি নির্ধারণ করতে পারে না যে এটির বেস টেবিলের কলামগুলি দেখে একের পর এক ভাগ করে ভাগ করা পিকে মাধ্যমে ম্যাপ করা হয়, তাই এটি প্রক্সিটি অর্থহীন তৈরি করে অধীর আগ্রহে আনতে হবে। এখানে আরও বিস্তারিত ব্যাখ্যা দেওয়া হল।

  2. বহু-থেকে-এক সমিতি (এবং এক থেকে অনেকগুলি স্পষ্টতই) এই সমস্যাটিতে ভুগছেন না। মালিক সত্তা সহজেই তার নিজস্ব এফকে চেক করতে পারে (এবং একাধিকের ক্ষেত্রে খালি সংগ্রহের প্রক্সি শুরুতে তৈরি করা হয় এবং চাহিদা অনুসারে জনবহুল হয়), তাই সমিতি অলস হতে পারে।

  3. একের সাথে একের সাথে একের সাথে প্রতিস্থাপন করা খুব ভাল ধারণা কখনও নয়। আপনি এটি অনন্য বহু-একের সাথে প্রতিস্থাপন করতে পারেন তবে অন্যান্য (সম্ভবত আরও ভাল) বিকল্প রয়েছে।

রব এইচ , একটি বৈধ বিন্দু আছে তবে আপনি আপনার মডেল উপর নির্ভর করে এটি বাস্তবায়ন করতে সক্ষম নাও হতে পারে (উদাঃ আপনার একের সাথে এক সমিতি যদি হয় nullable)।

এখন, যতদূর আসল প্রশ্ন যায়:

ক) @ManyToOne(fetch=FetchType.LAZY)ঠিক কাজ করা উচিত। আপনি কি নিশ্চিত যে এটি নিজেই ক্যোয়ারিতে ওভাররাইট করা হচ্ছে না? join fetchএইচকিউএল এবং / অথবা নির্দিষ্টভাবে ক্রিটারিয়া এপিআইয়ের মাধ্যমে স্পষ্টভাবে সেট আনার মোড সেট করা সম্ভব যা বর্গ টীকাটির চেয়ে বেশি অগ্রাধিকার গ্রহণ করবে। যদি এটি না হয় এবং আপনার এখনও সমস্যা হয় তবে দয়া করে আপনার ক্লাসগুলি পোস্ট করুন, ক্যোয়ারী করুন এবং এসকিউএলকে আরও টু দ্য পয়েন্ট কথোপকথনের জন্য ফলাফল দিন।

খ) @OneToOneকৌশলযুক্ত। যদি এটি অবশ্যই নিঃসরণযোগ্য না হয় তবে রব এইচ এর পরামর্শ নিয়ে যান এবং এটিকে নির্দিষ্ট করুন:

@OneToOne(optional = false, fetch = FetchType.LAZY)

অন্যথায়, আপনি যদি আপনার ডাটাবেস পরিবর্তন করতে পারেন (মালিকের টেবিলে একটি বিদেশী কী কলামটি যুক্ত করুন), এটি করুন এবং এটিকে "যোগদান" হিসাবে মানচিত্র করুন:

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

এবং অন্যান্যরূপে:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

যদি আপনি এটি করতে না পারেন (এবং উত্সাহিত আনার সাথে বাঁচতে না পারেন) বাইটকোড উপকরণ আপনার একমাত্র বিকল্প। আমার অবশ্য সিপারকিন্সের সাথে একমত হতে হবে - আপনার যদি 80 থাকে !!! আগ্রহী ওয়ানটোয়ান সংঘের কারণে যোগ দেয়, আপনার তখন বড় সমস্যা হয়েছে :-)


হতে পারে অন্য কোনও বিকল্প রয়েছে, তবে আমি ব্যক্তিগতভাবে এটি পরীক্ষা করে দেখিনি: সীমাবদ্ধ দিকে, one-to-oneএকটি সূত্র সহ একটি ব্যবহার করুন select other_entity.id from other_entity where id = other_entity.id। অবশ্যই, ক্যোয়ারী পারফরম্যান্সের জন্য এটি আদর্শ নয়।
ফ্রেডেরিক

1
alচ্ছিক = মিথ্যা, আমার পক্ষে কাজ করে না। @ ওনটোইন (ফেচ = ফেচটাইপ.এলজিওয়াই, ম্যাপডবাই = "ফান্ডসরিজ", =চ্ছিক = মিথ্যা) ব্যক্তিগত ফান্ডসারিজডেটল এন্টিটি ফান্ডসারিটেল;
ওলেগ কুটস

21

Nullable একের সাথে এক ম্যাপিং আপনি হাইবারনেট করতে দেওয়া প্রয়োজন উপর অলস লোড কাজ করে তার জন্য কম্পাইল সময় যন্ত্রানুষঙ্গের এবং একটি যোগ @LazyToOne(value = LazyToOneOption.NO_PROXY)একের সাথে এক সম্পর্ক করতে।

উদাহরণ ম্যাপিং:

@OneToOne(fetch = FetchType.LAZY)  
@JoinColumn(name="other_entity_fk")
@LazyToOne(value = LazyToOneOption.NO_PROXY)
public OtherEntity getOther()

পিঁপড়া বিল্ড ফাইল এক্সটেনশনের উদাহরণ (হাইবারনেট সংকলনের সময় উপকরণ করার জন্য):

<property name="src" value="/your/src/directory"/><!-- path of the source files --> 
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> 
<property name="destination" value="/your/build/directory"/><!-- path of your build directory --> 

<fileset id="applibs" dir="${libs}"> 
  <include name="hibernate3.jar" /> 
  <!-- include any other libraries you'll need here --> 
</fileset> 

<target name="compile"> 
  <javac srcdir="${src}" destdir="${destination}" debug="yes"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </javac> 
</target> 

<target name="instrument" depends="compile"> 
  <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </taskdef> 

  <instrument verbose="true"> 
    <fileset dir="${destination}"> 
      <!-- substitute the package where you keep your domain objs --> 
      <include name="/com/mycompany/domainobjects/*.class"/> 
    </fileset> 
  </instrument> 
</target>

3
কেন LazyToOneOption.NO_PROXYএবং না LazyToOneOption.PROXY?
টেল্মো মার্কস

এটি "কেন" এর উত্তর দেয় না, তবে এই বাস্তবতা এখানেও
দৃserted

12

হাইবারনেটে এক্সটিওনকে বোঝার প্রাথমিক ধারণাটি হ'ল তারা বেশিরভাগ ক্ষেত্রে অলস নয়।

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

সম্পাদিত: বিশদের জন্য, দয়া করে ChssPly76 এর উত্তর দেখুন । এইটি কম সঠিক এবং বিশদ, এটির অফার করার মতো কিছুই নেই। ধন্যবাদ ChssPly76।


এখানে বেশ কয়েকটি জিনিস ভুল রয়েছে - আমি নীচে একটি ব্যাখ্যা দিয়ে আরও একটি জবাব সরবরাহ করেছি (খুব বেশি জিনিস, কোনও মন্তব্যে মাপসই হবে না)
ChssPly76

8

এখানে এমন কিছু যা আমার জন্য কাজ করে চলেছে (যন্ত্র ছাড়াই):

@OneToOneউভয় পক্ষের পরিবর্তে , আমি @OneToManyসম্পর্কের বিপরীত অংশে (যার সাথে এক mappedBy) ব্যবহার করি। এটি সম্পত্তিটিকে একটি সংগ্রহ করে তোলে ( Listনীচের উদাহরণে), তবে আমি এটিকে গ্রাহকদের কাছে স্বচ্ছ করে তুলে গেটারের কোনও আইটেমে অনুবাদ করি te

এই সেটআপটি অলসভাবে কাজ করে, অর্থাৎ বাছাই করা কেবল তখনই করা হয় getPrevious()বা getNext()বলা হয় - এবং প্রতিটি কলের জন্য কেবল একটিই নির্বাচন করে।

টেবিল কাঠামো:

CREATE TABLE `TB_ISSUE` (
    `ID`            INT(9) NOT NULL AUTO_INCREMENT,
    `NAME`          VARCHAR(255) NULL,
    `PREVIOUS`      DECIMAL(9,2) NULL
    CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
                 FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);

শ্রেণী:

@Entity
@Table(name = "TB_ISSUE") 
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column
    private String name;

    @OneToOne(fetch=FetchType.LAZY)  // one to one, as expected
    @JoinColumn(name="previous")
    private Issue previous;

    // use @OneToMany instead of @OneToOne to "fake" the lazy loading
    @OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
    // notice the type isnt Issue, but a collection (that will have 0 or 1 items)
    private List<Issue> next;

    public Integer getId() { return id; }
    public String getName() { return name; }

    public Issue getPrevious() { return previous; }
    // in the getter, transform the collection into an Issue for the clients
    public Issue getNext() { return next.isEmpty() ? null : next.get(0); }

}

7

আমি যেমন এই নিবন্ধে ব্যাখ্যা করেছি , আপনি বাইকোড এনহ্যান্সমেন্ট ব্যবহার না করা পর্যন্ত আপনি অলসভাবে পিতামাতার পক্ষের @OneToOneসংযোগ পেতে পারবেন না ।

তবে, প্রায়শই, আপনি যদি @MapsIdক্লায়েন্টের পক্ষ থেকে ব্যবহার করেন তবে আপনার পিতামাতৃপক্ষের সমিতি প্রয়োজনও নয় :

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Post post;

    public PostDetails() {}

    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }

    //Getters and setters omitted for brevity
}

এর সাথে @MapsId, idশিশু টেবিলে থাকা সম্পত্তিটি মূল কী এবং বৈদেশিক কী উভয় হিসাবে পিতামাতার টেবিলে প্রাথমিক কী হিসাবে কাজ করে।

সুতরাং, যদি আপনার পিতামত্তা Postসত্তার কোনও রেফারেন্স থাকে তবে আপনি প্যারেন্ট সত্তা সনাক্তকারী ব্যবহার করে সহজেই শিশু সত্তা আনতে পারেন:

PostDetails details = entityManager.find(
    PostDetails.class,
    post.getId()
);

এইভাবে, আপনার কাছে N + 1 ক্যোয়ারী সমস্যা নেই যা mappedBy @OneToOneপিতামাতার পক্ষের অ্যাসোসিয়েশনের কারণে হতে পারে ।


এইভাবে আমরা পিতামাতার থেকে সন্তানের কাছে আর ক্যাসকেড অপারেশন করতে পারি না: /
হামদী

অবিরাম থাকার জন্য, এটি মুছে ফেলার জন্য কেবল একটি অতিরিক্ত ক্রমাগত কল, আপনি ডিডিএল ক্যাসকেড ইউএসডি করতে পারেন।
ভ্লাদ মিহলসিয়া

6

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


5
একটি ভাল পরামর্শ জন্য +1; দুর্ভাগ্যক্রমে এটি সর্বদা প্রযোজ্য নয় কারণ ডোমেন মডেলটির জন্য প্রকৃতপক্ষে অযোগ্যতা প্রয়োজন হতে পারে। টীকা এর মাধ্যমে এই মানচিত্র সঠিক উপায়@OneToOne(optional=false,fetch=FetchMode.LAZY)
ChssPly76

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

3

ChssPly76 দ্বারা ইতিমধ্যে নিখুঁতভাবে ব্যাখ্যা করা হয়েছে, হাইবারনেটের প্রক্সিগুলি এক-এক-এক সংঘবদ্ধ বা সংঘবদ্ধ (ন্যূনতম) সাথে সহায়তা করে না, তবে উপকরণ স্থাপনের এড়াতে এখানে একটি কৌশল ব্যাখ্যা করা হয়েছে । হাইবারনেটকে বোকা বানাতে এই ধারণাটি তৈরি করা হয় যে আমরা যে সত্তা শ্রেণিটি ব্যবহার করতে চাইছি তা ইতিমধ্যে চালিত হয়েছে: উত্স কোডে আপনি নিজে এটি তৈরি করেছেন। এটি সহজ! আমি এটিকে সিটিএলিবের সাথে বাইটকোড সরবরাহকারী হিসাবে প্রয়োগ করেছি এবং এটি কাজ করে (আপনার এইচবিএম-তে আপনি অলস = "নো-প্রক্সি" কনফিগার করেছেন এবং = "নির্বাচন করুন", "যোগদান" নয়) এনেছেন তা নিশ্চিত করুন)।

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


1

এই প্রশ্নটি বেশ পুরানো, তবে হাইবারনেট 5.1.10 এর সাথে আরও কিছু নতুন আরও আরামদায়ক সমাধান রয়েছে।

অলস লোডিং একটি @ ওনোটোওন অ্যাসোসিয়েশনের প্যারেন্ট পার্স ব্যতীত কাজ করে। কারণ হায়ারনেট এই ভেরিয়েবলের নাল বা প্রক্সি নির্ধারণ করবেন কিনা তা জানার কোনও উপায় নেই। আরও বিশদ আপনি এই নিবন্ধে জানতে পারেন

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

0

যদি সম্পর্কটি দ্বিপক্ষীয় না হয় তবে একটি অলস ওয়ান 2 ম্যানুয়াল সংগ্রহ ব্যবহারের চেয়ে @ এলিমেন্ট সংগ্রহটি আরও সহজ হতে পারে।

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