保存结果获取
保存指令通常返回修改后的实体。如果调用方需要指定的结果形状,可以把fetcher传给execute:
- Java
- Kotlin
BookFetcher fetcher = BookFetcher.$
.name()
.edition()
.store(BookStoreFetcher.$.name());
Book modifiedBook = sqlClient
.saveCommand(book)
.execute(fetcher)
.getModifiedEntity();
val fetcher = newFetcher(Book::class).by {
name()
edition()
store {
name()
}
}
val modifiedBook = sqlClient
.saveCommand(book)
.execute(fetcher)
.modifiedEntity
批量保存指令也提供相同的重载。也可以用生成的输出DTO类型代替fetcher,此时结果通过
modifiedView提供转换后的DTO。
结果如何被物化
Jimmer通过三个步骤构造结果:
- 复用保存后值已经确定的属性,例如直接由保存对象写入的普通属性。
- 如果数据库方言和当前变更形式支持,通过DML语句的
returning直接读取尚未解析的本表列。 - 只为请求形状中仍未解析的部分生成残余fetcher(residual fetcher),并用后续查询加载它们。
例如,同一个fetcher请求的标量列可以由update语句直接返回,而关联对象在之后查询。 调用方最终仍然得到一个符合所请求fetcher形状的实体。
这是实现层面的优化,而不是一种新的保存模式。禁用save returning、使用不支持的方言, 或者当前变更无法安全返回行,只会改变结果的获取方式,不会改变结果契约。
内置的当前行returning实现由H2Dialect和PostgresDialect提供。其他方言仍使用残余fetch,
除非方言实现本身提供了相应能力。
DML可以返回的内容
Save returning可以物化当前实体的列,包括:
- ID和普通标量列
- 版本列和鉴别器列
- 外键ID列
- 赋值表达式计算出的值
- 标记为
@DatabaseDefault且未加载的属性
对于JOINED继承,每个物理表独立决定是否使用returning。根表返回根属性,分支表返回分支属性。
fetcher中的以下内容通常属于残余部分,由普通查询加载:
- 关联和嵌套fetcher
- 公式属性和瞬态属性
- 不存储在当前物理表中的数据
- 当前数据库方言或DML形式无法返回的本表列
Jimmer不会为了使用returning而把高效的批量变更拆成逐行语句。只有在返回行能够通过ID、 键或方言保证的顺序与保存对象安全对应时才使用批量returning;否则保留批量变更,之后再获取 尚未解析的结果。
读取数据库中的实际值
默认情况下,没有使用自定义表达式写入的已加载属性被认为是已知的:Jimmer可以直接把保存 对象中的值复制到结果中。这通常是最快的行为。
有时数据库会再次修改这样的值,例如通过触发器、规范化规则、生成列或数据库特有的类型转换。
此时可以启用saveResultReadsAllProperties,让结果fetcher请求的属性从数据库读取,而不是由
保存对象补全。
该选项本身不会请求实体的所有属性,结果形状仍由fetcher决定。它只改变已请求本表属性的来源: 可以使用returning时由DML返回,否则使用后续查询。
自定义赋值结果或未加载的@DatabaseDefault属性本来就是未知值,Jimmer已经会自动从数据库
读取它们,无需为此启用该选项。
全局、Spring Boot和单条指令的配置方式请参见 保存结果配置。