hibernate跨数据库,columnDefinition不可移植性改造方案
背景&问题描述
很多项目选择jpa/hibernate,更多是为了代码的可移植性,不限制数据库的选择。特别是toB的业务系统,不同的客户,要求用不同的数据库。特别近几年,大力倡导软件国产化,国产数据库也在崛起,很多政府、国企、电力、银行在数据库的选择上,纷纷转向国产数据库。也许mybatis+mysql很香,但是代码可移植性却成了toB\toG软件类平台的关键竞争力。
hibernate,在字段定义,很多人都喜欢用columnDefinition注解去定义表的DDL,可能是曾经留下来的习惯吧。以前的hibernate对数据库字段及数据库移植性支持的比较差,像json、time、blob等一些特殊字段类型,表字段注释等支持的不好,为了方便,大家基本都会用columnDefinition注解直接去定义表,columnDefinition注解属性定义的ddl是不可移植性的,导致了整个项目不能跨数据库自动创建表。
下文讲解columnDefinition字段的替代方案。
解决方案
以mysql为例
@Colum(columnDefinition="decimal(15,2) comment '产品单价' default 1.5 ")
- 字段注释
需要将hibernate升级到5.6版本以上
字段注释使用注解org.hibernate.annotations.Comment
@Comment("产品单价")
- 字段长度、精度
字段长度使用javax.persistence.Column注解中的length属性,精度使用scale
@Column(length=15,scale=2)
- 字段默认值
字段默认值使用org.hibernate.annotations.ColumnDefault注解
注意:字符型,需要加单引号
@ColumnDefault("1.5")
- 大字段类型
大字段处理用javax.persistence.Lob,同时加注解@javax.persistence.Basic(fetch = FetchType.LAZY)懒加载,避免加载大数据,导致性能很差
对应数据库text\blob字段等
@Column
@Lob
@Basic(fetch = FetchType.LAZY)
- 时间类
时间类型字段使用注解javax.persistence.Temporal
通过传入注解参数TemporalType类型,可以指定时间类型
@Temporal(TemporalType.TIMESTAMP) // 时间戳
@Temporal(TemporalType.DATE) // 日期
@Temporal(TemporalType.TIME) // 时间
- json类型
json数据,如果数据库不支持json类型,hibernate会默认转为varchar类型,并且长度默认为255,对于json数据,通常都是大字段,下期将改造方案。
@TypeDef(name = "json", typeClass = JsonStringType.class)
public class Test {
@Type(type = "json")
private TestModel testModel;
}
@Type(type = "json") 其含义是将当前表格列映射成“Hibernate Types”中的 JsonStringType类型。JsonStringType进一步将对应的数据实体映射成VARCHAR类型。
hibernate支持json字段类型,可参考
?https://github.com/vladmihalcea/hibernate-types
注意事项
- 汉字编码长度不同
不同数据库编码对字符长度的定义不一致,比如,mysql,varchar(20)可以存放20个汉字,而sqlserver相同的字段定义只能存放10个汉字。建议在定义表字段时,取大不取小,考虑汉字占位符; - 批量操作限制
SqlServer批量入库不能超过2100条,而mysql没有此限制。在有批量入库操作,需要分批入库处理; - 参数限制
SqlServer传参不能超过1000个,对于一些表参数传递,尽可能用关联查询,推荐使用querydsl做关联查询,避免大量参数传递 - 唯一键名称重复限制
mysql中,唯一键就是索引,在同一个表,索引名称不能重复,而SqlServer是有唯一键,是整库内,所有唯一键名称不能重复。建议在建唯一键时,不填写索引name,hibernate会自动生产不重复name
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "age"})})