JSON return nested arrays instead of objects [Spring boot + JPA + MySQL + REST]

F. Regayeg :

ISSUE

Hello Guys please help me to solve this. I ve started building a REST API and got a problem when testing the URLs that I've made. Example: when I send request to get the list of one object, the request work fine but the data syntax returned by JSON is ugly: I got in result nested arrays instead of one global array containing json Objects inside it. Check my code please, I have 2 entities now that one of them depend on the other, I used @OneToMany to make relationships between them and no error has occured. Thanks in advance.

SOLUTION

The problem is: my query was returning a list of lists by default, so I had to modify my query by adding a constructor call. check this links please: using new keyword in HQL query

Also I added @JsonIgnore annotation to ignore some properties in my entities to prevent their show. Now the data is shown as formatted as I want :D thanks for your help. Check the new result here

Update

Hello again, I realized recently, that is bad to use @JsonIgnore annotation to prevent some properties from being send in the Json response, and the best way to customize which properties to send is to use DTOs class. Thanks again kj007

Entity 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;

    }

Entity 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;

}

Repository

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

}

Service

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

Controller

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

Result Check the JSON data returned here

kj007 :

Your current HQL will return list of objects that’s why you are seeing result like this.

you can either return entity or ID(type) from a HQL or JPA named query..not projected/custom columns.

To order to achieve your list of object you can do it via couple of ways..

  1. As HQL will retrun list of objects you can parse the object according to your need in your service class method.

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

2. Use DTO (Which is best way to it)

STEP1: Create DTO for projected columns you want, make sure constructure meet the parameters required from hql ..for example..

@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;
    }
}

STEP 2: define your HQL like this by passing full package path of DTO, use yours

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

STEP 3: Now it will return you LIST

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=99699&siteId=1