Springboot JPA使用达梦数据库时的主键生成策略

Springboot JPA使用达梦数据库时的主键生成策略

1.GeneratedValue

@GeneratedValue

该注解用于声明主键的生成策略,有两个参数:

1.strategy:

table:使用特定的数据库表存放主键

sequence:根据数据库序列生成主键

identity:数据库自动生成主键(主键自增)

auto:由程序自动控制(默认)

2.generator:

主键生成器的名称,如strategy使用sequence,则该值与@SequenceGenerator注解的name参数保持一致。

2.测试达梦可用的主键生成策略

不同数据库对strategy的四种取值支持情况不同,如Oracle不支持identity方式,一般使用sequence,mysql一般使用identity。下面测试达梦支持的该参数取值。

2.1strategy=sequence

Person.java关键代码

@Entity

@Table(name = "Person")

public class Person implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "IdSeq")

@SequenceGenerator(name="IdSeq", sequenceName="seqPersonId", allocationSize = 1, initialValue = 1)

protected long id;

@Column(name = "name")

protected String name;

该配置会自动在数据库端生成序列和表。

没有提前在数据库创建序列时,根据sequenceName生成序列,自动生成序列的命名规则为:

如果sequenceName值按驼峰命名规则命名,则不同单词之间用下划线连接,如sequenceName="seqPersonId",则生成的序列名称为seq_person_id。

如果sequenceName值全部小写,则数据库中生成序列名与sequenceName值保持一致。

SQL> SELECT ID FROM SYSOBJECTS WHERE TYPE$ = 'SCH' AND NAME = 'SYSDBA';

行号 ID

---------- -----------

1 150994945

已用时间: 1.708(毫秒). 执行号:127600.

SQL> SELECT NAME, CRTDATE, INFO3 initialValue, INFO4 allocationSize FROM SYSOBJECTS WHERE TYPE$='SCHOBJ' AND SUBTYPE$ = 'SEQ' AND SCHID = '150994945';

行号 NAME CRTDATE initialValue allocationSize

---------- ---------- -------------------------- -------------------- --------------------

1 seqpersond 2023-04-13 11:37:32.845000 1 1

SQL> SELECT NAME, CRTDATE, INFO3 initialValue, INFO4 allocationSize FROM SYSOBJECTS WHERE TYPE$='SCHOBJ' AND SUBTYPE$ = 'SEQ' AND SCHID = '150994945';

行号 NAME CRTDATE initialValue allocationSize

---------- ------------- -------------------------- -------------------- --------------------

1 seq_person_id 2023-04-13 12:32:31.781000 1

注意:

sequenceName="seqPersonId"时,数据库已存在名称为seqPersonId的序列时不报错,会创建seq_person_id序列。库中存在seq_person_id序列时不再创建,使用已存在的序列。

设置自增序列后,插入时带自增列的情况

save接口

@RequestMapping("/save")

@ResponseBody

public String save(){

Person person = new Person();

person.setId(101);

person.setName("张三");

personService.save(person);

return "插入成功";

}

hibernate 日志

Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from person person0_ where person0_.id=?

Hibernate: select seq_person_id.nextval

Hibernate: insert into person (name, id) values (?, ?)

不报错,但手动设置的person.setId(101)不会生效,id值为序列自动生成。

SQL> select *from person;

行号 id name

---------- -------------------- ----

1 1 张三

2 2 张三

已用时间: 0.722(毫秒). 执行号:127603.

2.2strategy=identity

Person.java关键代码

@Entity

@Table(name = "Person")

public class Person implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY,generator = "IdSeq")

// @SequenceGenerator(name="IdSeq", sequenceName="seqPersonId", allocationSize = 1, initialValue = 1)

protected long id;

@Column(name = "name")

protected String name;

使用DmDialect-for-hibernate5.0驱动时启动会报错如下:

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: org.hibernate.dialect.identity.IdentityColumnSupportImpl does not support identity key generation

需要使用DmDialect-for-hibernate5.3驱动。

自动生成的表结构

CREATE TABLE "SYSDBA"."person"

(

"id" BIGINT IDENTITY(1, 1) NOT NULL,

"name" VARCHAR(255),

NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ;

测试插入时指定自增列

save接口

@RequestMapping("/save")

@ResponseBody

public String save(){

Person person = new Person();

person.setId(101);

person.setName("张三");

personService.save(person);

return "插入成功";

}

表数据,即strategy=identity时,插入时指定自增列不会报错,也不会生效,自增列实际值还是自增生成。

SQL> select *from person;

行号 id name

---------- -------------------- ----

1 1 张三

已用时间: 0.993(毫秒). 执行号:127604.

从日志可以看到hibernate操作时直接省略了id列的插入。

Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from person person0_ where person0_.id=?

Hibernate: insert into person (name) values (?)

2.3strategy=auto

@Entity

@Table(name = "Person")

public class Person implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.AUTO,generator = "IdSeq")

