在开发里面时长会用到ID自增。目前大多数的ID自增都是依赖数据库实现的,不同数据库实现ID自增都有或多或少的差异。这样就导致程序在迁移不同数据库的时候需要针对ID自增这里做特殊处理。为了少做处理,这里直接用程序来实现自增ID。不依赖数据库特性。
具体思路是通过注解@GeneratedValue和@GenericGenerator 来自定义一个主键生成策略 SeqPKGenerator 实现 IdentifierGenerator,在主键生成方法generate()上自己定义主键生成规则。
关于ID生成规则注解@GeneratedValue和@GenericGenerator 的用法这里给一篇文章
ID注解之@GeneratorValue与@GenericGenerator使用心得_xqhys的博客-CSDN博客_genericgenerator注解ll
里面讲的很详细,不过介绍的都是依赖数据库的ID自增方式。下面说一下自定义主键生成策略。
首先定义一个class 作为主键生成器SeqPKGenerator,需要实现IdentifierGenerator并实现 generate()方法。在generate()方法里面去写自定义的主键生成策略。这里我们写的生成规则是通过参数 Object获取到这个类的Table注解,获取到对应实体的表名称。在通过查找注解
@javax.persistence.Id或@org.springframework.data.annotation.Id
来查找自增列所对应的字段名称。 然后通过mybatis查询自增列的最大值然后获得的最大值加一就是下一个ID。具体实现方法如下
@Component
public class SeqPKGenerator implements IdentifierGenerator {
protected static final String COL_ALIAS = "maxidval";
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object)
throws HibernateException {
//获取table注解,通过table获取表名
Table table = object.getClass().getAnnotation(Table.class);
if (ObjectUtils.isEmpty(table) || ObjectUtils.isEmpty(table.name()))
return PKGenerator.getPK(); //如果获取不到Table则返回随机ID
// 获取表名
String tablename = table.name();
DynamicTableService dynamicTableService = SpringContextUtils.getBean(DynamicTableService.class);
// 获取实体中的自增列,这里认为@javax.persistence.Id或@org.springframework.data.annotation.Id修饰的属性就是我们要的
Field pkCol = getPKCol(object.getClass());
if (ObjectUtils.isEmpty(pkCol) || ObjectUtils.isEmpty(pkCol.getName()))
return PKGenerator.getPK();
String pk = pkCol.getName();
// 获取自增列最大值, 用mybatis查询最大ID,dynamicTableService.selectByConditions方法可以自定义。参数是传入表名和自增列名
List<Map<String, Object>> resultList = dynamicTableService.selectByConditions(new DynamicSelectDTO(tablename,
CollectionUtil.fastList("max(" + pk + ") " + COL_ALIAS), CollectionUtil.fastList()));
// 下面各种找不到值的情况统一返回1
if (ObjectUtils.isEmpty(resultList))
return 1L;
Map<String, Object> map = resultList.get(0);
if (ObjectUtils.isEmpty(map))
return 1L;
Object maxid = map.get(COL_ALIAS);
if (ObjectUtil.isEmpty(maxid))
return 1L;
// 找到了+1返回
Long idLong = ObjectUtil.toLong(maxid);
return ++idLong;
}
/**
* @description 获取实体中的自增列,这里认为@javax.persistence.Id或@org.springframework.data.annotation.Id修饰的属性就是我们要的
* @param clz
* @return
*/
protected Field getPKCol(Class<?> clz) {
//获取实体的所有元素
Field[] allFields = ReflectUtil.getAllFields(clz);
for (Field field : allFields) {
//如果字段有javax.persistence.Id或者org.springframework.data.annotation.Id注解则返回此字段
if (!ObjectUtils.isEmpty(field.getAnnotation(javax.persistence.Id.class)) ||
!ObjectUtils.isEmpty(field.getAnnotation(org.springframework.data.annotation.Id.class)))
return field;
}
return null;
}
}
SeqPKGenerator 定义好之后就在需要自增主键的字段上面加上注解@GeneratedValue和@GenericGenerator 就可以了。
@GeneratedValue(generator = “mySeqPk”)
@GenericGenerator(name = “mySeqPk”, strategy = “com.unis.doc.core.pk.SeqPKGenerator”)
注意的是
@GeneratedValue 里面的generator 要和 @GenericGenerator 里面的name 相同,@GenericGenerator里面的strategy 要和自己定义的主键生成器SeqPKGenerator一致,需要包括包名。
这里我们定义了一个自增实体的基类BaseSeqDO,需要用到自增ID的继承这个类就可以了。
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseSeqDO implements Serializable {
/**
* 主键
*/
@Id
@GeneratedValue(generator = "mySeqPk")
@GenericGenerator(name = "mySeqPk", strategy = "com.unis.doc.core.pk.SeqPKGenerator")
@Column(name = "id", nullable = false, length = 20)
@ApiModelProperty(value = "主键")
private Long id;
/**
* 创建人
*/
// @CreatedBy
@Column(name = "createdby", nullable = true, length = 100)
@ApiModelProperty(value = "创建人")
private Long createdby;
/**
* 创建时间
*/
// @CreatedDate
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "createdtime")
@ApiModelProperty(value = "创建时间")
private Date createdtime;
/**
* 更新人
*/
// @LastModifiedBy
@Column(name = "lastmodifiedby", nullable = true, length = 20)
@ApiModelProperty(value = "更新人")
private Long lastmodifiedby;
/**
* 更新时间
*/
@LastModifiedDate
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "lastmodifiedtime")
@ApiModelProperty(value = "更新时间")
private Date lastmodifiedtime;
/**
* 逻辑删除标志位
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "deletetime")
@ApiModelProperty(value = "逻辑删除标志位")
private Date deletetime;
public BaseSeqDO(Long id) {
super();
this.id = id;
}
public BaseSeqDO(Long id, Long createdby, Date createdtime, Long lastmodifiedby, Date lastmodifiedtime) {
super();
this.id = id;
this.createdby = createdby;
this.createdtime = createdtime;
this.lastmodifiedby = lastmodifiedby;
this.lastmodifiedtime = lastmodifiedtime;
}
public BaseSeqDO(Long id, Date deletetime, Long createdby, Date createdtime, Long lastmodifiedby,
Date lastmodifiedtime) {
super();
this.id = id;
this.createdby = createdby;
this.createdtime = createdtime;
this.lastmodifiedby = lastmodifiedby;
this.lastmodifiedtime = lastmodifiedtime;
}
public BaseSeqDO() {
super();
}
@Override
public String toString() {
return this.getClass().getSimpleName()+" : "+JSON.toJSONString(this);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCreatedby() {
return createdby;
}
public void setCreatedby(Long createdby) {
this.createdby = createdby;
}
public Date getCreatedtime() {
return createdtime;
}
public void setCreatedtime(Date createdtime) {
this.createdtime = createdtime;
}
public Long getLastmodifiedby() {
return lastmodifiedby;
}
public void setLastmodifiedby(Long lastmodifiedby) {
this.lastmodifiedby = lastmodifiedby;
}
public Date getLastmodifiedtime() {
return lastmodifiedtime;
}
public void setLastmodifiedtime(Date lastmodifiedtime) {
this.lastmodifiedtime = lastmodifiedtime;
}
public Date getDeletetime() {
return deletetime;
}
public void setDeletetime(Date deletetime) {
this.deletetime = deletetime;
}
}
总结:通过实现IdentifierGenerator的方法自定义注解,可以摆脱数据库的约束,不过效率上会有一些欠缺。如果并发量比较高,就需要考虑主键生成规则的并发效率。