使用DTO语言
Jimmer提供了DTO语言。
开发人员可以使用此语言快速定义要保存的Input DTO的形状,编译后
- 
相关的Java/Kotlin Input DTO类将会被自动生成
 - 
Input DTO和实体之间的彼此转化逻辑会被自动生 成
 - 
与之形状吻合的对象抓取器会被自动生成。(这个功能和input DTO无关,所以本文不做论述)
 
开发人员用自动生成的Input DTO作为API的入参,一行代码保存它即可 (内部逻辑:调用自动生成的转化逻辑把Input DTO转化为动态实体对象,再用保存指令直接保存)。
这是消除Input DTO爆炸带来的痛苦的最高效方案
定义DTO的形状
本文侧重于讲解如何保存静态DTO类型,并非系统性介绍DTO语言,请参考对象篇/DTO转换/DTO语言以了解完整的DTO语言。
假如Book类的全名为com.yourcompany.yourproject.model.Book,你可以
- 
在实体定义所在项目中,建立目录
src/main/dto - 
在
src/main/dto下,按实体类型所处的包路径建立子目录com/yourcompany/yourproject/model - 
在上一步建立的目录下,建立文件
Book.dto,文件必须和实体类同名,扩展名必须为dto - 
编辑此文件,利用DTO语言,定义Book实体的各种DTO形状
Book.dtoinput BookInput {
#allScalars(Book)
id(store)
authors {
#allScalars(Author)
-id
}
}
input SimpleBookInput { ...略... }
...省略其他Input DTO形状定义...信息用作输入参数的Input DTO的形状定义必须用
input修饰符修饰。具体原因已经在对象篇/DTO转换/DTO语言中有详细的阐述,本文不再赘述
 
自动生成DTO类型
Jimmer负责编译dto文件,自动生成符合这些形状的DTO类型。
如果除了dto文件外还有其他Java/Kotlin原代码文件被修改了,直接点击IDE中运行按钮可以导致dto文件的重新编译
但是,如果除了dto文件外没有其他Java/Kotlin文件被修改,简单地点击IDE中运行按钮并不会导致dto文件被重新编译,除非显式地rebuild!
如果你使用的构建工具是Gradle,也可以使用社区提供的第三方Gradle插件来解决这个问题: jimmer-gradle
以上面代码中的BookInput为例,此dto文件被Jimmer成功编译后,会自动生成如下DTO类型
- Java
 - Kotlin
 
package com.yourcompany.yourproject.model.dto;
import com.yourcompany.yourproject.model.Book;
import org.babyfish.jimmer.Input;
@GeneratedBy(file = "<your_project>/src/main/dto/Book.dto")
public class BookInput implements Input<Book> {
    @Nullable
    private Long id; ❶
    private String name;
    private int edition;
    private BigDecimal price;
    @Nullable
    private Long storeId; ❷
    private List<TargetOf_authors> authors; ❸
    public BookInput(Book book) { ❹
        ...略...
    }
    @Override
    public Book toEntity() { ❺
        ...略...
    }
    @Data
    public static class TargetOf_authors {
        private String firstName;
        
        private String lastName;
        
        private Gender gender;
        ...省略其他成员...
    }
    ...省略其他成员...
}
package com.yourcompany.yourproject.model.dto
import com.yourcompany.yourproject.model.Book
import org.babyfish.jimmer.Input
@GeneratedBy(file = "<your_project>/src/main/dto/Book.dto")
data class BookInput(
    val id: Long? = null, ❶
    val name: String = "", 
    val edition: Int = 0,
    val price: BigDecimal, 
    val storeId: Long? = null, ❷
    val authors: List<TargetOf_authors> = emptyList() ❸
): Input<Book> {
    constructor(book: Book) : this(...略...) ❹
    override fun toEntity(): Book = ...略... ❺
    data class TargetOf_authors(
        val firstName: String,
        val lastName: String,
        val gender: Gender
    ) {
        ...省略其他成员...
    }
    ...省略其他成员...
}
- 
❶ 如果id被指定了自动生成策略,则id不是必须的。这也是保存指令的一个特性,具体细节请参考保存模式
信息- 
对于Jimmer实体而言,
id不可能为null,靠id属性的缺失 (即,不赋值) 来表达对象没有id的情况。 - 
对于Input DTO而言,静态的POJO类型没有属性缺失的概念,靠null来表达没有id的情况。
 
二者看似矛盾,但其实可以简单地处理:如果Input DTO的id为null,转化后的实体对象无id (虽然id不允许被赋为null,但动态对象可以不对属性赋值)
 - 
 - 
❷ 明确指定此InputDTO想以
的方式编辑实体的多对一关联Book.store。其中, - 
❸ 明确指定此InputDTO想以
的方式编辑实体的多对过关联Book.authors, 关联对象的类型也被内嵌的InputDTO类型BookInput.TargetOf_authors固化。 - 
❹ 将动态实体转化为静态InputDTO
 - 
❺ 将静态InputDTO转化为动态实体
 
HTTP API
DTO语言生成了相对完备的代码,所以,我们可以快速对外暴露保存数据的HTTP API
- Java
 - Kotlin
 
@PutMapping("/book")
public void saveBook(
    @RequestBody BookInput input
) {
    // `save(input)`等价于`save(input.toEntity())`
    bookRepository.save(input);
}
@PutMapping("/book")
fun saveBook(
    @RequestBody input: BookInput
) {
    // `save(input)`等价于`save(input.toEntity())`
    bookRepository.save(input)
}