You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot中如何实现查询数据以JSON格式返回?现有代码遇404错误求助

排查并修复你的REST服务404与数据返回问题

Hey,我帮你仔细梳理了代码里的问题,主要集中在路径匹配、数据映射、逻辑错误这几个方面,咱们一步步来解决:

一、核心问题拆解

1. Controller路径不匹配导致404

你的UserController类上标注了@RequestMapping("/users"),但getAllUsers方法又加了@GetMapping("/users"),这意味着这个接口的完整路径是/users/users,而不是你可能预期的/users。如果直接访问localhost:8080/users,Spring找不到对应的映射,自然返回404。

另外,即使你访问/users/user,因为Service层返回的是空列表,Postman也看不到任何内容。

2. Service层逻辑错误,永远返回空列表

UserServiceImplfindAll方法里,你调用了userDao.findAll()但完全没把结果赋值给返回的list,直接返回了空的ArrayList——这就是为什么Postman没有内容的直接原因:

public List<User> findAll() {
    List<User> list = new ArrayList<>();
    userDao.findAll(); // 这里只是调用了方法,但结果被丢弃了!
    return list;
}

3. Repository层SQL映射失败

  • 你注释掉了nativeQuery = true,JPA会把你的SQL当作JPQL解析,这必然会报错,因为JPQL的语法和原生SQL不一样。
  • 方法名findAllJpaRepository自带的方法重名,容易混淆;而且查询返回的字段和User实体不匹配:SQL返回的是nameamountcname,但User的字段是cName(大小写不一致),且查询结果里没有idUser的@Id字段是必填的),这会导致映射失败。

4. Entity类不符合JPA要求

  • User实体没有无参构造函数,JPA实例化对象时必须要有无参构造,否则会抛出实例化异常。
  • 你的User标注了@Entity,但它对应的不是单表,而是多表聚合的查询结果,用DTO(数据传输对象)来接收会更合适,而不是JPA实体。

二、修复后的完整代码

1. 创建DTO类接收查询结果

因为你的查询是多表聚合的结果,不是单表实体,所以新建一个UserSummaryDTO来接收数据:

public class UserSummaryDTO {
    private String name;
    private long amount;
    private String cName;

    // 构造函数参数名要和SQL的别名一致(这里SQL里是cname,所以参数名用cname)
    public UserSummaryDTO(String name, long amount, String cname) {
        this.name = name;
        this.amount = amount;
        this.cName = cname;
    }

    // 必须要有Getter方法,否则Spring无法序列化为JSON
    public String getName() {
        return name;
    }

    public long getAmount() {
        return amount;
    }

    public String getcName() {
        return cName;
    }
}

2. 修复Repository层

开启原生SQL,重命名方法避免冲突,并用DTO接收结果:

@Repository
public interface UserDao extends JpaRepository<User, Long> {
    @Query(value = "SELECT ss.name AS name, \n" +
            " SUM(pp.amount) AS amount, \n" +
            " REPLACE(ss.name, 'SomeName', 'DifferentName') AS cname \n" +
            " FROM payments AS pp\n" +
            " INNER JOIN auth_user AS au ON au.id = pp.creator_id\n" +
            " INNER JOIN services AS ss ON ss.id = pp.service_id\n" +
            " WHERE\n" +
            " pp.created_dt >= '2021-09-28' AND pp.created_dt < '2021-09-29' \n" +
            " AND ss.name = 'SomeName' AND pp.status = 'SUCCESS' \n" +
            " GROUP BY ss.name", nativeQuery = true)
    List<UserSummaryDTO> findUserSummary(); // 重命名方法,避免和JpaRepository的findAll冲突
}

3. 修复Service层

修正逻辑,直接返回查询结果,移除无用的方法:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public List<UserSummaryDTO> findAll() {
        return userDao.findUserSummary(); // 直接返回查询到的结果
    }

    // 如果不需要保存User的逻辑,可以删掉这个方法
    @Override
    public User save(User user) {
        return userDao.save(user);
    }
}

同时调整UserService接口的返回类型:

public interface UserService {
    List<UserSummaryDTO> findAll();
    User save(User user); // 不需要的话可以删除
}

4. 修复Controller层

调整路径,让接口更直观,确保路径匹配正确:

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    // 访问路径:http://localhost:8080/users
    @GetMapping
    public List<UserSummaryDTO> getAllUsers() {
        return userService.findAll();
    }

    // 如果需要保留/user路径,也可以保留,风格统一就行
    @GetMapping("/user")
    public List<UserSummaryDTO> listUser() {
        return userService.findAll();
    }

    // 不需要保存功能的话可以删除这个方法
    @PostMapping("/user")
    public User create(@RequestBody User user) {
        return userService.save(user);
    }
}

三、验证步骤

  1. 启动Spring Boot应用,检查控制台日志,确保没有启动或SQL执行异常。
  2. 用Postman访问GET http://localhost:8080/users,应该能看到JSON格式的查询结果。
  3. 如果还是有问题,检查:
    • 数据库连接配置是否正确(application.properties里的url、username、password)。
    • 数据库里是否存在符合条件的数据(比如pp.created_dt在指定日期范围内,ss.name='SomeName'等)。
    • 控制台是否有异常日志,比如SQL语法错误、映射错误等。

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

火山引擎 最新活动