JPA 不生成外键

在用jpa这种orm框架时,有时我们实体对象存在关联关系,但实际的业务场景可能不需要用jpa来控制数据库创建数据表之间的关联约束,这时我们就需要消除掉数据库表与表之间的外键关联。
但jpa在处理建立外键时存在一些问题,在stackoverflow上搜索了相关的jpa创建实体对象关联关系但不建立外键这一系列问题后,发现这个是jpa在处理外键时存在一定的bug,官方给出的答复是在hibernate 5.x会解决掉这个问题,但是经验证5.x的版本这个问题依旧存在。下面给出这个问题的解释以及这个问题如何解决。

下面会以techer和student对象来举例,teacher和student存在一对多关系,一个teacher关联多个student。

1.teacher与student设置外键关系

teacher和student之间通过@OneToMany和@ManyToOne建立外键关联关系
teacher:

@Entity
@Table(name = "TEACHER")
public class Teacher extends BaseDomain {
    @Id()
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column
    private String name;

    @OneToMany(mappedBy = "teacher")
    private List<Student> students;

    //getter&setter...
}

student:

@Entity
@Table(name = "STUDENT")
public class Student extends BaseDomain {

    @Id()
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column
    private String name;

    @ManyToOne
    @JoinColumn(name = "tid")
    private Teacher teacher;

    //getter&setter...
}

数据库生成表结果:

CREATE TABLE “public”.”student” (
“id” int8 DEFAULT nextval(‘student_id_seq’::regclass) NOT NULL,
“name” varchar(255) COLLATE “default”,
“teacher_id” int8,
CONSTRAINT “student_pkey” PRIMARY KEY (“id”),
CONSTRAINT “fk3y5qg5r9ewc48x7ek8lx5ua8h” FOREIGN KEY (“teacher_id”) REFERENCES “public”.”teacher” > (“id”) ON DELETE NO ACTION ON UPDATE NO ACTION
)
WITH (OIDS=FALSE)
;
ALTER TABLE “public”.”student” OWNER TO “postgres”;

可以看到设置了外键”fk3y5qg5r9ewc48x7ek8lx5ua8h” FOREIGN KEY (“teacher_id”)

2.只在student端加上@ForeignKey

student

@Entity
@Table(name = "STUDENT")
public class Student extends BaseDomain {

    @Id()
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column
    private String name;

    @ManyToOne
    @JoinColumn(name = "tid",foreignKey = @ForeignKey(name = "none",value = ConstraintMode.NO_CONSTRAINT))
    private Teacher teacher;

    //setter&getter
}

加上该注解之后,根据这个注解说明是可以去掉外键关联关系,但发现加上后然并卵,外键还是没有去掉。这里需要说明其中@ForeignKey的value值由如下代码所示几种情况:

/**
 * Used to control the application of a constraint.
 *
 * @since JPA 2.1
 */
public enum ConstraintMode {
    /**
     * Apply the constraint.
     */
    CONSTRAINT,
    /**
     * Do not apply the constraint.
     */
    NO_CONSTRAINT,
    /**
     * Use the provider-defined default behavior.
     */
    PROVIDER_DEFAULT
}

3.在teacher端加入@org.hibernate.annotations.ForeignKey(name = “none”)

在一的这端加上@org.hibernate.annotations.ForeignKey(name = “none”)这个被jpa废弃的注解。加上之前在student中设置的@ForeignKey(注意这个是javax.persistence包下的),可以去掉外键关联
teacher:

@Entity
@Table(name = "TEACHER")
public class Teacher extends BaseDomain {
    @Id()
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column
    private String name;

    @OneToMany(mappedBy = "teacher")
    @org.hibernate.annotations.ForeignKey(name = "none")
    private List<Student> students;

    //getter&setter...
}

结果:

CREATE TABLE “public”.”student” (
“id” int8 DEFAULT nextval(‘student_id_seq’::regclass) NOT NULL,
“createdtime” timestamp(6),
“name” varchar(255) COLLATE “default”,
“version” int4,
“tid” int8,
CONSTRAINT “student_pkey” PRIMARY KEY (“id”)
)
WITH (OIDS=FALSE)
;
ALTER TABLE “public”.”student” OWNER TO “postgres”;

可以看到student表中原来关联teacher的外键没了,说明该注解起作用。

4.需要注意的坑

1.如果teacher(1的这端)有student列表(多的这端),像这样:

@OneToMany(mappedBy = "teacher")
@org.hibernate.annotations.ForeignKey(name = "none")
private List<Student> students;

如果要去掉外键关联关系,student端也需要像在2小结提到样需要加上@JoinColumn(name = “tid”,foreignKey = @ForeignKey(name = “none”,value = ConstraintMode.NO_CONSTRAINT)),但此时你会发现其中@ForeignKey中value的值不管你设置为ConstraintMode.NO_CONSTRAINT还是ConstraintMode.CONSTRAINT,数据库都不会设置外键(这个是特么的真奇怪)。但是就是不管怎样,你就是不能不设置@ForeignKey,并且你还必须要设置其中的值不能为默认值,不然就是要生成外键。这里贴下@ForeignKey的源码:

