0

I have the following entities in my spring boot app, which is serving an API for a front end:

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_company")
    @SequenceGenerator(name = "seq_company", allocationSize = 1)
    @Column(nullable = false, updatable = false)
    private Long id;
    private String name;
    private String shortName;
}

@Entity
public class Plant {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_plant")
    @SequenceGenerator(name = "seq_plant", allocationSize = 1)
    @Column(nullable = false, updatable = false)
    private Long id;
    private String code;
    private String location;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="companyid", nullable = false)
    private Company company;
}

@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.LAZY)
    @JoinColumn(name="plantid", nullable = false)
    private Plant plant;
}

Each company can have one or more plants and each plant can have one or more plantareas. Notice, that I don't have any List properties in the entities, this is not needed.

For querying the entities I use @EntityGraph in order to avoid the famous n+1 select problem.

This is how I get all plants in the repository interface:

@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = {"company"})
List<Plant> findAll(); 

Since the API needs the related company entity, I fetch it. With this I get what I want, it's all fine.

Now, this is how I get all plant areas:

@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = {"plant", "plant.company"})
List<PlantArea> findAll();

Notice the plant.company attribute path. With this I get obviously the full chain of entities: plantarea -> plant -> company. This works too.

But for the front end if I query for all plant areas, I don't need the related company entities, just the plant entities. But if I remove the plant.company attribute path, I get the following error pointing to the company property in the plant entity:

No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer

I googled this and can resolve this, if I put this line of code (annotate the company property) in the plant entity:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

But in this case I get the n+1 select problem and on top I get the related company entities too (which I don't want).

So, the question is: how can I select all plantarea entities with the related plant entity, but without the related company entity? And all that without using the above @JsonIgnoreProperties annotation.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
derstauner
  • 1,478
  • 2
  • 23
  • 44
  • You’re looking to do partial JSON serialisation - you need a view. https://www.baeldung.com/jackson-json-view-annotation – Boris the Spider Jan 18 '22 at 19:34
  • if you consider to take my advice (2) that i already told you in [this](https://stackoverflow.com/a/70731411/339637) , you do not need do worry about such issue. I also just told another user the same advice in [this](https://stackoverflow.com/a/70761431/339637) – Ken Chan Jan 18 '22 at 20:02
  • You are certainly right and I will give it a try today evening. – derstauner Jan 19 '22 at 06:41

1 Answers1

0

After studying the DTO pattern, I created this dto:

public class PlantAreaDto {
    @JsonProperty("id")
    private Long id;
    @JsonProperty("code")
    private String code;
    @JsonProperty("name")
    private String name;
    @JsonProperty("plant")
    private Plant plant;
    @JsonProperty("company")
    private Company company;
}

Note, that the company property is no more part of the entity, I removed it and populate it in the mapper like this:

@Mapper(componentModel = "spring")
public interface PlantAreaMapper extends BaseMapper<PlantAreaDto, PlantArea> {
    @Override
    @Mapping(target = "company", source = "plant.company")
    PlantAreaDto toDto(PlantArea plantArea);
}

And the repository looks like this:

@Repository
public interface PlantAreaRepository extends JpaRepository<PlantArea, Long> {
    @EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = {"plant", "plant.company"})
    List<PlantArea> findAll();
}

With this I got, what I wanted:

  • no need to persist the company entity in the db, instead it will be populated from the plant property
  • the company property of the plant entity is now fetched from db, but I don't have the n+1 select problem and I don't get the No serializer found... error
  • I get all the data with one query
derstauner
  • 1,478
  • 2
  • 23
  • 44