[英]How to properly set Map as javaType in <arg> in MyBatis?
I have java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
when I try to get immutable Foo objects from DB with MyBatis and can't figure out what's wrong:( I have
java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
when I try to get immutable Foo objects from DB with MyBatis and can't找出问题所在:(
I have an unsuccessful case by annotating result object with @Value and successful case annotating with @Data (they are below under corresponding headers).我有一个不成功的案例,用@Value 注释结果 object 和用@Data 注释的成功案例(它们在相应的标题下)。 So, I'd like to use @Value but don't get what I'm doing wrong..
所以,我想使用 @Value 但不要明白我做错了什么..
So, I have the result class which is immutable:所以,我得到了不可变的结果 class :
@Value
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
It is stored in postgres in a table foos, params
field value is stored in JSON format in a corresponding column of the same table (type is VARCHAR, entries are "{key=value}"
in case of params = Map.of("key", "value")
. I'd like to instantiate my object of this class from DB and so I use the following MyBatis code:它存储在postgres中的一个表foos中,
params
字段值以JSON格式存储在同一个表的对应列中(类型为VARCHAR,条目为"{key=value}"
在params = Map.of("key", "value")
的情况下params = Map.of("key", "value")
。我想从 DB 实例化这个 class 的 object,所以我使用以下 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>
Here is my type handler (it's already been successfully used in another case with @Data object instead of @Value, so I guess it's correct by itself):这是我的类型处理程序(它已经在另一种情况下成功使用@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);
}
} }
Here is SQL:这是 SQL:
<select id="getFoos" resultMap="foo">
SELECT *
FROM foos
<![CDATA[WHERE needed_tm < now()]]>
</select>
And when I get my Foos I have java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
.当我得到我的 Foos 时,我有
java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
Funny thing is that it works like a charm when I use @Data instead of @Value:有趣的是,当我使用@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 and type handler are the same that in unsuccessful case. SQL 和类型处理程序与不成功的情况相同。
May you be so kind to give me a hint what I am possibly missing?能否请您给我一个提示,我可能缺少什么? Thanks in advance!
提前致谢!
@Value
@AllArgsConstructor
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
gives the same exception.给出同样的例外。 Anyway as I understand, @Value already has @AllArgsConstructor by design ( https://projectlombok.org/features/Value )
无论如何,据我所知,@Value 已经有 @AllArgsConstructor 设计( https://projectlombok.org/features/Value )
java.lang.NoSuchMethodException: bar.Foo.(java.lang.Long, java.lang.String, java.util.Map) <--- Means there is no constructor defined. java.lang.NoSuchMethodException: bar.Foo.(java.lang.Long, java.lang.String, java. 没有定义构造函数。
When you use @AllArgsConstructor, you are telling lombok to generate a constructor with all the arguments, (Long, String, Map), that is why that works for you.当您使用@AllArgsConstructor 时,您是在告诉 lombok 使用所有 arguments(Long、String、Map)生成一个构造函数,这就是为什么它适合您。
According to documentation @Data:根据文档@Data:
@Data All together now: A shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, @Setter on all non-final fields, and @RequiredArgsConstructor @Data 现在全部在一起:@ToString、@EqualsAndHashCode、所有字段上的 @Getter、所有非最终字段上的 @Setter 和 @RequiredArgsConstructor 的快捷方式
https://projectlombok.org/features/Data https://projectlombok.org/features/Data
That is why at that point it finds a constructor and does not give you an error (NoSuchMethod) when you use @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>
Since you are specifying the datatype to be java.lang.Long, it expects the constructor to be defined to have that parameter type.由于您将数据类型指定为 java.lang.Long,因此它希望将构造函数定义为具有该参数类型。
I believe with @Data, it is not the constructor that helped you, it was the Setters (boxing) long To Long.我相信@Data,它不是帮助你的构造函数,而是 Setters (boxing) long To Long。
Try to use @Value, and change long to Long in your class.尝试使用@Value,并在 class 中将 long 更改为 Long。 See if that works.
看看这是否有效。
Lombok will generate a constructor with (long, String, Map), not (Long, String, Map) Lombok 将生成一个带有 (long, String, Map) 的构造函数,而不是 (Long, String, Map)
Curious to know what java version you are using.很想知道您使用的是什么 java 版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.