হাইবারনেট: সমস্ত অলস সংগ্রহগুলি টানতে সেরা অনুশীলন


92

আমি কি আছে:

@Entity
public class MyEntity {
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
  @JoinColumn(name = "myentiy_id")
  private List<Address> addreses;

  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
  @JoinColumn(name = "myentiy_id")
  private List<Person> persons;

  //....
}

public void handle() {

   Session session = createNewSession();
   MyEntity entity = (MyEntity) session.get(MyEntity.class, entityId);
   proceed(session); // FLUSH, COMMIT, CLOSE session!

   Utils.objectToJson(entity); //TROUBLES, because it can't convert to json lazy collections
}

কি একটি সমস্যা:

সমস্যাটি হ'ল অধিবেশন বন্ধ হয়ে যাওয়ার পরে আমি অলস সংগ্রহ করতে পারি না। তবে আমি এগিয়ে চলার পদ্ধতিতে একটি সেশনও বন্ধ করতে পারি না ।

কি একটি সমাধান (মোটা সমাধান):

ক) অধিবেশন বন্ধ হওয়ার আগে হাইবারনেটকে অলস সংগ্রহগুলি টানতে বাধ্য করুন

entity.getAddresses().size();
entity.getPersons().size();

....

খ) @Fetch(FetchMode.SUBSELECT)টীকাটি ব্যবহার করা সম্ভবত আরও অভিজাত উপায়

প্রশ্ন:

এটি করার জন্য একটি সর্বোত্তম অনুশীলন / সাধারণ উপায় / আরও অভিজাত উপায় কী? মানে আমার বস্তুকে জেএসএনে রূপান্তর করে।

উত্তর:


102

অলস জিনিসগুলিকে আরম্ভ Hibernate.initialize()করার @Transactionalজন্য ব্যবহার করুন ।

 start Transaction 
      Hibernate.initialize(entity.getAddresses());
      Hibernate.initialize(entity.getPersons());
 end Transaction 

লেনদেনের এখন বাইরে আপনি অলস জিনিস পেতে সক্ষম হন।

entity.getAddresses().size();
entity.getPersons().size();

4
এটি আকর্ষণীয় দেখায়)। আমি যেমন বুঝি আমি @ ফ্যাচ (ফেচমোড.এফসিকেট) ব্যবহার করব কিনা, তার চেয়ে আমি কেবলমাত্র সমস্ত সংগ্রহ টানতে একবার হাইবারনেট.ইনাইটালাইজ করতে পারি call আমি কি সঠিক?
ভিবি_

4
আপনি যখন MyEntity এর সংগ্রহটি পুনরুদ্ধার করবেন তখন আপনি কীভাবে পরিচালনা করবেন?
অ্যালেক্সিস ডুফরনয়

4
আপনি যদি কোনও লেনদেনের কোনও সংগ্রহের জন্য "আকার ()" এর মতো কোনও পদ্ধতিকে কল করেন তবে এটি এটিকে আরম্ভও করবে যাতে আপনার সূচনা করার পরে আপনার উদাহরণটি সর্বোত্তম নয়। এটি বলেছিল, "হাইবারনেট.ইনাইটালাইজাইজ (...)" শব্দার্থগতভাবে আরও ভাল তবে কালেকশন.সাইজ (), সুতরাং আপনার কাছে সেরা পরামর্শ have
ত্রিস্তান

7

নীচের জেনেরিক সহায়ক শ্রেণীর সাহায্যে সমস্ত অলস শিশু অবজেক্টটি আগ্রহীভাবে এনেছে তা নিশ্চিত করার জন্য আপনি একই লেনদেনে হাইবারনেট অবজেক্টের গেটরসকে অতিক্রম করতে পারেন :

হাইবারনেট ইউটিল.ইনটায়ালাইজঅবজেক্ট (মাইবজেক্ট, "মাই.এপ.মডেল");

package my.app.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import org.aspectj.org.eclipse.jdt.core.dom.Modifier;
import org.hibernate.Hibernate;

