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

相關文章
相關標籤/搜索