• 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 TABLEpublic”.”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”) REFERENCESpublic”.”teacher” > (“id”) ON DELETE NO ACTION ON UPDATE NO ACTION
    )
    WITH (OIDS=FALSE)
    ;
    ALTER TABLEpublic”.”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 TABLEpublic”.”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 TABLEpublic”.”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”,foreignKey=@ForeignKey(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

  • 相关阅读:
    win10下vs2015配置Opencv3.1.0过程详解
    Int16, Int32, Int64 范围
    Microsoft Language and Locale Codes(微软语言和地区代码汇总)
    Azure china服务状态报告查看网址
    Azure IOT (EventHub + Stream Analytics + Table Storage)的使用
    java 继承extends 的相关知识点
    final
    java 垃圾回收机制
    Java 抽象类
    重载与重写、多态——java
  • 原文地址:https://www.cnblogs.com/icebutterfly/p/9389307.html
Copyright © 2020-2023  润新知