#java #mybatis #lombok
#java #mybatis #ломбок
Вопрос:
Описание проблемы:
У меня есть java.lang.NoSuchMethodException: bar.Foo.<init>(java.lang.Long, java.lang.String, java.util.Map)
, когда я пытаюсь получить неизменяемые объекты Foo из базы данных с помощью MyBatis и не могу понять, что не так: (
У меня есть неудачный случай, когда я аннотирую объект результата с помощью @Value, а успешный случай аннотируется с помощью @Data (они приведены ниже в соответствующих заголовках). Итак, я хотел бы использовать @Value, но не понимаю, что я делаю неправильно..
Неудачный случай:
Итак, у меня есть результирующий класс, который является неизменяемым:
@Value
public class Foo {
long entryId;
String description;
Map<String, String> params;
}
Он хранится в postgres в таблице foos, params
значение поля сохраняется в формате JSON в соответствующем столбце той же таблицы (тип — VARCHAR, записи "{key=value}"
в случае params = Map.of("key", "value")
.
Я хотел бы создать экземпляр моего объекта этого класса из базы данных, поэтому я использую следующий код 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 вместо @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 )
Комментарии:
1. Я буду снимать здесь в темноте, так как я думаю, что Java autoboxing должен разобраться в этом, но измените long на Long и используйте значение так же, как вы это сделали
2. Привет, Ник. Отметка ответа пометит вопрос как «решенный». Нет необходимости редактировать его в вашем названии или вопросе. Я вернулся к предыдущей версии вашего вопроса, чтобы удалить упоминания о «решаемой».
3. Привет, ТТ, спасибо, хорошо 🙂
Ответ №1:
java.lang.Исключение NoSuchMethodException: bar.Foo.(java.lang.Длинный, java.lang.Строка, java.util.Map) <— Означает, что конструктор не определен.
Когда вы используете @AllArgsConstructor, вы говорите lombok создать конструктор со всеми аргументами (Long, String, Map), вот почему это работает для вас.
Согласно documentation @Data:
Теперь @Data Все вместе: ярлык для @toString, @EqualsAndHashCode, @Getter для всех полей, @Setter для всех полей, не являющихся конечными, и @RequiredArgsConstructor
https://projectlombok.org/features/Data
Вот почему в этот момент он находит конструктор и не выдает ошибку (noSuchMethod ) при использовании @Data .
<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.Длинный, он ожидает, что конструктор будет определен с этим типом параметра.
Я считаю, что с @Data вам помог не конструктор, а установщики (боксы) long To Long .
Попробуйте использовать @Value и измените long на Long в своем классе. Посмотрите, работает ли это.
Lombok сгенерирует конструктор с (long, String, Map), а не (Long, String, Map)
Любопытно узнать, какую версию Java вы используете.
Комментарии:
1. Спасибо! После того, как я изменил поля класса на Long, это наконец-то работает 🙂
2. Из любопытства, какую версию java вы используете 🙂 @Ник
3. Java 11, jdk 11.0.9