// @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "IdSeq")

// @SequenceGenerator(name="IdSeq", sequenceName="seqPersonId", allocationSize = 1, initialValue = 1)

protected long id;

@Column(name = "name")

protected String name;

自动生成的表结构

CREATE TABLE "SYSDBA"."person"

(

"id" BIGINT NOT NULL,

"name" VARCHAR(255),

NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ;

hibernate日志。此时自动生成一个名为generator参数值的序列,即id_seq,其初始值与步长均为1.

Hibernate: create table person (id bigint not null, name varchar(255), primary key (id))

Hibernate: create sequence id_seq increment by 1 start with 1

插入测试

插入或不插入自增列时,都使用序列值

Person person = new Person();

// person.setId(101);

person.setName("张三");

personService.save(person);

return "插入成功";

}

Hibernate: select id_seq.nextval

Hibernate: insert into person (name, id) values (?, ?)

SQL> select *from person;

行号 id name

---------- -------------------- ----

1 1 张三

已用时间: 0.859(毫秒). 执行号:127608.

但插入带自增列,且自增列唯一时,hibernate在插入前向数据库查询有无该ID值,即便最终自增列值仍使用序列填充。

Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from person person0_ where person0_.id=?

Hibernate: select id_seq.nextval

Hibernate: insert into person (name, id) values (?, ?)

同样,同名序列存在时,使用已存在序列的定义,不再重新创建。

2.4strategy=table

City.java配置

@Entity

@Table(name = "Person")

public class Person implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.TABLE, generator = "idGenerator01")

// @GeneratedValue(generator = "idGenerator01")

// @GenericGenerator(strategy = "guid", name = "idGenerator01")

// @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "IdSeq")

// @SequenceGenerator(name="IdSeq", sequenceName="seqPersonId", allocationSize = 1, initialValue = 1)

protected long id;

@Column(name = "name")

protected String name;

hibernate日志。这种方式会为每张存在自增列的表创建一张辅助表,当前创建id_generator02表存放自增列信息

Hibernate: create table city (id varchar(255) not null, name varchar(255), primary key (id))

Hibernate: create table id_generator01 (sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name))

Hibernate: insert into id_generator01(sequence_name, next_val) values ('person',0)

Hibernate: create table id_generator02 (sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name))

Hibernate: insert into id_generator02(sequence_name, next_val) values ('city',0)

Hibernate: create table person (id bigint not null, name varchar(255), primary key (id))

查看id_generator01表结构和数据,next_val列为bigint,则使用此种方式时,自增列数据类型必须为数值型。

CREATE TABLE "SYSDBA"."id_generator01"

(

"sequence_name" VARCHAR(255) NOT NULL,

"next_val" BIGINT,

NOT CLUSTER PRIMARY KEY("sequence_name")) STORAGE(ON "MAIN", CLUSTERBTR) ;

表数据

SQL> select *from id_generator01;

行号 sequence_name next_val

---------- ------------- --------------------

1 person 50

已用时间: 2.046(毫秒). 执行号:127613.

此种方式在插入时带自增列会忽略列值,使用辅助表生成的值。

2.5不添加GeneratedValue注解

Person.java

@Entity

@Table(name = "Person")

