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")