Укажите имя ограничения внешнего ключа при использовании Map и @ElementCollection с Hibernate
У меня есть своего рода экзотическое отображение для поля:
@ElementCollection
@CollectionTable(name = "studentGradeLevel", joinColumns = @JoinColumn(name = "studentId"))
@MapKeyJoinColumn(name = "schoolYearId")
@Column(name = "gradeLevel", nullable = false)
@ForeignKey(name = "fkStudentGrade2Student")
private Map<SchoolYear, GradeLevel> gradeLevels;
SchoolYear - это сущность, а GradeLevel - это перечисление.
Я использую инструменты Hibernate для генерации DDL для схемы. Схема, которая генерируется ниже:
create table studentGradeLevel (
studentId numeric(19,0) not null,
gradeLevel int not null,
schoolYearId int not null,
primary key (studentId, schoolYearId)
);
alter table studentGradeLevel
add constraint FK1BCA4A883A97C498
foreign key (schoolYearId)
references schoolYear;
alter table studentGradeLevel
add constraint fkStudentGrade2Student
foreign key (studentId)
references student;
Проблема заключается в том, что я не могу изменить имя ограничения для внешнего ключа между таблицей коллекции и таблицей для объекта, используемого в качестве ключа карты.
Я использовал @ForeignKey для указания имен ограничений для @OneToMany, @ManyToMany и других @ElementCollections без проблем. Я пробовал атрибут @ForiegnKey "inverseName", но он, кажется, игнорируется. @MapKeyJoinColumn не имеет никаких свойств, которые могли бы повлиять на это.
Кто-нибудь знает, есть ли способ сделать это?
Ответы
Ответ 1
Мне пришлось исправлять Hibernate, чтобы создавать разные имена внешних ключей, потому что созданные Hibernate для меня не были действительно полезными.
Я взял источник Hibernate и поместил Source класса org.hibernate.mapping.Table
в мою исходную папку, которая является началом пути к классам (итоговая баночка в моем проекте начинается с буквы ниже, чем hibernate.jar, так что это даже работает в webapps).
Я заменил функцию uniqueColumnString на следующий код (исходный код в верхней части функции):
public String uniqueColumnString(Iterator iterator, String referencedEntityName) {
// int result = 0;
// if ( referencedEntityName != null ) {
// result += referencedEntityName.hashCode();
// }
// while ( iterator.hasNext() ) {
// result += iterator.next().hashCode();
// }
// return ( Integer.toHexString( name.hashCode() ) + Integer.toHexString( result ) ).toUpperCase();
StringBuilder retVal = new StringBuilder();
retVal.append("_").append(referencedEntityName);
while( iterator.hasNext() ) {
Column c = (Column)iterator.next();
retVal.append("_");
retVal.append(c.getName());
}
return retVal.toString();
}
Это возвращает автоматически красивые строки, такие как "_Entity_attributeName_id", которые будут использоваться для создания внешних ключей, таких как "fk_Entity_attributeName_id"! Никогда не нужно снова указывать свои имена вручную:)))
Ответ 2
У меня была такая же проблема, и потому, что я не мог найти способ сделать это, в итоге обратился к самой базе данных, чтобы получить эту информацию.
Запуск этого запроса в SQL SERVER
select kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, tc.CONSTRAINT_TYPE, kcu.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
join INFORMATION_SCHEMA.KEY_COLUMN_USAGE as kcu
on kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
and kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
and kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
and kcu.TABLE_NAME = tc.TABLE_NAME
Вы получите имя_таблицы, имя ограничения (например, FK1BCA4A883A97C498), тип (например, ограничение UNIQUE) и имя столбца.
Этого должно быть достаточно, чтобы вернуть значимое сообщение об ошибке.
Я знаю, что это не здорово, потому что вы теряете переносимость db, но, по-видимому, нет способа делать то, о чем вы просите в данный момент...