2010-06-30 8 views
17

Estoy usando Hibernate 3.5.2-FINAL con anotaciones para especificar mis asignaciones de persistencia. Estoy luchando con el modelado de una relación entre una aplicación y un conjunto de plataformas. Cada aplicación está disponible para un conjunto de plataformas.Conjunto persistente de entradas en un mapeo unidireccional de muchos a varios

De toda la lectura y búsqueda que he hecho, creo que necesito que la plataforma enum class se persista como Entity, y tener una tabla join para representar la relación many-to-many. Quiero que la relación sea unidireccional a nivel de objeto, es decir, quiero poder obtener la lista de plataformas para una aplicación determinada, pero no necesito encontrar la lista de aplicaciones para una plataforma determinada.

Éstos son mis clases del modelo simplificado:

@Entity 
@Table(name = "TBL_PLATFORM") 
public enum Platform { 
    Windows, 
    Mac, 
    Linux, 
    Other; 

    @Id 
    @GeneratedValue 
    @Column(name = "ID") 
    private Long id = null; 

    @Column(name = "NAME") 
    private String name; 

    private DevicePlatform() { 
     this.name = toString(); 
    } 

    // Setters and getters for id and name... 
} 

@Entity 
@Table(name = "TBL_APP") 
public class Application extends AbstractEntity implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Column(name = "NAME") 
    protected String _name; 

    @ManyToMany(cascade = javax.persistence.CascadeType.ALL) 
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE}) 
    @JoinTable(name = "TBL_APP_PLATFORM", 
       joinColumns = @JoinColumn(name = "APP_ID"), 
       inverseJoinColumns = @JoinColumn(name = "PLATFORM_ID")) 
    @ElementCollection(targetClass=Platform.class) 
    protected Set<Platform> _platforms; 

    // Setters and getters... 
} 

Cuando ejecuto la herramienta hbm2ddl Hibernate, veo el siguiente (estoy usando MySQL):

create table TBL_APP_PLATFORM (
    APP_ID bigint not null, 
    PLATFORM_ID bigint not null, 
    primary key (APP_ID, PLATFORM_ID) 
); 

Las claves externas son adecuadas también creado a partir de esta tabla para la tabla de la aplicación y la plataforma. Hasta aquí todo bien.

Un problema que estoy corriendo en es cuando trato de persistir un objeto de aplicación:

Application newApp = new Application(); 
newApp.setName("The Test Application"); 
Set<DevicePlatform> platforms = EnumSet.of(Platform.Windows, Platform.Linux); 
newApp.setPlatforms(platforms); 
applicationDao.addApplication(newApp); 

Lo que me gustaría que suceda es para las filas apropiadas en la tabla de plataforma para crear, es decir, crear una fila para Windows y Linux, si aún no existen. Luego, se debe crear una fila para la nueva aplicación y luego la asignación entre la nueva aplicación y las dos plataformas en la tabla de unión.

Un problema que estoy corriendo en está recibiendo la siguiente excepción en tiempo de ejecución:

2010-06-30 13:18:09,382 6613126-0 ERROR FlushingEventListener Could not synchronize database state with session org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.model.Platform 

De alguna manera, el conjunto de la plataforma no está siendo persistió cuando trato de persistir la aplicación. Se supone que las anotaciones en cascada se encargan de eso, pero no sé qué ocurre.

Así que mis preguntas son:

  1. ¿Hay una mejor manera de modelar lo que quiero hacer, por ejemplo, está usando un Enum apropiado?
  2. Si mi modelo está bien, ¿cómo puedo conservar correctamente todos los objetos?

He estado luchando con esto durante horas y he tratado de volver a crear todo el código anterior, pero puede que no sea completo y/o preciso. ¡Espero que alguien señale algo obvio!

Respuesta

32

Debería decidir si su Platform es una entidad o no.

Si se trata de una entidad, no puede ser un enum, porque la lista de posibles plataformas se almacena en la base de datos, no en la aplicación. Debería ser una clase regular con la anotación @Entity y tendrá una relación normal de muchos a muchos.

Si no es una entidad, entonces no necesita la tabla TBL_PLATFORM, y no tiene una relación muchos a muchos.En este caso, puede representar un conjunto de Platform s como un campo entero con marcas de bits, o como una simple relación de uno a muchos. JPA 2.0 hace que el último caso sencillo con @ElementCollection:

@ElementCollection(targetClass = Platform.class) 
@CollectionTable(name = "TBL_APP_PLATFORM", 
    joinColumns = @JoinColumn(name = "APP_ID")) 
@Column(name = "PLATFORM_ID") 
protected Set<Platform> _platforms; 

-

create table TBL_APP_PLATFORM ( 
    APP_ID bigint not null, 
    PLATFORM_ID bigint not null, -- the ordinal number of enum value 
    primary key (APP_ID, PLATFORM_ID) 
); 

y enum Platform sin anotaciones.

+0

que estaba usando una versión anterior de Hibernate para generar el esquema. Al seguir http://stackoverflow.com/questions/2734971/hibernate3-maven-plugin-dependencies-for-newer-version-of-hibernate, establezco una dependencia a 3.5.3-FINAL y "org.hibernate.MappingException: No se pudo determinar el tipo para: java.util.Set "desapareció. Seguí el método @ElementCollection que sugirió, utilizando una enumeración simple sin entidad, y ahora Set se asigna a una tabla de unión. Y, al establecer @Enumerated en la propiedad, puedo establecer si los valores enum se almacenan en la base de datos como enteros o cadenas. – gnuf

+0

@axtavt ¿me pueden ayudar con http://stackoverflow.com/questions/7687409/hibernate-criteria-query-for-collection-table/7693793#7693793 Adjunté una recompensa allí ... Necesito ayuda ... –

+0

@ axtavt ¿Puedes agregar a tu publicación la estructura de la tabla principal "TBL_APP" por favor? Thnx –

2

Uso simple debajo del mapeo en su entidad. Supongamos que tenemos:

public enum TestEnum { A, B } 

A continuación, en la clase de entidad:

@ElementCollection(targetClass = TestEnum.class) 
@CollectionTable(
     name = "yourJoinTable", 
     joinColumns = @JoinColumn(name = "YourEntityId") 
) 
@Column(name = "EnumId") 
private final Set<TestEnum> enumSet= new HashSet<TestEnum >(); 
Cuestiones relacionadas