tutorial for english java hibernate jpa orm hibernate-mapping

java - for - hibernate orm



¿Cómo tener una relación basada en un campo no clave? (6)

¿Por qué no estás usando la relación @ManyToMany ?

@Entity public class Car implements java.io.Serializable { @Id @GeneratedValue long id; @Column(name="transmission", nullable = false) String transmission; @ManyToMany @JoinTable( name="CARFACTORY", joinColumns={@JoinColumn(name="transmission", referencedColumnName="transmission")}, inverseJoinColumns={@JoinColumn(name="factory_id", referencedColumnName="id")}) Set<Factory> factories; ... }

... no probé el código, pero debería funcionar.

Tengo dos entidades como las siguientes, cuando intento agregar elementos a la tabla de mi automóvil, muestra el siguiente mensaje de error, por lo tanto, no me permite tener más de un automóvil con transmisión "Auto".

Error:

#1062 - Duplicate entry ''Auto'' for key ''UK_bca5dfkfd4fjdhfh4ddirfhdhesr''

Entidades:

Coche

@Entity public class Car implements java.io.Serializable { @Id @GeneratedValue long id; @Column(name="transmission", nullable = false) String transmission; @OneToMany(fetch = FetchType.LAZY, mappedBy = "car") Set<CarFactory> factories; ... }

Valores de muestra para la tabla de coches :

10 Auto 12 Auto 43 Manual 54 Manual 65 Normal 68 Standard 90 Normal 99 NoGear

CarFactory

@Entity public class CarFactory implements java.io.Serializable { @Id @JoinColumn(name="transmission",referencedColumnName = "transmission") @ManyToOne Car car; @Id @JoinColumn(name="factory_id", referencedColumnName= "id") @ManyToOne Factory factory; ... }

Valores esperados para la tabla de CarFactory

Auto Fac1 Auto Fac2 Manual Fac1 Auto Fac5 Standard Fac6 Normal Fac3 NoGear Fac1

He seguido la respuesta de esta question también, pero no funcionó.

En pocas palabras, necesito tener una tabla con dos claves externas de otras tablas, con clave principal combinada. No debe forzar una clave externa única en las mesas participantes.


El problema aquí es que está utilizando un campo de clave no primaria como una clave externa que parece ser incorrecta y su campo de transmission , debe ser único, esta línea es incorrecta:

@JoinColumn(name="transmission",referencedColumnName = "transmission")

Aquí tiene una asignación de Many-To-Many que necesita una propiedad @EmbeddedId en la tabla de asociación, y su código debería ser así:

Clase de carfactory

@Entity public class CarFactory { private CarFactoryId carFactoryId = new CarFactoryId(); @EmbeddedId public CarFactoryId getCarFactoryId() { return carFactoryId; } public void setCarFactoryId(CarFactoryId carFactoryId) { this.carFactoryId = carFactoryId; } Car car; Factory factory; //getters and setters for car and factory }

Clase CarFactoryId

@Embeddable public class CarFactoryId implements Serializable{ private static final long serialVersionUID = -7261887879839337877L; private Car car; private Factory factory; @ManyToOne public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @ManyToOne public Factory getFactory() { return factory; } public void setFactory(Factory factory) { this.factory = factory; } public CarFactoryId(Car car, Factory factory) { this.car = car; this.factory = factory; } public CarFactoryId() {} }

Clase de coche

@Entity public class Car { @Id @GeneratedValue long id; @Column(name="transmission", nullable = false) String transmission; private Set<CarFactory> carFactories = new HashSet<CarFactory>(); @OneToMany(mappedBy = "primaryKey.car", cascade = CascadeType.ALL) public Set<CarFactory> getCarFactories() { return carFactories; } ...

}

Y lo mismo para la clase Factory , tenga en cuenta que hay varias formas de definir una embedded id o una composite id , observe:

