Hibernate 自定义表名映射

问题描述

Hibernate映射介绍

Hibernate中,默认的生成关系是将我们驼峰命名的实体进行拼接下划线同时转小写。

Hibernate 自定义表名映射

Hibernate 自定义表名映射

这种情况我们可以接受,默认的设置很规范。

Hibernate 自定义表名映射

Hibernate 自定义表名映射

但是这样,我们在实体之上声明了@Table注解,并说我们的表名是Mandatory_Instrument_Apply,但是Hibernate还是将我们的数据表映射为小写加下划线的形式。这种情况看起来就有些不合理了。

业务需求

因为需要兼容老项目,老项目的数据表命名不很很规范,所以需要用最小的成本实现数据库的兼容。

所以设计的表名映射格式为,如果不加@Table注解,则将实体名按照Hibernate默认的生成规则进行生成,如果加了@Table注解,则填写的name就作为表名映射,不进行任何处理。

功能实现

入门

抛出来一个问题,无从下手。

打开浏览器,看看有没有前人的经验,GoogleGoogle去发现找不着啥有价值的信息。但是在StackOverflow上找到一篇引人思索的问题。

Spring boot JPA insert in TABLE with uppercase name with Hibernate - StackOverflow

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

将这个配置声明为org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl,就能实现大写的转换。

试试再说:

将配置按该问题的回答进行修改:

Hibernate 自定义表名映射

测试一番:

Hibernate 自定义表名映射

Hibernate 自定义表名映射

加了@Table注解的,是我们想要的配置,直接映射。那不加注解的呢?

Hibernate 自定义表名映射

Hibernate 自定义表名映射

这个又不是我们想要的了,这个注解应该是直接将注解中的名或实体名映射到数据表,不做任何修改。

发现新大陆

正当一筹莫展之时,再去看一下配置,有了新的领悟。

org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl,这个配置的不就是一个实现类吗?我是不是也可以写一个类然后将我写的类配置上呢?

Hibernate 自定义表名映射

点进去,发现不过是一个实现了PhysicalNamingStrategySerializable两个接口的类。

Hibernate 自定义表名映射

看实现的toPhysicalTableName方法,应该就是生成数据表名的方法。直接将name返回,这就和我们之前猜想的一致,这个配置是直接将@Table或实体名映射到数据表。

YunzhiNamingStrategy

建立配置类YunzhiNamingStrategy.java,云智命名策略,分别实现上述PhysicalNamingStrategyStandardImpl实现的两个接口。同时实现接口中声明的方法。

package com.mengyunzhi.demo.config;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import java.io.Serializable;

/**
 * @author zhangxishuo on 2018/6/15
 * 云智命名策略
 * 实体与数据表名的关系配置
 */
public class YunzhiNamingStrategy implements PhysicalNamingStrategy, Serializable {
    @Override
    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return null;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return null;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return null;
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return null;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return null;
    }
}

Hibernate 自定义表名映射

然后将该项配置修改为我们自己建立的实现类。

兼容原配置项

因为该接口中有多个配置项,如:数据库名、字段名等,我们只想修改实体到数据表的命名策略,所以我们找到了另一个实现PhysicalNamingStrategy命名策略的实现类:SpringPhysicalNamingStrategy

这就是我们第一次演示的策略,无论添不添加@Table注解,都会映射到小写的加下划线的表名。

同时这里的字段映射为小写下划线我们是需要保留的,为了代码的复用,我们用到了面向对象的继承大法。

package com.mengyunzhi.demo.config;

import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;

/**
 * @author zhangxishuo on 2018/6/15
 * 云智命名策略
 * 实体与数据表名的关系配置
 */
public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy {
    
}

我们对父类中的toPhysicalTableName方法不满意,Command + N,重写父类方法。

package com.mengyunzhi.demo.config;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;

/**
 * @author zhangxishuo on 2018/6/15
 * 云智命名策略
 * 实体与数据表名的关系配置
 */
public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy {
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return super.toPhysicalTableName(name, jdbcEnvironment);
    }
}

注解判断

我们可以开始我们的逻辑了。

@Table注解的,就按@Table中的名称中,否则就按Spring默认的父类走。

但是问题出现了,Identifier中给我们的名称就是已经处理好的名称。

Identifier

假如这么写:

@Entity
public class MandatoryInstrumentApply {
}

那我们的Identifier中的text值就是MandatoryInstrumentApply

@Entity
@Table(name = "Mandatory_Instrument_Apply")
public class MandatoryInstrumentApply {
}

那我们的Identifier中的text值就是Mandatory_Instrument_Apply

Hibernate是把应该处理好的名称告诉我们,但是不会告诉我们这个名称是实体的名还是在注解上获取的。

解决方案

Hibernate 自定义表名映射

package com.mengyunzhi.demo.config;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;

import javax.persistence.Table;

/**
 * @author zhangxishuo on 2018/6/15
 * 云智命名策略
 * 实体与数据表名的关系配置
 */
public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy {

    // 定义包名
    private static final String packageName = "com.mengyunzhi.demo.entity.";

    /**
     * 重写父类生成表名的方法
     */
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        try {
            // 获取实体类
            Class entityClass = Class.forName(packageName + name.getText());
            // 判断类上是否有Table注解
            Boolean hasAnnotation = entityClass.isAnnotationPresent(Table.class);
            // 存在Table注解
            if (hasAnnotation) {
                // 获取Table注解实例
                Table table = (Table) entityClass.getAnnotation(Table.class);
                // 如果注解中的name字段不为空
                if (!table.name().equals("")) {
                    // 不对名称进行处理
                    return name;
                }
            }
            // 表示这是一个类名,按父类操作进行处理
            return super.toPhysicalTableName(name, jdbcEnvironment);
        } catch (ClassNotFoundException e) {
            // 找不到实体类,说明肯定是@Table注解中的名称
            return name;
        }
    }
}

测试

Hibernate 自定义表名映射

Hibernate 自定义表名映射

Hibernate 自定义表名映射

Hibernate 自定义表名映射

Hibernate 自定义表名映射

Hibernate 自定义表名映射

相关推荐