Подтвердить что ты не робот

Как иметь отношения на основе неключевого поля?

У меня есть два объекта, как показано ниже, когда я пытаюсь добавить элементы в свой автомобильный стол, он показывает следующее сообщение об ошибке, поэтому он не позволяет мне иметь более одного автомобиля с передачей "Авто".

Ошибка:

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

Объекты:

Автомобиль

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

Примеры значений для таблицы автомобилей:

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;

   ...
}

Ожидаемые значения для таблицы CarFactory

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

Ive последовал за ответом этого question, но это не сработало.

Короче говоря, мне нужно иметь таблицу с двумя внешними ключами из других таблиц с комбинированным первичным ключом. Он не должен форсировать уникальный внешний ключ в участвующих таблицах.

4b9b3361

Ответ 1

Я подражал вашему прецеденту, и вы можете найти тест на 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;
    }
}

Вот как вы добавляете некоторые тестовые данные:

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

И тест отлично работает:

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

Update

Вы получаете исключение из-за следующего уникального ограничения:

alter table Car add constraint UK_iufgc8so6uw3pnyih5s6lawiv  unique (transmission)

Это нормальное поведение, так как 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;
    }
}

Таким образом, vin является уникальным, и ассоциация Child может ссылаться на один и только один родитель.

Ответ 2

Проблема заключается в том, что вы используете не первичное ключевое поле в качестве внешнего ключа, который кажется неправильным, а поле transmission должно быть уникальным, эта строка неверна:

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

У вас есть отображение 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
}

Класс 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() {}

}

Класс автомобиля

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

    ...

}

И то же самое для класса Factory, обратите внимание, что существует несколько способов определить embedded id или composite id, посмотрите:

Примечание:

В моем примере я не использовал поле transmission в составном идентификаторе, но вы можете его использовать, вы можете увидеть пример ниже:

Ответ 3

Почему вы не используете отношения @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;
    ...
}

... не тестировал код, но он должен работать.

Ответ 4

В вашем CarFactory есть отношение ManyToOne, ссылающееся на поле передачи в Car. Это означает, что поле передачи в Car должно быть уникальным.

Это похоже на то, что вы пытаетесь добавить несколько элементов с тем же значением передачи в свой стол Car, однако ваш дизайн предполагает, что вам нужна только одна запись в таблице Car для каждой передачи, и вам нужно добавить только несколько CarFactory записей на передачу.

Ответ 5

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

Измените с помощью этого

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

Ответ 6

В вашем вопросе остается место для интерпретации: для одной модели автомобиля, которая строится на разных фабриках, может потребоваться множество заводов. Или разные части экземпляра автомобиля строятся на разных заводах. Для вашего образца и ожидаемых данных решения нет. Если на разных заводах могут производиться автомобили с одинаковыми типами передач, невозможно определить, какой автомобиль был изготовлен в/связан с правом factory. Вы можете только сказать, что это была одна из фабрик с той же передачей. Однако это не поддерживается никаким сопоставлением. Если вы хотите связать правильные factory/фабрики, вам нужно добавить дополнительную информацию о CarFactory в таблицу Car (например, Factory). Это зависит от ваших требований, но, я думаю, ответ chsdk близок к ним.