Naming 命名策略详解及其实践

用 JPA 离不开 @Entity 实体,我都知道实体里面有字段映射,而字段映射的方法有两种:

  • 显式命名:在映射配置时,设置的数据库表名、列名等,就是进行显式命名,即通过 @Column 注解配置。
  • 隐式命名:显式命名一般不是必要的,所以可以选择当不设置名称,这时就交由 Hibernate 进行隐式命名,另外隐式命名还包括那些不能进行显式命名的数据库标识符,即不加 @Column 注解时的默认映射规则。

Naming 命名策略详解

我们通过源码发现:Naming 的源码发现 Hibernate 4 的时候隐式命名策略是 org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy;而我们发现 Hibernate 5 将隐式命名策略拆分成了两步:implicitStrategy 和 physicalStrategy。从官方的文档中得知这样做的目的是提高灵活性,减少构建命名策略过程中用到的重复的信息。

  • 第一个阶段是从对象模型中提取一个合适的逻辑名称,这个逻辑名称可以由用户指定,通过 @Column 和 @Table 等注解完成,也可以通过被 Hibernate 的 ImplicitNamingStrategy 指定。
  • 第二个阶段是将上述的逻辑名称解析成物理名称,物理名称是由 Hibernate 中的 PhysicalNamingStrategy 决定,两个阶段是有先后顺序的。

(1)ImplicitNamingStrategy

当一个实体对象没有显式的指明它要映射的数据库表或者列的名称时,在 Hibernate 内部就要为我们隐式处理,比如一个实体没有在 @Table 中的指明表名,那么表名隐式的被认为是实体名,或者 @Entity 中的提供的名称。比如一个实体没有在 @Column 中的指明列名,那么列名隐式的被认为是该实体对应的字段名,而我们看到源码默认的隐式策略采用的类是:“org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy”。

Hibernate 中定义了多个 ImplicitNamingStrategy 的实现,可以开箱即用,而之前的逻辑名名称就是物理名称这种策略只是其中一种,其他还包括:

  • ImplicitNamingStrategyJpaCompliantImpl:默认的命名策略,兼容 JPA 2.0 的规范;
  • ImplicitNamingStrategyLegacyHbmImpl:兼容 Hibernate 老版本中的命名规范;
  • ImplicitNamingStrategyLegacyJpaImpl:兼容 JPA 1.0 规范中的命名规范;
  • ImplicitNamingStrategyComponentPathImpl:大部分与 ImplicitNamingStrategyJpaCompliantImpl,但是对于 @Embedded 等注解标志的组件处理是通过使用 attributePath 完成的,因此如果我们在使用 @Embedded 注解的时候,如果要指定命名规范,可以直接继承这个类来实现。

看一下 UML 类图:

SpringImplicitNamingStrategy 继承 ImplicitNamingStrategyJpaCompliantImpl,从源码可以看到,对外键、链表查询、索引如果未定义,都有下划线的处理策略,而 table 和 column 名字都默认与字段一样。

(2)PhysicalNamingStrategy

在这个阶段中,是根据业务需要制定自己的命名规范,通过使用 PhysicalNamingStrategy 可以实现这些规则,而不需要将表名和列名通过 @Table 和 @Column 等注解显式指定,无论对象模型中是否显式地指定列名或者已经被隐式决定,PhysicalNamingStrategy 都会被调用。但是对于 ImplicitNamingStrategy,仅仅只有当没有显式地提供名称时才会使用,也就是说当对象模型中已经指定了 @Table 或者 @Entity 等 name 时,设置的 ImplicitNamingStrategy 并不会起作用。

所以可以看应用场景,我们可以根据实际需求来定义自己的策略是继承 ImplicitNamingStrategy 还是继承 PhysicalNamingStrategy,比如加上前缀 t_ 等等,或者使用分隔符“-”等等。

