2010-04-12 10 views
6

Estoy experimentando con Hibernate para ganar experiencia. He creado una clase Person con dos subclases: Student y Worker:Hibernate: ¿Qué pasa con esta asignación a una subclase unida a una clave externa?

public abstract class Person { 
    private Long id; 
    ... 
} 

public class Student extends Person { ... } 

Otra clase, Employer, tiene una relación bidireccional uno-a-muchos con Worker.

public class Worker extends Person { 
    private Employer employer; 
    ... 
} 

public class Employer { 
    private String taxId; 
    private Set<Worker> employees = new HashSet<Worker>(); 
    ... 
} 

para el que el mapeo es

<class name="Employer" table="EMPLOYER"> 
    <id name="taxId" column="TAX_ID" length="11"> 
     <generator class="assigned"/> 
    </id> 
    ... 
    <set name="employees" inverse="true"> 
     <key column="EMPLOYER_TAX_ID"/> 
     <one-to-many class="Worker"/> 
    </set> 
</class> 

la jerarquía de herencia se modela con una estrategia mixta, donde Student está asignada a la mesa PERSON, pero Worker se almacena en su propia tabla, se unió con una clave externa:

<class name="Person" table="PERSON"> 
    <id name="id" column="PERSON_ID" type="long" unsaved-value="0"> 
     <generator class="native"/> 
    </id> 
    <discriminator column="PERSON_TYPE" type="string"/> 
    ... 
    <subclass name="Student" discriminator-value="STU"> ... </subclass> 

    <subclass name="Worker" discriminator-value="WRK"> 
     <join table="WORKER"> 
      <key column="WORKER_ID"/> 
      <many-to-one name="employer" column="EMPLOYER_TAX_ID" cascade="save-update"/> 
      ... 
     </join> 
    </subclass> 
</class> 

utilizo Apache Derby 10.5.3.0 y autogenerar el esquema mediante el establecimiento de hibernate.hbm2ddl.auto a create-drop.

Para probar todo esto, he creado una prueba DBUnit con el siguiente conjunto de datos:

<EMPLOYER TAX_ID   = "1234567890" 
      ... 
/> 
<PERSON PERSON_ID   = "12345" 
      PERSON_TYPE  = "WRK" 
      ... 
/> 
<WORKER WORKER_ID   = "12345" 
      EMPLOYER_TAX_ID = "1234567890" 
      ... 
/> 

tengo una prueba que carga la entidad de los trabajadores y comprueba que tiene el empleado correcto. Esto pasa. A continuación, una prueba para la dirección opuesta:

String taxId = "1234567890"; 

    Employer employer = (Employer) session.get(Employer.class, taxId); 

    assertNotNull(employer); 
    assertThat(employer.getEmployees().size(), is(1)); 

Tras la ejecución, la última aserción falla porque el conjunto de los empleados está vacía.

Indagando más a fondo, descubrí que por alguna razón Hibernate busca (y crea) en la columna de la EMPLOYER_TAX_ID PERSONA tabla en lugar de TRABAJADOR! También está presente en WORKER, pero ese no se usa en la consulta. Las instrucciones de selección para poblar el conjunto de empleados son:

select 
    employees0_.EMPLOYER_TAX_ID as EMPLOYER10_1_, 
    employees0_.PERSON_ID as PERSON1_1_, 
    employees0_.PERSON_ID as PERSON1_1_0_, 
    employees0_.FIRST_NAME as FIRST3_1_0_, 
    employees0_.FAMILY_NAME as FAMILY4_1_0_, 
    employees0_.DATE_OF_BIRTH as DATE5_1_0_, 
    employees0_.HOME_ADDRESS as HOME6_1_0_, 
    employees0_.CITY as CITY1_0_, 
    employees0_.ZIP as ZIP1_0_, 
    employees0_1_.EMPLOYER_TAX_ID as EMPLOYER2_2_0_, 
    employees0_1_.JOB_TITLE as JOB3_2_0_, 
    employees0_1_.JOB_GRADE as JOB4_2_0_, 
    employees0_1_.START_DATE as START5_2_0_ 
from 
    PERSON employees0_ 
inner join 
    WORKER employees0_1_ 
     on employees0_.PERSON_ID=employees0_1_.WORKER_ID 
where 
    employees0_.EMPLOYER_TAX_ID=? 

¿Por qué es esto? Y ¿cómo puedo hacer que Hibernate encuentre EMPLOYER_TAX_ID en la tabla WORKER?

Tenga en cuenta que, dado que se trata de un proyecto experimental, puedo cambiar prácticamente cualquier cosa. Aprecio cualquier solución alternativa, pero preferiría entender lo que está pasando y arreglar este mapeo tal como está (tanto como sea posible).

Actualización: si cambio a una estrategia de mapeo de herencia limpia <joined-subclass>, el esquema generado se ve como debe y pasa la prueba. Esta es una solución bastante buena, pero todavía tengo curiosidad de saber si hay alguna manera de que la estrategia mixta funcione correctamente.

+0

¿Por qué seguir usando esos desagradables archivos XML si puede usar anotaciones JPA? – Kdeveloper

+0

@Kdeveloper Nuestro proyecto real usa XML, por lo que este es mi principal objetivo para experimentar. Pero ampliaré mi proyecto mascota a anotaciones en el futuro, a medida que el tiempo lo permita ... –

+1

Solo para referencia posterior, se definiría una relación bidireccional de uno a varios usando las anotaciones de la siguiente manera: '@OneToMany (mappedBy =" nameOfSingleInstanceInOtherEntity ")', \t '@ManyToOne @JoinColumn (name =" nameWhichYouWishToGiveToIdField ")' –

Respuesta

2

Esto suena como fallo conocido: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1015

Se sabe desde hace siglos y se ha informado de toneladas de veces. Aún así, no lo están arreglando ...

+0

Por cierto, el problema con es que da como resultado grandes consultas No es tan grande con dos subclases, pero cada subclase siguiente se agrega al tamaño de SQL considerablemente. – doublep

+0

¡Gracias, este parece ser el mismo caso! –

0

Me enfrenté al mismo problema y lo arreglé de esta manera:
Cambiando la subclase por la subclase unida.
En su caso sería:

<joined-subclass name="Worker" table="WORKER"> 
    <key column="WORKER_ID"/> 
    <many-to-one name="employer" column="EMPLOYER_TAX_ID" cascade="save-update"/> 
    ... 
</joined-subclass> 

Espero que esto ayude.

+0

Usar una subclase unida implica que no hay forma de utilizar otra estrategia de innheritance. Tal vez no hay [posibilidad de hacerlo] (http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/inheritance.html#inheritance-mixing-tableperclass-tablepersubclass). –

Cuestiones relacionadas