Tengo el siguiente entidad (sólo asignaciones correspondientes se muestra):JPA/Hibernate: @ManyToOne y @OneToOne relaciones etiquetadas como FetchType.LAZY y opcional = falso no cargando perezosamente en em.find()?
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY) // lazy XToOne
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Tendering tendering;
...
}
nota de las observaciones anteriores: hay tres @XToOne
relaciones con otras entidades:
usuario (una clase SecurityIdentity sub con un simple ID como PK, referenciado por PQ que representa el propietario):
@Entity
@Table(name = "Users")
@DiscriminatorValue(value = "user")
public class User extends SecurityIdentity
{
@Column
private String name;
@OneToMany(mappedBy = "user")
private Set<PQ> pqs = new HashSet<PQ>();
...
}
Grupo (una lso una subclase SecurityIdentity con una identificación simple como PK, hace referencia al PQ para representar un conjunto de usuarios que pueden interactuar con el PQ):
@Entity
@Table(name = "Groups")
@DiscriminatorValue(value = "group")
public class Group extends SecurityIdentity
{
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
licitación:
@Entity
@Table(name = "Tenderings")
public class Tendering implements Serializable
{
@Id
@Column(name = "pq_id", insertable = false, updatable = false)
private Integer pqId;
@Column(name = "external_code")
private String externalCode;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
no hacer confundirse acerca de los grupos y usuarios que comparten ID, solo trátelos como ID simples. Una licitación es solo un objeto de documento separado (uno a uno).
Como puede ver, hay tres relaciones @XToOne
en la entidad PQ, que, si no se estableció un tipo de búsqueda, se cargarían con entusiasmo (valor predeterminado de JPA). Para evitar esto, etiqueté todas las relaciones @XToOne
como FetchType.LAZY
.
Ahora cuando se utiliza
em.find(PQ.class, someExistingId);
consigo la salida de Hibernate:
23:53:55,815 INFO [stdout] Hibernate: select pq0_.id as id291_0_, pq0_.description as descript2_291_0_, pq0_.name as name291_0_, pq0_.submission_date as submission4_291_0_, pq0_.user_id as user5_291_0_ from PQs pq0_ where pq0_.id=?
23:53:55,818 INFO [stdout] Hibernate: select user0_.id as id280_0_, user0_1_.identity_type_id as identity2_280_0_, user0_.is_enabled as is1_297_0_, user0_.name as name297_0_, user0_.password as password297_0_, user0_.person_id as person5_297_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
23:53:55,821 INFO [stdout] Hibernate: select group0_.id as id280_0_, group0_1_.identity_type_id as identity2_280_0_, group0_.pq_id as pq2_281_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
23:53:55,823 INFO [stdout] Hibernate: select tendering0_.pq_id as pq1_296_0_, tendering0_.binary_file as binary2_296_0_, tendering0_.customer_id as customer6_296_0_, tendering0_.description as descript3_296_0_, tendering0_.external_code as external4_296_0_, tendering0_.title as title296_0_ from Tenderings tendering0_ where tendering0_.pq_id=?
Los tres SELECTs adicionales se derivan de las relaciones @XToOne (como se describe en muchos lugares en la red). La fuente que estaba buscando en su mayoría es la siguiente:
Making a OneToOne-relation lazy
Como se menciona allí, el @ManyToOne
relación User user
no debe ser exagerado:
@ManyToOne(fetch=FetchType.LAZY)
debería funcionar bien.
... aquí la relación de PQ a usuario, pero es descabellada como se puede ver en la declaración select user0_.id as id280_0_, ...
...
Para los otros dos Group group
y Tendering tendering
, ambas @OneToOne
inversa asignaciones, las claves foráneas hacen referencia a PK (ID) de la tabla PQs, lo que da como resultado el mismo mapeo en la entidad PQ.
Tenga en cuenta que las tres relaciones no son opcionales: una PQ siempre tiene un propietario (usuario), y una PQ siempre es referenciada por una licitación y una entidad de grupo. Simplemente no había modelado eso en JPA por encima todavía ...
Por lo tanto, cuando se añade optional = false
a las tres relaciones de la entidad PQ:
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Tendering tendering;
...
}
... Me da la siguiente salida de Hibernate:
00:47:34,397 INFO [stdout] Hibernate: select pq0_.id as id361_0_, pq0_.description as descript2_361_0_, pq0_.name as name361_0_, pq0_.submission_date as submission4_361_0_, pq0_.user_id as user5_361_0_ from PQs pq0_ where pq0_.id=?
00:47:34,410 INFO [stdout] Hibernate: select user0_.id as id350_0_, user0_1_.identity_type_id as identity2_350_0_, user0_.is_enabled as is1_367_0_, user0_.name as name367_0_, user0_.password as password367_0_, user0_.person_id as person5_367_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
00:47:34,413 INFO [stdout] Hibernate: select group0_.id as id350_0_, group0_1_.identity_type_id as identity2_350_0_, group0_.pq_id as pq2_351_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
Tenga en cuenta, que sólo he estado jugando con el optional = false
en la entidad PQ, ya que este es el que uso en em.find(...)
. (Si esto no es suficiente, por favor me ilumine.)
Mi pregunta ahora es doble:
- ¿Por qué es el
@ManyToOne
a la entidadUser
fue a buscar ansiosamente (dado que es fue dice que está trabajando con pereza , ver Making a OneToOne-relation lazy)? - ¿Por qué solo se obtiene la relación
OneToOne
con la entidadTendering
? ¿Es porque la entidadTendering
hace referencia a la columna PK de la PQ como un PK mismo (@Id
enTendering
), que la entidadGroup
no tiene (relación regular con la PK de la PQ)?
¿Qué pasa? ¿Cómo hago perezosas estas relaciones no opcionales? (sin instrumentación de código u otros hacks, simplemente anotaciones ...)
Sé que lo PERDIDO es solo una pista para que el proveedor de JPA haga algo acerca de la carga diferida o no, pero en este caso parece como si algo más está mal (como parte de ello está funcionando).
PD: Estoy usando Hibernate 4.0 BETA, la versión que viene con JBoss 7.0.0.Final junto con las anotaciones JPA solamente (todas las anteriores son todas compatibles con JPA 1.0).
Tal vez sea solo un error. Ver https: // hibernate.onjira.com/browse/HHH-3930, que es similar. –