问题:SpringBoot JPA 项目中实体类字段和数据库字段映射不够灵活。
我想实现的是
- 字段如果没加 @Column 注解,就用驼峰式命名。
- 如果加了 @Column 注解就按照注解中 name 的来。
重写了一个配置类后发现还是很不灵活,不过这个思路应该是对的,这里做个记录,以后解决了再补充。
jpa 提供了配置属性可以改变映射规则,代码如下
jpa:
hibernate:
naming:
physical-strategy: org.springframework.boot.orm.jpa.hibernate.PhysicalNamingStrategyStandardImpl
- SpringPhysicalNamingStrategy
- PhysicalNamingStrategyStandardImpl
默认采用的配置是 SpringPhysicalNamingStrategy,即不加 @Column 时默认驼峰字段转成下划线映射,但问题是,项目中有些地方加了这个注解 name 的值被转成含下划线的了,比如
@Column(name = "orderStatus")
private Integer orderStatus;
我想要的是采用 name 后面的字符串作为映射结果,可是默认的映射是变成了 order_status, 竟然改变了注解中 name 的值。本来这里数据库中应该是下划线命名的,但是由于数据库字段命名不规范,所以在不改数据库的前提下,只能改变映射结果。 如果我用 PhysicalNamingStrategyStandardImpl 这个配置类映射结果是 @Column 里 name 属性的值,但是没法实现我想要有些实体类不加 @Column 时默认驼峰字段转下划线映射。
所以接下来实现重写 PhysicalNamingStrategyStandardImpl 中的 toPhysicalColumnName 方法来尝试解决这个问题。
Identifier 类是如何拿到 @Column 的 name 属性的,不好意思,我没看懂。但实际上我要拿的是字段名和字段名上有没有注解,这时要用到反射,先拿到实体类的反射再拿到字段,但问题是要把 @Table 从实体类上拿掉,Identifier 中才保存类名,这样改动又很大了。后面可以把这些类名存入 map 再循环反射从而根据每个类的属性的情况做自定义映射。
思路大概是这样,因为实现后代码中要改的地方也很多,所以我直接都加上了注解。
新建配置类继承 PhysicalNamingStrategyStandardImpl
@Slf4j
@Data
public class MyJpaPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
String text = name.getText();
//有大写字符,说明没加 @Column 注解,映射到数据库的字段名按驼峰式命名
if(hasUpperCase(text)){
return apply(name, jdbcEnvironment);
}else{
//加了 @Column 注解,按 name 原文命名
return new Identifier(text, name.isQuoted());
}
}
//是否包含大写字母
public static boolean hasUpperCase(String text){
for(Short i = 0; i < text.length(); i++){
if(Character.isUpperCase(text.charAt(i))){
return true;
}
}
return false;
}
private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
if (name == null) {
return null;
} else {
StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
for(int i = 1; i < builder.length() - 1; ++i) {
if (this.isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
builder.insert(i++, '_');
}
}
return this.getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
}
}
private boolean isUnderscoreRequired(char before, char current, char after) {
return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
}
protected Identifier getIdentifier(String name, boolean quoted, JdbcEnvironment jdbcEnvironment) {
if (this.isCaseInsensitive(jdbcEnvironment)) {
name = name.toLowerCase(Locale.ROOT);
}
return new Identifier(name, quoted);
}
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return true;
}
}
最后把 yml 那里改成自己创建的配置类。
参考:
segmentfault.com/a/119000001…
www.jianshu.com/p/a0bb6d40b…
完。