1

I've worked with Spring and Hibernate. Now having a look at Spring Data JPA (2.0.3) with JPA 2.2

AgencyTicketType

@Entity
@Table(name = "agency_ticket_type", catalog = "test")
public class AgencyTicketType implements java.io.Serializable {
   private Long id;
   private String name;
   private Agency agency;
   private Set<AgencyTicketCategory> agencyTicketCategories = new HashSet<AgencyTicketCategory>(0);

   @Id
   @GeneratedValue(strategy = IDENTITY)
   @Column(name = "id", unique = true, nullable = false)
   public Long getId() {
     return this.id;
   }
   public void setId(Long id) {
    this.id = id;
   }
   @Column(name = "name", nullable = false, length = 100)
   public String getName() {
    return this.name;
   }
   public void setName(String name) {
    this.name = name;
   }
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "agency_id", nullable = false)
   public Agency getAgency() {
     return this.agency;
   }
   public void setAgency(Agency agency) {
     this.agency = agency;
   }
   @OneToMany(fetch = FetchType.LAZY, mappedBy = "agencyTicketType")
   public Set<AgencyTicketCategory> getAgencyTicketCategories() {
      return this.agencyTicketCategories;
   }
   public void setAgencyTicketCategories(Set<AgencyTicketCategory> agencyTicketCategories) {
      this.agencyTicketCategories = agencyTicketCategories;
   }
}

AgencyTicketCategory

@Entity
@Table(name = "agency_ticket_category", catalog = "waytest")
public class AgencyTicketCategory implements java.io.Serializable {
    private Long id;
    private AgencyTicketType agencyTicketType;
    private String name;
    private BigDecimal price;
    private Set<TripTicket> tripTickets = new HashSet<TripTicket>(0);

    @Id
    @GeneratedValue(strategy = IDENTITY)

    @Column(name = "id", unique = true, nullable = false)
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "agency_ticket_type_id", nullable = false)
    public AgencyTicketType getAgencyTicketType() {
        return this.agencyTicketType;
    }
    public void setAgencyTicketType(AgencyTicketType agencyTicketType) {
        this.agencyTicketType = agencyTicketType;
    }
    @Column(name = "name", nullable = false, length = 100)
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Column(name = "price", nullable = false, precision = 8)
    public BigDecimal getPrice() {
        return this.price;
    }
    public void setPrice(BigDecimal price) {
        this.price = price;
    }
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "agencyTicketCategory")
    public Set<TripTicket> getTripTickets() {
        return this.tripTickets;
    }
    public void setTripTickets(Set<TripTicket> tripTickets) {
        this.tripTickets = tripTickets;
    }
}

Repository

public interface TicketTypeRepository extends JpaRepository<AgencyTicketType, Long> {

  @EntityGraph(attributePaths={ "agencyTicketCategories" }, type=EntityGraphType.LOAD)
  @Query("select type from AgencyTicketType type where type.agency.code=?1")
  List<AgencyTicketType> findByAgency(String agencyCode);
}

Service

@Service
public class TicketServiceImpl implements TicketService {       
    @Autowired private TicketTypeRepository ticketType;

    @Transactional(readOnly=true)
    @Override
    public List<AgencyTicketType> findByName(String code) {
        return ticketType.findByAgency(code);
    }    
}

When debugged on Service, it seems, the query eagerly fetches all the lazy loaded properties - agency, agencyTicketCategories - and all their inner lazy loaded properties, which leads to JSON serilization error.

Need to fetch only these

AgencyTicketTypes [
                   {
                     id, name,
                     agencyTicketCategories [
                                              {id,name,price},....
                                            ]
                   },.....    
                 ]

Can I do this with @EntityGraph? What I am missing?

MWiesner
  • 8,868
  • 11
  • 36
  • 70
Ramanujan R
  • 1,601
  • 2
  • 23
  • 43

3 Answers3

0

Specifying lazy loading is only a hint for the JPA provider. Depending on the provider you use (Hibernate, EclipseLink etc.) it may be completely ignored and the dependencies may be eagerly fetched.

What you need to do is configure how your classes are mapped to json. Assuming you are using Jackson you may need to use annotations like @JsonIgnore or @JsonView. You may also map your class that only has the fields you need.

Apokralipsa
  • 2,674
  • 17
  • 28
0

You can use Jackson annotations @JsonBackReference/@JsonManagedReference. They address problem of infinite recursion with bidirectional links in object model. As far as I understand it is your case.

See http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion for more information.

Anton Tupy
  • 951
  • 5
  • 16
0

One thing to point is that debugging while the transaction is open (touching the collection) will cause it to be loaded even if at real time it doesn't .. the other thing is that as @Apokralipsa mentioned , LAZY loading is just a hint that can be totally ignored and should never be relied upon whatever technique you are using

osama yaccoub
  • 1,884
  • 2
  • 17
  • 47