JSON 返回嵌套数组而不是对象 [Spring boot + JPA + MySQL + REST]

Posted

技术标签:

【中文标题】JSON 返回嵌套数组而不是对象 [Spring boot + JPA + MySQL + REST]【英文标题】:JSON return nested arrays instead of objects [Spring boot + JPA + MySQL + REST] 【发布时间】:2019-03-08 11:26:20 【问题描述】:

问题

大家好,请帮我解决这个问题。 我已经开始构建 REST API,但在测试我制作的 URL 时遇到了问题。示例:当我发送请求以获取一个对象的列表时,请求工作正常,但 JSON 返回的数据语法很难看:我得到的是结果嵌套数组,而不是一个包含 json 对象的全局数组。请检查我的代码,我现在有 2 个实体,其中一个依赖于另一个,我使用 @OneToMany 在它们之间建立关系并且没有发生错误。提前致谢。

解决方案

问题是:我的查询默认返回一个列表列表,所以我不得不通过添加构造函数调用来修改我的查询。请检查此链接:using new keyword in HQL query

我还添加了@JsonIgnore 注释以忽略我的实体中的某些属性以防止它们显示。现在数据显示为我想要的格式:D 感谢您的帮助。 Check the new result here

更新

再次您好,我最近意识到,使用@JsonIgnore 注解来防止在Json 响应中发送某些属性是不好的,而自定义要发送哪些属性的最佳方法是使用DTOs 类。再次感谢kj007

实体 1

import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import lombok.Data;

 @Data
    @Table(name = "x_assureurs") // this is the table name in DB
    @Entity(name = "Assureurs") // This tells Hibernate to make a table out of this class
    public class Assureurs 

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "n_assureur")
        private String id;

        @Column(name = "nom_assureur")
        private String name;

        @OneToMany(mappedBy="assureur",fetch = FetchType.LAZY)
        private List<Contrats> contrats;

    

实体 2

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import lombok.Data;

@Data
@Table(name = "contrats") // this is the table name in DB
@Entity(name = "Contrats") // This tells Hibernate to make a table out of this class
public class Contrats 

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "num_contrat")
    private String num;

    @Column(name = "nom_police")
    private String nomPolice;

    @ManyToOne
    @JoinColumn(name = "courtier")
    private Courtiers courtier;

    @ManyToOne
    @JoinColumn(name = "assureur")
    private Assureurs assureur;


存储库

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import tn.igase.gestdoc.models.entities.Assureurs;

// This will be AUTO IMPLEMENTED by Spring into a Bean called assureurRepository

@Repository
public interface AssureurRepository extends JpaRepository<Assureurs, String> 

    // CONSTANTS
    String FIND_ALL_BY_CONTRATS = "SELECT DISTINCT(contrat.assureur.id) as n_assureur, assureur.name \n"
            + " FROM Contrats contrat \n" + " JOIN Assureurs assureur ON contrat.assureur.id = assureur.id ";
    String BY_ONE_COURTIER = "WHERE contrat.courtier.id = :idCourtier";

    // QUERIES
    @Query(FIND_ALL_BY_CONTRATS)
    Iterable<Assureurs> findAllByContrats();

    @Query(FIND_ALL_BY_CONTRATS + BY_ONE_COURTIER)
    Iterable<Object> findAllByContratsAndCourtier(@Param("idCourtier") int idCourtier);


服务

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import tn.igase.gestdoc.daos.AssureurRepository;
import tn.igase.gestdoc.models.entities.Assureurs;

@Service
public class AssureurService 

   @Autowired
   AssureurRepository assureurRepository;   

   public Iterable<Assureurs> findAllByContrats() 
        return assureurRepository.findAllByContrats();
    

控制器

import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import tn.igase.gestdoc.models.entities.Assureurs;
import tn.igase.gestdoc.service.AssureurService;
import tn.igase.gestdoc.service.ContratService;

/**
 * 
 * Assureur controller
 * 
 * @author fre
 */
@RestController
@RequestMapping(path = "/api/assureurs")
public class AssureurController extends MainController 

    @Autowired
    private AssureurService assureurService;
    /**
     * Revert all assureurs that all have contrats
     * 
     * @return list
     */
    @RequestMapping(path = "/all", produces=MediaType.APPLICATION_JSON_VALUE)
    public Iterable<Assureurs> getAll() 
        // This returns a JSON or XML with the users

        Iterable<Assureurs> assureurs = new ArrayList<>();
        assureurs = assureurService.findAllByContrats();
        return assureurs;
    

结果 Check the JSON data returned here

【问题讨论】:

为什么你没有从你的存储库返回 List 而不是 Iterable?? 没有区别,因为 List 扩展了 Iterable。但是告诉你,我已经尝试了 List 并且没有任何变化! 也将getter和setter添加到您的实体类中。并确保您的类路径中有jackson依赖项(如果maven检查maven依赖项) 我认为没有必要添加getter和setter,因为我正在使用@Data。正如你告诉我的那样,我会检查 Jackson 依赖项,谢谢 org.projectlomboklombok1.18.0provided 尝试在 pom.xml 中添加 tis 依赖项,它实际上需要在 bean 中自动生成 getter 和 setter 【参考方案1】:

您当前的 HQL 将返回对象列表,这就是您看到这样的结果的原因。

您可以从 HQL 或 JPA 命名查询中返回实体或 ID(类型)。不是投影/自定义列。

要实现您的对象列表,您可以通过几种方式来实现..

    由于 HQL 将重新运行对象列表,您可以根据需要在服务类方法中解析对象。

    @Query(FIND_ALL_BY_CONTRATS)
    List<Object> findAllByContrats();
    

2。使用 DTO(这是最好的方法)

STEP1:为您想要的投影列创建 DTO,确保构造满足 hql 所需的参数..例如..

@Data
public class AssureursDTO 

    private Long n_assureur;

    private String name;

    public AssureursDTO(Long n_assureur, String name) 
        this.n_assureur = n_assureur;
        this.name = name;
    

第 2 步:通过传递 DTO 的完整包路径来定义你的 HQL,使用你的

String FIND_ALL_BY_CONTRATS = "SELECT DISTINCT new com.example.demomysql21.entity.AssureursDTO(assureur.id as n_assureur, assureur.name) \n"
            + " FROM Contrats contrat \n" + " JOIN Assureurs assureur ON contrat.assureur.id = assureur.id";

第 3 步:现在它会返回 LIST

@Query(FIND_ALL_BY_CONTRATS)
List<AssureursDTO> findAllByContrats();

【讨论】:

控制器返回一个 Iterable。根据 cmets 的说法, List 根本没有工作。我不认为问题出在数据访问层,而是与 Jackson 或 Lombok 生成的代码有关 我不是指不影响结果的列表或 Iterbabke,问题在于 HQL 和存储库方法返回类型 它完成了我发现它检查了我上面的答案,谢谢@kj007,它是构造函数调用,不需要进行DTO(只是现在)。检查我上面的添加和截图中的结果,谢谢大家

以上是关于JSON 返回嵌套数组而不是对象 [Spring boot + JPA + MySQL + REST]的主要内容,如果未能解决你的问题,请参考以下文章

从 Spring WebFlux 返回 Flux<String> 返回一个字符串而不是 JSON 中的字符串数组

将 Json 数组嵌套到对象

PHP json_encode将行作为对象而不是数组返回[重复]

jQuery Ajax 调用返回 JSON 字符串而不是对象数组

如何使用 Laravel response()->json() 返回空对象而不是空数组

如何从通过 Moya.Response 查询返回的对象解析嵌套的 JSON 数组