  • Asignación de ManyToMany con clave principal compuesta y anotación.
  • Cómo crear una clave compuesta de hibernación usando anotaciones

Nota:

En mi ejemplo, no he usado el campo de transmission en el ID compuesto, pero puede usarlo, puede ver el siguiente ejemplo:


Emulé tu caso de uso y puedes encontrar la prueba en GitHub .

Estas son las asignaciones:

@Entity(name = "Car") public static class Car implements Serializable { @Id @GeneratedValue long id; @Column(name="transmission", nullable = false) String transmission; @OneToMany(fetch = FetchType.LAZY, mappedBy = "car") Set<CarFactory> factories; } @Entity(name = "Factory") public static class Factory implements Serializable { @Id @GeneratedValue long id; } @Entity(name = "CarFactory") public static class CarFactory implements Serializable { @Id @ManyToOne @JoinColumn(name = "transmission", referencedColumnName = "transmission") Car car; @ManyToOne @Id Factory factory; public void setCar(Car car) { this.car = car; } public void setFactory(Factory factory) { this.factory = factory; } }

Así es como se agregan algunos datos de prueba:

doInTransaction(session -> { Car car = new Car(); car.transmission = "Auto"; Car car1 = new Car(); car1.transmission = "Manual"; Factory factory = new Factory(); session.persist(factory); session.persist(car); session.persist(car1); CarFactory carFactory = new CarFactory(); carFactory.setCar(car); carFactory.setFactory(factory); CarFactory carFactory1 = new CarFactory(); carFactory1.setCar(car1); carFactory1.setFactory(factory); session.persist(carFactory); session.persist(carFactory1); });

Y la prueba funciona bien:

@Test public void test() { doInTransaction(session -> { List<CarFactory> carFactoryList = session.createQuery("from CarFactory").list(); assertEquals(2, carFactoryList.size()); }); }

Actualizar

Obtienes una excepción debido a la siguiente restricción única:

alter table Car add constraint UK_iufgc8so6uw3pnyih5s6lawiv unique (transmission)

Este es el comportamiento normal, ya que un FK debe identificar de forma única una fila PK. Como no puede tener más filas con el mismo PK, no puede tener una referencia de identificador FK más de una fila.

Tu mapeo es el problema. Necesitas hacer referencia a otra cosa, no a la transmision . Necesita un identificador de automóvil único, como un VIN (Número de identificación del vehículo), por lo que su asignación se convierte en:

@Entity(name = "Car") public static class Car implements Serializable { @Id @GeneratedValue long id; @Column(name="vin", nullable = false) String vin; @OneToMany(fetch = FetchType.LAZY, mappedBy = "car") Set<CarFactory> factories; } @Entity(name = "CarFactory") public static class CarFactory implements Serializable { @Id @ManyToOne @JoinColumn(name = "vin", referencedColumnName = "vin") Car car; @ManyToOne @Id Factory factory; public void setCar(Car car) { this.car = car; } public void setFactory(Factory factory) { this.factory = factory; } }

De esta manera, el vin es único y la asociación Child puede hacer referencia a uno y solo a uno de los padres.


Hay una relación ManyToOne en su CarFactory referencia al campo de transmisión en Car. Eso significa que el campo de transmisión en Car debe ser único.

Parece que está intentando agregar varios elementos con el mismo valor de transmisión a su tabla de Automóviles, sin embargo, su diseño sugiere que solo necesita una entrada en su tabla de Car por transmisión, y solo necesita agregar múltiples entradas de CarFactory por transmisión.


Su pregunta deja espacio para la interpretación: un conjunto de fábricas puede ser necesario para un modelo de automóvil, que se construye en diferentes fábricas. O se construyen diferentes partes de una instancia de automóvil en diferentes fábricas. Para su muestra y datos esperados, no hay solución. Si diferentes fábricas pueden producir / tener automóviles con el mismo tipo de transmisiones, no hay forma de determinar qué automóvil se produjo en / está asociado con la fábrica correcta. Solo se puede decir que fue una de las fábricas con la misma transmisión. Sin embargo, esto no es compatible con ningún mapeo. Si desea asociar la fábrica / fábricas adecuadas, debe agregar más información acerca de CarFactory a la tabla de Automóviles (por ejemplo, Fábrica). Depende de sus requisitos, pero supongo que la respuesta de chsdk se acerca a ellos.


@JoinColumn(name="transmission",referencedColumnName = "transmission")

Cambia con esto

@JoinColumn(name="car_id",referencedColumnName = "id")