hibernación ignora fetch = “unir” en una colección al navegar por el árbol de objetos con el iterador
Pregunta
tengo un foro con 1..n miembros que tienen cada uno 1..n artículos, por lo que este es mi mapeo:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="true">
<class name="Forum" table="forum">
<id name="id">
<generator class="identity" />
</id>
<property name="name" />
<set name="members" table="members" inverse="true">
<key column="forum_id" not-null="true" />
<one-to-many class="Member" />
</set>
</class>
<class name="Article" table="article">
<id name="id">
<generator class="identity" />
</id>
<property name="title" />
<many-to-one name="member" column="member_id" class="Member"
not-null="true">
</many-to-one>
</class>
<class name="Member" table="member">
<id name="id">
<generator class="identity" />
</id>
<property name="name" />
<many-to-one name="forum" column="forum_id" class="Forum"
not-null="true">
</many-to-one>
<set name="articles" fetch="join" table="articles"
inverse="true">
<key column="member_id" not-null="true" />
<one-to-many class="Article" />
</set>
</class>
</hibernate-mapping>
Ahora tengo dos pruebas:
falla éste con LazyInitializationException:
public void testFetchJoinByIteratorNavigation ( )
{
Forum forum = (Forum) repository.findById(Forum.class, forumId);
Member member = forum.getMembers().iterator().next();
assertEquals(member.getName(), "firstMember");
endTransaction();
assertEquals(1, member.getArticles().size());
}
éste tiene éxito
public void testFetchJoinByGraphNavigation ( )
{
Member member = (Member) repository.findById(Member.class, memberId);
assertEquals(member.getName(), "firstMember");
endTransaction();
assertEquals(1, member.getArticles().size());
}
La única diferencia es la forma en que cargue el miembro.
La referencia de hibernación dice:
"La estrategia ha podido recuperar la define en el documento de mapeo afecta:
- recuperación a través get () o de la carga ()
- recuperación que ocurre implícitamente cuando se navega una asociación
- Criterios de consultas
- consultas HQL si se utiliza ir a buscar subselección "
El ensayo siguiente es el caso 1. (recuperación a través get () o de la carga ()) La prueba no es el caso 2 en mi humilde opinión
¿Por qué "forum.getMembers (). Iterador (). Next ()" no cargar todos los artículos del miembro?
EDIT 1:
Este es mi repositorio:
package hibernate.fetch;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class Repository extends HibernateDaoSupport
{
public Object findById ( Class clazz, Long objectId )
{
return getHibernateTemplate().load(clazz, objectId);
}
public void save ( Object object )
{
getHibernateTemplate().saveOrUpdate(object);
}
}
Este es mi pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>hibernate-fetch</groupId>
<artifactId>hibernate-fetch</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<downloadSources>true</downloadSources>
<ajdtVersion>1.5</ajdtVersion>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.8.0.GA</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.2-504.jdbc3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jmock</groupId>
<artifactId>jmock</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Editar 2: este es mi caso de prueba:
package hibernate.fetch;
import org.springframework.test.AbstractTransactionalSpringContextTests;
public class ForumTest extends AbstractTransactionalSpringContextTests
{
private Repository repository;
private Long memberId;
private Long forumId;
private String name = "test";
@Override
protected String[] getConfigLocations ( )
{
return new String[] { "applicationContext.xml" };
}
@Override
protected void onSetUpBeforeTransaction ( ) throws Exception
{
System.out.println(">> preparing Test");
Forum forum = new Forum();
forum.setName(name);
repository.save(forum);
forumId = forum.getId();
Member member = new Member();
member.setName(name);
forum.addMember(member);
repository.save(member);
memberId = member.getId();
Article article = new Article();
article.setTitle(name);
member.addArticle(article);
repository.save(article);
super.onSetUpBeforeTransaction();
System.out.println(">> Test prepared");
}
public void testFetchJoinByGraphNavigation ( )
{
System.out.println(">> testFetchJoinByGraphNavigation");
Member member = (Member) repository.findById(Member.class, memberId);
assertEquals(member.getName(), name);
endTransaction();
assertEquals(1, member.getArticles().size());
}
public void testFetchJoinByIteratorNavigation ( )
{
System.out.println(">> testFetchJoinByIterationNavigation");
Forum forum = (Forum) repository.findById(Forum.class, forumId);
Member member = forum.getMembers().iterator().next();
assertEquals(member.getName(), name);
endTransaction();
// throws LazyInitializationException because articles were NOT loaded
assertEquals(1, member.getArticles().size());
}
public Repository getRepository ( )
{
return repository;
}
public void setRepository ( Repository repository )
{
this.repository = repository;
}
}
Realmente no lo entiendo!
Solución 3
Creo que es un error de documentación de hibernación.
En la documentación de referencia que dice:
"Por otro lado, puede utilizar unirse traer, que no es perezoso por naturaleza "
En el libro JPwH que dice, p. 579
Por lo tanto, un fetch = "unen" desactiva perezoso de carga.
No es cierto. Cuando me cambio de artículos de mapeo a
<set name="articles" fetch="join" lazy="false" table="articles" inverse="true">
prueba de funcionamiento tanto bien, pero la segunda prueba no se ejecuta una unirse al cargar el miembro!
Así que mi conclusión: Eager ir a buscar con la estrategia de combinaciones no afecta initalization de proxy ni desactivar la carga diferida. Esto está en contraste con la documentación de hibernación.
Otros consejos
Los dos casos son realmente muy diferentes.
En el ejemplo en su defecto, que está cargando la entidad Foro, que tiene una colección perezosa inicializado de las entidades miembros. Cuando se trata de navegar a través de la colección, se produce un error porque la colección es un perezoso, y que no se cargó la colección antes de la sesión se cerró.
El ejemplo de trabajo, esto no es "navegación gráfica", que está cargando la entidad miembro directamente, y así no hay semántica perezoso de carga implicados.
La documentación que usted se refiere a formas diferentes involucrados de frasear el primer caso, donde la especificación de carga impaciente en el conjunto de los miembros impediría la falla.
¿Está utilizando HQL en el método findById (...)? Al usar HQL, Hibernate no respetó su FETCH / unirse a los ajustes; es necesario utilizar una cláusula de HQL, como "izquierda unirse a buscar a", para hacer que las asociaciones sean cargados con impaciencia. Además, si usted es 2.x de hibernación, que no será capaz a buscar ansiosamente más de una colección en una sola consulta. Ver la avanzada Problemas frecuentes en el sitio web de hibernación: