it-swarm.com.de

Hibernate: Best Practice, um alle faulen Sammlungen abzurufen

Was ich habe:

@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
}

Was für ein Problem:

Das Problem ist, dass ich nach dem Schließen der Sitzung keine Lazy-Collection abrufen kann. Ich kann auch keine Sitzung in der continue -Methode schließen. 

Was für eine Lösung (grobe Lösung):

a) Bevor die Sitzung geschlossen ist, muss der Winterschlaf gezwungen werden, faule Sammlungen zu ziehen

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

....

b) Vielleicht ist der ellegantere Weg die Verwendung von @Fetch(FetchMode.SUBSELECT) Annotation

Frage:

Was ist eine bewährte Methode/üblicher Weg/elleganter Weg, dies zu tun? Mittel meinen Objekt in JSON konvertieren.

69
VB_

Verwenden Sie Hibernate.initialize() innerhalb von @Transactional, um Lazy-Objekte zu initialisieren. 

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

Jetzt können Sie außerhalb der Transaktion faule Objekte abrufen. 

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

Sie können das Objekt "Getter des Ruhezustands" in derselben Transaktion durchlaufen, um sicherzustellen, dass alle faulen untergeordneten Objekte mit der folgenden generic helper-Klasse eifrig abgerufen werden:

HibernateUtil.initializeObject (meinObjekt, "mein.app.model");

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;
}
}
7
Florian Sager

Nicht die beste Lösung, aber hier habe ich:

1) Kommentieren Sie den Getter, den Sie mit dieser Anmerkung initialisieren möchten: 

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

}

2) Verwenden Sie diese Methode (kann in eine generische Klasse eingefügt werden oder Sie können T mit Object-Klasse ändern) für ein Objekt, nachdem Sie es aus der Datenbank gelesen haben:

    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();
    }
}
4
Damian

Platzieren Sie die Utils.objectToJson (Entität). Anruf vor dem Schließen der Sitzung.

Oder Sie können versuchen, den Abrufmodus einzustellen und mit diesem Code zu spielen

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
StanislavL

Mit Hibernate 4.1.6 wird eine neue Funktion eingeführt, um diese problematischen Assoziationsprobleme zu behandeln. Wenn Sie die Eigenschaft hibernate.enable_lazy_load_no_trans in hibernate.properties oder hibernate.cfg.xml aktivieren, haben Sie keine LazyInitializationException mehr. 

Weitere Informationen finden Sie unter: https://stackoverflow.com/a/11913404/286588

3
Farm

Es handelt sich wahrscheinlich nicht überall um eine Best Practice, aber normalerweise rufe ich eine SIZE in der Sammlung auf, um die Kinder in derselben Transaktion zu laden, wie Sie es vorgeschlagen haben. Es ist sauber, immun gegen jegliche Änderungen in der Struktur der untergeordneten Elemente und führt zu SQL mit geringem Aufwand.

2
davek

Wenn Sie mehrere Sammlungen abrufen müssen, müssen Sie:

  1. JOIN FETCH eine Kollektion
  2. Verwenden Sie den Hibernate.initialize für die verbleibenden Sammlungen.

In Ihrem Fall benötigen Sie also eine erste JPQL-Abfrage wie diese:

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

Auf diese Weise können Sie Ihr Ziel mit 2 SQL-Abfragen erreichen und ein kartesisches Produkt vermeiden.

0
Vlad Mihalcea

Verwenden Sie die Bibliothek Gson, um Objekte in Json zu konvertieren

Beispiel mit Servlets: 

  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
Mohamed Nagy

wenn Sie das jpa-Repository verwenden, setzen Sie properties.put ("hibernate.enable_lazy_load_no_trans", true); jpaPropertymap

0
userSait