3

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

Firas RG
  • 111
  • 6
  • 15
  • Why you are not returning List from your repository instead of Iterable?? – Shubham Dixit Oct 03 '18 at 09:14
  • There is no difference because List extends Iterable. But to tell you, I've tried with List and no changes! – Firas RG Oct 03 '18 at 09:17
  • Add getter and setters to your entity class too.And ensure you have jackson dependency in your class path (if maven check for maven dependency) – Shubham Dixit Oct 03 '18 at 09:57
  • I think there is no need to add getters and setters as I'm using @Data. I will check the Jackson dependency as you told me thanks – Firas RG Oct 03 '18 at 09:59
  • org.projectlombok lombok 1.18.0 provided try adding tis dependency in your pom.xml its actually needed to generate getters and setters automatically in bean – Shubham Dixit Oct 03 '18 at 10:03
  • Can you share the getters and setters generated by Lombok for your classes – sechanakira Oct 03 '18 at 12:05
  • the getters,setters,toString,equals and hashcode methods, are available but NOT shown in the entity class; They can be used wherever I want, thanks to @Data annotation I can call an abstract setter for my Assureurs object to set a name field for example. otherwise, in some other situations, I can redefine my setter inside my entity to add some logic its pretty simple – Firas RG Oct 03 '18 at 12:59
  • I know, I thought Lombok was generating bad code which is why I wanted you to check it out. Please mark the answer below as correct – sechanakira Oct 03 '18 at 15:35
  • its already done! – Firas RG Oct 03 '18 at 15:40

1 Answers1

4

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();
kj007
  • 6,073
  • 4
  • 29
  • 47
  • The controller is returning an Iterable. According to the comments, List has not worked at all. I don't think the problem is in the Data Access layer, but it's something to do with Jackson or the code Lombok is generating – sechanakira Oct 03 '18 at 12:03
  • I am not referring list or Iterbabke that’s not impact the result, issue is in HQL and repository method return type – kj007 Oct 03 '18 at 12:07
  • Its done I found it check my answer above, thank you @kj007, it was the constructor call, without need to make DTO (just for now). Check my adds above and the result in screenshot, thanks everyone – Firas RG Oct 03 '18 at 15:25