tenemos un n + 1 problema de selección con Hibernate 3.3.Hibernate OneToOne obtención automática de uniones (resolviendo n + 1 problema)
Por simplicidad, voy a hacer un breve ejemplo abstracto.
Supongamos que tenemos las siguientes clases simples:
class MainEntity {
@Id
public Long id; //we have a table generator create this id
@OneToOne (mappedBy ="main")
public SubEntity subEntity;
}
class SubEntity {
@Id
@Column(name = "mainId") //note that this is the same column as the join column below
public Long mainId; //in order to have the exact same id as the corresponding MainEntity
@OneToOne (fetch = FetchType.LAZY)
@JoinColumn (name = "mainId", insertable = false, updatable = false, nullable = false)
public MainEntity main; //this is used for navigation and queries (" ... subentity.main = :x")
}
Así como se puede ver SubEntity
tiene una relación con MainEntity
que se expresa mediante dos propiedades, donde la propiedad mainId
es el responsable de la gestión de la relación/clave externa.
Esto funciona bastante bien y se adapta perfectamente a nuestras necesidades.
Sin embargo, hay un problema al cargar ansiosamente el SubEntity
junto con el MainEntity
.
Supongamos que tengo una consulta que devuelve una colección de MainEntity
. Con la configuración actual, Hibernate emitirá n + 1 selects: la consulta misma + n selecciona para cada SubEntity
.
Por supuesto que podría agregar un join fetch
a la consulta, pero prefiero que Hibernate lo haga automáticamente. Así que traté de agregar @Fetch(FetchMode.JOIN)
, pero eso no hizo nada.
lo haría también tienen ningún problema con @Fetch(FetchMode.SUBSELECT)
, lo que debería reducir las instrucciones select a 2 - la consulta original y un selecto para las entidades sub (al menos eso es lo que sucede en otra propiedad anotado con @CollectionOfElements
y @Fetch(FetchMode.SUBSELECT)
).
Así que la pregunta es: ¿cómo iba a decirle a Hibernate automáticamente join fetch o utilizar una sola selección con el fin de cargar con impaciencia las entidades sub? ¿Me estoy perdiendo de algo?
Gracias de antemano,
Thomas
PD: Una cosa que podría ser un problema podría ser la mappedBy = "main"
, que no hace referencia a la columna de la real identificación, pero no puedo cambiarlo a mappedBy = "id"
.
No tenía conocimiento del '@ MapsId', gracias por eso. Voy a tratar de salir. Incluso en el caso de que el problema n + 1 no se resuelva, aún preferiría ese enfoque al actual. Solo una pregunta: ¿eso me permitiría simplemente configurar el ID sin tener una referencia a 'MainEntity'? Tenemos casos en los que faltan instancias 'SubEntity' y solo tenemos el id del' MainEntity' correspondiente. Si es posible, me gustaría no tener que cargar 'MainEntity' para simplemente establecer la referencia. En otras palabras: ¿qué pasaría si 'id' tuviera un valor y' mainEntity' fuera 'null'? – Thomas
que acaba de tener un vistazo a la documentación de 'MapsId' @ se ha vinculado, y por desgracia parece que esta anotación se introdujo con Hibernate 3.5 (y por tanto JPA 2). Sin embargo, actualmente estamos atrapados con JBoss 4.2.3 (que no es compatible con JPA 2) y, por lo tanto, no podemos usar Hibernate 3.5 (estamos trabajando en la migración a JBoss 6, pero eso llevará un tiempo). – Thomas
@Thomas Para responder a su primer comentario. Cómo crear un 'SubEntity' con solo el Id de' MainEntity'. Necesitará usar 'EntityManager.getReference' para obtener una referencia a' MainEntity' sin cargarlo y establecerlo en la relación 'SubEntiy.main' sin cargar el Main. Sin –