如何建立基于非关键字段的关系?

Dan*_*own 21 java orm hibernate jpa hibernate-mapping

我有两个实体如下,当我尝试将项目添加到我的汽车表时,它显示以下错误消息;因此,它不允许我有多个汽车进行'自动'传输.

错误:

 #1062 - Duplicate entry 'Auto' for key 'UK_bca5dfkfd4fjdhfh4ddirfhdhesr' 
Run Code Online (Sandbox Code Playgroud)

实体:

汽车

@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;
    ...
}
Run Code Online (Sandbox Code Playgroud)

汽车表的样本值:

10 Auto
12 Auto
43 Manual
54 Manual
65 Normal
68 Standard
90 Normal
99 NoGear
Run Code Online (Sandbox Code Playgroud)

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;

   ...
}
Run Code Online (Sandbox Code Playgroud)

CarFactory表的预期值

Auto Fac1
Auto Fac2
Manual Fac1
Auto Fac5
Standard Fac6
Normal Fac3
NoGear Fac1
Run Code Online (Sandbox Code Playgroud)

我也回答了这个问题,但它没有奏效.

长话短说,我需要有一个表,其中包含来自其他表的两个外键,并带有组合主键.它不应该在参与表中强制使用唯一的外键.

Vla*_*cea 8

我模仿了你的用例,你可以在GitHub上找到测试.

这些是映射:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是您添加一些测试数据的方式:

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);
});
Run Code Online (Sandbox Code Playgroud)

测试工作正常:

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

更新

由于以下唯一约束,您会收到异常:

alter table Car add constraint UK_iufgc8so6uw3pnyih5s6lawiv  unique (transmission)
Run Code Online (Sandbox Code Playgroud)

这是正常行为,因为FK必须唯一地标识PK行.就像你不能有更多具有相同PK的行一样,你不能有一个FK标识符引用多于一行.

你映射是个问题.你需要引用别的东西,而不是transmision.您需要一个唯一的汽车标识符,如VIN(车辆识别号),因此您的映射将变为:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,它vin是唯一的,并且子关联可以引用一个且仅一个父代.

  • 检查我更新的答案.它工作得很好. (2认同)

cнŝ*_*ŝdk 7

这里的问题是你使用非主键字段作为外键似乎是不正确的,你的transmission字段应该是唯一的,这一行不正确:

@JoinColumn(name="transmission",referencedColumnName = "transmission")
Run Code Online (Sandbox Code Playgroud)

你有一个Many-To-Many映射,在关联表中需要一个@EmbeddedId属性,你的代码应该是这样的:

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
}
Run Code Online (Sandbox Code Playgroud)

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() {}

}
Run Code Online (Sandbox Code Playgroud)

汽车课

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

    ...
Run Code Online (Sandbox Code Playgroud)

}

对于Factory类来说同样的事情,请注意有几种方法来定义embedded id或者a composite id,看看:

注意:

在我的示例中,我没有transmission在复合ID中使用字段,但您可以使用它,您可以看到以下示例: