【问题标题】:Specify foreign key constraint name when using Map and @ElementCollection with Hibernate在 Hibernate 中使用 Map 和 @ElementCollection 时指定外键约束名称
【发布时间】:2011-06-24 14:46:49
【问题描述】:

我对一个字段有一种奇异的映射:

@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 似乎没有任何会影响这一点的属性。

有谁知道有没有办法做到这一点?

【问题讨论】:

  • 你的意思是'FK1BCA4A883A97C498'?为什么要管理这个名字。
  • 是的。原因是如果违反了约束,返回的错误将包含一个有意义的名称。
  • 有人在这里开了一张票:opensource.atlassian.com/projects/hibernate/browse/HCANN-24。您可能希望对此进行投票,以便 Hibernate 开发人员知道解决此问题的重要性。
  • 我不会指望自动生成任何重要的模式。无论如何,您都必须指定表空间、检查约束、索引和各种东西。使用专用的手写脚本,里面有 cmets。
  • @Nizet 绝对是,但它是一个非常有用的起点,能够为具有数百个表的模式处理生成的脚本。

标签: java hibernate hibernate-annotations


【解决方案1】:

我不得不修补 Hibernate 以创建不同的外键名称,因为 Hibernate 为我创建的那些并不是真正有用的。

我获取了 Hibernate 源代码,并将类 org.hibernate.mapping.Table 的源代码放入我的源文件夹中,这是类路径的开头(我的项目中生成的 jar 以低于 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”这样的外键!再也不用手动指定我的名字了:)))

【讨论】:

  • 这是个好主意,我会试试看。
  • 请告诉我结果如何!我从来没有在 ElementCollection 上尝试过这个!恕我直言,我的解决方案在独特性方面甚至更好。他们的解决方案可能会生成重复值,而我的解决方案始终是唯一的(至少只要它保持在方言的标识符长度限制以下)。
【解决方案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 约束)和列名。 这应该足以返回有意义的错误消息。

我知道这不是很好,因为您失去了数据库的可移植性,但显然目前没有办法满足您的要求......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-06
    • 2015-11-16
    • 1970-01-01
    • 2016-08-14
    • 2016-11-10
    • 1970-01-01
    • 2011-03-18
    相关资源
    最近更新 更多