2015-04-08 3 views
1

При выполнении следующего кода, я получаю org.hibernate.exception.ConstraintViolationException исключениеorg.hibernate.exception.ConstraintViolationException при использовании нескольких ManyToMany отношений

жаловалась, что

org.h2.jdbc.JdbcSQLException: NULL not allowed for column "SECONDARYELEMENTS_ID"; 

Я знаю, что это вызвано наличием двух @ManyToMany отношений от объекта Container до объектов Element. Если я удалю

@ManyToMany(cascade = CascadeType.ALL) 
List<Element> secondaryElements; 

из класса Container все работает нормально.

Что мне здесь не хватает?

Дайте мне знать, если вам нужна дополнительная информация.

@Transactional 
public class JPA2Runner { 
    //hidding Spring Data JPA repository 
    @Autowired 
    ContainerDAO containerDAO; 
    public boolean run() throws Exception{ 
     Container container1 = new Container(); 
     Container container2 = new Container(); 
     Element element1 = new Element(container1, container2); 
     Element element2 = new Element(container2, container1); 
     container1.getPrimaryElements().add(element1); 
     container1.getSecondaryElements().add(element2); 
     container2.getPrimaryElements().add(element2); 
     container2.getSecondaryElements().add(element1); 
     containerDAO.saveContainer(container1); 
     return true; 
    } 
} 

@Entity 
public class Container extends AbstractEntity {   
    @ManyToMany(cascade = CascadeType.ALL) 
    List<Element> primaryElements; 
    @ManyToMany(cascade = CascadeType.ALL) 
    List<Element> secondaryElements; 

    public Container(){ 
     primaryElements =new ArrayList<Element>(); 
     secondaryElements = new ArrayList<Element>(); 
    } 
} 

@Entity 
public class Element extends AbstractEntity { 
    @ManyToOne(cascade = CascadeType.ALL) 
    private Container dedicatedContainer1; 
    @ManyToOne(cascade = CascadeType.ALL) 
    private Container dedicatedContainer2; 

    public Element(){}  
    public Element(Container container1, Container container2){ 
     this.dedicatedContainer1 = container1; 
     this.dedicatedContainer2 = container2; 
    } 
} 

Update 1: Может быть, требуется указать @JoinTable в случае, если есть несколько отношения к такому же типу?

Update 2: Благодаря @ducksteps комментариев и подсказки, которые я смог найти обходной путь для этой проблемы. Проблема состоит в том, что приведенное выше определение генерирует присоединиться таблицу с ключами для обоих списков элементов, т.е.

create table Container_Element (Container_id bigint not null, secondaryElements_id bigint not null, primaryElements_id bigint not null) 

однако, сохранение контейнер генерирует следующий вставку в соединительной таблице

insert into Container_Element (Container_id, primaryElements_id) values (?, ?) 

который вызывает Исключение ConstraintViolation. Исправление состоит в том, чтобы явно определить две таблицы Join, используя

@ManyToMany(cascade = CascadeType.ALL) 
@JoinTable(name="Container_PrimaryElements") 
List<Element> primaryElements; 
@ManyToMany(cascade = CascadeType.ALL) 
@JoinTable(name="Container_SecondaryElements") 
List<Element> secondaryElements; 

который, похоже, работает.

Однако, я все еще интересно, есть ли

  • являются лучшие решения для этой проблемы?
  • с использованием таблицы соединений для отношений ManyToMany «должен» фактически работать (согласно спецификации)?

ответ

1

Я могу думать о двух возможных причинах:

  1. NOT NULL или REFERENCES ограничение для SECONDARYELEMENTS_ID столбца в таблице CONTAINER_ELEMENT отношений не отложенное. Во время вызова saveContainer() вы сохраняете связь с нерезидентным объектом. Поскольку это отношение является круглым (Element относится к Container относится к Element относится к [...]), это не может быть решено путем переупорядочения. Я не уверен, как h2 справляется с этим, но я столкнулся с этой проблемой при работе с Postgres.

  2. Генерация идентификатора (в вашем случае) не работает с каскадными правилами.По какой-то причине Element не получает сгенерированный идентификатор - поэтому JPA сохраняется с идентификатором NULL (если это позволяет таблица ELEMENT).

Позвольте мне знать, если вам нужна дополнительная информация.

От вас:

Debug выход, особенно сгенерированные операторы SQL (в идеале с ответами) поможет выяснить, в какой момент сделка не удается. Определения таблиц (CREATE TABLE [...], особенно определения ограничений) были бы полезными, чтобы выяснить, может ли быть первая причина в этом случае.

От других:

Кто-то более опытное с h2 мог бы сказать, если вам нужно «волшебство» (как сделать REFERENCES откладываемые в Postgres) для вставки данных с круговыми отношениями.

Update 1: Может быть, что требуется указать @JoinTable в случае, если есть несколько отношения к такому же типу?

Возможно. Спецификация имеет этот пример:

Сущность сотрудника сопоставляется с таблицей EMPLOYEE. Патент Entity Patent сопоставляется с таблицей PATENT. Существует таблица соединений, которая называется EMPLOYEE_PATENT (сначала имя владельца). Эта таблица соединений имеет два столбца внешнего ключа. Один столбец с внешним ключом относится к таблице EMPLOYEE и имеет того же типа, что и первичный ключ EMPLOYEE. Этот столбец внешнего ключа имеет имя EMPLOYEE_, где обозначается имя столбца первичного ключа таблицы EMPLOYEE. Другой столбец внешнего ключа относится к таблице ПАТЕНТ и имеет тот же тип, что и первичный ключ PATENT. Этот столбец внешнего ключа равен с именем PATENTS_, где обозначается имя столбца первичного ключа таблицы PATENT.

Таким образом, имя таблицы происходит от имен сущностей, а имена столбцов производятся из имен целевого столбца отношения и имен полей. Какую структуру имеет ваша таблица соединений? Если у него больше двух столбцов, это ваша проблема.

Обновление 2: [...] лучшие решения по этой проблеме?

Зависит от вашего прецедента. Вы можете использовать другой уровень наследования, т. Е. PrimaryElement extends Element и SecondaryElement extends Element, а затем использовать одно поле List<Element> elements для хранения ваших данных (вы все равно можете запросить определенный тип). Конечно, это работает только в том случае, если тип Element является первичным или вторичным.

Otoh, используя две таблицы соединений, может даже улучшиться, опять же в зависимости от вашего варианта использования (дополнительный JOIN против большего стола).

используя таблицу соединения для обоих отношений ManyToMany «должны» реально работать (в соответствии со спецификацией)

Попробуйте удалить NOT NULL ограничения для primaryElements_id и secondaryElements_id.

+0

Спасибо, я отредактирую пост как можно скорее. –

+0

Однако, если я правильно вас понял, обе проблемы, о которых вы упомянули, должны также существовать, когда есть только список «первичных элементов»? Однако это прекрасно работает ... или я пропустил те моменты, о которых вы говорили? –

+0

Когда вы не учитываете 'secondaryElements', больше нет прямого отношения от контейнера1 к элементу2 (но все же непрямо от контейнера1 до элемента1 до контейнера2 к элементу2), вероятно, вызывая JPA INSERT в другом порядке, в результате чего генерируется идентификатор работают по-другому. Вот почему я попросил журналы SQL;) – duckstep

Смежные вопросы