Hibernate композитный идентификатор слияния
Я использую Hibernate с JPA и имею отношение как это:
@Entity
@Table(name = "first")
public class First {
...
@OneToMany(mappedBy = "first")
private List<Availability> availabilities;
...
}
@Entity
@Table(name = "second")
public class Second {
...
@OneToMany(mappedBy = "second")
private List<Availability> availabilities;
...
}
@Entity
@Table(name="availability")
public class Availability implements Serializable {
@Id
@ManyToOne
@JoinColumn(name = "first_id")
private First first;
@Id
@ManyToOne
@JoinColumn(name = "second_id")
private Second second;
@Column(name = "availability")
private Integer availability;
...
hashcode and equals
}
Я хочу управлять этими 3 объектами отдельно. Первый и второй работают нормально, но когда я пытаюсь слить () третий, postgresql получает нулевое значение вместо идентификатора и возникает исключение нарушения ограничения. Зачем? Могу ли я даже использовать слияние на этой сущности, чтобы добавить новые строки в таблицу?
обновление: слияние что-то вроде этого:
public Availability setAvailability(Availability a) {
return em.merge(a);
}
где доступность десериализуется из внешнего интерфейса (просто чтобы упомянуть, что коллекции "ключевых" классов в нем отделены).
2 ответа
Я решил проблему, избегая использования Composite ID. Переосмыслив проблему, я обнаружил, что в этом случае я действительно могу использовать уникальное ограничение.
@Entity
@Table(name = "availabilities", uniqueConstraints = { @UniqueConstraint(columnNames = {
"first_id", "second_id" }) })
@SequenceGenerator(initialValue = 1, name = "availabilities_sequence", sequenceName = "availabilities_sequence")
public class Availability implements IAvailability, Serializable {
private static final long serialVersionUID = -2977047920673617888L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "availabilities_sequence")
@Column(name = "id")
private Integer id;
@ManyToOne
@JoinColumn(name = "first_id", nullable=false)
private First first;
@ManyToOne
@JoinColumn(name = "second_id", nullable=false)
private Second second;
@Column(name = "availability", nullable=false)
private Integer availability;
...
}
Вы уже решили свою проблему, но я оставляю здесь предложение для пользователей, которым необходимо использовать первичные ключи из нескольких столбцов и которые не могут изменить базу данных.
Очевидно, что использование нескольких аннотаций @Id само по себе приводит к несогласованности при попытке слияния: вы присваиваете все поля, а затем во время слияния поля, помеченные @Id, получают присвоенные нулевые значения (и слияние завершается неудачно). Я могу угадать объяснение: hibernate не ожидает, что будет неуправляемый объект с назначенными @Ids; но это проблема только с несколькими первичными ключами столбцов, так что, похоже, это ошибка.
Примечание: вызов сохранится вместо слияния (но в некоторых случаях вы дублируете строки или получаете ошибки ограничения базы данных, поскольку первичный ключ в этом случае проверяется только на уровне базы данных).
Я решил похожую проблему, используя @IdClass. В вашем старом коде вы должны:
- Создайте класс (например, AvailabilityId) с полями first и second (точно совпадающими с полями, аннотированными @Id в классе Availability) и переопределив методы equals и hashCode
- В AvailabilityId аннотации не требуются, кроме @Override (кстати, если вы используете @Column в полях Availability и AvailabilityId, вы можете получить чудовищные ошибки). Это не должна быть сущность или таблица, это POJO.
- Аннотировать доступность класса с помощью @IdClass(AvailabilityId.class)
Это должно быть так. Извините за опоздание на 3 года, но если вы избавились от первичного ключа из нескольких столбцов, вы все равно оказались в лучшем мире.
Я попробовал @EmbeddedId, но он нарушил пару других требований (простое преобразование json->object и запросы, работающие как в SQL, так и в HQL с небольшими изменениями).