Search code examples
hibernatejpaspring-data-jpaspring-data

no extra query for related entity


I have this entity:

@Entity
public class PlantArea {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_plantarea")
    @SequenceGenerator(name = "seq_plantarea", allocationSize = 1)
    @Column(nullable = false, updatable = false)
    private Long id;
    private String code;
    private String name;
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="plantid", nullable = false)
    private Plant plant;
}

In the repo I get all plantareas like this:

@Override
    public List<PlantArea> getPlantAreas() {
        return plantAreaRepository.findAll();
    }

So, nothing special.

I have two question to this:

  1. Hibernate fires two queries: one for selecting the plantareas and one for selecting the related plants. How can I avoid this and how can I force hibernate to do only one query? Like in sql (pseudo): select all plantareas inner join plants. I suppose I have to write some jpql/hql, whatever query?

  2. Querying the plantareas with the simple findAll() gives this json result:

    [ { "id": 1, "code": "122", "name": "auto", "plant": { "id": 1, "code": "130", "location": "some city", "company": { "id": 1, "name": "test company long name", "shortName": "test company " } } } ]

As you can see, every plant has a company too. So, I get the full chain of the related objects: plantaraea -> plant -> company. The question is, how can I suppress the company object? Basically I need a query like this (pseudo sql):

select plantarea.*, plant.*, company.* from plantarea 
inner join plant on plantarea.plant_id=plant.id
inner join company on plant.company_id=company.id

Solution

  • (1) Those additional queries to select Plant is because you configure FetchType.EAGER in PlantArea.plant which is a code smell (refer this for details). To solve it , you can use 'fetch join' to get a list of PlantArea together with its Plant.Something likes:

    public interface PlantAreaRepository extends JpaRepository<PlantArea, Long> {
        
        @Query("select p from PlantArea p left join fetch p.plant")
        List<PlantArea> findAllWithPlant(); 
    
    }
    

    (2) Another code smell to me . For a non trivial application , I would prefer to create another DTO for API JSON response rather than directly expose the entities to the outside world. It just make you difficult to evolve your application as whenever you change your entities , you have to worry about that it may affect the existing API client. So just create a separate DTO which its structure is exactly the same as your API response. Then map PlantArea to these DTO and return back to the client.

    @Data
    public class PlantAreaDto {
        private Long id;
        private String code;
        private String name;
        private PlantDto plant;
    }
    
    @Data
    public class PlantDto {
        private Long id;
        private String location;
    }