当前位置: 首页 > news >正文

xbatis-ddl-auto:轻量自动建表工具,功能丰富且安全有保障!

xbatis-ddl-auto简介

xbatis-ddl-auto是一个基于xbatis实体元数据的轻量自动建表工具。它复用xbatis的 `@Table`、`@TableId`、`@TableField`、`@ColumnDefinition`等注解解析结果,根据实体类生成并执行数据库DDL,提供接近JPA `ddl-auto=create/update`的使用体验,但不引入JPA或Hibernate。

功能特性

它具有以下功能:根据xbatis实体生成 `CREATE TABLE` SQL;表不存在时自动建表;表存在时可在 `UPDATE` 模式下自动追加新增字段;支持只生成SQL,不执行数据库操作;通过JDBC `DatabaseMetaData` 判断表、字段和索引是否存在;支持常见Java类型到数据库列类型映射;支持 `@ColumnDefinition` 配置字段长度、精度、小数位、默认值、唯一约束、非空和字段注释;支持类级 `@Index` 创建普通索引和唯一索引。

安全边界

`UPDATE` 模式只会自动新增字段和缺失索引,不会自动执行以下高风险操作:删除数据库已有字段、修改字段类型、修改字段长度、修改字段是否可空、修改默认值、重命名字段、修改或删除已有索引、删除或重建表。这些操作可能造成数据丢失或生产事故,建议通过人工审核SQL或专业迁移工具处理。

Maven坐标

本工具Maven坐标为:cn.xbatisxbatis-ddl-auto1.0.1 运行依赖xbatis core:cn.xbatisxbatis-core1.10.6 默认测试使用JUnit 5和H2;需要真实数据库的集成测试通过Maven profile单独执行。

快速开始

首先定义xbatis实体,示例如下:

import cn.xbatis.db.annotations.ColumnDefinition;import cn.xbatis.db.annotations.Table;import cn.xbatis.db.annotations.TableId;import java.math.BigDecimal;import java.time.LocalDateTime;@Table("sys_user")public class SysUser { @TableId private Long id; @ColumnDefinition(length = 64, nullable = false) private String username; @ColumnDefinition(precision = 10, scale = 2, defaultValue = "0") private BigDecimal balance; private LocalDateTime createdAt;}
然后执行自动建表:
import db.sql.api.DbType;DDLAuto.of(DbType.MYSQL).add(SysUser.class).execute(dataSource);

CREATE模式

`CREATE` 是默认模式,执行方式为:

DDLAuto.of(DbType.MYSQL).add(SysUser.class).execute(dataSource);
其行为是:表不存在时执行 `CREATE TABLE`;表已存在时跳过,不做任何变更。

UPDATE模式

`UPDATE` 模式用于补新增字段和缺失索引,执行方式为:

import cn.xbatis.ddl.auto.Mode;DDLAuto.of(DbType.MYSQL).mode(Mode.UPDATE).add(SysUser.class).execute(dataSource);
其行为是:表不存在时执行 `CREATE TABLE`;表已存在时读取数据库已有列和索引,只对实体中新增的字段执行 `ALTER TABLE ... ADD COLUMN ...`,并创建缺失索引。重复执行 `UPDATE` 模式不会重复添加已存在字段或已存在索引。

只生成SQL/预览SQL

不连接数据库,只生成建表SQL的方式为:

List sqlList = DDLAuto.of(DbType.PGSQL).add(SysUser.class).sqlList();
如果要按当前数据库状态预览将要执行的SQL,可以传入 `DataSource` 或 `Connection`。该方法只读取JDBC元数据并生成SQL,不会执行DDL:
List sqlList = DDLAuto.of(DbType.MYSQL).mode(Mode.UPDATE).add(SysUser.class).sqlList(dataSource);
其行为是:表不存在时返回 `CREATE TABLE` 及附属DDL;表已存在且是 `CREATE` 模式时返回空列表;表已存在且是 `UPDATE` 模式时只返回缺失字段的 `ALTER TABLE ... ADD COLUMN ...` 及附属DDL,以及缺失索引的 `CREATE INDEX`。也可以使用底层构建器生成单个字段的新增列SQL:
import cn.xbatis.ddl.auto.DDLBuilder;import cn.xbatis.ddl.auto.DefaultDDLBuilder;DDLBuilder builder = new DefaultDDLBuilder();String sql = builder.addColumnSql(DbType.MYSQL, SysUser.class, "email");
生产或准生产环境建议先通过 `sqlList(dataSource)` 或 `sqlList(connection)` 生成SQL并审核,再决定是否执行。

