JPA创建或修改数据库的表注释和字段注释

待我称王封你为后i 2021-09-24 15:08 499阅读 0赞

目录

  • jpacomment-spring-boot-starter
    • 需求说明
    • 思路说明

jpacomment-spring-boot-starter

JPA创建或修改数据库的表注释和字段注释

jpacomment-spring-boot-starter 使用非常简单,目前支持三种主流关系型数据库:

  1. Mysql
  2. Sqlserver
  3. Oracle

后期可以增加其他数据库。

GitHub 地址,使用查看README.md
https://github.com/dwhgygzt/jpacomment-spring-boot-starter

需求说明

JPA 比较方便,让开发免于手动创建表操作,但有一个问题表中字段无注释,虽然JPA有提供方法,但无法适应所有主流数据库。
JPA 自身提供方法如下:

  1. public class MyEntity {
  2. @Column(nullable = false,columnDefinition = "int(2) comment '我是年龄注释...'")
  3. private Integer age;
  4. }

其中 columnDefinition 其实就是写 Native Sql,这样违背了JPA的初衷“屏蔽底层数据库差异”,
因此我们计划自己写一套解决方案。

思路说明

  • 自定义注解 @TableComment@ColumnComment 到实体类和其属性上
  • 通过 JPA EntityManager 获取java 属性对应的数据库表和字段
  • 调用 SQL Alter 语句修改字段和表注释

