2011-08-11 14 views
8

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" .

Respuesta

6

Si desea claves primarias compartidos entre MainEntity y subentidad utilizar PrimaryKeyJoinColumn y MapsId anotación.

Mediante el uso de PrimaryKeyJoinColumn la entidad se carga al unirse a la mesa de MainEntity con la tabla SubEntity utilizando la misma clave primaria. Debería resolver los problemas n + 1.

El MapsId anotación piden Hibernate para copiar el identificador de otra entidad asociada en nuestro ejemplo copiará el SubEntity.mainEntity.id a SubEntity.id.

@Entity 
public class MainEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name = "main_Id") 
    private Long id; 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    private SubEntity subEntity ; 
} 


@Entity 
public class SubEntity 
{ 
    @Id @Column(name="main_Id_FK") Long id; 

    @MapsId 
    @OneToOne 
    @JoinColumn(name = "main_Id_FK")  
    @PrimaryKeyJoinColumn 
    private MainEntity mainEntity;   

} 

Hibernate documentación Reference:

PrimaryKeyJoinColumn
MapsId

+0

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

+0

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

+0

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

1

Hay tres opciones para evitar las preguntas n 1:

Lot size 

subselect 

Make a LEFT JOIN in the query 

Here FAQ1 Here FAQ2

+0

Ambos enlaces me llevan a la misma página, los enlaces más específicos? Además: 'Tamaño del lote': supongo que te refieres al' Batch size' - no funciona aquí, 'subselect' - Lo intenté, pero como ya se mencionó, no funcionó,' left join' - Prefiero hacer una unión de búsqueda, pero como dije, me gustaría no tener que agregarla a cada consulta. – Thomas

Cuestiones relacionadas