2014-06-10 4 views
7

हाइबरनेट के लिए नया।कई लोगों को हाइबरनेट करें - fetch विधि उत्सुक बनाम आलसी

मेरे पास कई समूह के लिए उपयोगकर्ता समूह है। तीन टेबल: उपयोगकर्ता, समूह और उपयोगकर्ता समूह मैपिंग तालिका।

संस्थाओं:

@Entity 
@Table(name = "user") 
public class User { 

@Id 
@Column (name = "username") 
private String userName; 

@Column (name = "password", nullable = false) 
private String password; 


@ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER) 
@JoinTable(name="usergroup", 
      joinColumns={@JoinColumn(name="username")}, 
      inverseJoinColumns={@JoinColumn(name="groupname")}) 
private Set<Group> userGroups = new HashSet<Group>(); 

... setter and getters 



@Entity 
@Table(name = "group") 
public class Group { 

@Id 
@Column(name = "groupname") 
private String groupName; 

@Column(name = "admin", nullable = false) 
private String admin; 

@ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER) 
private Set<User> users = new HashSet<User>(); 

... setter and getters 

सूचना है कि समूह इकाई में मैं विधि उत्सुक लाने उपयोग कर रहा हूँ। अब, जब मैं अपने डीएओ फोन कर रहा हूँ निम्नलिखित मानदंड का उपयोग कर प्रणाली में सभी समूहों पुनर्प्राप्त करने के लिए:

Criteria criteria = session.createCriteria(Group.class); 
    return criteria.list(); 

मैं mappgin तालिका (usergroup) से सभी पंक्तियों को हो रही है के बजाय हो रही वास्तविक समूहों की संख्या ...

उदाहरण के लिए अगर मैं समूह तालिका में उपयोगकर्ता तालिका

username password 
----------------- 
user1  user1 
user2  user2 

में है

groupname admin 
--------------- 
grp1  user1 
grp2  user2 
usergroup तालिका में

username groupname 
------------------ 
user1  grp1 
user2  grp2 
user1  grp2 
user2  grp1 

परिणाम निम्न सूची होगा - {grp1, grp2, grp2, grp1}

बजाय

{grp1, grp2}

अगर मैं बदल समूह इकाई में लाज़ी के लिए fetch विधि मुझे सही परिणाम मिल रहे हैं लेकिन हाइबरनेट मुझे किसी अन्य स्थान पर LazyException फेंकता है ...

कृपया सहायता करें कि f ईटीएच विधि का उपयोग करना चाहिए और क्यों?

धन्यवाद!

उत्तर

14

आलसी लोग आपको हमेशा FetchType.EAGER काउंटर-इंट्यूटिवली का उपयोग करने के लिए कहेंगे। ये वे लोग हैं जो आम तौर पर डेटाबेस प्रदर्शन के बारे में चिंता नहीं करते हैं और केवल अपने विकास को आसान बनाने के बारे में परवाह करते हैं। मैं कहने जा रहा हूं कि बढ़ते प्रदर्शन लाभ के लिए आपको FetchType.LAZY का उपयोग करना चाहिए। चूंकि डेटाबेस एक्सेस आमतौर पर अधिकांश अनुप्रयोगों की प्रदर्शन बाधा है, हर छोटी मदद करता है।

यदि आपको वास्तव में एक समूह के लिए उपयोगकर्ताओं की एक सूची प्राप्त करने की आवश्यकता है, तब तक जब तक आप एक लेनदेन सत्र के भीतर से getUsers() पर कॉल करें, तो आपको LazyLoadingException नहीं मिलेगा जो कि सभी नए हाइबरनेट उपयोगकर्ताओं का केंद्र है।

:

निम्नलिखित कोड उन समूहों

//Service Class 
@Transactional 
public List<Group> findAll(){ 
    return groupDao.findAll(); 
} 

निम्नलिखित कोड डीएओ स्तर पर आप उन समूहों के लिए उपयोगकर्ताओं के साथ सभी समूहों मिल जाएगा के लिए उपयोगकर्ताओं की सूची को आबाद करने के बिना आप सभी समूहों मिल जाएगा

//DAO class 
@SuppressWarnings("unchecked") 
public List<Group> findAllWithUsers(){ 
    Criteria criteria = getCurrentSession().createCriteria(Group.class); 

    criteria.setFetchMode("users", FetchMode.SUBSELECT); 
    //Other restrictions here as required. 

    return criteria.list(); 
} 

संपादित करें 1: इस कोड

के लिए एड्रियन शुम के लिए धन्यवाद FetchMode 'के विभिन्न प्रकार के बारे में अधिक जानकारी के लिए रों देख here

तुम सिर्फ अपने संग्रह वस्तु का उपयोग करने के लिए एक अलग डीएओ विधि लिखने के लिए नहीं करना चाहते हैं, जब तक आप कर रहे हैं के रूप में ही Session कि था में पैरेंट ऑब्जेक्ट लाने के लिए उपयोग किया जाता है, आप Hibernate.initialize() विधि का उपयोग अपने बच्चे संग्रह ऑब्जेक्ट के प्रारंभिकरण को बल देने के लिए कर सकते हैं। मैं गंभीरता से अनुशंसा नहीं करता कि आप इसे मूल वस्तुओं के List<T> के लिए करें। इससे डेटाबेस पर काफी भारी भार आएगा।

