我在一个SQLite数据库中存储播放列表信息。这是我的实体类;
@Data
@Builder
public class Playlist {
private String id;
private String playlistName;
private PlaylistType type;
}
字符串PlaylistType
是(“TYPE_A”,“TYPE_B”)的enum
首先,在我的Map器XML中,我尝试使用resultType="Playlist"
,当数据库列名和Playlist
属性的顺序相同时,它可以工作。但是如果我改变属性的顺序,例如如下所示,
public class Playlist {
private String id;
private PlaylistType type; // just moved PlaylistType property
private String playlistName;
}
型
它给了我这个错误:
Caused by: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'name' from result set. Cause: java.lang.IllegalArgumentException: No enum constant com.example.demo.PlaylistType.p1
型
然后一些线程建议使用resultMap
,但问题仍然存在;
这是我使用resultMap
的Map器XML
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.PlaylistRepository">
<resultMap id="toPlaylist" type="com.example.demo.Playlist">
<id column="id" property="id"/>
<result column="name" property="playlistName"/>
<result column="type" property="type"/>
</resultMap>
<select id="findById" resultMap="toPlaylist">
select * from playlist where id = #{id}
</select>
</mapper>
型
有没有更好的方法来Map数据库列?它不应该在愚蠢的事情上有太多的限制,比如实体类中属性的顺序。或者我错过了什么?
2条答案
按热度按时间iezvtpos1#
添加
@NoArgsConstructor
是最简单的解决方案。这个答案是给那些不想给不可变类添加无参数构造函数的人准备的。
当您将
@Builder
添加到类中时,Lombok生成以下构造函数。字符串
虽然它是包私有的,但MyBatis可以(必须;因为没有其他构造函数)使用反射[1]使用这个构造函数。
为了将结果Map到这个构造函数,MyBatis提供了四个不同的方法。
1.基于顺序的构造函数自动Map
1.基于参数名的构造函数自动Map
1.具有
<constructor>
但不具有name
属性的结果Map1.带有
name
属性的<constructor>
结果Maptl;dr
对于大多数简单的情况,方法2应该足够了。
使用方法3或4进行高级Map或在需要最佳性能时使用。
1.基于顺序的构造函数自动Map
这是不使用
<resultMap>
时的默认行为。在您的示例中,构造函数接受三个参数,因此结果集中的第一、第二和第三列分别Map到
id
、playlistName
和type
。当你改变类中的字段顺序时,构造函数参数的顺序也会改变,你也必须改变列的顺序。
就我个人而言,我不推荐这种基于顺序的构造函数自动Map,因为有一个已知的问题可能会让人挠头。
如果你有兴趣的话,我在answer中解释了它。
2.基于参数名的构造函数自动Map
这是当您1)在配置中启用
argNameBasedConstructorAutoMapping
和2)不使用<resultMap>
时的行为。使用此方法,MyBatis会查找与构造函数参数[2]同名的列。列的顺序无关紧要。
请注意,在您的示例中,列名
name
与目标参数名playlistName
不匹配,因此您可能必须在SELECT语句中指定列别名。argNameBasedConstructorAutoMapping
是在3.5.10版本中添加的。3.带
<constructor>
不带name
属性的结果图使用结果Map时,需要
<constructor>
、<idArg>
和<arg>
元素来执行构造函数Map。型
为了完整起见,这里是在JavaMap器中声明的相同结果Map[3]。
型
使用此方法时,列顺序无关紧要,但XML元素顺序必须与构造函数参数顺序匹配。
如果您很难维护XML元素的顺序(例如,目标类经常更新),请参阅下一节。
4.
name
属性为<constructor>
的结果图当指定
name
属性时,XML元素的顺序不必与实际构造函数参数的顺序相匹配[4]。型
在您的例子中,构造函数参数类型总是匹配字段类型,因此
javaType
可以省略。型
这是使用注解的相同结果图。
型
使用上面的结果Map,MyBatis搜索具有以下三个参数的构造函数,但顺序任意。
id
,类型=java.lang.String
playlistName
,类型=java.lang.String
type
,类型=pkg.PlaylistType
当有多个构造函数符合条件时,您需要将
@AutomapConstructor
添加到正确的构造函数。找到构造函数后,指定列的值将Map到每个构造函数参数。
因此,使用此方法,XML元素顺序和列顺序都无关紧要,但如果更改字段名,则可能需要编辑
name
值。此方法需要3.4.3或更高版本。
[1]如果您使用Java平台模块系统(JPMS),您可能必须允许MyBatis访问此构造函数。
[2]要在二进制文件中包含参数名称,您必须1)指定
-parameters
编译器选项或2)为每个参数添加@Param
注解。[3]如果您使用的版本早于3.5.4,则可能需要
@ConstructorArgs
。[4]
<idArg>
必须在<arg>
之前写入,因为它是由DTD强制执行的。lhcgjxsq2#
我不敢相信我用一种稍微不同的方式制作了same mistake。为了我和其他人的利益,让我分享一些见解。
这个问题看起来像是从mybatis中出现的,但实际上不是。Lombok注解是导致问题的原因。在这种情况下,实际的Map操作可以追溯到
字符串
可以看出,构造函数参数是一个接一个的,因此实体类中属性的顺序很重要。这是因为
@Builder
注解将创建一个全参数构造函数。因此mybatis不能使用reflection
来Map参数,因为它没有默认的构造函数(此检查在DefaultResultSetHandler.java的第686行完成)。相反,它会遍历构造函数参数并设置值。这就是为什么顺序很重要。解决方案很简单,只需在
Playlist
类的顶部添加@NoArgsConstructor
和@AllArgsConstructor
。