MyBatis 如何正确设置 Map 为 javaType in <arg>?
How to properly set Map as javaType in <arg> in MyBatis?
问题描述:
当我尝试使用 MyBatis 从数据库中获取 immutable Foo objects 时,我有 java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
并且无法弄清楚出了什么问题:(
我有一个用@Value注解结果object的不成功案例和用@Data注解的成功案例(它们在下面相应的headers下)。所以,我想使用 @Value 但不要理解我做错了什么..
不成功案例:
所以,我得到的结果是 class,即 immutable:
@Value
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
它存储在postgres中的一个tablefoos中,params
字段值以JSON格式存储在相同table的相应列中(类型为VARCHAR,在 params = Map.of("key", "value")
的情况下条目为 "{key=value}"
。
我想从 DB 实例化我的 object 这个 class ,所以我使用以下 MyBatis 代码:
<resultMap id="foo" type="bar.Foo">
<constructor>
<arg column="entry_id" javaType="java.lang.Long"/>
<arg column="description" javaType="java.lang.String"/>
<arg column="params" javaType="java.util.Map" jdbcType="VARCHAR" typeHandler="bar.MapToJsonStringHandler"/>
</constructor>
</resultMap>
这是我的类型处理程序(它已经在另一个案例中成功使用了 @Data object 而不是 @Value,所以我想它本身是正确的):
public class MapToJsonStringHandler implements TypeHandler<Map<String, String>> {
private final ObjectMapper objectMapper = new ObjectMapper();
@SneakyThrows(IOException.class)
@Override
public void setParameter(PreparedStatement ps, int i, Map<String, String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.VARCHAR);
} else {
ps.setString(i, objectMapper.writeValueAsString(parameter));
}
}
@Override
public Map<String, String> getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
return convertToMap(result);
}
@Override
public Map<String, String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
return convertToMap(result);
}
@Override
public Map<String, String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String result = cs.getString(columnIndex);
return convertToMap(result);
}
@SneakyThrows(IOException.class)
private Map<String, String> convertToMap(String result) {
return result == null ? Collections.emptyMap() : objectMapper.readValue(result, Map.class);
}
}
这里是SQL:
<select id="getFoos" resultMap="foo">
SELECT *
FROM foos
<![CDATA[WHERE needed_tm < now()]]>
</select>
当我得到我的 Foos 时,我有 java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
。
有趣的是,当我使用@Data 而不是@Value 时,它就像一个魅力:
成功案例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
<resultMap id="foo" type="bar.Foo">
<result column="entry_id" property="entryId"/>
<result column="description" property="description"/>
<result column="params" property="params" typeHandler="bar.MapToJsonStringHandler"/>
</resultMap>
SQL 和类型处理程序与不成功的情况相同。
能否请您指点一下我可能遗漏了什么?提前致谢!
更新 1:
@Value
@AllArgsConstructor
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
给出了同样的例外。无论如何,据我了解,@Value 已经设计为 @AllArgsConstructor (https://projectlombok.org/features/Value)
java.lang.NoSuchMethodException: bar.Foo.(java.lang.Long, java.lang.String, java.util.Map) <--- 表示没有定义构造函数。
当你使用
@AllArgsConstructor,您是在告诉 lombok 生成一个包含所有参数(Long、String、Map)的构造函数,这就是为什么它对您有用。
根据文档@Data:
@数据
现在全部在一起:@ToString、@EqualsAndHashCode、所有字段上的@Getter、所有非最终字段上的@Setter 和@RequiredArgsConstructor
的快捷方式
https://projectlombok.org/features/Data
这就是为什么此时它会找到构造函数并且在您使用 @Data 时不会给您错误 (NoSuchMethod)。
<resultMap id="foo" type="bar.Foo">
<constructor>
<arg column="entry_id" javaType="java.lang.Long"/>
<arg column="description" javaType="java.lang.String"/>
<arg column="params" javaType="java.util.Map" jdbcType="VARCHAR" typeHandler="bar.MapToJsonStringHandler"/>
</constructor>
</resultMap>
由于您将数据类型指定为 java.lang.Long,它希望构造函数被定义为具有该参数类型。
我相信@Data,帮助你的不是构造函数,而是 Setters (boxing) long To Long。
尝试使用@Value,将你的class中的long改为Long。看看是否可行。
Lombok 将生成一个带有 (long, String, Map) 的构造函数,而不是 (Long, String, Map)
想知道您使用的 java 版本。
问题描述:
当我尝试使用 MyBatis 从数据库中获取 immutable Foo objects 时,我有 java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
并且无法弄清楚出了什么问题:(
我有一个用@Value注解结果object的不成功案例和用@Data注解的成功案例(它们在下面相应的headers下)。所以,我想使用 @Value 但不要理解我做错了什么..
不成功案例:
所以,我得到的结果是 class,即 immutable:
@Value
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
它存储在postgres中的一个tablefoos中,params
字段值以JSON格式存储在相同table的相应列中(类型为VARCHAR,在 params = Map.of("key", "value")
的情况下条目为 "{key=value}"
。
我想从 DB 实例化我的 object 这个 class ,所以我使用以下 MyBatis 代码:
<resultMap id="foo" type="bar.Foo">
<constructor>
<arg column="entry_id" javaType="java.lang.Long"/>
<arg column="description" javaType="java.lang.String"/>
<arg column="params" javaType="java.util.Map" jdbcType="VARCHAR" typeHandler="bar.MapToJsonStringHandler"/>
</constructor>
</resultMap>
这是我的类型处理程序(它已经在另一个案例中成功使用了 @Data object 而不是 @Value,所以我想它本身是正确的):
public class MapToJsonStringHandler implements TypeHandler<Map<String, String>> {
private final ObjectMapper objectMapper = new ObjectMapper();
@SneakyThrows(IOException.class)
@Override
public void setParameter(PreparedStatement ps, int i, Map<String, String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.VARCHAR);
} else {
ps.setString(i, objectMapper.writeValueAsString(parameter));
}
}
@Override
public Map<String, String> getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
return convertToMap(result);
}
@Override
public Map<String, String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
return convertToMap(result);
}
@Override
public Map<String, String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String result = cs.getString(columnIndex);
return convertToMap(result);
}
@SneakyThrows(IOException.class)
private Map<String, String> convertToMap(String result) {
return result == null ? Collections.emptyMap() : objectMapper.readValue(result, Map.class);
}
}
这里是SQL:
<select id="getFoos" resultMap="foo">
SELECT *
FROM foos
<![CDATA[WHERE needed_tm < now()]]>
</select>
当我得到我的 Foos 时,我有 java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
。
有趣的是,当我使用@Data 而不是@Value 时,它就像一个魅力:
成功案例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
<resultMap id="foo" type="bar.Foo">
<result column="entry_id" property="entryId"/>
<result column="description" property="description"/>
<result column="params" property="params" typeHandler="bar.MapToJsonStringHandler"/>
</resultMap>
SQL 和类型处理程序与不成功的情况相同。
能否请您指点一下我可能遗漏了什么?提前致谢!
更新 1:
@Value
@AllArgsConstructor
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
给出了同样的例外。无论如何,据我了解,@Value 已经设计为 @AllArgsConstructor (https://projectlombok.org/features/Value)
java.lang.NoSuchMethodException: bar.Foo.(java.lang.Long, java.lang.String, java.util.Map) <--- 表示没有定义构造函数。
当你使用 @AllArgsConstructor,您是在告诉 lombok 生成一个包含所有参数(Long、String、Map)的构造函数,这就是为什么它对您有用。
根据文档@Data:
@数据 现在全部在一起:@ToString、@EqualsAndHashCode、所有字段上的@Getter、所有非最终字段上的@Setter 和@RequiredArgsConstructor
的快捷方式https://projectlombok.org/features/Data
这就是为什么此时它会找到构造函数并且在您使用 @Data 时不会给您错误 (NoSuchMethod)。
<resultMap id="foo" type="bar.Foo">
<constructor>
<arg column="entry_id" javaType="java.lang.Long"/>
<arg column="description" javaType="java.lang.String"/>
<arg column="params" javaType="java.util.Map" jdbcType="VARCHAR" typeHandler="bar.MapToJsonStringHandler"/>
</constructor>
</resultMap>
由于您将数据类型指定为 java.lang.Long,它希望构造函数被定义为具有该参数类型。
我相信@Data,帮助你的不是构造函数,而是 Setters (boxing) long To Long。
尝试使用@Value,将你的class中的long改为Long。看看是否可行。
Lombok 将生成一个带有 (long, String, Map) 的构造函数,而不是 (Long, String, Map)
想知道您使用的 java 版本。