public class HibernateUtil {

public static byte[] hibernateCollectionPackage = "org.hibernate.collection".getBytes();

public static void initializeObject( Object o, String insidePackageName ) {
    Set<Object> seenObjects = new HashSet<Object>();
    initializeObject( o, seenObjects, insidePackageName.getBytes() );
    seenObjects = null;
}

private static void initializeObject( Object o, Set<Object> seenObjects, byte[] insidePackageName ) {

    seenObjects.add( o );

    Method[] methods = o.getClass().getMethods();
    for ( Method method : methods ) {

        String methodName = method.getName();

        // check Getters exclusively
        if ( methodName.length() < 3 || !"get".equals( methodName.substring( 0, 3 ) ) )
            continue;

        // Getters without parameters
        if ( method.getParameterTypes().length > 0 )
            continue;

        int modifiers = method.getModifiers();

        // Getters that are public
        if ( !Modifier.isPublic( modifiers ) )
            continue;

        // but not static
        if ( Modifier.isStatic( modifiers ) )
            continue;

        try {

            // Check result of the Getter
            Object r = method.invoke( o );

            if ( r == null )
                continue;

            // prevent cycles
            if ( seenObjects.contains( r ) )
                continue;

            // ignore simple types, arrays und anonymous classes
            if ( !isIgnoredType( r.getClass() ) && !r.getClass().isPrimitive() && !r.getClass().isArray() && !r.getClass().isAnonymousClass() ) {

                // ignore classes out of the given package and out of the hibernate collection
                // package
                if ( !isClassInPackage( r.getClass(), insidePackageName ) && !isClassInPackage( r.getClass(), hibernateCollectionPackage ) ) {
                    continue;
                }

                // initialize child object
                Hibernate.initialize( r );

                // traverse over the child object
                initializeObject( r, seenObjects, insidePackageName );
            }

        } catch ( InvocationTargetException e ) {
            e.printStackTrace();
            return;
        } catch ( IllegalArgumentException e ) {
            e.printStackTrace();
            return;
        } catch ( IllegalAccessException e ) {
            e.printStackTrace();
            return;
        }
    }

}

private static final Set<Class<?>> IGNORED_TYPES = getIgnoredTypes();

private static boolean isIgnoredType( Class<?> clazz ) {
    return IGNORED_TYPES.contains( clazz );
}

private static Set<Class<?>> getIgnoredTypes() {
    Set<Class<?>> ret = new HashSet<Class<?>>();
    ret.add( Boolean.class );
    ret.add( Character.class );
    ret.add( Byte.class );
    ret.add( Short.class );
    ret.add( Integer.class );
    ret.add( Long.class );
    ret.add( Float.class );
    ret.add( Double.class );
    ret.add( Void.class );
    ret.add( String.class );
    ret.add( Class.class );
    ret.add( Package.class );
    return ret;
}

private static Boolean isClassInPackage( Class<?> clazz, byte[] insidePackageName ) {

    Package p = clazz.getPackage();
    if ( p == null )
        return null;

    byte[] packageName = p.getName().getBytes();

    int lenP = packageName.length;
    int lenI = insidePackageName.length;

    if ( lenP < lenI )
        return false;

    for ( int i = 0; i < lenI; i++ ) {
        if ( packageName[i] != insidePackageName[i] )
            return false;
    }

    return true;
}
}

এই উত্তরের জন্য আপনাকে ধন্যবাদ। আমি জানি এটি কিছুক্ষণ হয়ে গেছে তবে আমি এটি সমাধান করার চেষ্টা করছিলাম এবং আমি এখানে আপনার কোডটি না পড়া পর্যন্ত এটি ধীরে ধীরে চলছিল। আমি দ্বিতীয় পদ্ধতির আরম্ভের অবজেক্টের (আইটেম, সেকডবজেক্টস, প্যাকেজনামের ভিতরে) if (object instanceof List) { for(Object item : (List<Object>) object) { initializeObject(item, seenObjects, insidePackageName); } return; } else if (object instanceof Set) { for(Object item : (Set<Object>) object) { initializeObject(item, seenObjects, insidePackageName); } return; } আইফএস যুক্ত করেছি : আইট্রেট লিস্ট অন্যথায় উপেক্ষা করা হবে।
চিপ