需要注意的是:PhysicalNamingStrategy 永远是在 ImplicitNamingStrategy 之后执行的,并且永远会被执行到。我们看下 PhysicalNamingStrategyStandardImpl 的源码:

public class PhysicalNamingStrategyStandardImpl implements PhysicalNamingStrategy, Serializable {public static final PhysicalNamingStrategyStandardImpl INSTANCE = new PhysicalNamingStrategyStandardImpl();@Overridepublic Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {return name;}@Overridepublic Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {return name;}@Overridepublic Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {return name;}@Overridepublic Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {return name;}@Overridepublic Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {return name;}
}

默认情况下,使用就是这种策略,将 ImplicitNamingStrategy 传过来的逻辑名直接作为数据库中的物理名称,什么都没干直接返回。看下类图:

实际工作中的一些扩展

实际工作中,我们有这样的应用场景:当我们使用 MySQL 数据库的时候,一般架构师会给我们定义规范和要求,字段一般都是小写加 "_" 下划线组成,而我们的 Java 类当中都是驼峰式的字段命名方式。如果我们一个新的微服务,每个表都非常标准和规范,那么就可以让实体当中省去 @column 指定名称的过程。

定义自己的 PhysicalNamingStrategy 继承 PhysicalNamingStrategyStandardImpl 类即可内容如下:

package com.jack.model.naming;
public class MyPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
//重载PhysicalColumnName方法,修改字段的物理名称。@Overridepublic Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {String text = warp(name.getText());if(Objects.equals(text.charAt(0) , '_')){text = text.replaceFirst("_","");}return super.toPhysicalColumnName(new Identifier(text,name.isQuoted()), context);}
//将驼峰式命名转化成下划线分割的形式public static String warp(String text){text = text.replaceAll("([A-Z])","_$1").toLowerCase();if(Objects.equals(text.charAt(0) , '_')){text = text.replaceFirst("_","");}return text;}
}

修改 application.properties 添加如下内容即可:

spring.jpa.hibernate.naming.physical-strategy=com.jack.model.naming.MyPhysicalNamingStrategy

总结:

不过在实际工作中还是建议大家用显式 @Table @Column 等注解的时候就指明比较好,而可以在我们扩展的 MyPhysicalNamingStrategy 做规则验证工作,避免有些程序员没有按照我们的数据库规范命名。

H2 Database

H2 Database 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,支持 SQL,同时 H2 提供了一个十分方便的 Web 控制台用于操作和管理数据库内容。是一种内存数据库,Spring Data JPA 官方也推荐使用,不过作者建议读者在实际生产环境,我们都是用来跑测试用例的。这样可以对各种关键的 DB 没有影响,因为它是每次运行创建一份新的数据库的。

使用起来比较方便,不需要安装 Server 和 Client 的任何软件和工具,只需要我们用的时候引用一个 Jar 就可以自带 SQL 的 Server 和 Client 的操作了,非常适合演示项目使用。

我们来看一个使用案例。

(1)Gradle 引入 H2 的 jar 包

testCompile group: 'com.h2database', name: 'h2', version: '1.3.148'

(2)配置 application.properties 如下:

//# JDBC Url to use H2 DB File for persisting
spring.datasource.url:jdbc:h2:./database/samspledb;AUTO_SERVER=TRUE
//# Use H2 DB Driver
spring.datasource.driverClassName:org.h2.Driver

(3)其他什么都用改,直接启动项目就可以操作我们默认的 JPA @Entity了。

spring.jpa.hibernate.ddl-auto=update

我们再加上 ddl-auto 的配置成 update,就可以完美实现内存数据库方式的 test case 了。

