jpa 每次启动都要创建外键索引_JPA 不生成外键

本文探讨了JPA在处理外键时存在的问题,即使在Hibernate 5.x版本中仍存在。通过示例展示了如何使用@ForeignKey和@org.hibernate.annotations.ForeignKey注解来消除外键关联,以及重写Dialect类防止外键生成。总结了解决JPA在启动时创建外键索引的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在用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 extendsBaseDomain {

@Id()

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name= "ID")privateLong id;

@ColumnprivateString name;

@OneToMany(mappedBy= "teacher")private Liststudents;//getter&setter...

}

student:

@Entity

@Table(name= "STUDENT")public class Student extendsBaseDomain {

@Id()

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name= "ID")privateLong id;

@ColumnprivateString name;

@ManyToOne

@JoinColumn(name= "tid")privateTeacher teacher;//getter&setter...

}

数据库生成表结果:

CREATE TABLE “public”.”student” (

“id” int8DEFAULT 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 UPDATENO 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 extendsBaseDomain {

@Id()

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name= "ID")privateLong id;

@ColumnprivateString name;

@ManyToOne

@JoinColumn(name= "tid",foreignKey = @ForeignKey(name = "none",value =ConstraintMode.NO_CONSTRAINT))privateTeacher teacher;//setter&getter

}

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

/*** Used to control the application of a constraint.

*

*@sinceJPA 2.1*/

public enumConstraintMode {/*** 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 extendsBaseDomain {

@Id()

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name= "ID")privateLong id;

@ColumnprivateString name;

@OneToMany(mappedBy= "teacher")

@org.hibernate.annotations.ForeignKey(name= "none")private Liststudents;//getter&setter...

}

结果:

CREATE TABLE “public”.”student” (

“id” int8DEFAULT 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 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 @interfaceForeignKey {/*** (Optional) The name of the foreign key constraint. Defaults to a provider-generated name.

*

*@returnThe 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.

*

*@returnThe 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()defaultConstraintMode.CONSTRAINT;

}

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

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

@OneToMany(mappedBy = "teacher")

@Transientprivate List 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包的时候需要注意下。

亲测可用;

第二种办法:

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

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

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

Java代码  

importorg.hibernate.dialect.PostgreSQL9Dialect;/*** 不生成外键,通过类似于SQL注入的方法,为每个数据库修改创建外键的SQL*/

public class PostgreSQL9DialectWithoutFK extendsPostgreSQL9Dialect {

@OverridepublicString getAddForeignKeyConstraintString(

String constraintName,

String[] foreignKey,

String referencedTable,

String[] primaryKey,booleanreferencesPrimaryKey) {//设置foreignkey对应的列值可以为空

return " alter "+ foreignKey[0] +" set default null ";

}

}

Java代码

importorg.hibernate.dialect.Oracle10gDialect;/*** 不生成外键,通过类似于SQL注入的方法,为每个数据库修改创建外键的SQL*/

public class Oracle10gDialectWithoutFK extendsOracle10gDialect {

@OverridepublicString getAddForeignKeyConstraintString(

String constraintName,

String[] foreignKey,

String referencedTable,

String[] primaryKey,booleanreferencesPrimaryKey) {//通过修改外键列的默认值,而不是添加外键,避免生成外键

return " modify "+ foreignKey[0] +" default null ";

}

}

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

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

另:

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

JPA中@JoinColumn不生成外键的配置

postgresql中alter语法如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值