@Target({})
@Retention(RUNTIME)
public @interface ForeignKey {
    /**
     * (Optional) The name of the foreign key constraint.  Defaults to a provider-generated name.
     *
     * @return The foreign key name
     */
    String name() default "";

    /**
     * (Optional) The foreign key constraint definition.  Default is provider defined.  If the value of
     * disableForeignKey is true, the provider must not generate a foreign key constraint.
     *
     * @return The foreign key definition
     */
    String foreignKeyDefinition() default "";

    /**
     * (Optional) Used to specify whether a foreign key constraint should be generated when schema generation is in effect.
     */
    ConstraintMode value() default ConstraintMode.CONSTRAINT;
}

真的是X了狗了。。。我表示久久不能理解。。。

2.teacher(1这端)没有student列表或者student列表被@Transient所修饰,像这样:

@OneToMany(mappedBy = "teacher")
@Transient
private List<Student> students;

那么也是无论你在student端设置ConstraintMode的值,都不会设置外键.but!!!你就是不能不在student端(多的这端)设置@JoinColumn(name=”tid”,[email protected](name=”none”,value=ConstraintMode.NO_CONSTRAINT)),否则也是会生成外键

总结

所以要使数据表中没有外键关联关系。
1.当两边都有关联关系字段,1的这端利用@org.hibernate.annotations.ForeignKey(name = “none”),多的那端在JoinColumn中加上foreignKey = @ForeignKey(name = “none”,value = ConstraintMode.NO_CONSTRAINT)

2.当只有多的那端有关联字段,一的那段没有关联字段或者关联字段被@Transient所修饰,请在多的那端在JoinColumn中加上foreignKey = @ForeignKey(name = “none”,value = ConstraintMode.NO_CONSTRAINT)

最后需要说明的是@org.hibernate.annotations.ForeignKey(name = “none”)这个注解之后可能会在之后的版本会被直接移除掉,所以更新jar包的时候需要注意下。

参考资料:
①.https://hibernate.atlassian.net/browse/HHH-8805
②.https://hibernate.atlassian.net/browse/HHH-8862

亲测可用;

第二种办法:

关于如何禁用Hibernate生成外键,网上有使用设置ForeignKey(name="null")方式,使Hibernate不生成外键关联,但是需要在每个关联关系上设置,比较繁琐,很难统一控制保证数据库中不存在外键关联。而且经测试在@JoinColumn设置[email protected](name="null")不会生成外键,在@JoinTable中此种设置方式还是可以生成外键。

下面提供一种禁用Hibernate外键的方式,在创建数据库表时不生成外键关联,但是个人感觉还不是最好的解决方案,希望多多指教。

思路:因为Hibernate为了处理不同数据库SQL的差异,为每个数据库定义了dialect,在执行SQL时会由dialect类的方法中获取相应的SQL,所以可以通过重写dialect类中生成外键SQL的方法不生成数据库外键关联。分别重写的Postgresql数据库和Oracle数据库的Dialect类如下:

Java代码  

import org.hibernate.dialect.PostgreSQL9Dialect;
/**
 * 不生成外键,通过类似于SQL注入的方法,为每个数据库修改创建外键的SQL
 */
public class PostgreSQL9DialectWithoutFK extends PostgreSQL9Dialect {
    @Override
    public String getAddForeignKeyConstraintString(
            String constraintName,
            String[] foreignKey,
            String referencedTable,
            String[] primaryKey,
            boolean referencesPrimaryKey) {
//      设置foreignkey对应的列值可以为空
        return " alter "+ foreignKey[0] +" set default null " ;
    }
}  

Java代码

import org.hibernate.dialect.Oracle10gDialect;
/**
 * 不生成外键,通过类似于SQL注入的方法,为每个数据库修改创建外键的SQL
 */
public class Oracle10gDialectWithoutFK extends Oracle10gDialect {
    @Override
    public String getAddForeignKeyConstraintString(
            String constraintName,
            String[] foreignKey,
            String referencedTable,
            String[] primaryKey,
            boolean referencesPrimaryKey) {
//      通过修改外键列的默认值,而不是添加外键,避免生成外键
        return " modify "+ foreignKey[0] +" default null " ;
    }
}  

在重写生成外键SQL时考虑过使用SQL注入的方式在创建完外键后,再删除外键,但是这种方式比较复杂,做了一点后就放弃了。

创建了重写的Dialect类,通过hibernate.dialect=Oracle10gDialectWithoutFK配置后,在生成数据库表时外键策略就会生效。

另:

  • 关于是否在数据库中生成外键的讨论如下图,具体的讨论内容可以按关键字搜索相关内容。

    • JPA中@JoinColumn不生成外键的配置
    • postgresql中alter语法如下

