Skip to content

自动建表

启动时扫描实体,对照现有库表,执行 CREATE TABLE / ALTER TABLE ADD / CREATE INDEX

DDL 文本由 jkit-sqlSqlEntities 按方言生成;本模块只做 JDBC 元数据对比和执行。jkit-sql 不执行 SQL、不带 JDBC 驱动。运行时零第三方依赖。JDK 8+(Boot 3 starter 要 JDK 17+)。

按你的项目选一条路:

项目类型加哪个包还要写启动代码吗
Spring Boot 2.xjkit-sql-auto-spring-boot-2不用。就绪后自动跑,用应用里的 DataSource
Spring Boot 3.xjkit-sql-auto-spring-boot-3同上
普通 Java / Servlet / 自己管 mainjkit-sql-auto要。在启动入口调一次 SqlAuto.run(...)

三个 starter / 核心包都会带上 jkitjkit-sql。JDBC 驱动仍由宿主提供。


1. 写实体

扫描认三种标记(反射按类名,没有 JPA / MyBatis-Plus 编译依赖):

  • jkit @SqlTable / @SqlId / @SqlColumn / @SqlGenerated
  • JPA @Entity / @Table / @Id / @Column / @GeneratedValuejavaxjakarta
  • MyBatis-Plus @TableName / @TableId
java
import com.alianga.jkit.sql.entity.SqlColumn;
import com.alianga.jkit.sql.entity.SqlGenerated;
import com.alianga.jkit.sql.entity.SqlId;
import com.alianga.jkit.sql.entity.SqlTable;

@SqlTable(name = "demo_user", comment = "用户", indexes = {"idx_email:email"})
public class User {
    @SqlId
    @SqlGenerated          // 整数列 → AUTO_INCREMENT / IDENTITY;字符串/UUID 只当主键
    private Long id;

    @SqlColumn(name = "user_name", length = 32, nullable = false, comment = "用户名")
    private String name;

    @SqlColumn(length = 64, unique = true)
    private String email;
}

已经在用 JPA 的类不用改注解,例如:

java
@Entity
@Table(name = "file_storage")
public class FileStorage {
    @Id
    @Column(name = "id", length = 32)
    @GeneratedValue(generator = "system-uuid")  // 字符串 UUID,不会写成 IDENTITY
    private String id;
    // ...
}

索引:@SqlTable(indexes = {"col"})"name:col1,col2";JPA @Table(indexes = @Index(...)) 同样认。未写名字时生成 {table}_{col}_idx

更细的类型映射、注释、外键见 实体扫描


2. Spring Boot 怎么用

加对应 starter,配 jkit.sql.auto.packages(或 entities),不必main 里调 SqlAuto.runApplicationReadyEvent 时自动执行一次,数据源用容器里的 DataSource(通常就是 spring.datasource.*)。

Boot 2.x(JDK 8+)

xml
<dependency>
    <groupId>com.alianga</groupId>
    <artifactId>jkit-sql-auto-spring-boot-2</artifactId>
    <version>2.0.1</version>
</dependency>

Boot 3.x(JDK 17+)

xml
<dependency>
    <groupId>com.alianga</groupId>
    <artifactId>jkit-sql-auto-spring-boot-3</artifactId>
    <version>2.0.1</version>
</dependency>

application.yml

yaml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop
    username: shop
    password: secret

jkit:
  sql:
    auto:
      enabled: true          # 默认 true;false 则整段跳过
      mode: update           # none / validate / update / create / create-drop
      packages: com.example.entity
      # entities:            # 不想扫包时,列出全限定名
      #   - com.example.entity.User
      show-sql: true

packagesentities 至少配一项,否则启动日志会是 no entities, skip

生产建议保持 mode: update(只加表/列/索引,不改已有列、不删列)。开发可以 create / create-drop

关掉:

yaml
jkit.sql.auto.enabled: false

mode: none

Boot 2 注册走 spring.factories,Boot 3 走 AutoConfiguration.imports,引入坐标即可,不用 @Import