执行监听

DDL会按SQL列表逐条执行。若中途失败,数据库可能已经保留前面成功执行的DDL。可以配置执行监听器记录已执行SQL:

import cn.xbatis.ddl.auto.Mode;import cn.xbatis.ddl.auto.DDLExecutionListener;import java.util.ArrayList;import java.util.List;List executedSqlLog = new ArrayList<>();DDLAuto.of(DbType.MYSQL).mode(Mode.UPDATE).executionListener(new DDLExecutionListener() { @Override public void afterExecute(String sql, List executedSqlList) { executedSqlLog.add(sql); } @Override public void onExecuteError(String sql, SQLException exception, List executedSqlList) { // sql为当前失败SQL,executedSqlList为失败前已成功执行的SQL。 }}).add(SysUser.class).execute(dataSource);
生产或准生产环境建议先通过 `sqlList(dataSource)` 或 `sqlList(connection)` 生成SQL并审核,再决定是否执行。执行失败时抛出的 `SQLException` 消息也会包含当前失败SQL和失败前已执行SQL。

支持的注解

@Table

用于解析表名和schema,示例如下:

@Table("sys_user")public class SysUser {}
@TableId

用于识别主键和数据库自增,示例如下:

@TableIdprivate Long id;
默认自增类型为xbatis的 `IdAutoType.AUTO`。单主键会按数据库方言生成自增片段;联合主键不会为每个主键字段自动生成自增片段。如果主键使用 `IdAutoType.SQL` 通过数据库序列取值,会从 `sql` 中解析序列名并在建表前生成 `CREATE SEQUENCE`:
@TableId(dbType = DbType.Name.PGSQL, value = IdAutoType.SQL, sql = "select nextval('id_test_id_seq')")@TableId(dbType = DbType.Name.ORACLE, value = IdAutoType.SQL, sql = "select id_test_seq.NEXTVAL FROM dual")@TableId(dbType = DbType.Name.SQL_SERVER, value = IdAutoType.SQL, sql = "select next value for id_test_sqlserver_seq")@TableId(dbType = DbType.Name.DB2, value = IdAutoType.SQL, sql = "select next value for id_test_db2_seq from sysibm.sysdummy1")private Long id;
当前支持解析:PostgreSQL:`nextval('sequence_name')`;Oracle / DM:`sequence_name.NEXTVAL`;SQL Server / DB2:`NEXT VALUE FOR sequence_name`;其他数据库:兜底解析上述常见形式,并生成通用序列DDL:
CREATE SEQUENCE my_sequence START WITH 1 INCREMENT BY 1;
`UPDATE` 模式会读取数据库已有序列,只创建缺失序列,不重复创建。
@ColumnDefinition

用于控制建表字段定义,示例如下:

@ColumnDefinition( length = 64, nullable = false, unique = true, comment = "用户名")private String username;
常用配置有:`length`(字符串长度)、`precision`(数值精度)、`scale`(小数位数)、`defaultValue`(数据库默认值SQL片段)、`nullable`(是否允许为空)、`unique`(是否唯一)、`definition`(字段类型片段,配置后优先替代Java类型到数据库类型的自动推导,`length`、`precision`、`scale`、`defaultValue`、`nullable`、`unique`、`comment` 等其他配置仍会继续生效)、`comment`(字段注释;MySQL使用列内联 `COMMENT`,PostgreSQL / Oracle / DM使用独立 `COMMENT ON COLUMN`,SQL Server使用 `sys.sp_addextendedproperty`)。当 `definition` 本身没有包含括号参数时,会按配置补齐长度或精度,例如 `@ColumnDefinition(definition = "VARCHAR", length = 64)` 会生成 `VARCHAR(64)`;如果已经写成 `VARCHAR(64)`,则不会再追加参数。`unique = true` 目前表示单字段唯一约束:`CREATE` 模式下,多数关系型数据库使用列内联 `UNIQUE`;`UPDATE` 模式新增字段时,SQLite不支持 `ALTER TABLE ADD COLUMN ... UNIQUE`,会改为先新增字段再生成 `CREATE UNIQUE INDEX`;ClickHouse不支持传统唯一约束,配置 `unique = true` 时会直接抛出异常,避免生成无效SQL;不支持联合唯一、部分唯一索引、命名唯一约束和已存在字段的唯一约束同步。
@Index

用于在实体类上声明数据库索引,示例如下:

import cn.xbatis.db.IndexDirection;import cn.xbatis.db.annotations.Index;import cn.xbatis.db.annotations.IndexField;import cn.xbatis.db.annotations.Table;@Index(name = "idx_sys_user_username", fields = @IndexField(name = "username"))@Index( name = "uk_sys_user_username_created_at", unique = true, fields = { @IndexField(name = "username"), @IndexField(name = "createdAt", direction = IndexDirection.DESC) })@Table("sys_user")public class SysUser {}
说明如下:`name`(索引名;为空时按表名和列名生成稳定索引名)、`unique`(是否唯一索引)、`fields`(索引字段,`name` 使用实体字段名,也兼容已映射的列名)、`direction`(索引字段排序,支持 `ASC`、`DESC`,默认不追加排序片段)、`CREATE` 模式下,建表后生成 `CREATE INDEX`;`UPDATE` 模式下,只按索引名创建数据库中缺失的索引,不修改或删除已有索引;ClickHouse不支持传统 `CREATE INDEX` 时会直接抛出异常,避免生成无效SQL。

类型映射

默认类型映射包括:`String` / `Character` / `UUID` -> `VARCHAR`;`Integer` / `int` -> `INTEGER`;`Long` / `long` / `BigInteger` -> `BIGINT`;`Short` / `short` -> `SMALLINT`;`Boolean` / `boolean` -> `BOOLEAN`、MySQL为 `TINYINT(1)`;`BigDecimal` -> `DECIMAL(precision, scale)`;`Float` / `Double` -> 浮点类型;`byte[]` -> 二进制大字段;`LocalDate` -> `DATE`;`LocalTime` -> `TIME`,Oracle / DM使用 `TIMESTAMP`;`LocalDateTime` / `Timestamp` / `Date` -> `TIMESTAMP`,MySQL使用 `DATETIME`,SQL Server使用 `DATETIME2`;普通 `enum` -> `VARCHAR(64)`;实现xbatis `EnumSupport` 的枚举 -> 按 `T` 的类型映射,例如 `EnumSupport` -> `INTEGER`。具体类型会根据 `DbType` 做方言调整。

http://www.jsqmd.com/news/1117891/

相关文章:

  • VDA5050协议:实现跨品牌AGV统一调度的工业通信标准
  • 系统调用的性能成本深度分析:一次read()背后的上下文切换代价量化
  • 终极macOS开发工具箱:DevToysMac如何提升你的编码效率
  • 大模型微调实战:金融领域高效适配与优化
  • 【JAVA毕设源码分享】基于springboot便民社区图书销售系统的设计与开发的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 圆偏振光的实现方式:相位延迟片原理及悟赫德方案选型——以iPhone 17护眼钢化膜为例
  • STM32通过MC74HC165A扩展16按钮的SPI接口设计
  • 3000元成本72小时赚50万美元——AI短剧出海怎么落地
  • GPT 输出不符合预期?先学会这套结构化提问方法
  • 城通网盘解析工具完整指南:3步实现高速下载加速
  • 论文通关利器!好用的AI论文软件,成稿速度破纪录
  • AI Agent平台工程化架构:从状态机到生产落地的系统设计
  • Dell笔记本风扇噪音终极解决方案:智能风扇控制全攻略
  • (十四)「JVS-Rules规则引擎 V2.5」— 条件分支节点
  • 戴森球计划工厂蓝图宝典:从新手到专家的高效建造指南
  • 一文理解 Apache Hadoop 机架感知
  • STM32与DS28EC20 EEPROM的嵌入式数据存储方案
  • PCF8591与TM4C129ENCZAD的混合信号处理方案
  • Android应用安全加固实战:从InsecureBankv2漏洞修复到工程化实践
  • OmenSuperHub终极指南:深度解锁惠普暗影精灵笔记本性能控制
  • TIDAL无损音乐下载终极指南:轻松获取24-bit/192kHz高解析度音频
  • FigmaCN中文插件:打破语言壁垒,让Figma设计更高效
  • 从零到精通:S32K144车规级MCU完整开发实战指南
  • 智能体本地运行时选型:LM Studio与Ollama深度对比
  • Akamai Bot Manager实战:四层智能引擎精准识别与管理自动化流量
  • JMeter从零到一:环境搭建、核心配置与首个性能测试实战
  • 数据库安全工具的革命:MDUT如何打破多数据库利用的壁垒
  • UI自动化测试中文件上传难题的四种解决方案与实战指南
  • Si4732与STM32F373VC数字收音机方案设计与优化
  • ConvShatter:边缘计算中的DNN模型安全保护技术