Estoy usando Hibernate con anotaciones (en la primavera), y tengo un objeto que tiene una ordenada, muchos-a-uno de la relación que un objeto secundario que tiene una clave principal compuesta, uno de los componentes de lo que es una clave externa para la identificación del objeto primario.

La estructura se ve algo como esto:

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

He probado una gran variedad de combinaciones de anotaciones, ninguno de los cuales parece funcionar. Esto es lo más cercano que he sido capaz de llegar con:

@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;

    ...
}

Llegué a esta después de un largo combate de experimentación en el que la mayoría de mis otros intentos produjo entidades que hibernar ni siquiera podía cargar por varias razones.

ACTUALIZACIÓN: Gracias a todos por los comentarios, me han hecho algunos progresos. He hecho un par de retoques y creo que está más cerca (he actualizado el código anterior). Ahora, sin embargo, el problema está en insertar. El objeto principal parece guardar bien, pero el niño de los objetos no son de ahorro, y por lo que he podido constatar es que la hibernación no es llenar la parentId parte de la (composite) de la clave principal de el niño de los objetos, así que me estoy poniendo un no-único error:

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

Estoy rellenando la name y pos atributos en mi propio código, pero, por supuesto, no conozco a los padres de IDENTIFICACIÓN, porque no se ha guardado aún. Cualquier idea sobre cómo convencer a hibernate para que llene esto?

Gracias!

  • La mejor de las suertes. Un proyecto en el que estaba en tuvo que abandonar totalmente las claves compuestas en favor de claves sintéticas debido a la mala apoyo desde el estado de Hibernación.
  • La excepción podría ser causada por el uso de la Lista de<>. Debe usar Set<> para evitar la clave duplicación mediante el uso de hash y es igual a la definición. Hibernate trata de dos «lógicamente idénticos» elementos en una lista de dos elementos, a continuación, provocó la excepción.
InformationsquelleAutor Kris Pruden | 2010-04-09

