I have 2 Entities:
public class Restaurant {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "restaurant")
private Set<Vote> votes;
}
and
public class Vote {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "restaurant_id", nullable = false)
private Restaurant restaurant;
}
if I try to get both of them like that
#Query("SELECT r FROM Restaurant r JOIN FETCH r.vote ")
I get Infinite Recursion with Jackson JSON. So I managed to find a way to handle that:
public class Restaurant {
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "restaurant")
private Set<Vote> votes;
}
public class Vote {
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "restaurant_id", nullable = false)
private Restaurant restaurant;
}
Now I can get restaurant with votes like that?
#Query("SELECT r FROM Restaurant r JOIN FETCH r.vote ")
But now I CAN'T GET Votes with restaurant
#Query("SELECT v FROM Vote v JOIN FETCH v.restaurant ")
because #JsonBackReference meant
private Restaurant restaurant;
wont be serialized. But i need both of this bidirectional relationship in my controllers. What should i do?
For serialization of entities with bidirectional relationship use #JsonIdentityInfo and remove the #JsonBackReference and #JsonManagedReference. The property of the #JsonIdentityInfo refer to your entity id property used to identify entity.
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Restaurant {
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Vote {
Related
I have a ValuedCustomer and Order and I want to do a bidirectional relationship in hibernate. How do I do this? Thanks
#Entity
#Table(name="VCUST")
#DiscriminatorValue("VCUST")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="ID"),
#PrimaryKeyJoinColumn(name="CUST_TYPE",referencedColumnName="TYPE")
})
public class ValuedCustomer extends Customer {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,orphanRemoval = true)
#JoinColumn(name = "customer_order", nullable = true)
private Set<Order> orders = new HashSet<>();
}
#Entity
#Table(name=“Order”)
public class Order {
#Id
private int id;
}
That's easy. Important is the mappedBy attribute in #OneToMany that indicates that the realtionship is managed by the Order entity.
#Entity
#Table(name="VCUST")
#DiscriminatorValue("VCUST")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="ID"),
#PrimaryKeyJoinColumn(name="CUST_TYPE",referencedColumnName="TYPE")
})
public class ValuedCustomer extends Customer {
#OneToMany(mappedBy= 'valuedCustomer" fetch = FetchType.LAZY, cascade = CascadeType.ALL,orphanRemoval = true)
private Set<Order> orders = new HashSet<>();
}
#Entity
#Table(name=“Order”)
public class Order {
#Id
private int id;
#ManyToOne
private ValuedCustomer valuedCustomer;
}
I have multiple back-reference classes in a class. Since I use #JsonBackReference for them, I get an error. I assigned #JsonIdentityInfo annotation for those classes, but I still get the same error.
public class X implements Serializable {
....
//bi-directional many-to-one association to Booking
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "xxA", nullable = false)
#JsonBackReference
private A a;
//bi-directional many-to-one association to Client
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "xxB", nullable = false)
#JsonBackReference
private B b;
...getters setters
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class B implements Serializable {
........
//bi-directional many-to-one association to BookedClient
#OneToMany(mappedBy = "b", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference
private List < X > xxB;
........ getters setters
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class A implements Serializable {
........
//bi-directional many-to-one association to BookedClient
#OneToMany(mappedBy = "a", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference
private List < X > xxA;
........ getters setters
}
error:
com.fasterxml.jackson.databind.JsonMappingException: Multiple back-reference properties with name 'defaultReference'
How can I resolve this error? Can I not use multiple back-reference in a class?
According to Jackson's javadoc, both #JsonManagedReference and #JsonBackReference accept a name value that binds them together:
#JsonBackReference("a")
private A a;
#JsonManagedReference("a")
private List < X > xxA;
I also faced this issue, but in the last I resolved it.
//This is parent class
#Entity
#Table(name = "checklist")
#JsonIgnoreProperties("inspection")
public class Checklist implements java.io.Serializable {
#ManyToOne
#JoinColumn(name = "product_id", referencedColumnName = "id")
#JsonBackReference
private Product product;
#OneToMany(mappedBy = "checklists", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<Inspection> inspection = new HashSet<Inspection>();
//Constructor
//Getter and Setter
}
//This is child class
#Entity
#Table(name = "inspections")
public class Inspection {
#ManyToOne
#JoinColumn(name = "chk_id", referencedColumnName = "id")
private Checklist checklists;
//Constructor
//Getter and Setter
}
By mentioning #JsonIgnoreProperties("inspection") and #JsonManagedReference in Parent class
Resolved the issue raised by using two #JSONBackRefrence in same parent class.
So this did actually take me a while...
You can annotate your referencse accordingly using #JsonIdentityReference(alwaysAsId = true) and then leave it off the master reference
#JsonIdentityReference(alwaysAsId = true)
private Set<PackInstructionGroup> groups = new TreeSet<>();
I made some domains below.
#Entity
public class Conference {
...
#OneToMany(
targetEntity = ProgramDate.class,
mappedBy = "conference",
cascade = CascadeType.REMOVE,
fetch = FetchType.EAGER
)
#JsonBackReference
private List<ProgramDate> programDateList;
}
#Entity
public class Program {
...
#ManyToOne
#JoinColumn(name = "program_date_id")
#JsonBackReference
private ProgramDate date;
#ManyToOne
#JoinColumn(name = "room_id")
private Room room;
...
}
#Entity
public class ProgramDate {
...
#OneToMany(
targetEntity = Program.class,
mappedBy = "date",
fetch = FetchType.EAGER
)
#JsonBackReference
private List<Program> programList;
#ManyToOne
#JoinColumn(name = "conference_id")
private Conference conference;
}
#Entity
public class Room {
...
#OneToMany(
targetEntity = Program.class,
mappedBy = "room",
fetch = FetchType.EAGER
)
#JsonBackReference
private List<Program> programList;
}
And I made freemarker like below code.
<#list conference.programDateList as date>
...
</#list>
I meet a problem that is infinite recursion reference with JPA OneToMany, ManyToOne Relationship. I try to add #JsonBackReference, but it only resolved about the json recursive problem.
Use json ignore annotation :
#OneToMany(
targetEntity = Program.class,
mappedBy = "date",
fetch = FetchType.EAGER
)
#JsonIgnore
private List<Program> programList;
Why do you include Room & Programdate in Conference, then in Program add a Conference along a Room & a Programdate which should already be contained in Conference ? Then in ProgramDate you have another reference to... conference and a list of program...
Basically you shouldn't try to "hack" out of these loops with some fancy annotations, but you should work on your data model. While Conference looks ok, Program could be a list of conferences only, and a Programdate should be... a date.
Here is my JPA structure:
Movie (look at cascade types):
#Entity
#Table(name = "movie")
public class Movie {
#Id
#Column(name = "movie_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
//#OneToMany(cascade = CascadeType.ALL, mappedBy = "primaryKey.movie") //stack overflow
#OneToMany(mappedBy = "primaryKey.movie") //works fine
private List<Rating> ratings;
....
}
Rating:
#Entity
#Table(name = "rating")
#AssociationOverrides({#AssociationOverride(name = "primaryKey.movie", joinColumns = #JoinColumn(name = "movie_id")),
#AssociationOverride(name = "primaryKey.user", joinColumns = #JoinColumn(name = "imdb_user_id"))})
public class Rating {
#EmbeddedId
private RatingId primaryKey = new RatingId();
#Column(name = "rating_value")
private Integer ratingValue;
.....
}
RatingId:
#Embeddable
public class RatingId implements Serializable{
#ManyToOne
private Movie movie;
#ManyToOne
private User user;
}
When I call entityManager.merge(Movie movie) with CascadeType.ALL I get the StackOverflowError. If remove cascading, merge call doesn't throw the error. Where may be a problem?
I think this problem related to composite primary key. There is no error when merge performed on another entities with the same one-to-many relationship, but without composite id.
StackOverflow was caused by cyclic relations. To avoid exception I marked keys in many-to-many table as #ManyToOne(fetch = FetchType.LAZY).
That's how my tables look after modifications: https://stackoverflow.com/a/32544519/2089491
I have 3 JPA entities such as:
#Entity
public class Link implements {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "network", referencedColumnName = "id")
private Network network;
//...
}
#Entity
public class Network implements LinkOwner {
#OneToMany(mappedBy = "network")
#Cascade(value = { CascadeType.ALL })
private Set<Link> links;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project", referencedColumnName = "id", nullable = false)
private Project Network.project;
//...
}
#Entity
public class Project {
#OneToMany(mappedBy = "project", orphanRemoval = true)
#Cascade(value = { CascadeType.ALL })
#Fetch(FetchMode.SELECT)
private Set<Network> networks;
}
And I do a JPA query such as:
SELECT l FROM Link l left join fetch l.subnetwork sann
where sann.project.id = :projectId
and it generates a SQL query similar to:
select * from RMT6.link, SUBNETWORK where link.subnetwork = SUBNETWORK.id
and SUBNETWORK.project=?
How can I trigger a JPQL query that selects only the fields of the first entity and exclude those of the second one?
What do I need to change in my JPQL query?
Base on your entity relationship, you don't need to use JOIN query, I think.
SELECT * FROM LINK l WHERE l.network.project.id = :projectId