Hibernate 自定义表名映射

问题描述

Hibernate映射介绍

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

clipboard.png

clipboard.png

这种状况咱们能够接受,默认的设置很规范。spring

clipboard.png

clipboard.png

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

业务需求

由于须要兼容老项目,老项目的数据表命名不很很规范,因此须要用最小的成本实现数据库的兼容。浏览器

因此设计的表名映射格式为,若是不加@Table注解,则将实体名按照Hibernate默认的生成规则进行生成,若是加了@Table注解,则填写的name就做为表名映射,不进行任何处理。ide

功能实现

入门

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

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

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

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

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

试试再说:设计

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

clipboard.png

测试一番:

clipboard.png

clipboard.png

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

clipboard.png

clipboard.png

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

发现新大陆

正当束手无策之时,再去看一下配置,有了新的领悟。

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

clipboard.png

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

clipboard.png

看实现的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;
    }
}

clipboard.png

而后将该项配置修改成咱们本身创建的实现类。

兼容原配置项

由于该接口中有多个配置项,如:数据库名、字段名等,咱们只想修改实体到数据表的命名策略,因此咱们找到了另外一个实现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是把应该处理好的名称告诉咱们,可是不会告诉咱们这个名称是实体的名仍是在注解上获取的。

解决方案

clipboard.png

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;
        }
    }
}

测试

clipboard.png

clipboard.png

clipboard.png

clipboard.png

clipboard.png

clipboard.png

相关文章
相关标签/搜索