//Service Class 
@Transactional 
public Group findWithUsers(UUID groupId){ 
    Group group = groupDao.find(groupId); 

    //Forces the initialization of the collection object returned by getUsers() 
    Hibernate.initialize(group.getUsers()); 

    return group; 
} 

मुझे ऐसी स्थिति में नहीं आया है जहां मुझे उपरोक्त कोड का उपयोग करना पड़ा है, लेकिन यह अपेक्षाकृत कुशल होना चाहिए। अधिक जानकारी के लिए के बारे में Hibernate.initialize() देख here

मैं नहीं बल्कि उन्हें डीएओ में प्राप्त करने में कठिनाई की तुलना में सेवा परत में यह किया है, क्योंकि तब आप केवल बल्कि साथ ही एक अलग डीएओ विधि बनाने से सेवा में एक नई विधि बनाने के लिए किया है। महत्वपूर्ण बात यह है कि आपने लेनदेन के भीतर getUsers() कॉल को लपेट लिया है, इसलिए एक सत्र बनाया जाएगा कि अतिरिक्त प्रश्न चलाने के लिए हाइबरनेट का उपयोग किया जा सकता है। यह आपके संग्रह में मानदंडों को शामिल करके डीएओ में भी किया जा सकता है, लेकिन मुझे इसे खुद कभी नहीं करना पड़ा।

उस ने कहा, अगर आपको लगता है कि आप दूसरी विधि को कॉल कर रहे हैं, तो आप पहले से कॉल कर रहे हैं, तो अपने fetch प्रकार को EAGER पर बदलने और डेटाबेस को आपके लिए काम करने पर विचार करें।

+0

बहुत विस्तृत उत्तर के लिए धन्यवाद !! आपने बहुत मदद की :) –

+1

बहुत अच्छी व्याख्या। उत्सुकता केवल तभी उपयोग की जानी चाहिए जब इसकी अपेक्षा की जाती है और पूरे एप्लिकेशन में वास्तव में आवश्यकता होती है। –

1

हालांकि जेम्सएनएल का जवाब लगभग सही है, इसमें कुछ महत्वपूर्ण पहलू की कमी है।

वह क्या कर रहा है लेनदेन अभी भी सक्रिय होने पर आलसी लोडिंग प्रॉक्सी को लोड करने के लिए मजबूर करना है। हालांकि यह LazyInitialization त्रुटि हल हो गया है, आलसी लोडिंग अभी भी एक करके किया जा रहा है, जो बेहद खराब प्रदर्शन देने जा रहा है। अनिवार्य रूप से, यह केवल FetchType.EAGER के मैन्युअल रूप से एक ही परिणाम प्राप्त कर रहा है (और इससे भी बदतर तरीके से, क्योंकि हमने जॉइन और सबबेल रणनीति का उपयोग करने की संभावनाओं को याद किया), जो प्रदर्शन की चिंता के विपरीत भी है।

भ्रम से बचने के लिए: LAZY fetch प्रकार का उपयोग करना सही है।

हालांकि, आलसी लोडिंग अपवाद से बचने के लिए, आपको अपने भंडार (या डीएओ?) को आवश्यक गुण प्राप्त करना चाहिए।

सबसे अक्षम तरीका यह है कि संबंधित संपत्ति तक पहुंचकर आलसी लोडिंग को ट्रिगर करें। कुछ वास्तव में बड़ी कमी हैं:

  1. कल्पना करें कि क्या होता है यदि आपको एकाधिक स्तर के डेटा को पुनर्प्राप्त करने की आवश्यकता है।
  2. यदि परिणाम सेट बड़ा होने वाला है, तो आप डीबी को एन + 1 एसक्यूएल जारी कर रहे हैं।

एक प्रश्न (या कुछ) में सभी संबंधित डेटा लाने का प्रयास करने का अधिक उचित तरीका है।

बस वाक्य रचना की तरह वसंत-डेटा का उपयोग कर (बंदरगाह हाइबरनेट भंडार/डीएओ handcraft के लिए पर्याप्त सहज ज्ञान युक्त होना चाहिए) एक उदाहरण देता हूँ:

interface GroupRepository { 
    @Query("from Group") 
    List<Group> findAll(); 

    @Query("from Group g left join fetch g.users") 
    List<Group> findAllWithUsers(); 
} 

प्राप्त कर रहा है में शामिल हों मानदंड एपीआई में समान रूप से सरल है (हालांकि लगता है केवल बाईं शामिल हो उपलब्ध है), हाइबरनेट डॉक से उद्धृत:

List cats = session.createCriteria(Cat.class) 
    .add(Restrictions.like("name", "Fritz%")) 
    .setFetchMode("mate", FetchMode.EAGER) 
    .setFetchMode("kittens", FetchMode.EAGER) 
    .list(); 
संबंधित मुद्दे