原文地址:https://www.cnblogs.com/icebutterfly/p/9506904.html

时间: 2024-11-12 14:18:51

JPA 不生成外键的相关文章

Entity Framework code first设置不在数据库中生成外键

你现在用的EF是什么版本?我用EF6,你可以重写SqlServerMigrationSqlGenerator的生成外键和更新外键的方法,把不需要的表都过滤掉不就ok了? 1 public class ExtendedSqlGenerator : SqlServerMigrationSqlGenerator 2 { 3 #region 外键 4 protected override void Generate(DropForeignKeyOperation dropForeignKeyOperat

3小时找hibernate反向生成类为什么没有生成外键

然后解决方法是找到了 第一 jdk要8以上 第二 hibernate要5.0以上 但是 是只有n个外键时 会生成n-1个外键 是的 没错是hibernate问题 特意上github看的issue看到的 然后我就绝望了 但是还是看了老外怎么说 然后我直接放总结 不用外键了 因为 数据库应用外键约束会很影响性能,可以通过最终一致性来避免. so现在一起非关系型数据库那么火.

Code First 指定外键名称

指定类外键有注释(DataAnnotation)和FluentAPI两种方式, 目前掌握的知识我们先把DataAnnotation可用的四种方式总结如下 第一种方法: //1-指定导航属性,会自动生成外键,命名规则为:"表名_主键名" //这种方式兼容性非常好,稍后需要总结的关于一个类中出现两个或者多个相同类属性的时候就使用的这种方式,而下面的第三种方式就不成功,会出现错误,具体细节在下一篇博客中介绍 public class User { //[Key] public int Use

PowerDesigner 12小技巧-pd小技巧-pd工具栏不见了-pd修改外键命名规则-pd添加外键

PowerDesigner 12小技巧-pd小技巧-pd工具栏不见了-pd修改外键命名规则-pd添加外键 1. 附加:工具栏不见了 调色板(Palette)快捷工具栏不见了PowerDesigner 快捷工具栏 palette 不见了,怎么重新打开,找回来呢 上网搜索了一下"powerdesigner 图形工具栏",找到了找回PowerDesigner工具栏palette的方法Tools(工具栏)customsize toolbars(自定义工具栏)palette(调色板)勾选 2.

Entity Framework 4.1 - Code First 指定外键名称

Entity Framework 4.1 中,生成外键的方式有以下几种: 1-指定导航属性,会自动生成外键,命名规则为:“表名_主键名”2-默认情况下与导航属性的主键名称相同的字段会自动被标记为外键,3-通过[ForeignKey]标记指定实体类的属性为外键,4-方式2的升级版,与导航属性的主键名称相同的字段会自动被标记为外键,然后指定字段对应的数据库中的列名 方式3.4可指定生成的数据库中的列名. public class User { public int UserID { get; set

EF Code First 导航属性 与外键

一对多关系 项目中最常用到的就是一对多关系了.Code First对一对多关系也有着很好的支持.很多情况下我们都不需要特意的去配置,Code First就能通过一些引用属性.导航属性等检测到模型之间的关系,自动为我们生成外键.观察下面的类: public class Destination { public int DestinationId { get; set; } public string Name { get; set; } public string Country { get; s

Entity Framework Code First主外键关系映射约定

本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个不同表之间可以存在外键依赖关系,一个表自身也可以有自反关系(表中的一个字段引用主键,从而也是外键字段). Entity Framework Code First默认多重关系的一些约定规则: 一对多关系:两个类中分别包含一个引用和一个集合属性,也可以是一个类包含另一个类的引用属性,或一个类包含另一个类

MS SQL巡检系列&mdash;&mdash;检查外键字段是否缺少索引

前言感想:一时兴起,突然想写一个关于MS SQL的巡检系列方面的文章,因为我觉得这方面的知识分享是有价值,也是非常有意义的.一方面,很多经验不足的人,对于巡检有点茫然,不知道要从哪些方面巡检,另外一方面,网上关于MS SQL巡检方面的资料好像也不是特别多.写这个系列只是一个分享,自己的初衷是一个知识梳理.总结提炼过程,有些知识和脚本也不是原创,文章很多地方融入了自己的一些想法和见解的,不足和肤浅之处肯定也非常多,抛砖引玉,也希望大家提意见和建议.补充,指正其中的不足之处.Stay Hungry

Oracle系统表外键的更名

正在看的ORACLE教程是:Oracle系统表外键的更名. Oracle中表的外键是保证系统参照完整性的手段,而参照完整性是指分布在两个表中的列所满足的具有主从性质的约束关系.外键涉及到两个表,其中一个称之为父项表,另一个称之为子项表. 父项表(parent table)是参照约束的基础,即通过检查这张表的有效数据情况来判断约束是否成立,它是参照约束的条件,影响约束,而不受约束的任何影响. 子项表(child table)是参照约束的对象,当其发生变化,如有新数据输入时,通过比较父项表中的有效数