2012-10-09 40 views
6

Tengo una relación de muchos a muchos con una columna adicional en la tabla de enlaces. Lo configuré de una manera que el lado propietario atrae a los niños ansiosos (así que no obtengo LazyInitializationException) y en la dirección opuesta es flojo. Esto funciona.@Transactional (readOnly = true) lleva a LazyInitializationException

Ahora quería afinar las transacciones (antes sólo había @Transactional en el nivel de clase de las clases DAO y el servicio que defina Método getById a readOnly = true:.

@Transactional(readOnly = true) 
public Compound getById(Long id) { 
    return compoundDAO.getById(id); 
} 

Después de este cambio recibo una LazyInitializationException en siguiente fragmento:

Compound compound = compoundService.getById(6L);   
Structure structure = compound.getComposition().get(0).getStructure(); 
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException 

Si quito (readOnly = true) esto funciona puede alguien explicar este comportamiento utilizo primavera + Hibernate algo confuso ya que no veo ninguna razón por la TH!?. ¿Debería afectar qué datos se cargan?


EDIT:

fragmentos de definiciones de relaciones. Este es un muchos-a-muchos con una columna en la tabla de enlaces.

lado propietario (por ejemplo el Compuesto contiene estructuras):

@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound", 
    cascade = CascadeType.ALL, orphanRemoval = true) 
@OrderBy("pk.structure.id ASC") 
private List<CompoundComposition> composition = new ArrayList<>(); 

Pertenece a lado:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure", 
cascade = CascadeType.ALL) 
@OrderBy("pk.compound.id ASC") 
private List<CompoundComposition> occurence; 

Muchos-a-uno en la clase ID @Embeddable

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Compound getCompound() { 
    return compound; 
} 

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Structure getStructure() { 
    return structure; 
} 

EDIT 2 :

St ACK traza

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na] 
    at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na] 

Datos 3:

también ver mi comentario:

Log es muy diferente con readOnly y le falta la parte eran las relaciones se cargan, por ejemplo. algunas selectas faltan en el registro.

EDITAR 4:

Así que cansado con una DriverManagerDataSource básico y no hay piscina conexión. El problema es exactamente el mismo. Para mí parece un problema en Hibernate.

+0

¿Cuál es la definición de transacción de nivel de clase? –

+0

Just @Transactional. –

+0

¿Se pueden publicar fragmentos de las clases modelo? Y, ¿todavía tienes la anotación '@ Transactional' en la parte superior de la clase DAO? – sbzoom

Respuesta

3

Esto es simplemente asombroso. Estoy empezando a entender por qué algunas personas odian los ORM ... solo siento que constantemente tengo que pasar horas para resolver un problema extraño y la solución es un conjunto muy específico de anotaciones + algún código para evitar las limitaciones de dicho anotaciones

En primer lugar, ¿por qué sucede esto? ¿Por qué significa con qué anotaciones, pero no en términos de sentido lógico, que es el problema real aquí como el uso del sentido común es inútil. Sólo prueba y error ayuda). En el lado propietario, en @OneToMany tengo orphanRemoval = true (que he descubierto que es necesario para la coherencia, uno pensaría que las restricciones de la base de datos deberían manejar eso ... solo una de las muchas cosas que pueden volverte loco). Parece que si la transacción no es de sólo lectura, entonces este ajuste lleva a ser exagerado, incluso por lo que su pereza, es decir, aquí algunos datos:

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Structure getStructure() { 
    return structure; 
} 

En una transacción de sólo lectura, ir a buscar esta no sucede. Supongo que porque si no puede cambiar nada, tampoco tendrá que eliminar a los huérfanos y, por lo tanto, los datos que la lógica detrás de esta configuración requiere no son necesarios en un tx de solo lectura.

Así que la solución obvia estaría en la relación anterior para cambiar a FetchType.EAGER. ¡Incorrecto! Si lo hace, no podrá actualizar el lado propietario (Compuesto) utilizando session.merge. Esto conducirá a un StackOverFlowError.

La solución real ya se mencionó. Acaba de salir de la configuración que no es más que cargar explícitamente las relaciones deseadas en la capa de servicio:

@Transactional(readOnly = true) 
@Override  
public Compound getById(Long id) { 

    Compound compound = compoundDAO.getById(id); 
    for (CompoundComposition composition : compound.getComposition()){ 
     Hibernate.initialize(composition.getStructure()); 
    }   
    return compound; 
} 

Admito que estoy tendiendo a caer en la trampa prematura optimización. Esto no parece muy eficiente y también parece romper la forma en que SQL funciona en primer lugar. Pero luego estoy en la posición afortunada de que en la mayoría de los casos CompoundComposition contendrá solo 1 o 2 elementos.

+1

Sus primeros dos párrafos resumen los 6 o más años que pasé con Hibernate/JPA/ORM. – millhouse

0

Tal vez usted podría poner

value.getComposition().get(i).getStructure(); 

en el cuerpo del método getById(), por lo que la carga diferida sucede dentro de la transacción. Me doy cuenta de que en este caso tendrías que pasar por encima de i, lo que podría ser un inconveniente.