Conjunto persistente de enumeraciones en una asignación unidireccional de muchos a muchos

Estoy usando Hibernate 3.5.2-FINAL con anotaciones para especificar mis asignaciones de persistencia. Estoy teniendo problemas para modelar una relación entre una aplicación y un conjunto de plataformas. Cada aplicación está disponible para un conjunto de plataformas.

De todas las lecturas y búsquedas que he realizado, creo que debo mantener la clase de enumeración de la plataforma como una Entidad y tener una tabla de unión para representar la relación de muchos a muchos. Quiero que la relación sea unidireccional en el nivel del 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.

Aquí están mis clases de 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 _platforms; // Setters and getters... } 

Cuando ejecuto la herramienta Hibernate hbm2ddl, veo lo 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 apropiadas también se crean desde esta tabla a la tabla de la aplicación y la tabla de la plataforma. Hasta aquí todo bien.

Un problema que estoy encontrando es cuando bash persistir un objeto de aplicación:

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

Lo que me gustaría que sucediera es crear las filas apropiadas en la tabla de la Plataforma, 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 encontrando es obtener la siguiente excepción de 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 se conserva cuando bash persistir la aplicación. Se supone que las anotaciones en cascada se ocupan de eso, pero no sé qué está mal.

Entonces mis preguntas son:

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

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

Debe decidir si su Platform es una entidad o no.

Si es una entidad, no puede ser una enum , ya que 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 de muchos a muchos. En este caso, puede representar un conjunto de Platform como un campo entero con indicadores de bits o como una relación simple de uno a muchos. JPA 2.0 simplifica este último caso con @ElementCollection :

 @ElementCollection(targetClass = Platform.class) @CollectionTable(name = "TBL_APP_PLATFORM", joinColumns = @JoinColumn(name = "APP_ID")) @Column(name = "PLATFORM_ID") protected Set _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.

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

 public enum TestEnum { A, B } 

Luego en tu clase de Entidad:

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