Есть только одна проблема: Base
следует аннотировать @MappedSuperClass
вместо @Entity
.После этого - и поставка пом - он работает нормально, с или без @Transactional
в методе тестирования.Я положил его на GitHub.Вы можете просмотреть его или клонировать и запустить его с помощью
git clone git://github.com/zzantozz/testbed tmp
cd tmp
mvn clean test -pl stackoverflow/7809543-hibernate-spring-jpa
. Я думаю, что, перегоняя его, вы исключаете причину проблемы.Иногда, когда вы просто полностью потерялись, полезно создать ветку вашего проекта (если вы в git) или скопировать его в отдельный каталог (если вы обременены SVN) и начать взламывать материалэто не является проблематичным, пока вы не сможете точно описать код / конфигурацию, вызывающую проблему.
Обновление: Понимание рассвета.Первая проблема: причина, по которой он ведет себя по-разному в зависимости от того, является ли тест @Transactional или нет, заключается в том, что ваша тестовая транзакция отличается от вашей DataGenerator
транзакции.В тесте транзакция откатывается в конце теста .В DataGenerator
оно совершено.Что еще более важно EntityManager не сбрасывается в тесте, потому что это обычно происходит только во время фиксации.flush()
- это то, что вызывает выдачу SQL в базу данных, и именно отсюда ваша ошибка.Если вы вводите свой тест с помощью EntityManager и вызываете flush()
в конце метода теста, то вы увидите то же поведение в вашем тесте @Transactional, что и в тесте, не являющемся @ Transactional.,Это довольно стандартная плата за тесты, когда вы выполняете откат транзакций для поддержания чистоты вашей базы данных.
Вторая проблема: поскольку Base - это сущность, а не просто суперкласс, содержащий некоторые общие поля, как я изначально думал, вы 'имеем дело с отображением наследования .(Основанное на аннотациях наследование рассматривается в отдельной справке Hibernate Annotations .) Независимо от того, осознали вы это или нет, вы неявно выбрали стратегию наследования "одна таблица" .Это JPA по умолчанию для отображения наследования, к лучшему или к худшему.Это означает, что все поля Base, ChildTypeA и ChildTypeB содержатся в базовой таблице.Если вы выключите вход в систему для отладки, вы увидите, что Hibernate генерирует эту структуру таблицы:
create table Base (DTYPE varchar(31) not null, id bigint generated by default as identity (start with 1), primary key (id))
create table Container (id bigint generated by default as identity (start with 1), primary key (id))
create table Container_Base (Container_id bigint not null, childrenB_id bigint not null, childrenA_id bigint not null, primary key (Container_id, childrenA_id), unique (childrenB_id), unique (childrenA_id))
Ваша проблема исходит от Container_Base.Если вы посмотрите внимательно, то увидите, что запись в этой таблице должна иметь первичный ключ ChildTypeA и первичный ключ ChildTypeB.Это не точно отражает вашу объектную модель, где это две не связанные коллекции.Я не уверен, почему Hibernate делает это.Я предполагаю, что он просто видит два (неявных) отображения таблиц соединений - это две таблицы соединений для ваших отношений «один ко многим», которые имеют одно и то же имя таблицы и объединяют их вместе.Это выглядит как кандидат на сообщение об ошибке для меня.В любом случае, есть как минимум два способа решить эту проблему:
- Переключиться на стратегию наследования таблиц на класс , добавив
@Inheritance(strategy = InheritanceType.JOINED)
к базе,Это создаст отдельные таблицы для Base, ChildTypeA и ChildTypeB.Тогда объединяемые таблицы будут сортироваться самостоятельно, потому что нет никакой двусмысленности. - Явно назовите объединяющие таблицы в ваших отображениях на Контейнере, используя, например,
@JoinTable(name = "containerChildA")
и @JoinTable(name = "containerChildB")
какподходящее.Это создаст две отдельные таблицы объединения, которые вам нужны, но при этом все Base, ChildTypeA и ChildTypeB будут находиться в одной таблице.