Draft
之前的文档我们发现
-
用户定义了
Book类型,例子代码就可以使用BookDraft类型 -
用户定义了
TreeNode类型,例子代码就可以使用TreeNodeDraft类型
这些名称以Draft结尾且和用户定义的类型一一对应的类型,叫Draft类型
使用预编译器
定义TreeNode
用户定先义不可变数据接口,这里无需使用ORM实体注解@Entity,使用非ORM的注解@Immutable即可达到演示目的。
- Java
- Kotlin
package yourpackage;
import javax.validation.constraints.Null;
import java.util.List;
import org.babyfish.jimmer.Immutable;
@Immutable
public interface TreeNode {
String name();
@Null
TreeNode parent();
List<TreeNode> childNodes();
}
package yourpackage
import org.babyfish.jimmer.Immutable
@Immutable
interface TreeNode {
val name: String
val parent: TreeNode?
val childNodes: List<TreeNode>
}
生成TreeNodeDraft
要自动生成TreeNodeDraft需要启用预编译器
-
Java: 使用AnnotationProcessor
jimmer-apt -
Kotlin: 使用KSP
jimmer-ksp
如何使用jimmer-apt/jimmer-ksp、以及遇到可能的问题后该如何处理,
在生成代码中有非常详细的介绍,本文不再赘述。
- Java
- Kotlin
package org.babyfish.jimmer.example.core.model;
import java.util.List;
import org.babyfish.jimmer.DraftConsumer;
import org.babyfish.jimmer.lang.OldChain;
public interface TreeNodeDraft extends TreeNode, Draft {
TreeNodeDraft.Producer $ = Producer.INSTANCE;
@OldChain
TreeNodeDraft setName(String name);
TreeNodeDraft parent();
TreeNodeDraft parent(boolean autoCreate);
@OldChain
TreeNodeDraft setParent(TreeNode parent);
@OldChain
TreeNodeDraft applyParent(DraftConsumer<TreeNodeDraft> block);
@OldChain
TreeNodeDraft applyParent(TreeNode base, DraftConsumer<TreeNodeDraft> block);
List<TreeNodeDraft> childNodes(boolean autoCreate);
@OldChain
TreeNodeDraft setChildNodes(List<TreeNode> childNodes);
@OldChain
TreeNodeDraft addIntoChildNodes(DraftConsumer<TreeNodeDraft> block);
@OldChain
TreeNodeDraft addIntoChildNodes(TreeNode base, DraftConsumer<TreeNodeDraft> block);
class Producer {
private Producer() {}
public TreeNode produce(
DraftConsumer<TreeNodeDraft> block
) {
return produce(null, block);
}
public TreeNode produce(
TreeNode base,
DraftConsumer<TreeNodeDraft> block
) {
...omit...
}
...省略其他代码...
}
...省略其他代码...
}
@DslScope
public interface TreeNodeDraft : TreeNode {
public override var name: String
public override var parent: TreeNode?
public override var childNodes: List<TreeNode>
public fun parent(): TreeNodeDraft
public fun childNodes(): MutableList<TreeNodeDraft>
public object `$` {
...省略其他代码...
public fun produce(
base: TreeNode? = null,
block: TreeNodeDraft.() -> Unit
): TreeNode {
...omit code...
}
}
...省略其他代码...
}
public fun ImmutableCreator<TreeNode>.`by`(
base: TreeNode? = null,
block: TreeNodeDraft.() -> Unit
): TreeNode =
TreeNodeDraft.`$`.produce(base, block)
public fun MutableList<TreeNodeDraft>.addBy(
base: TreeNode? = null,
block: TreeNodeDraft.() -> Unit
): MutableList<TreeNodeDraft> {
add(TreeNodeDraft.`$`.produce(base, block) as TreeNodeDraft)
return this
}
public fun TreeNode.copy(block: TreeNodeDraft.() -> Unit): TreeNode =
TreeNodeDraft.`$`.produce(this, block)
你可以这样使用
-
从头创建全新的对象
- Java
- Kotlin
TreeNode oldTreeNode = Immutables.createTreeNode(treeNodeDraft -> {
...省略...
});vak oldTreeNode = TreeNode {
...省略...
} -
根据已有的对象,进行某些“变更”后,创建新的对象
- Java
- Kotlin
TreeNode newTreeNode = Immutables.createTreeNode(oldTreeNode, treeNodeDraft -> {
...省略...
});val newTreeNode = TreeNode(oldTreeNode) {
...省略...
}或
val newTreeNode = oldTreeNode.copy {
...省略...
}
标量属性
TreeNode.name是一个标量属性。TreeNodeDraft会定义如下一个setter方法/可写属性
- Java
- Kotlin
public interface TreeNodeDraft extends TreeNode, Draft {
@OldChain
TreeNodeDraft setName(String name);
...省略其他代码...
}
@DslScope
public interface TreeNodeDraft : TreeNode {
// var, not val
public override var name: String
...省略其他代码...
}
开发人员可以通过此方法设置draft代理的name属性
- Java
- Kotlin
TreeNode treeNode = Immutables.createTreeNode(draft -> {
draft.setName("Root Node");
});
val treeNode = TreeNode {
name = "Root Node"
}
引用关联
TreeNode.parent是一个关联属性。其类型是对象,而非集合。如果用ORM的话来讲,是一对一或多对一关联。
TreeNodeDraft为其定义了多个方法
覆盖getter parent()
- Java
- Kotlin
public interface TreeNodeDraft extends TreeNode, Draft {
TreeNodeDraft parent();
...省略其他代码...
}
@DslScope
public interface TreeNodeDraft : TreeNode {
public fun parent(): TreeNodeDraft
...省略其他代码...
}
注意,此方法的返回类型是TreeNodeDraft,而非TreeNode。
即,如果draft对象的引用关联被设置过且被设置为非null,那么该方法一定返回draft对象。这样,用户就可以直接修改更深的关联对象。
- Java
- Kotlin
TreeNode newTreeNode = Immutables.createTreeNode(treeNode, draft -> {
draft.parent().setName("Daddy");
draft.parent().parent().setName("Grandpa");
});
@DslScope
var newTreeNode = TreeNode(treeNode) {
parent().name = "Daddy"
parent().parent().name = "Grandpa"
}
新增getter parent(boolean)
- Java
- Kotlin
public interface TreeNodeDraft extends TreeNode, Draft {
TreeNode parent(boolean autoCreate);
...省略其他代码...
}
@DslScope
public interface TreeNodeDraft : TreeNode {
// 这个属性的getter等价于Java的`parent(false)`
override fun parent: TreeNode
// 这个函数等价于Java的`parent(true)`
public fun parent(): TreeNodeDraft
...省略其他代码...
}
Java的parent(false)和Kotlin的parent,具备以下两个问题:
-
如果draft对象的属性
parent未被设置,访问它会导致异常 -
如果draft对象的属性
parent被设置为null,访问它会返回null,null无法支持进一步修改。
parent(true)可以解决以上的问题,如果上述任何一种情况满足,就自动创建并设置一个关联对象,然后允许用户修改。这是一个非常实用的功能,尤其是从头创建对象时。
- Java
- Kotlin
TreeNode treeNode = Immutables.createTreeNode(/* No `base` here */ draft -> {
draft.parent(true).setName("Daddy");
draft.parent(true).parent(true).setName("Grandpa");
});
val treeNode = TreeNode /* No `base` here */ {
parent().setName("Daddy");
parent().parent().setName("Grandpa");
}
新增setParent
- Java
- Kotlin
public interface TreeNodeDraft extends TreeNode, Draft {
@OldChain
TreeNodeDraft setParent(TreeNode parent);
...省略其他代码...
}
@DslScope
public interface TreeNodeDraft : TreeNode, Draft {
// var, not val
public var parent: TreeNode
...省略其他代码...
}
该setter允许用户替换整个关联对象。
- Java
- Kotlin
TreeNode treeNode = Immutables.createTreeNode(draft -> {
draft.setParent(
Immutables.createTreeNode(daddyDraft -> {
daddyDraft.setName("Daddy")
})
)
});
val treeNode = TreeNode {
parent = TreeNode {
name = "Daddy"
}
}