简体   繁体   English

如何正确设置 Map 为 javaType in<arg> 在 MyBatis 中?</arg>

[英]How to properly set Map as javaType in <arg> in MyBatis?

Problem description:问题描述:

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 但不要明白我做错了什么..

Unsuccessful case:失败案例:

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 时,它就像一种魅力:

Successful case:成功案例:

@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!提前致谢!

Update 1:更新1:

@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM