You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java REST API GET请求返回500错误,请求排查原因

问题定位与解决方案

问题描述

我有一个关联Java应用的REST API,通过JDBC连接数据库,当前使用Swagger UI进行测试:

  • POST请求完全正常
  • 当PERSON表为空时,GET请求能返回正常响应
  • 一旦PERSON表中有数据,发送GET请求会出现500服务器错误

错误栈追踪如下:

GET /api/user 500 Server Error
java.lang.reflect.UndeclaredThrowableException: null
    at jdk.proxy2/jdk.proxy2.$Proxy16.getPersons(Unknown Source)
    at module.PersonModule.lambda$new$41b6884b$1(PersonModule.java:23)
    at io.jooby.internal.handler.DefaultHandler.apply(DefaultHandler.java:23)
    at io.jooby.internal.handler.WorkerHandler.lambda$apply$0(WorkerHandler.java:23)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NoSuchMethodException: no such constructor: domain.Person.<init>()void/newInvokeSpecial
    at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:974)
    at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1117)
    at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3649)
    at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:2750)
    at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties$PropertiesHolder.<init>(BeanPropertiesFactory.java:202)
    at org.jdbi.v3.core.config.JdbiCaches.lambda$declare$0(JdbiCaches.java:49)
    at org.jdbi.v3.core.config.JdbiCaches$1.lambda$get$1(JdbiCaches.java:63)
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
    at org.jdbi.v3.core.config.JdbiCaches$1.get(JdbiCaches.java:63)
    at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties.getProperties(BeanPropertiesFactory.java:81)
    at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize0(PojoMapper.java:99)
    at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize(PojoMapper.java:80)
    at org.jdbi.v3.core.result.ResultSetResultIterator.<init>(ResultSetResultIterator.java:38)
    at org.jdbi.v3.core.result.ResultIterable.lambda$of$0(ResultIterable.java:54)
    at org.jdbi.v3.core.result.ResultIterable.stream(ResultIterable.java:228)
    at org.jdbi.v3.core.result.ResultIterable.collect(ResultIterable.java:284)
    at org.jdbi.v3.sqlobject.statement.internal.ResultReturner$CollectedResultReturner.mappedResult(ResultReturner.java:275)
    at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.lambda$configureReturner$0(SqlQueryHandler.java:61)
    at org.jdbi.v3.sqlobject.statement.internal.CustomizingStatementHandler.invoke(CustomizingStatementHandler.java:178)
    at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.invoke(SqlQueryHandler.java:27)
    at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.lambda$invoke$0(SqlObjectInitData.java:132)
    at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44)
    at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26)
    at org.jdbi.v3.core.LazyHandleSupplier.lambda$invokeInContext$1(LazyHandleSupplier.java:77)
    at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44)
    at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26)
    at org.jdbi.v3.core.LazyHandleSupplier.invokeInContext(LazyHandleSupplier.java:76)
    at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.call(SqlObjectInitData.java:138)
    at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.invoke(SqlObjectInitData.java:132)
    at org.jdbi.v3.sqlobject.SqlObjectFactory.lambda$attach$2(SqlObjectFactory.java:110)
    at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$invoke$5(OnDemandExtensions.java:98)
    at org.jdbi.v3.core.internal.exceptions.Unchecked.lambda$function$4(Unchecked.java:76)
    at org.jdbi.v3.core.internal.OnDemandExtensions.invoke(OnDemandExtensions.java:98)
    at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$2(OnDemandExtensions.java:82)
    at org.jdbi.v3.core.Jdbi.callWithExtension(Jdbi.java:476)
    at org.jdbi.v3.core.Jdbi.withExtension(Jdbi.java:463)
    at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$3(OnDemandExtensions.java:82)
    ... 8 common frames omitted
Caused by: java.lang.NoSuchMethodError: domain.Person: method 'void <init>()' not found
    at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method)
    at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1085)
    at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1114)
    ... 43 common frames omitted

相关代码片段:

  1. PersonModule中报错的第23行(return dao.getPersons();):
public PersonModule(PersonDAO dao){
    
    path("/api/user", () -> {
        get("", ctx -> {
            return dao.getPersons();
        });
        
        post("", ctx -> {
            Person person = ctx.body().to(Person.class);
            if (dao.getPerson(person.getEmail()) == null) {
                dao.savePerson(person);
                return ctx.send(StatusCode.CREATED);
            } else {
                return ctx
                        .setResponseCode(StatusCode.UNPROCESSABLE_ENTITY)
                        .render(new ErrorMessage("That email already exists in the system"));
            }
        });
    });
}
  1. JDBI的PersonDAO中获取人员列表的方法:
@Override
@SqlQuery("SELECT * FROM PERSON ORDER BY EMAIL")
@RegisterBeanMapper(Person.class)
public Collection<Person> getPersons();
  1. Person类(仅展示构造函数,包含常规getter和setter):
public Person(Integer personID, String email, String password, String firstName, String lastName, String phone, String mobile, String industry, String bio, String city, Boolean active, String findOut, String role, String notes) {
    this.personID = personID;
    this.email = email;
    this.password = password;
    this.firstName = firstName;
    this.lastName = lastName;
    this.phone = phone;
    this.mobile = mobile;
    this.industry = industry;
    this.bio = bio;
    this.city = city;
    this.active = active;
    this.findOut = findOut;
    this.role = role;
    this.notes = notes;
}

备注:之前测试GET请求时,数据库有数据也能正常工作,DAO的单元测试无错误。

错误分析

从错误栈的核心原因**java.lang.NoSuchMethodError: domain.Person: method 'void <init>()' not found**可以看出:

  • 你使用的@RegisterBeanMapper是JDBI的Bean映射器,它的工作逻辑是先通过无参构造函数创建Person对象实例,再调用对应的setter方法为属性赋值。
  • 当前Person类只定义了带参构造函数,没有默认的无参构造函数,导致JDBI无法实例化Person对象,从而抛出异常。
  • 表为空时GET请求正常,是因为没有数据需要映射,JDBI不会触发对象实例化流程;单元测试可能直接使用带参构造创建对象,所以不会触发这个问题。

解决方法

有两种可行的解决方案:

方案1:添加无参构造函数

在Person类中添加一个无参构造函数,保证JDBI的Bean映射器可以正常实例化对象:

public Person() {
    // 无参构造函数,无需任何逻辑
}

方案2:改用构造函数映射

如果不想添加无参构造函数,可以将@RegisterBeanMapper替换为@RegisterConstructorMapper,让JDBI直接使用带参构造函数来映射数据库结果:

@Override
@SqlQuery("SELECT * FROM PERSON ORDER BY EMAIL")
@RegisterConstructorMapper(Person.class)
public Collection<Person> getPersons();

注意:使用该方案时,数据库查询结果的列名需要和Person构造函数的参数名(按顺序或名称匹配)对应,确保映射正确。

内容的提问来源于stack exchange,提问作者othnuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 00:30:57