O.getClass ()। GetMethods (); এ সিকিউরিটি এক্সেপশন নিক্ষেপ করা হলে কী হবে ??
ওলেকসেই কিসলিটসিন

6

সেরা সমাধান নয়, তবে আমি যা পেয়েছি তা এখানে:

1) আপনি এই টিকাটি দিয়ে আরম্ভ করতে চান এনটেট গেটর:

@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {

}

২) ডাটাবেস থেকে পড়ার পরে কোনও অবজেক্টে এই পদ্ধতিটি (জেনেরিক ক্লাসে রাখা যেতে পারে, বা আপনি অবজেক্ট ক্লাস দিয়ে টি পরিবর্তন করতে পারেন) ব্যবহার করুন:

    public <T> void forceLoadLazyCollections(T entity) {

    Session session = getSession().openSession();
    Transaction tx = null;
    try {

        tx = session.beginTransaction();
        session.refresh(entity);
        if (entity == null) {
            throw new RuntimeException("Entity is null!");
        }
        for (Method m : entityClass.getMethods()) {

            Lazy annotation = m.getAnnotation(Lazy.class);
            if (annotation != null) {
                m.setAccessible(true);
                logger.debug(" method.invoke(obj, arg1, arg2,...); {} field", m.getName());
                try {
                    Hibernate.initialize(m.invoke(entity));
                }
                catch (Exception e) {
                    logger.warn("initialization exception", e);
                }
            }
        }

    }
    finally {
        session.close();
    }
}

অলস সংগ্রহগুলি লোড করতে আমি পুনরাবৃত্তিতে সেশন.ফ্রেস ব্যবহার করি। এবং প্রতিবার আমি যখন আমার প্রোগ্রামটি আমার সত্তার একজনের জন্য চালিত করি তখনই আমি LazyInitializationException এবং অন্যদের সংগ্রহ সেশান.ফ্রেশকে ফোন করার পরে লোড করেছি। কীভাবে এটি ঘটতে পারে
সাবা সাফভি

5

Utils.objectToJson (সত্তা) রাখুন; সেশন বন্ধ হওয়ার আগে কল করুন।

অথবা আপনি আনার মোড সেট করার চেষ্টা করতে পারেন এবং এই জাতীয় কোড সহ খেলতে পারেন

Session s = ...
DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id));
dc.setFetchMode("innerTable", FetchMode.EAGER);
Criteria c = dc.getExecutableCriteria(s);
MyEntity a = (MyEntity)c.uniqueResult();

ফেচমোড.এগ্রের হ্রাস করা হয়েছে। জাভাডোক এখনই ফেচমোড.জয়াইন ব্যবহার করার পরামর্শ দেয়।
অ্যালেক্সিস ডুফরনয়

4

হাইবারনেট ৪.১..6 এর সাহায্যে অলস অ্যাসোসিয়েশন সমস্যাগুলি পরিচালনা করতে একটি নতুন বৈশিষ্ট্য চালু করা হয়েছে। আপনি যখন হাইবারনেট.এনেবল_লজি_লোড_নো_ট্রান্স সম্পত্তি হাইবারনেট.প্রোপার্টি বা হাইবারনেট সিএফজি.এক্সএমএল এ সক্ষম করবেন তখন আপনার আর কোনও LazyInitializationException হবে না।

আরও পড়ুন: https://stackoverflow.com/a/11913404/286588


4
এটি আসলে একটি অ্যান্টি-প্যাটার্ন। আরও তথ্যের জন্য: vladmihalcea.com/…
Ph03n1x

3

একাধিক সংগ্রহ আনার সময়, আপনার প্রয়োজন:

  1. এক সংগ্রহে যোগদান করুন
  2. Hibernate.initializeবাকী সংগ্রহের জন্য ব্যবহার করুন ।

সুতরাং, আপনার ক্ষেত্রে আপনার প্রথম জেপিকিউএল কোয়েরি দরকার:

