向类中添加@NoArgsConstructor
是最简单的解决方案。
本答案针对那些不想在不可变类中添加无参构造函数的人群。
当你在类中添加@Builder
注解时,Lombok会生成如下构造函数:
Playlist(String id, String playlistName, PlaylistType type) {
this.id = id;
this.playlistName = playlistName;
this.type = type;
}
虽然它是包私有的,但MyBatis仍可以通过反射(且必须这样做,因为没有其他构造函数可供选择)使用此构造函数[1]。
为了将结果集映射到这个构造函数,MyBatis提供了四种不同的方法。
- 基于顺序的构造器自动映射
- 基于参数名的构造器自动映射
- 使用
<constructor>
元素的结果映射(未指定name
属性)
- 使用
<constructor>
元素的结果映射(已指定name
属性)
简要说明:
对于大多数简单情况,方法2应该足够有效。
对于高级映射或需要最佳性能的情况,请使用方法3或4。
1. 基于顺序的构造器自动映射
这是你不使用<resultMap>
时的默认行为。
在你的示例中,构造函数接受三个参数,所以结果集中的第一、第二和第三个列分别被映射到id
、playlistName
和type
。当你更改类中字段的顺序时,构造函数参数的顺序也会发生变化,此时你必须相应地更改列顺序。
个人而言,我不推荐这种基于顺序的构造器自动映射,因为它有一个已知问题,可能会让人感到困惑。如果你感兴趣,我在另一个回答中对此进行了详细解释。
2. 基于参数名的构造器自动映射
这种方法要求你在配置中启用argNameBasedConstructorAutoMapping
选项,并且不使用<resultMap>
。使用这种方法时,MyBatis会查找具有与构造函数参数同名的列[2]。列的顺序无关紧要。
注意,在你的示例中,列名name
与目标参数名playlistName
不匹配,因此你可能需要在SELECT语句中指定列别名。
argNameBasedConstructorAutoMapping
特性是在3.5.10版本中新增的。
3. 使用<constructor>
元素的结果映射(未指定name
属性)
使用结果映射时,你需要使用<constructor>
、<idArg>
和<arg>
元素执行构造函数映射。
<resultMap>
<constructor>
<idArg column="id" javaType="string" />
<arg column="name" javaType="string" />
<arg column="type" javaType="pkg.PlaylistType" />
</constructor>
</resultMap>
为了完整性,这里展示了用Java映射器声明相同结果映射的方法[3]。
@Arg(id = true, column = "id", javaType = String.class)
@Arg(column = "name", javaType = String.class)
@Arg(column = "type", javaType = PlaylistType.class)
Playlist findById(String id);
使用这种方法时,列的顺序无关紧要,但XML元素的顺序必须与构造函数参数的实际顺序一致。如果维护XML元素顺序对你来说比较困难(比如目标类频繁更新),请查看下一节。
4. 使用<constructor>
元素的结果映射(已指定name
属性)
当指定了name
属性时,XML元素的顺序无需与实际构造函数参数的顺序匹配[4]。
<resultMap>
<constructor>
<idArg column="id" name="id" javaType="string" />
<arg column="name" name="playlistName" javaType="string" />
<arg column="type" name="type" javaType="pkg.PlaylistType" />
</constructor>
</resultMap>
在你的案例中,构造函数参数类型始终与字段类型相匹配,因此可以省略javaType
。
<resultMap>
<constructor>
<idArg column="id" name="id" />
<arg column="name" name="playlistName" />
<arg column="type" name="type" />
</constructor>
</resultMap>
以及使用注解表示相同结果映射的方式:
@Arg(id = true, column = "id", name = "id")
@Arg(column = "name", name = "playlistName")
@Arg(column = "type", name = "type")
Playlist findById(String id);
采用上述结果映射,MyBatis会搜索具有任意顺序但满足以下条件的构造函数:
- 名称:
id
,类型=java.lang.String
- 名称:
playlistName
,类型=java.lang.String
- 名称:
type
,类型=pkg.PlaylistType
当有多个构造函数满足这些条件时,你需要在正确的构造函数上添加@AutomapConstructor
注解。一旦找到构造函数,指定列的值将被映射到每个构造函数参数。
因此,使用这种方法时,无论是XML元素顺序还是列顺序都不重要,但如果更改字段名称,你可能需要编辑name
值。
这种方法需要MyBatis 3.4.3或更高版本。
[1] 如果你使用Java平台模块系统(JPMS),你可能需要允许MyBatis访问这个构造函数。
[2] 为了在二进制文件中包含参数名,你必须要么指定-parameters
编译器选项,要么向每个参数添加@Param
注解。
[3] 如果你使用的版本早于3.5.4,你可能需要使用@ConstructorArgs
。
[4] <idArg>
必须写在<arg>
前面,这是由DTD强制规定的。