2012-09-10 23 views
5

He revisado muchas publicaciones en Internet y ninguna aborda mi problema. Realmente aprecio si alguien puede ayudar! Tengo OneToMany relación padre-hijo. Hibernate no inserta en cascada el elemento secundario (Nivel 2) cuando una de las claves compuestas secundarias es la clave externa del elemento primario (Nivel 1). Especifiqué cascade = "all" y no importa que el inverso sea verdadero o falso, los resultados son los mismos. No hay excepción, y simplemente no se inserta. Debajo de la impresión, se muestra que el Nivel1 se insertó con éxito, pero el Nivel 2 solo se seleccionó, no se insertó.No hay inserción en cascada para el niño con clave externa en el padre

de hibernación: insertar en sst.level_1 (, estado nombre) valores Hibernate (?,?): Seleccionar level2x_.LEVEL_1_ID, level2x_.TIMESEGMENT, level2x_.CATEGORY como CATEGORY4_ de sst.level_2 level2x_ donde level2x_.LEVEL_1_ID =? y level2x_.TIMESEGMENT =?

Las API son Hibernate 4.1.6/Spring 3.1.2.

Aquí es la definición de la tabla de MySQL:

CREATE TABLE level_1 
(
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
STATUS int, 
NAME varchar(255) 
); 

CREATE TABLE level_2 
(
LEVEL_1_ID int, 
TIMESEGMENT int, 
CATEGORY varchar(2), 
PRIMARY KEY (LEVEL_1_ID, TIMESEGMENT), 
FOREIGN KEY (LEVEL_1_ID) REFERENCES level_1(ID) ON DELETE CASCADE 
); 

Aquí está el código de prueba.

public class Test { 

    public static void main(String[] args) { 
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
     doInsert(context); 
    } 

    private static void doInsert(ApplicationContext context) { 

     Level1 level1 = new Level1(); 
     Level2 level2 = new Level2(); 

     level1.setName("LEVEL 1 NAME"); 
     level1.setStatus(1); 
     level1.getLevel2s().add(level2); 

     level2.setLevel1(level1); 
     level2.setCategory("CA"); 
     level2.setId(new Level2Id(level1.getId(), 10)); 

     Level1DAO level1DAO = (Level1DAO) context.getBean("Level1DAO"); 
     level1DAO.save(level1); 
    } 
} 

Mapeo para el Nivel 1:

<hibernate-mapping> 
    <class name="com.jc.hibernate.Level1" table="level_1" catalog="sst"> 
     <id name="id" type="java.lang.Integer"> 
      <column name="ID" /> 
      <generator class="identity" /> 
     </id> 
     <property name="status" type="java.lang.Integer"> 
      <column name="STATUS" /> 
     </property> 
     <property name="name" type="java.lang.String"> 
      <column name="NAME" /> 
     </property> 
     <set name="level2s" inverse="true" cascade="all"> 
      <key> 
       <column name="LEVEL_1_ID" not-null="true" /> 
      </key> 
      <one-to-many class="com.jc.hibernate.Level2" /> 
     </set> 
    </class> 
</hibernate-mapping> 

Mapeo para el Nivel 2:

<hibernate-mapping> 
    <class name="com.jc.hibernate.Level2" table="level_2" catalog="sst"> 
     <composite-id name="id" class="com.jc.hibernate.Level2Id"> 
      <key-property name="level1Id" type="java.lang.Integer"> 
       <column name="LEVEL_1_ID" /> 
      </key-property> 
      <key-property name="timesegment" type="java.lang.Integer"> 
       <column name="TIMESEGMENT" /> 
      </key-property> 
     </composite-id> 
     <many-to-one name="level1" class="com.jc.hibernate.Level1" update="false" insert="false" fetch="select"> 
      <column name="LEVEL_1_ID" not-null="true" /> 
     </many-to-one> 
     <property name="category" type="java.lang.String"> 
      <column name="CATEGORY" length="2" /> 
     </property> 
    </class> 
</hibernate-mapping> 

Respuesta

1

Su asignación parece ser correcta.

Su problema parece que es la línea

level2.setId(new Level2Id(level1.getId(), 10)); 

Cuando se ejecuta esta línea, el level1 todavía no tiene su ID asignado a la misma, por lo que será nulo. Como level_1_id y segmento son clave primaria compuesta, ninguno de ellos puede ser nulo. por lo que tendría que hacer primero

