2009-09-29 21 views
11

Tengo una situación en la que necesito unir tablas en un objeto en una jerarquía de clase ORM donde la columna de combinación NO es la clave principal de la clase base. Este es un ejemplo del diseño de la tabla:¿Cómo puedo unir tablas en columnas de clave no primaria en tablas secundarias?

CREATE TABLE APP.FOO 
(
    FOO_ID INTEGER NOT NULL, 
    TYPE_ID INTEGER NOT NULL, 
    PRIMARY KEY(FOO_ID) 
) 

CREATE TABLE APP.BAR 
(
    FOO_ID INTEGER NOT NULL, 
    BAR_ID INTEGER NOT NULL, 
    PRIMARY KEY(BAR_ID), 
    CONSTRAINT bar_fk FOREIGN KEY(FOO_ID) REFERENCES APP.FOO(FOO_ID) 
) 

CREATE TABLE APP.BAR_NAMES 
(
    BAR_ID INTEGER NOT NULL, 
    BAR_NAME VARCHAR(128) NOT NULL, 
    PRIMARY KEY(BAR_ID, BAR_NAME), 
    CONSTRAINT bar_names_fk FOREIGN KEY(BAR_ID) REFERENCES APP.BAR(BAR_ID) 
) 

Y aquí están las asignaciones (captadores y definidores eliminadas por razones de brevedad

@Entity 
@Table(name = "FOO") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "TYPE_ID", discriminatorType = javax.persistence.DiscriminatorType.INTEGER) 
public abstract class Foo { 
    @Id 
    @Column(name = "FOO_ID") 
    private Long fooId; 
} 

@Entity 
@DiscriminatorValue("1") 
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") }) 
public class Bar extends Foo{ 
    @Column(table = "BAR", name = "BAR_ID") 
    Long barId; 
}  

¿Cómo puedo agregar la asignación de BAR_NAMES dado que su unirse a la columna no está FOO_ID, pero BAR_ID

he intentado lo siguiente:?

@CollectionOfElements(fetch = FetchType.LAZY) 
@Column(name = "BAR_NAME") 
@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(table = "BAR", name = "BAR_ID", referencedColumnName="BAR_ID")) 
List<String> names = new ArrayList<String>(); 

esto falla porque el SQL para recuperar el objeto Barra trata de obtener un valor BAR_ID de la mesa FOO. También he intentado reemplazar la anotación joinTable con

@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(name = "BAR_ID")) 

Esto produce ningún error SQL, sino que también recupera ningún dato porque la consulta contra BAR_NAMES está utilizando el FOO_ID como el valor unirse en lugar de la BAR_ID.

Para propósitos de prueba, he poblaron la base de datos con los siguientes comandos

insert into FOO (FOO_ID, TYPE_ID) values (10, 1); 
insert into BAR (FOO_ID, BAR_ID) values (10, 20); 
insert into BAR_NAMES (BAR_ID, BAR_NAME) values (20, 'HELLO'); 

Muchas de las soluciones que parecen trabajar volverán una colección vacía al conseguir objeto de Foo ID 10 (en oposición a una colección que contiene 1 nombre)

Respuesta

6

que fue capaz de encontrar una solución a esto. Si asigna la clase bar al igual que

@Entity 
@DiscriminatorValue("1") 
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") }) 
public class Bar extends Foo { 
    @OneToOne 
    @JoinColumn(table = "BAR", name = "BAR_ID") 
    MiniBar miniBar; 
} 

y agregar la siguiente clase

@Entity 
@SqlResultSetMapping(name = "compositekey", entities = @EntityResult(entityClass = MiniBar.class, fields = { @FieldResult(name = "miniBar", column = "BAR_ID"), })) 
@NamedNativeQuery(name = "compositekey", query = "select BAR_ID from BAR", resultSetMapping = "compositekey") 
@Table(name = "BAR") 
public class MiniBar { 
    @Id 
    @Column(name = "BAR_ID") 
    Long barId; 
} 

A continuación, puede añadir cualquier tipo de mapeo desea la clase MiniBar como si barid eran la clave primaria, y luego, vuelva a estar disponible en la clase externa Bar.

+0

'barId' todavía está marcado con' @ Id', ¿cuál es la diferencia? – deathangel908

2
No

seguro de cómo hacer esto con APP/anotaciones, pero con los archivos XML de mapeo de Hibernate que sería algo así como:

<class name="Bar" table="BAR"> 
    <id name="id" type="int"> 
     <column name="BAR_ID"/> 
     <generator class="native"/> 
    </id> 
    <set name="barNames" table="BAR_NAMES"> 
     <!-- Key in BAR_NAMES table to map to this class's key --> 
     <key column="BAR_ID"/> 
     <!-- The value in the BAR_NAMES table we want to populate this set with --> 
     <element type="string" column="BAR_NAME"/> 
    </set> 
</class> 
+0

Creo que esto es equivalente a @JoinTable (name = "BAR_NAMES", joinColumns = @JoinColumn (name = "BAR_ID")), que no funciona porque usa el valor de FOO_ID para unirse a la tabla BAR_NAMES. – Jherico

+0

Lo siento, leí mal lo que estaba diciendo. No puedo asignar el BAR_ID como la clave principal para la clase Bar porque es un elemento secundario del objeto Foo que usa herencia de tabla única con tablas secundarias o que usa herencia de subclase unida. – Jherico

2

No podrás hacer lo que quieras. @CollectionOfElements (y @OneToMany, para el caso) son siempre mapeados a través de la clave principal de la entidad del propietario.

La forma de asignar la herencia de Foo/bar es bastante extraño, así - que claramente no están en la misma mesa; parece que usar JoinedSubclass sería un mejor enfoque. Tenga en cuenta que aún no ayudará a trazar bar_names-bar_id porque el valor de clave principal se comparte entre la jerarquía (a pesar de que el nombre de columna puede ser diferente para subclase).

Una alternativa posible es utilizar @OneToOne mapeo entre Foo y Bar en lugar de herencia. Esa es la única forma en que será capaz de asignar a bar_namesbar_id y la asignación más adecuada para usted estructura de la tabla (aunque, tal vez, no para su modelo de dominio).

+0

En el entorno en vivo Foo es una clase base para muchas subclases con identificadores de tipos diferentes. Hemos utilizado el modelo de subclase unificado y el modelo de clase única con tablas secundarias y hemos encontrado que ambos tienen sus fortalezas y debilidades. Cambiar la jerarquía no es una opción. – Jherico

+0

Para ser sincero, una vez que aumenta el enfoque de "tabla por jerarquía" con tablas secundarias, pierde la única ventaja que tiene (un rendimiento algo más rápido) en comparación con el enfoque "tabla por clase", pero eso no es relevante para su pregunta. Como dije, no podrá asignar su colección de la forma que desee sin cambiar la asignación de jerarquía o la estructura de su base de datos (por ejemplo, omitiendo 'bar_id' y usando' foo_id' como PK en toda la jerarquía). – ChssPly76

+0

El referenciadoColumnName está específicamente allí para abordar este tipo de problema. Si creo una tabla TYPE_NAMES que se une a TYPE_ID de la tabla FOO, funciona como se esperaba. No permitir el mapeo en una columna en una tabla secundaria es un error o una limitación de diseño. No estoy tratando de determinar cuál. – Jherico

Cuestiones relacionadas