开发者社区> 问答> 正文

在Java记录和BeanPropertyRowMapper中找不到默认构造函数

我正在使用新的Java 14和Spring Boot。我使用了新的酷记录,而不是数据持有者的常规Java类。

public record City(Long id, String name, Integer population) {}

稍后在服务类中,我使用Spring BeanPropertyRowMapper来获取数据。

@Override
public City findById(Long id) {
    String sql = "SELECT * FROM cities WHERE id = ?";
    return jtm.queryForObject(sql, new Object[]{id},
            new BeanPropertyRowMapper<>(City.class));
}

我最终遇到以下错误:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zetcode.model.City]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.zetcode.model.City.<init>()
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:145) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]

如何为记录添加默认构造函数,或者是否有其他方法可以解决此问题?

问题来源:Stack Overflow

展开
收起
montos 2020-03-27 10:14:24 830 0
1 条回答
写回答
取消 提交回答
  • 只需通过为字段提供默认值即可明确声明它:

    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")));
    }
    

    或者您可以使用反射

    反射API

    以下公共方法将添加到java.lang.Class:

    RecordComponent[] getRecordComponents() boolean isRecord() 方法getRecordComponents()返回一个java.lang.reflect.RecordComponent对象的数组,其中java.lang.reflect.RecordComponent是一个新类。该数组的元素与记录的组件相对应,其顺序与在记录声明中出现的顺序相同。可以从数组中的每个RecordComponent提取其他信息,包括其名称,类型,通用类型,注释及其访问方法。

    如果将给定的类声明为记录,则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) {
        }
    }
    

    回答来源:Stack Overflow

    2020-03-27 10:16:25
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Cloud Alibaba - 重新定义 Java Cloud-Native 立即下载
The Reactive Cloud Native Arch 立即下载
JAVA开发手册1.5.0 立即下载