public class Person implements Serializable {

@Id

// @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "IdSeq")

// @SequenceGenerator(name="IdSeq", sequenceName="seqPersonId", allocationSize = 1, initialValue = 1)

protected long id;

@Column(name = "name")

protected String name;

表结构

CREATE TABLE "SYSDBA"."person"

(

"id" BIGINT NOT NULL,

"name" VARCHAR(255),

NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ;

插入测试

save方法

@RequestMapping("/save")

@ResponseBody

public String save(){

Person person = new Person();

// person.setId(101);

person.setName("张三");

personService.save(person);

return "插入成功";

}

插入时不设置自增列,不报错,自动插入值为0,第二次插入时,提示id重复报错。

SQL> select *from person;

行号 id name

---------- -------------------- ----

1 0 张三

已用时间: 0.886(毫秒). 执行号:127605.

插入时带自增列则插入附带的参数值

@RequestMapping("/save")

@ResponseBody

public String save(){

Person person = new Person();

person.setId(101);

person.setName("张三");

personService.save(person);

return "插入成功";

}

SQL> select *from person;

行号 id name

---------- -------------------- ----

1 101 张三

已用时间: 0.781(毫秒). 执行号:127607.

2.6使用hibernate中DefaultIdentifierGeneratorFactory的内置类

hibernate的中内置了一些生成ID的类,如下:

public DefaultIdentifierGeneratorFactory(boolean ignoreBeanContainer) {

this.generatorStrategyToClassNameMap = new ConcurrentHashMap();

this.ignoreBeanContainer = ignoreBeanContainer;

this.register("uuid2", UUIDGenerator.class);

this.register("guid", GUIDGenerator.class);

this.register("uuid", UUIDHexGenerator.class);

this.register("uuid.hex", UUIDHexGenerator.class);

this.register("assigned", Assigned.class);

this.register("identity", IdentityGenerator.class);

this.register("select", SelectGenerator.class);

this.register("sequence", SequenceStyleGenerator.class);

this.register("seqhilo", SequenceHiLoGenerator.class);

this.register("increment", IncrementGenerator.class);

this.register("foreign", ForeignGenerator.class);

this.register("sequence-identity", SequenceIdentityGenerator.class);

this.register("enhanced-sequence", SequenceStyleGenerator.class);

this.register("enhanced-table", TableGenerator.class);

}

使用guid生成id

@Id

@GeneratedValue(generator = "idGenerator02")

@GenericGenerator(strategy = "guid", name = "idGenerator02")

protected String id;

生成的表结构

CREATE TABLE "SYSDBA"."city"

(

"id" VARCHAR(255) NOT NULL,

"name" VARCHAR(255),

NOT CLUSTER PRIMARY KEY("id")) STORAGE(ON "MAIN", CLUSTERBTR) ;

hibernate日志

Hibernate: select GUID()

2023-04-13 15:02:48.212 WARN 18748 --- [nio-8080-exec-1] org.hibernate.id.GUIDGenerator : HHH000113: GUID identifier generated: 936E6753DAB44D5345A4B9BA1A98D89F

Hibernate: insert into city (name, id) values (?, ?)

表数据

SQL> select *from city;

行号 id name

---------- -------------------------------- ----

1 936E6753DAB44D5345A4B9BA1A98D89F 张三

已用时间: 1.790(毫秒). 执行号:127609.

插入时指定ID列值,还是使用hibernate生成的guid

Hibernate: select city0_.id as id1_0_0_, city0_.name as name2_0_0_ from city city0_ where city0_.id=?

Hibernate: select GUID()

2023-04-13 15:12:05.735 WARN 24268 --- [nio-8080-exec-1] org.hibernate.id.GUIDGenerator : HHH000113: GUID identifier generated: 4995BFC6259246FFF2EDDDB6D4A778A4

Hibernate: insert into city (name, id) values (?, ?)

@RequestMapping("/citySave")

@ResponseBody

public String save(){

City city = new City();

city.setId("101eab");

city.setName("张三");

cityService.save(city);

return "插入成功";

}

SQL> select *from city;

行号 id name

---------- -------------------------------- ----

1 4995BFC6259246FFF2EDDDB6D4A778A4 张三

已用时间: 0.535(毫秒). 执行号:127610.

使用其他内置类时同理, 修改strategy=类名即可。

3测试总结

3.1汇总

@GeneratedValue注解的strategy参数四个取值达梦均支持,具体如下:

自动生成自增列值

创建序列(已存在是否重建)

自增列插入(报错)

创建辅助表

其他

strategy=sequence

是(不重建)

不生效(不报错)

strategy=identity

不生效(不报错)

strategy=auto

是(不重建)

不生效(不报错)

strategy=table

不生效(不报错)

需要ID列为数值型

不添加注解

是(自增列插入为0)

生效(不报错)

使用内置类

不生效(不报错)

部分内置类需要ID列为字符型

3.2注意事项

1.使用生成序列的取值类型时,如果库中已存在同名序列则不再替换,使用已存在的序列定义。

2.strategy=identity需要使用DmDialect-for-hibernate5.3。

3.strategy=table时ID列必须为数值型。

相关推荐

视频制作工具哪个好,新手快速入门的五款软件
世界杯单场个人进球之最(探秘历届世界杯上单场个人进球的最高纪录与突破者)
如何正确攻略病娇
天龙八部龙纹扩展属性升级需要多少材料?