需求描述
需求是这样的: 因为我们目前的一个老项目是 Oracle 数据库的, 这个库呢, 数据库是没有注释的, 而且字段名和表名都是大写风格, 比如
在代码层面的 po 呢, 以前也是没有任何注释的, 但是经过这些年, 大家慢慢踩坑多了, 也给 po 加上了一些注释了, 比如:
现状就是这样, 再说说目标是: 希望把这个库能转成 MySQL, 表名和字段名最好都用下划线分隔每个单词, 字段呢, 最好能有注释. 也就是差不多下面这样:
方案分析
最早我尝试的就是 hibernate 正向工程, 建一个空的 MySQL 库, 然后配置 hibernate 的选项为:
这样的话呢, 就会自动在我们指定的 MySQL 数据库生成表了, 不过, 有两个瑕疵是:
生成的表, 字段和表名都是和 PO 里一样的驼峰格式;
没有注释.
第一个问题, 我这边是通过覆盖 hibernate 源码的方式解决, 将驼峰转换为了下划线;
第二个问题, 麻烦一些, 因为要做到字段带注释的话, 那就得看看哪里能拿到注释. hibernate 执行过程中, 从 PO 类里? 不可能, 编译好的 class 里, 怎么会有注释呢? 那就只能从源文件着手了, PO 类的源码里, field 上是有注释的, 那就必须要去解析 PO 类的 java 文件, 从里面提取出每个 PO 类中: 字段 --》注释的对应关系来.
大方向已定, 我们开搞!
最后我这里解决这两个问题, 是覆盖了三个 hibernate 的类的源码, 大概如下:
在继续之前, 先说明一下, 这个肯定是要修改 hibernate 源码的, 这里只讲讲怎么覆盖某个 jar 包里的类:
我这里是 spring mvc 的老项目, 最后是部署在 tomcat 运行, tomcat 的 webAppClassloader, 负责加载以下两个路径的 class:
覆盖的原理, 就是依赖其查找 class 的先后顺序来做, 比如 lib 下的某个 jar 包有: org.hibernate.mapping.Table 这个类, 正常情况下, 都会加载到这个类; 但如果我们在 classes 下放一个同包名同类名的类, 那么就会优先加载我们的这个 class 了. 但是假设这个类引用了 hibernate 的其他类 B, 不影响, 毕竟我们没覆盖类 B, 所以还是会到 lib 下查找, 最后还是会使用 hibernate jar 包中的 B.
最终源码已经放在了:
问题 1 解决步骤: 驼峰格式的建表语句转下划线
知道怎么覆盖了, 再说说怎么去找要覆盖哪儿, 这个需要一点经验. 我这里先还原成没修改时的样子, 跑一下项目, 发现日志有以下输出:
- 2019-10-23 13:47:11.819 [main] DEBUG [] org.hibernate.SQL -
- drop table if exists KPIRECORD
- 2019-10-23 13:47:11.823 [main] DEBUG [] org.hibernate.SQL -
- create table KPIRECORD (
- kpiRecordId varchar(255) not null,
- endTime varchar(255),
- evaluatorId varchar(255),
- kpiComment varchar(255),
- kpiDate datetime,
- kpiValue double precision,
- roleCode integer,
- startTime varchar(255),
- superiors varchar(255),
- userId varchar(255),
- primary key (kpiRecordId)
- )
- 2019-10-23 13:47:11.988 [main] INFO [] org.hibernate.tool.hbm2ddl.SchemaExport - HHH000230: Schema export complete
其他不重要, 我们看最后一行, 里面包含了 Schema export complete, 这个肯定是代码里的日志, 我们拿这个东西, 在代码里搜一波 (这一步, 要求 maven 是下载了 jar 包的源码):
接下来, 我们点进去, 因为 maven 下载了源码的关系, 所以再利用 idea 的 findUsage 功能, 剩下的, 就是在觉得比较靠谱的地方打上断点, 运行一下, debug 一下, 大概就知道流程了.
找啊找, 找到了下面的地方,(org.hibernate.mapping.Table#sqlCreateString)
怎么覆盖, 不用多说了吧, 如果是 spring mvc(或者 spring boot) 架构, 都要在最上层的 module 里的 src 下操作, 加上这么一个全路径一致的类, 然后将里面的 sqlCreateString 改写.
我这里附上改写后的:
到这里, 基本搞定了第一个问题.
问题 2 解决步骤: 给建表语句增加注释
其实这个步骤分成了 2 个小步骤, 第一步是拿到下面这样的数据:
第二步, 就是像上面第一步那样, 在生成 create table 语句时, 根据 table 名称, 取到上面这样的数据, 然后再根据列名, 取到注释, 拼成一条下面这样的 (重点是下面加粗部分):
start_time varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '考评开始时间',
问题 2 解决步骤之第一步: 获取表字段注释
这部分纯粹考验字符串解析功力了, 我说下思路, 也可以直接看源码. 主要是逐行读取 java 文件, 然后看该行是否为注释 (区分单行注释和多行注释):
单行:
- /** 被考评人 */
- private String userId;
多行:
- /**
- * 主键, 考评记录 ID
- */
- private String kpiRecordId;
单行注释的话, 直接用正则匹配; 多行的话, 会引入一个状态变量, 最后还是会转换为一个单行注释.
匹配上后, 提取出注释, 存到一个全局变量; 如果下一行正则匹配了一个 field, 则将之前的注释和这个 field 凑一对, 存到 map 里.
大致流程就是这样的, 代码如下:
展开查看
问题 2 解决步骤之第二步: 覆盖 hibernate 源码, 建表过程中构造注释
这次覆盖了 org.hibernate.cfg.Configuration#generateSchemaCreationScript 方法:
然后里面的内容也不用我细说了, 再次根据列名查找注释, 构造建表 sql 就行了.
这里加个成果展示:
总结
希望对大家有所帮助, 有疑问可以直接加我.
源码在:
来源: https://www.cnblogs.com/grey-wolf/p/11726237.html