Skip to content

注解模型设计

设计原则

@Model / @Field 的设计借鉴了 oinone-pamirs 的「外层注解承载基础元信息 + 内嵌注解承载类型与高级配置」的范式:

  • 外层注解描述“是什么”@Model 标识“这是一个模型”,@Field 标识“这是一个字段”
  • 嵌套注解描述“高级特性”@Model.Advanced / @Field.Advanced 承载更细的配置
  • 嵌套类型子注解描述“类型语义”@Field.String / @Field.Date / @Field.BigDecimal 显式表达数据库列类型

这样做的收益:

  • 外层注解参数清爽,不会因为「String 字段需要 length」「BigDecimal 字段需要 precision/scale」这种少数派需求把 @Field 撑成几十个参数
  • 同一字段可同时叠加 @Field(基础元信息)+ @Field.Advanced(持久化配置)+ @Field.String(类型与长度),各司其职
  • 后续扩展新类型只需新增一个内嵌注解,不破坏向后兼容

所有注解都打了 @Target(ElementType.TYPE/FIELD)@Retention(RetentionPolicy.RUNTIME) 前置知识。这是反射读注解的硬性前提。

元注解决定了什么?
  • @Target 决定注解只能贴在哪类元素上(类、字段、方法…)
  • @Retention 决定注解保留到哪个阶段,必须是 RUNTIME 才能被反射读到
  • 改成 CLASS(默认值)的话,clazz.getAnnotation(Model.class) 永远返回 null
  • 完整说明

注解树全貌

@Model / @Model.Advanced@TargetTYPE@Field 及所有子注解的 @TargetFIELD。默认值与字段含义详见 API 参考 · 注解 API

注解组合的推导结果

把上述注解树叠加到一个 POJO 上后,框架会按以下规则解析每个字段的列名与类型。完整可复制代码示例请见 使用指南 · 注解使用示例,这里只列出推导路径对照表

字段写法推导出的列名推导出的类型
@Field.String(length = 64) + @Field.Advanced(column = "user_name") String nameuser_name(显式声明)VARCHAR(64)
@Field.Integer Integer ageage(驼峰下划线)INT
@Field.BigDecimal(precision = 12, scale = 2) BigDecimal balancebalanceDECIMAL(12,2)
@Field.Date(type = DateType.TIMESTAMP) LocalDateTime registerTimeregister_timeTIMESTAMP
@Field + @Field.Advanced(store = false) String runtimeToken不落库

枚举:模型与字段类型

  • ModelTypeEnum 描述模型在 DDL 体系中的身份。当前阶段只承诺 STORE 模型的扫描结果可用,其余取值(TRANSIENT / ABSTRACT / PROXY)预留语义到后续阶段使用。完整取值表 → 注解使用示例 · 附录 · ModelTypeEnum
  • FieldType 描述字段业务类型,由 FieldTypeHelper 在扫描期间从「子注解 → Java 类型」双层推导得出,详见 扫描机制 · 类型推导
  • DateType@Field.Date(type = ...) 上使用,取值 DATE / DATETIME / TIMESTAMP / TIME,完整语义 → 注解使用示例 · 附录 · DateType

嵌套注解的弊端与对策

嵌套注解读起来很美,但有一个使用层面的尖角:@Field.Stringjava.lang.String 同名。如果在业务代码里 import cn.cvking.forge.ddl.annotation.Field.String;,会跟 java.lang.String 冲突。

对策:

  • 注解定义内部用 java.lang.String 全限定名(见 Field.java:18Field.java:23
  • 使用方不要 import 嵌套类型,直接 @Field.String(length = 64) 用复合名引用,避免冲突