自定义注解 @TableComment@ColumnComment 到实体类和其属性上

  1. @Target({ ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. public @interface ColumnComment {
  5. /** * 字段注释 * * @return String */
  6. String value() default "";
  7. }
  8. @Target({ ElementType.TYPE})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Inherited
  11. public @interface TableComment {
  12. /** * 字段注释 * * @return String */
  13. String value() default "";
  14. }
  15. @Data
  16. @Entity
  17. @Table(name = "T_SYS_PEOPLE")
  18. @TableComment("普通人员表")
  19. public class SysPeopleEntity implements Serializable {
  20. private static final long serialVersionUID = 1L;
  21. @EmbeddedId
  22. @ColumnComment("联合主键 NAME ID_NUMBER")
  23. private PeopleKey id;
  24. @ColumnComment("年龄")
  25. @Column(name = "AGE")
  26. private int age;
  27. @ColumnComment("地址")
  28. @Column(name = "ADDRESS")
  29. private String address;
  30. @ColumnComment("乐观锁版本号")
  31. @Version
  32. @Column(name = "RVERSION", nullable = false)
  33. private Integer rversion;
  34. @ColumnComment("创建时间")
  35. @Column(name = "CREATE_DATE", nullable = false)
  36. private Date createDate;
  37. @ColumnComment("创建人员信息")
  38. @Column(name = "CREATE_USER", nullable = false)
  39. private String createUser;
  40. @ColumnComment("更新时间")
  41. @Column(name = "UPADATE_DATE")
  42. private Date updateDate;
  43. @ColumnComment("更新人员信息")
  44. @Column(name = "UPDATE_USER")
  45. private String updateUser;
  46. @ColumnComment("备注")
  47. @Column(name = "REMARK", length = 1024)
  48. private String remark;
  49. }

通过 JPA EntityManager 获取java 属性对应的数据库表和字段

  1. public Map<String, TableCommentDTO> findAllTableAndColumn() {
  2. Map<String, TableCommentDTO> tableCommentMap = new HashMap<>(256);
  3. //通过EntityManager获取factory
  4. EntityManagerFactory entityManagerFactory = entityManager.getEntityManagerFactory();
  5. SessionFactoryImpl sessionFactory = (SessionFactoryImpl) entityManagerFactory.unwrap(SessionFactory.class);
  6. Map<String, EntityPersister> persisterMap = sessionFactory.getMetamodel().entityPersisters();
  7. for (Map.Entry<String, EntityPersister> entity : persisterMap.entrySet()) {
  8. SingleTableEntityPersister persister = (SingleTableEntityPersister) entity.getValue();
  9. Class targetClass = entity.getValue().getMappedClass();
  10. TableCommentDTO table = new TableCommentDTO();
  11. // 表注释
  12. getTableInfo(persister, table, targetClass);
  13. //除主键外的属性注释
  14. getColumnInfo(persister, table, targetClass);
  15. // 主键字段注释
  16. getKeyColumnInfo(persister, table, targetClass);
  17. tableCommentMap.put(table.getName(), table);
  18. }
  19. return tableCommentMap;
  20. }
  21. @SuppressWarnings("rawtypes")
  22. private void getTableInfo(SingleTableEntityPersister persister, TableCommentDTO table, Class targetClass) {
  23. table.setColumnCommentDTOList(new ArrayList<>(32));
  24. table.setName(persister.getTableName());
  25. TableComment tableComment = AnnotationUtil.getAnnotation(targetClass, TableComment.class);
  26. if (tableComment != null) {
  27. table.setComment(tableComment.value());
  28. } else {
  29. table.setComment("");
  30. }
  31. }
  32. /** * 递归获取所有父类的类对象 包括自己 * 最后的子类在第一个 * * @param targetClass targetClass * @param list list */
  33. @SuppressWarnings("rawtypes")
  34. private void getAllClass(Class targetClass, List<Class> list) {
  35. list.add(targetClass);
  36. if (!Object.class.equals(targetClass.getSuperclass())) {
  37. getAllClass(targetClass.getSuperclass(), list);
  38. }
  39. }
  40. @SuppressWarnings("rawtypes")
  41. private void getColumnInfo(SingleTableEntityPersister persister, TableCommentDTO table, Class targetClass) {
  42. // 情况比较复杂,必须还要判断是否有父类,存在父类则还要取父类的字段信息,优先取得子类字段为依据
  43. List<Class> classList = new ArrayList<>(2);
  44. getAllClass(targetClass, classList);
  45. Set<String> alreadyDealField = new HashSet<>(32);
  46. Set<String> allColumnField = new HashSet<>(32);
  47. Iterable<AttributeDefinition> attributes = persister.getAttributes();
  48. //属性
  49. for (AttributeDefinition attr : attributes) {
  50. allColumnField.add(attr.getName());
  51. }
  52. classList.forEach(classItem -> Arrays.stream(ClassUtil.getDeclaredFields(classItem)).forEach(field -> {
  53. if (allColumnField.contains(field.getName())) {
  54. // 判断是否已经处理过
  55. if (!alreadyDealField.contains(field.getName())) {
  56. //对应数据库表中的字段名
  57. String[] columnName = persister.getPropertyColumnNames(field.getName());
  58. getColumnComment(table, classItem, field.getName(), columnName);
  59. alreadyDealField.add(field.getName());
  60. }
  61. }
  62. }));
  63. }
  64. @SuppressWarnings("rawtypes")
  65. private void getKeyColumnInfo(SingleTableEntityPersister persister, TableCommentDTO table, Class targetClass) {
  66. String idName = persister.getIdentifierPropertyName();
  67. String[] idColumns = persister.getIdentifierColumnNames();
  68. getColumnComment(table, targetClass, idName, idColumns);
  69. }
  70. @SuppressWarnings("rawtypes")
  71. private void getColumnComment(TableCommentDTO table, Class targetClass, String propertyName, String[] columnName) {
  72. ColumnComment idColumnComment = AnnotationUtil.getAnnotation(
  73. ClassUtil.getDeclaredField(targetClass, propertyName), ColumnComment.class);
  74. Arrays.stream(columnName).forEach(item -> {
  75. ColumnCommentDTO column = new ColumnCommentDTO();
  76. column.setName(item);
  77. if (idColumnComment != null) {
  78. column.setComment(idColumnComment.value());
  79. } else {
  80. column.setComment("");
  81. }
  82. table.getColumnCommentDTOList().add(column);
  83. });
  84. }

调用 SQL Alter 语句修改字段和表注释

  1. public interface AlterCommentService {
  2. /** * 获取当前数据库 schema * * @return 数据库 schema */
  3. String getSchema();
  4. /** * 设置当前的schema */
  5. void setSchema(String schema);
  6. /** * 修改表注释 * * @param tableName 表名称 * @param tableComment 表注释 */
  7. void alterTableComment(String tableName, String tableComment);
  8. /** * 修改表字段注释 * * @param tableName 表名称 * @param columnName 字段名称 * @param columnComment 字段注释 */
  9. void alterColumnComment(String tableName, String columnName, String columnComment);
  10. /** * 获取 jdbcTemplate * * @param jdbcTemplate jdbcTemplate */
  11. void setJdbcTemplate(JdbcTemplate jdbcTemplate);
  12. }

各数据具体实现继承该接口实现自己的方法

例如Mysql的

  1. public class MysqlAlterCommentServiceImpl implements AlterCommentService {
  2. private String schema;
  3. private JdbcTemplate jdbcTemplate;
  4. @Override
  5. public void setSchema(String schema) {
  6. this.schema = schema;
  7. }
  8. @Override
  9. public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  10. this.jdbcTemplate = jdbcTemplate;
  11. }
  12. /** * 1注释 2表名称 3字段名称 */
  13. String updateTableComment = " ALTER TABLE %s.%s COMMENT = ?";
  14. /** * 1注释 2表名称 3字段名称 */
  15. String getUpdateColumnComment = " SELECT CONCAT('ALTER TABLE `',a.TABLE_SCHEMA,'`.`',a.TABLE_NAME,'` MODIFY COLUMN `',a.COLUMN_NAME,'` ',a.COLUMN_TYPE,\n" +
  16. " (CASE WHEN a.IS_NULLABLE = 'NO' THEN ' NOT NULL ' ELSE\t'' END), \n" +
  17. " (CASE WHEN a.COLUMN_DEFAULT IS NOT NULL THEN CONCAT(' DEFAULT ''',a.COLUMN_DEFAULT,''' ') ELSE\t'' END) ,' COMMENT ?') ALTER_SQL\n" +
  18. "FROM information_schema.`COLUMNS` a\n" +
  19. "WHERE a.TABLE_SCHEMA = ? \n" +
  20. "AND a.TABLE_NAME = ?\n" +
  21. "AND a.COLUMN_NAME = ? ";
  22. @Override
  23. public void alterTableComment(String tableName, String tableComment) {
  24. jdbcTemplate.update(String.format(updateTableComment, "`" + schema + "`", "`" + tableName + "`"), tableComment);
  25. }
  26. @Override
  27. public void alterColumnComment(String tableName, String columnName, String columnComment) {
  28. String updateColumnComment = jdbcTemplate.queryForObject(getUpdateColumnComment, String.class, schema, tableName, columnName);
  29. if (StrUtil.isNotBlank(updateColumnComment)) {
  30. jdbcTemplate.update(updateColumnComment, columnComment);
  31. }
  32. }
  33. @Override
  34. public String getSchema() {
  35. return schema;
  36. }
  37. }

GitHub 地址,使用查看README.md 十分简单。
https://github.com/dwhgygzt/jpacomment-spring-boot-starter

发表评论

表情:
评论列表 (有 0 条评论,499人围观)

还没有评论,来说两句吧...

相关阅读