Spring Data JPA 从入门到精通~Naming命名策略详解及其实践相关推荐

  1. Spring Data JPA 从入门到精通~Auditing及其事件详解

    Auditing 及其事件详解 Auditing 翻译过来是审计和审核,Spring 的优秀之处在于帮我们想到了很多繁琐事情的解决方案,我们在实际的业务系统中,针对一张表的操作大部分是需要记录谁什么时 ...

  2. Spring data JPA 之 Jackson 在实体里面的注解详解

    8 Spring data JPA 之 Jackson 在实体里面的注解详解 经过前⾯课时的讲解,相信你已经对实体⾥⾯的 JPA 注解有了⼀定的了解,但是实际⼯作中你会发现实体⾥⾯不仅有 JPA 的注 ...

  3. Spring Data JPA 从入门到精通~@Version处理乐观锁的问题

    @Version 处理乐观锁的问题 @Version 乐观锁介绍 我们在研究 Auditing 的时候,发现了一个有趣的注解 @Version,源码如下: package org.springfram ...

  4. Spring Data JPA 从入门到精通~默认数据源的讲解

    默认数据源 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://1 ...

  5. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

  6. Spring Data JPA 从入门到精通~实际工作的应用场景

    在实际工作中,有哪些场景会用到自定义 Repository 呢,这里列出几种实际在工作中的应用案例. 1. 逻辑删除场景 可以用到上面说的两种实现方式,如果有框架级别的全局自定义 Respositor ...

  7. Spring Data JPA 从入门到精通~自定义实现Repository

    EntityManager 的获取方式 我们既然要自定义,首先讲一下 EntityManager 的两种获取方式. 1. 通过 @PersistenceContext 注解. 通过将 @Persist ...

  8. Spring Data JPA 从入门到精通~EntityManager介绍

    EntityManager 介绍 我们前面已经无数次提到了,JPA 的默认 Repository 的实现类是 SimpleJpaRepository,而里面的具体实现就是调用的 EntityManag ...

  9. Spring Data JPA 从入门到精通~JpaSpecificationExecutor示例

    新建两个实体 @Entity(name = "UserInfoEntity") @Table(name = "user_info", schema = &quo ...

最新文章

  1. 『科学计算』可视化二元正态分布3D科学可视化实战
  2. 谈谈CNN中的位置和尺度问题
  3. nginx+uWSGI+django+virtualenv+supervisor发布web服务器
  4. c语言扫描图片的坐标,tc 如何在指定坐标处 输出bmp图片??
  5. Android API中被忽略的几个函数接口
  6. django-rest-swagger显示接口备注内容
  7. LeetCode 40. 组合总和 II(排列组合 回溯)
  8. zookeeper客户端下载与使用
  9. html css表格样式模板_HTML amp; CSS 基础入门【4】列表及其样式
  10. php基础知识和函数
  11. 1.1 WEB API 在帮助文档页面进行测试
  12. php读取Excel xlsx 2007+并导入MySQL
  13. java 实现excel与txt 相互转化
  14. ant如何形成时间轴和图库_如何排版微信公众平台的文章?
  15. html有序列表标签圆点,什么是无序列表、有序列表、定义列表?html列表标签学习笔记...
  16. ONLYOFFICE文档转换开发技术
  17. 1.3 熟悉 Ubuntu 桌面环境
  18. LVGL8学习之Background Styles
  19. linux下webservice压力测试,pylot压力测试支持linux及windowsWebService性能及扩展性的工具.docx...
  20. 学python能赚钱吗-利用python能怎么挣钱

热门文章

  1. 论文浅尝 - AAAI2020 | 小样本知识图谱补全
  2. 论文浅尝 | 基于属性嵌入的知识图谱间实体对齐方法
  3. 论文浅尝 | 基于常识知识图谱感知和图注意力机制的对话生成
  4. 完美世界2020编程题-救雅典娜 英雄AB PK
  5. 暑期实习NLP算法岗面经总结
  6. 我们的实践: 400万全行业动态事理图谱Demo
  7. python 词云小demo
  8. 为Openstack制作CentOS7镜像
  9. 洛谷 P1824 进击的奶牛 【二分答案】(求最大的最小值)
  10. 常用数据库的分页实现