Pregunta

estoy usando Hibernate con anotaciones (en primavera), y tener un objeto que tiene una ordenada, muchos-a-uno que un objeto secundario que tiene una clave principal compuesta, uno de cuyos componentes es una clave externa de nuevo a la identificación del objeto padre.

La estructura es como la siguiente:

+=============+                 +================+
| ParentObj   |                 | ObjectChild    |
+-------------+ 1          0..* +----------------+
| id (pk)     |-----------------| parentId       |
| ...         |                 | name           |
+=============+                 | pos            |
                                | ...            |
                                +================+

He intentado una variedad de combinaciones de anotaciones, ninguna de las cuales parece funcionar. Esto es lo más cerca que he sido capaz de llegar a:

@Entity
public class ParentObject {
    @Column(nullable=false, updatable=false)
    @Id @GeneratedValue(generator="...")
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    ...
}

@Entity
public class ChildObject {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(nullable=false, updatable=false)
        private String parentId;

        @Column(nullable=false, updatable=false)
        private String name;

        @Column(nullable=false, updatable=false)
        private int pos;

        @Override
        public String toString() {
            return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
        }

        ...
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name="parentId")
    private ParentObject parent;

    ...
}

Me llegó a esta después de un largo combate de la experimentación en la que la mayoría de mis otras entidades que los intentos de hibernación ni siquiera podía cargar por varias razones produjo.

ACTUALIZACIÓN: Gracias a todos por los comentarios; He hecho algun progreso. He hecho algunos cambios y creo que está más cerca (He actualizado el código anterior). Ahora, sin embargo, la cuestión es en el inserto. El objeto principal parece guardar muy bien, pero los objetos secundarios no están ahorrando, y lo que he podido determinar es que hibernación no está llenando la parte parentId de la clave primaria (compuesto) de los objetos secundarios, por lo que' m conseguir un error no es exclusivo:

org.hibernate.NonUniqueObjectException:
   a different object with the same identifier value was already associated 
   with the session: [org.kpruden.ObjectChild#null.attr1[0]]

Estoy poblar los atributos name y pos en mi propio código, pero por supuesto no sé el ID de los padres, ya que no se ha guardado todavía. Cualquier ideas sobre cómo convencer hibernación para llenar esto?

Gracias!

¿Fue útil?

Solución 4

Después de mucha experimentación y la frustración, que finalmente determinó que no puedo hacer exactamente lo que quiero.

En última instancia, se adelantó y dio el objeto hijo su propia clave sintética y dejar que Hibernate manejarlo. Es un no es ideal, ya que la clave es casi tan grande como el resto de los datos, pero funciona.

Otros consejos

El libro Manning Java Persistence con Hibernate tiene un ejemplo esbozar cómo hacer esto en la Sección 7.2 . Afortunadamente, incluso si usted no es dueño del libro, se puede ver un ejemplo de código fuente de esta descargando la versión APP de la proyecto de ejemplo caveat.emptor (enlace directo aquí ) y examinando el Category clases y CategorizedItem en el paquete auction.model.

También voy a resumir las anotaciones claves abajo. Hágamelo saber si todavía está a-Go no.

ParentObject:

@Entity
public class ParentObject {
   @Id @GeneratedValue
   @Column(name = "parentId", nullable=false, updatable=false)
   private Long id;

   @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
   @IndexColumn(name = "pos", base=0)
   private List<ChildObject> attrs;

   public Long getId () { return id; }
   public List<ChildObject> getAttrs () { return attrs; }
}

ChildObject:

@Entity
public class ChildObject {
   @Embeddable
   public static class Pk implements Serializable {
       @Column(name = "parentId", nullable=false, updatable=false)
       private Long objectId;

       @Column(nullable=false, updatable=false)
       private String name;

       @Column(nullable=false, updatable=false)
       private int pos;
       ...
   }

   @EmbeddedId
   private Pk id;

   @ManyToOne
   @JoinColumn(name="parentId", insertable = false, updatable = false)
   @org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
   private ParentObject parent;

   public Pk getId () { return id; }
   public ParentObject getParent () { return parent; }
}

Debe incorporar la referencia ParentObject solo en ChildObject.Pk en lugar de un mapa de los padres y parentId por separado:

(captadores, organismos, Hibernate atributos no relacionado con problemas de acceso y miembro de palabras clave omitidas)

class ChildObject { 
    @Embeddable
    static class Pk {
        @ManyToOne...
        @JoinColumn(name="parentId")
        ParentObject parent;

        @Column...
        String name...
        ...
    }

    @EmbeddedId
    Pk id;
}

En ParentObject a continuación, sólo hay que poner @OneToMany(mappedBy="id.parent") y funciona.

En primer lugar, en el ParentObject, "fijar" el atributo mappedBy que debe ser ajustado a "parent". También (pero esto es quizás un error tipográfico) añadir una anotación @Id:

@Entity
public class ParentObject {
    @Id
    @GeneratedValue
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    // getters/setters
}

A continuación, en ObjectChild, añadir un atributo name a la objectId en la clave compuesta:

@Entity
public class ObjectChild {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(name = "parentId", nullable = false, updatable = false)
        private String objectId;

        @Column(nullable = false, updatable = false)
        private String name;

        @Column(nullable = false, updatable = false)
        private int pos;
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private ParentObject parent;

    // getters/setters

}

y también añadir a la insertable = false, updatable = false @JoinColumn porque estamos repitiendo la columna de la parentId en el mapeo de esta entidad.

Con estos cambios, la persistencia y la lectura de las entidades está bien para mí (probado con Derby) de trabajo.

Se han encontrado esta pregunta en busca de la respuesta a la misma del problema, pero es respuestas no solucionaron mi problema, porque yo estaba buscando @OneToMany que no es tan bueno de un ajuste de la estructura de tabla que iba después. @ElementCollection es el sistema más adecuado en mi caso. Uno de los aspectos críticos de que yo creo es que aunque se ve en toda la fila de las relaciones como ser único, no sólo el ID filas.

@Entity
public class ParentObject {
@Column(nullable=false, updatable=false)
@Id @GeneratedValue(generator="...")
private String id;

@ElementCollection
@CollectionTable( name = "chidren", joinColumns = @JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;

...
}

@Embeddable
public static class ObjectChild implements Serializable {
    @Column(nullable=false, updatable=false)
    private String parentId;

    @Column(nullable=false, updatable=false)
    private String name;

    @Column(nullable=false, updatable=false)
    private int pos;

    @Override
    public String toString() {
        return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
    }

    ... getters and setters REQUIRED (at least they were for me)
}

Parece que ha llegado muy cerca, y yo estoy tratando de hacer lo mismo en mi sistema actual. Empecé con la clave sustituta, pero me gustaría sacarlo a favor de una clave principal compuesta que consta de PK de los padres y el índice de la lista.

Yo era capaz de conseguir una relación uno-a-uno que comparte el PK de la tabla maestra mediante el uso de un generador de "extranjero":

@Entity
@GenericGenerator(
    name = "Parent",
    strategy = "foreign",
    parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
    @Id
    @GeneratedValue(generator = "Parent")
    @Column(name = "parent_id")
    private int parentId;

    @OneToOne(mappedBy = "childObject")
    private ParentObject parentObject;
    ...
}

Me pregunto si podría añadir la @GenericGenerator y @GeneratedValue para resolver el problema de Hibernate no asignar la matriz de PK recién adquirido durante la inserción.

Después de guardar el objeto padre, usted tiene que establecer explícitamente la parentId en el que los objetos secundarios para las inserciones en los objetos del Niño para el trabajo.

Después de pasar tres días en esto, creo que he encontrado una solución, pero para ser honesto, no me gusta y que sin duda se puede mejorar. Sin embargo, funciona y resuelve nuestro problema.

Aquí está su constructor entidad, sino que también podría hacerlo en el método de selección. Además, he usado un objeto de colección pero debe ser igual o similar con la lista:

...
public ParentObject(Collection<ObjectChild> children) {
    Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
    for(ObjectChild obj:children){
        obj.setParent(this);
        occ.add(obj);
    }
    this.attrs = occ;
}
...

Básicamente, como alguien sugirió, primero tenemos que configurar manualmente todos Identificación del padre de los niños antes de guardar el padre (junto con todos los niños)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top