Integer generatedId = (Integer) Session.save(level1) 

Esto hace una inserción inmediata en la base de datos y devuelve el identificador. continuación, puede utilizar el identificador para crear Lever2Id

level2.setId(new Level2Id(generatedId , 10)); 

He intentado esto en MySQL con las asignaciones que nos ha facilitado y su funcionamiento muy bien.

Si no lo guarda por primera vez el primer level1, me da error

Column 'LEVEL_1_ID' cannot be null 

ACTUALIZACIÓN

Desgraciadamente parece que la cascada no funciona automáticamente cuando hay claves compuestas en el niño y una de la columna en la clave compuesta se refiere a la clave principal del elemento primario. Por lo tanto, debe establecer los id y las relaciones manualmente como se muestra arriba.

Tenga en cuenta que si el elemento secundario tiene una identificación de una sola columna y su relación es uno a uno, Hibernate puede deducir que la columna principal secundaria proviene de Parent y puede establecerse automáticamente.Ver here y here

Los documentos también sugieren que este here

“Usted no puede utilizar un IdentifierGenerator para generar claves compuestas. lugar la aplicación debe asignar sus propios identificadores.”

La clase de hibernación que representa la cartografía Identificación del compuesto es org.hibernate.mapping.Component (que se utiliza para el componente, elemento compuesto, identificador compuesto, etc) y el valor predeterminado La estrategia de generación de id. está "asignada" a menos que haya proporcionado un generador de id. personalizado que lo lleve a un camino de hacking creando su propio CompositeUserType (para Id compuesto) y use un generador de identificador personalizado como se menciona here. Pero esto se ve feo y excesivo.

cuanto a ahorro de padres e hijos y el establecimiento de relación de forma manual en una transacción Primera eliminar la cascada de todas las opciones de su elemento del registro padre o ponerlo en "ninguno", por lo que no hay persistencia transitiva por Hibernate. Guardar padre solo guardará padre y no hijo.

Luego prepare Parent y Child con todas las propiedades que tiene disponibles y establezca la relación de objeto desde ambos lados de esta manera (para asegurar la consistencia del modelo). Método addChild

Parent parent = new Parent(); 
    parent.setStatus(1 ); 
    parent.setName("Parent"); 
    ChildId childId = new ChildId();  
    Child child = new Child(childId);  
    child.setCategory("TH");  
    parent.addChild(child); 
    child.getId().setTimesegment(10); 

de los padres añade secundario a la colección, así como establecer es el padre del niño en un método conveniente

public void addChild(Child child){ 
    if (child !=null){ 
     getChildrens().add(child); 
     child.setParent(this); 
    } 

    } 

Luego de tener un método en el DAO que toma niño como parámetro y también es transaccional, algo como esto (aunque la mejor manera es hacer que la capa de servicios transaccionales en lugar de la capa DAO)

public void save(Child child){ 

    //get Session 
    Session session =....... 
    Transaction tx = session.beginTransaction(); 
    Integer generatedId = (Integer)session.save(child.getParent()); 
    ChildId childId = child.getId(); 
    childId.setChildId(generatedId);  
    session1.save(child); 
    tx1.commit(); 
    HibernateUtil.closeSession(); 
} 

a partir de ahora esto es lo que puedo sugerir.

+0

Gracias por la respuesta. Tiene sentido que el identificador de nivel 1 sea nulo, lo que causó el problema. En este caso, ¿cómo configuraría la transacción para que sea atómica para los niveles 1 y 2? – John

+0

Ver mis actualizaciones para salvar tanto al niño como a la madre en la transacción – Shailendra

0

Solo para actualizar. Eliminé la identificación compuesta. En su lugar, use la clave sustituta generada en la base de datos en la tabla secundaria y una clave externa en la ID principal. Esto funciona con la relación uno a muchos con cascade = "all". Guardar padre guarda hijo en un solo paso. Esto no resuelve la pregunta original, sino una alternativa.

CREATE TABLE level_11 (
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
STATUS int, NAME varchar(255)); 

CREATE TABLE level_22 (ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
LEVEL_11_ID int NOT NULL, 
TIMESEGMENT int NOT NULL, 
FOREIGN KEY (LEVEL_11_ID) REFERENCES level_11(ID) ON DELETE CASCADE, 
CONSTRAINT UQ_LEVEL2 UNIQUE (LEVEL_11_ID, TIMESEGMENT)); 
Cuestiones relacionadas