Просто объявите это явно, указав поля по умолчанию:
public record City(Long id, String name, Integer population) {
public City() {
this(0L, "", 0)
}
}
Важное примечание. BeanPropertyRowMapper сканирует сеттеры / геттеры для раздувания вашего экземпляра записи, так как запись неизменна, нет сеттера и она не совместима со спецификацией java bean, вы получите и очистите запись , Пожалуйста, прочитайте это SO . Единственный способ создать запись - использовать конструктор. Итак, у вас есть два варианта: либо использовать обычный java bean-компонент, либо внедрить свой собственный преобразователь строк.
Самый простой способ, которым это может выглядеть, это:
@Override
public City findById(final Long id) {
final var sql = "SELECT * FROM cities WHERE id = ?";
return jtm.queryForObject(
sql,
new Object[]{ id },
(rs, rowNum) -> new City(
rs.getLong("id"),
rs.getString("name"),
rs.getInt("population")));
}
или вы можете используйте отражение :
Reflection API
Следующие публичные c методы будут добавлены к java .lang.Class:
RecordComponent[] getRecordComponents()
boolean isRecord()
Метод getRecordComponents () возвращает массив объектов java .lang.reflect.RecordComponent, где java .lang.reflect.RecordComponent - новый класс. Элементы этого массива соответствуют компонентам записи в том же порядке, в котором они указаны в объявлении записи. Дополнительная информация может быть извлечена из каждого RecordComponent в массиве, включая его имя, тип, тип generi c, аннотации и его метод доступа.
Метод isRecord () возвращает true, если данный класс был объявлен как запись. (Сравните с isEnum ().)
Используя эти методы и Class # getConstructor (Class ... parameterTypes) и Constructor # newInstance (Object ... initargs ) вы можете динамически создавать записи. Но имейте в виду, что рефлексия может принести некоторые накладные расходы и повлиять на вашу производительность.
Я добавил пример RecordRowMapper с использованием рефлексии и пару тестов :
package by.slesh.spring.jdbc.core;
import org.springframework.jdbc.IncorrectResultSetColumnCountException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
public class RecordRowMapper<T> implements RowMapper<T> {
private final Constructor<T> ctor;
private final List<Arg> args;
public RecordRowMapper(final Class<T> model) {
if (!model.isRecord()) {
throw new IllegalArgumentException(
model + " should be a record class");
}
final RecordComponent[] components = model.getRecordComponents();
this.args = new ArrayList<>(components.length);
final Class<?>[] argTypes = new Class[components.length];
for (int i = 0; i < components.length; ++i) {
final RecordComponent c = components[i];
this.args.add(new Arg(i, c.getName(), c.getType()));
argTypes[i] = c.getType();
}
try {
this.ctor = model.getConstructor(argTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Couldn resolve constructor for types " + Arrays.toString(argTypes));
}
}
@Override
public T mapRow(final ResultSet resultSet, final int rowNumber) throws SQLException {
final var metaData = resultSet.getMetaData();
final int columnCount = metaData.getColumnCount();
if (columnCount < args.size()) {
throw new IncorrectResultSetColumnCountException(
args.size(), columnCount);
}
try {
return ctor.newInstance(extractCtorParams(
resultSet, createPropertyToColumnIndexMap(
metaData, columnCount)));
} catch (InstantiationException
| IllegalAccessException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private Object[] extractCtorParams(
final ResultSet resultSet,
final Map<String, Integer> propertyToColumnIndexMap)
throws SQLException {
final var params = new Object[args.size()];
for (final var arg : args) {
final int columnIndex = propertyToColumnIndexMap.get(arg.name);
params[arg.order] = JdbcUtils.getResultSetValue(
resultSet, columnIndex, arg.type);
}
return params;
}
private Map<String, Integer> createPropertyToColumnIndexMap(
final ResultSetMetaData metaData,
final int columnCount)
throws SQLException {
final Map<String, Integer> columnPropertyToIndexMap = new HashMap<>(columnCount);
for (int columnIndex = 1; columnIndex <= columnCount; ++columnIndex) {
final String propertyName = JdbcUtils.convertUnderscoreNameToPropertyName(
JdbcUtils.lookupColumnName(metaData, columnIndex));
columnPropertyToIndexMap.put(propertyName, columnIndex);
}
return columnPropertyToIndexMap;
}
private static record Arg(int order, String name, Class<?>type) {
}
}