0

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
Ken Chan
  • 84,777
  • 26
  • 143
  • 172
derstauner
  • 1,478
  • 2
  • 23
  • 44

1 Answers1

0

(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;
}
Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • There is the question how to supress loading the `company` entity is still open. With the above query I get the plant areas with the plants and since I fetch the plants, the associated companies are fetched too. How can this be avoided? – derstauner Jan 17 '22 at 19:08
  • i guess it is because you are also map Plant 's company as `FetchType.EAGER`. change it to lazy – Ken Chan Jan 17 '22 at 19:13
  • I changed it already to `LAZY`, but since I fetch the `plant` manuall with the query, I force hibernate to load the `plant` entity eagerly with all its related entities, right? It seems to me, that at the end I will end up writing a native query. – derstauner Jan 17 '22 at 19:29
  • yes. you need to end up to write a query by yourself if you do not want hibernate issue separate SELECT query. (BTW , this query is called JPQL/HQL but not native query). Or you try to use EntityGraph mentioned by @fladdimir , which you do not need to write a query by yourself but allow you to configure the fetch join declaratively. – Ken Chan Jan 17 '22 at 19:42