3. 非 Spring 怎么用

只加核心包:

xml
<dependency>
    <groupId>com.alianga</groupId>
    <artifactId>jkit-sql-auto</artifactId>
    <version>2.0.1</version>
</dependency>

再自己提供 JDBC 驱动。在 main、Servlet 监听器等启动入口调 一次

java
import com.alianga.jkit.sql.auto.SqlAuto;
import com.alianga.jkit.sql.auto.SqlAutoMode;
import com.alianga.jkit.sql.auto.SqlAutoOptions;

public static void main(String[] args) {
    SqlAuto.run(SqlAutoOptions.defaults()
            .url("jdbc:mysql://localhost:3306/shop")
            .username("root")
            .password("secret")
            .packages("com.example.entity")
            .mode(SqlAutoMode.UPDATE));
    // 再启动 Web / 业务
}

已有 DataSource

java
SqlAuto.run(SqlAutoOptions.defaults()
        .dataSource(dataSource)
        .packages("com.example.entity"));

已有 Connection(调用方负责开关连接):

java
SqlAuto.run(connection, SqlAutoOptions.defaults().entities(User.class, Order.class));

用配置文件、不写链式 API

SqlAuto.run() / SqlAutoOptions.fromConfig()jkit.sql.auto.*。没有 jkit.sql.auto.url 时回落 spring.datasource.*(普通 Java 也可以把 Spring 风格的 yml 当配置用)。

yaml
# application.yml,放在工作目录或 classpath
jkit:
  sql:
    auto:
      url: jdbc:mysql://localhost:3306/shop
      username: root
      password: secret
      packages: com.example.entity
      mode: update
java
public static void main(String[] args) {
    SqlAuto.run(); // = SqlAuto.run(SqlAutoOptions.fromConfig())
}

独立进程(classpath 含本模块、jkit-sql、JDBC 驱动和配置文件):

text
java com.alianga.jkit.sql.auto.SqlAuto

4. 只看 SQL、不改库

dryRun(true)SqlAuto.run(options) 不打开 JDBC。URL 只用来推断方言,按空库规划全量 CREATE TABLE,库没启动也能打印:

java
SqlAutoPlan plan = SqlAuto.run(SqlAutoOptions.defaults()
        .dialect(SqlDialect.POSTGRES)   // 或 .url("jdbc:postgresql://...")
        .packages("com.example.entity")
        .mode(SqlAutoMode.CREATE_DROP)
        .dryRun(true));
List<String> sqls = plan.sql();

要对照现有表ALTER,把已打开的 Connection 传给 run(connection, options),同样可以 dryRun(true)(只规划不执行)。

配置项:jkit.sql.auto.dry-run: true


5. 配置项

前缀一律 jkit.sql.auto.。Spring Boot starter 绑同一套;非 Spring 的 fromConfig() 也读这一套。