MyEntity entity = session.createQuery("select e from MyEntity e join fetch e.addreses where e.id 
= :id", MyEntity.class)
.setParameter("id", entityId)
.getSingleResult();

Hibernate.initialize(entity.persons);

এইভাবে, আপনি 2 এসকিউএল ক্যোয়ারী দিয়ে আপনার লক্ষ্য অর্জন করতে পারেন এবং কার্টেসিয়ান পণ্য এড়াতে পারেন।


হাই ভ্লাদ, Hibernate#initialize(entity.getSubSet())গেটসুবসেট ফেরত পেলে আমি ফোন করলে কি এটি কাজ করে Collections.unmodifyableSet(this.subSet)? আমি চেষ্টা করেছিলাম কিন্তু তা হয়নি। আন্ডারলাইংয়ের সংগ্রহটি 'পার্সেন্টেন্টসেট'। কল করার সাথে একই গল্প#size()
ভাদিম কিরিলচুক

তবে সম্ভবত সমস্যাটি হ'ল আমি পরে কল করেছি এবং আমার সমান প্রত্যক্ষ ক্ষেত্রের অ্যাক্সেস ব্যবহার করে এবং প্রাপ্তিগুলি ব্যবহার করে না ..
ভাদিম কিরিলচুক

আপনি যদি আমার উত্তরের প্রদত্ত পদক্ষেপগুলি অনুসরণ করেন তবে এটি কাজ করে।
ভ্লাদ মিহলসিয়া

2

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


0

Gsonজিনিসগুলিকে জসনে রূপান্তর করতে লাইব্রেরি ব্যবহারের চেষ্টা করুন

সার্লেলেট সহ উদাহরণ:

  List<Party> parties = bean.getPartiesByIncidentId(incidentId);
        String json = "";
        try {
            json = new Gson().toJson(parties);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(json);

0

আপনি যদি জেপিএ সংগ্রহস্থল ব্যবহার করেন, বৈশিষ্ট্যগুলি সেট করুন putপুট ("হাইবারনেট.নেবল_লাজি_লোড_নো_ট্রান্স", সত্য); to jpaPropertymap


0

আপনি @NamedEntityGraphআপনার ক্যোয়ারিতে কোন সংগ্রহগুলি লোড করতে চান তা সেট করে এমন একটি লোডযোগ্য কোয়েরি তৈরি করতে আপনি আপনার সত্তায় টীকাটি ব্যবহার করতে পারেন ।

এই পছন্দটির প্রধান সুবিধা হ'ল হাইবারনেট সত্তা এবং এর সংগ্রহগুলি পুনরুদ্ধার করতে এবং একটিমাত্র যখন আপনি এই গ্রাফটি ব্যবহার করতে পছন্দ করেন কেবল তখনই এই জাতীয় একটি কোয়েরি করে:

সত্তা কনফিগারেশন

@Entity
@NamedEntityGraph(name = "graph.myEntity.addresesAndPersons", 
attributeNodes = {
    @NamedAttributeNode(value = "addreses"),
    @NamedAttributeNode(value = "persons"
})

ব্যবহার

public MyEntity findNamedGraph(Object id, String namedGraph) {
        EntityGraph<MyEntity> graph = em.getEntityGraph(namedGraph);

        Map<String, Object> properties = new HashMap<>();
        properties.put("javax.persistence.loadgraph", graph);

        return em.find(MyEntity.class, id, properties);
    }

0

জেপিএ-হাইবারনেটে অলস সংগ্রহগুলি সম্পর্কে এক ধরণের ভুল ধারণা রয়েছে। প্রথমে পরিষ্কার করা যাক যে অলস সংগ্রহটি পড়ার চেষ্টা করা ব্যতিক্রম ছোঁড়ে এবং কেবল রূপান্তর বা পরবর্তী ব্যবহারের ক্ষেত্রে নুলকে ফেরত দেয় না কেন?

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

সুতরাং উপরে উল্লিখিত হিসাবে আমি সুপারিশ:

  1. পছন্দসই বস্তুটিকে সংশোধন করার আগে বা জিজ্ঞাসার জন্য স্টেটলেস সেশন ব্যবহার করার আগে আলাদা করুন
  2. অলস ক্ষেত্রগুলি কাঙ্ক্ষিত মানগুলিতে হেরফের করুন (শূন্য, নাল, ইত্যাদি)

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

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