8 Comentarios

  1. 14

    La Dotación libro Persistencia Java con Hibernate tiene un ejemplo de esbozar cómo hacer esto en la Sección 7.2. Afortunadamente, incluso si usted no es el propietario del libro, usted puede ver el código fuente de ejemplo de esta descargando el JPA versión de la Caveat Emptor proyecto de ejemplo (enlace directo aquí) y el examen de las clases de Category y CategorizedItem en el auction.model paquete.

    También voy a resumir la clave de las anotaciones. Me dejan saber si sigue siendo un no-go.

    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; }
    }
    • Lamentablemente, eso no ayuda: me sale el mismo error. Los únicos cambios que he visto desde lo que he tenido son la suma de la @ForeignKey atributo, y la adición de la {insertable,actualizable}=false en el atributo primario. Hay algo que no estoy viendo? Gracias!
    • Hay algunos otros cambios. (1) Tratar de cambiar el parentId tipo de datos a Tiempo; (2) creo que los captadores son necesarios; (3) prestar mucha atención a los nombres de columna para ParentObject#id, ParentObject#attrs, ChildObject.Pk#objectId, y ChildObject#de los padres; (4) El campo de id de ParentObject necesidades de la @Id de anotación (y @GeneratedValue si no estás manualmente el suministro de la identificación); (5) he cambiado el nombre de ChildObject#pk a ChildObject#id, aunque no creo que este cambio era necesario. Si tienes todo esto, se puede suministrar el código de lectura que tirar con algunas contexto que lo rodea?
    • Re (2): Getters y setters no son necesarias. Hibernate puede construir poderes para hacer lo que se necesita hacer. Me parece mejor considerar esta «magia».
    • Gracias por la aclaración acerca de los métodos getter/setter. @Kris: yo también, no creo que el parentId campo de la realidad necesita a ser Largo; sólo estoy tratando de minimizar el número de diferencias entre mi código de trabajo y el código.
    • Debe usar Set<> para evitar la clave duplicación mediante el uso de hash y es igual a la definición. Hibernate trata de dos «lógicamente idénticos» elementos en una lista de dos elementos
  2. 6

    Debe incorporar la ParentObject de referencia sólo en ChildObject.Pk vez que el mapa de los padres y parentId por separado:

    (getters, setters, Hibernate atributos no relacionados con el problema y miembros claves de acceso omitido)

    class ChildObject { 
        @Embeddable
        static class Pk {
            @ManyToOne...
            @JoinColumn(name="parentId")
            ParentObject parent;
    
            @Column...
            String name...
            ...
        }
    
        @EmbeddedId
        Pk id;
    }

    En ParentObject usted, a continuación, acaba de poner @OneToMany(mappedBy="id.parent") y funciona.

    • limpio y claro. también funciona! Gracias.
  3. 2

    En primer lugar, en el ParentObject, «fijar» el mappedBy atributo que se debe establecer en "parent". También (pero esto es tal vez un error tipográfico) agregar un @Id anotación:

    @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
    }

    Luego, en ObjectChild, agregar un name atributo 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 agregar insertable = false, updatable = false a la @JoinColumn porque estamos repitiendo el parentId columna en la asignación de esta entidad.

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

    • Solo estuvimos una media hora tratando de resolver este problema y mi problema es que mi legado de base de datos tiene la clave externa en la tabla como «forecastId» y eso es lo que he copiado en mi JPA anotación y por razones desconocidas, la Primavera decidió que la columna debe ser «forecast_id» en la base de datos y se caía. Debe ser un algoritmo para identificar automáticamente las columnas que estaba un poco mal. He cambiado el @Column(name= a «forecastid» (todo en minúsculas) y la Primavera decidió comportarse.
  4. 2

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

    Finalmente, me fui por delante y le dio al niño objeto de su propia sintético clave y dejar de Hibernación de gestionar. Es un no es ideal, ya que la clave es casi tan grande como el resto de los datos, pero funciona.

    • ‘@JoinColumns({ @JoinColumn(name=»userfirstname_fk», referencedColumnName=»nombre»), @JoinColumn(name=»userlastname_fk», referencedColumnName=»apellido») })’
    • Una buena decisión. Tratando de hacer cualquier cosa, excepto de cosas básicas en modo de Hibernación exige demasiado de la onu-intuitivo cachondeo. Yo suelo hacer lo mismo – dar TODO lo principal que mantiene las cosas más simples para Hibernar a entender.
  5. 1

    Encontrado esta pregunta buscando la respuesta a su problema, pero las respuestas no resolver mi problema, porque yo estaba buscando @OneToMany que no es tan buena de un ajuste de la estructura de la tabla que me iba después. @ElementCollection es el más adecuado en mi caso. Uno de los errores de los que yo creo es que aunque se ve en toda la fila de las relaciones como un ser único, no sólo en las filas de identificación.

    @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)
    }
  6. 0

    Parece que tienes muy cerca, y estoy tratando de hacer lo mismo en mi sistema actual. Empecé con la clave suplente pero quisiera eliminar en favor de una clave principal compuesta compuesto por el padre del PK y el índice en la lista.

    Yo era capaz de conseguir un uno-a-uno que comparte la PK de la tabla maestra mediante el uso de un «extranjero» generador:

    @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 usted podría agregar el @GenericGenerator y @GeneratedValue para resolver el problema de la Hibernación no la asignación de los padres de la recién adquirida PK durante la inserción.

  7. 0

    Después de guardar el objeto Padre, usted tiene que establecer explícitamente el parentId en el Niño de los objetos para los inserta en el Niño de los objetos de trabajo.

  8. 0

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

    Aquí está su entidad constructor, pero también puedes hacerlo en el método setter.
    También, he utilizado un objeto de Colección, pero debe ser el mismo 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 debemos configurar manualmente todos los hijos del padre de identificación antes de guardar el /los padre (junto con todos los niños)

Dejar respuesta

Please enter your comment!
Please enter your name here