key默认说明
enabledtruefalserun() 直接返回
modeupdatenone / validate / update / create / create-drop;也认 ddl-auto
packages(空)扫描包,列表或逗号分隔;别名 package / base-package / base-packages
entities(空)实体 FQCN 列表
dialect从 URL / DatabaseMetaData 推断mysql / postgres / oracle / oracle12 / h2 / dm …(SqlDialect.fromName
urlspring.datasource.urlJDBC URL
usernamespring.datasource.username用户名
passwordspring.datasource.password密码
driver按 URL 猜驱动类;也认 driver-class-name
fail-fasttrue一条 DDL 失败是否立即抛错
alter-columnfalse类型不一致时是否 ALTER/MODIFY
drop-extra-columnsfalse是否删除实体里没有的列
create-indextrue是否补 CREATE INDEX
table-prefix(空)表名统一前缀,如 t_;作用于建表 / 改表 / 删表 / 索引 / 序列 / 外键目标表
index-prefix-enabledtrue自动派生的索引名是否也带 table-prefix(如 t_user 的索引是 t_user_idx 还是 user_idx);实体里显式写的 @Index(name=…) 始终原样保留,不受此开关影响
quote-identifiersfalse标识符加方言引号
show-sqltrue打日志
dry-runfalse只规划不执行
catalog / schemaJDBC 默认DatabaseMetaData 查找范围

链式 API 与配置一一对应,例如 .mode(SqlAutoMode.UPDATE).packages("a","b").alterColumn(true)。另有只在代码里设的项:postgresIdentityStyle(SERIAL)foreignKeys(false)autoIncrement(false)


6. 模式

对标 JPA spring.jpa.hibernate.ddl-auto

SqlAutoMode行为
NONE什么都不做
VALIDATE缺表 / 缺列 / 类型不兼容时抛 SqlAutoException,不改库
UPDATE(默认)缺表则建、缺列则加、缺索引则建;默认不改已有列类型、不删列/表
CREATEDROP 托管表再按实体重建(开发用)
CREATE_DROP启动同 CREATE,JVM 退出时再删表

UPDATE 是生产默认:只追加,不收缩。已有列类型对不上时默认跳过,要改列需 alter-column: true。实体里没有的列默认保留,要删需 drop-extra-columns: true

显式删托管表(按外键逆序):

java
SqlAuto.drop(SqlAutoOptions.defaults().url(url).entities(User.class));

7. 方言

未配置 dialect 时:

  1. JDBC URL 前缀(jdbc:mysql: → MYSQL,jdbc:postgresql: → POSTGRES,jdbc:oracle: → ORACLE,达梦 jdbc:dm: → DAMENG …)
  2. DatabaseMetaData.getDatabaseProductName()(Oracle 12c+ 产品名会落到 ORACLE12
  3. 再不行默认 MYSQL

jdbc:oracle: 推断的是经典 ORACLE(标识符 30 字符,自增走 SEQUENCE + TRIGGER)。要用 12c 的 IDENTITY / OFFSET FETCH,显式 dialect: oracle12


8. 行为细节与常见坑

会做

  • 扫描包或显式实体列表,按外键把被引用表排在前面
  • 表不存在 → CREATE TABLE(随后可跟 CREATE INDEX、注释附录、Oracle 11g SEQUENCE)
  • 表在、列缺 → ALTER TABLE … ADD
  • 索引缺 → CREATE INDEX
  • 表/列注释按方言拆成独立语句(MySQL 内联 COMMENT,PG/Oracle COMMENT ON,SQL Server sp_addextendedproperty

默认不做

  • 改已有列类型、删多余列/表、改列名、改主键、迁数据
  • 存储过程 / 视图 / 触发器(Oracle 11g 自增触发器除外)
  • 把 JDBC 驱动打进本模块

主键 / 自增

  • 整数 + @SqlGenerated / @GeneratedValue(IDENTITY|AUTO)AUTO_INCREMENT / GENERATED … AS IDENTITY / SERIAL
  • 字符串、UUID、@GeneratedValue(generator="system-uuid")GenerationType.UUID只写 PRIMARY KEY,不会给 PostgreSQL 生成 VARCHAR … IDENTITY(会语法错误)
  • 经典 Oracle(ORACLE)整数自增:CREATE SEQUENCE {table}_{column}_seq + BEFORE INSERT 触发器;CREATE_DROP / drop 会先 DROP SEQUENCE

索引名 / 标识符长度

未命名索引 {table}_{col}_idx。经典 Oracle 上限 30 字符,超长截断并追加 4 位散列;ORACLE12 为 128。序列名、触发器名同一规则。

继承列

子类与 MappedSuperclass / 父类同时声明 create_time 时只生成一次,避免 PostgreSQL column specified more than once

窄产品

  • OpenGauss 老版本:.postgresIdentityStyle(SERIAL)
  • GBase 8a:.foreignKeys(false).createIndex(false)
  • DuckDB:.autoIncrement(false).foreignKeys(false).createIndex(false)
  • H2 内存库 CREATE_DROP:URL 加 DB_CLOSE_DELAY=-1,否则连接一关库就没了

基于 Apache License 2.0 发布