我将播放列表信息存储在 SQLite 数据库中。这是我的实体类;
@Data
@Builder
public class Playlist {
private String id;
private String playlistName;
private PlaylistType type;
}
PlaylistType
是 ("TYPE_A", "TYPE_B") 的 enum
首先,在我的映射器 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
的映射器 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>
有没有更好的方法来映射数据库列?对于像实体类中属性的顺序这样的愚蠢问题,它不应该有太多限制。还是我错过了什么?
我不敢相信我以稍微不同的方式犯了同样的错误。为了我和其他人的利益,让我分享一些见解。
这个问题看起来像是从mybatis中出现的,但事实并非如此。 Lombok 注释是导致问题的原因。本例中实际的映射操作可以追溯到
private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
for (int i = 0; i < constructor.getParameterTypes().length; i++) {
Class<?> parameterType = constructor.getParameterTypes()[i];
String columnName = rsw.getColumnNames().get(i);
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
return foundValues;
}
来自 org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java#L783
可以看出,构造函数参数是一一获取的,因此实体类中属性的顺序很重要。这是因为@Builder
注释将创建一个全参数构造函数。因此,mybatis 无法使用
reflection
来映射参数,因为它没有默认构造函数(此检查在 DefaultResultSetHandler.java的第 686 行完成)。相反,它会迭代构造函数参数并设置值。这就是为什么顺序很重要。 解决方案很简单,只需在
@NoArgsConstructor
类之上添加
@AllArgsConstructor
和 Playlist
即可。