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.
Related
I have a scenario like below.
Lets say EntityA has three nested entities EntityB, EntityC, EntityD. And all of EntityB, EntityC, EntityD has several nested entities inside them.
But while selecting for EntityA it selects the whole tree of nested entities. Whereas I want to fetch a specific branch. Lets say only EntityA, EntityB and all sub entities of EntityB are to be fetched leaving EntityC and EntityD back then I am not sure how to do that. As spring jpa brings all the nested objects back to me.
I am using below collection mapping.
#Entity
#Table(name = "customer_party_mapping")
#Data
public class CustomerPartyMappingEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "customer_id")
private Integer custmerId;
#Column(name = "orgtype_id")
private Integer orgTypeId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyBookingLocationEntity> customerPartyBookingLocation=new ArrayList<CustomerPartyBookingLocationEntity>();
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyFieldMappingEntity> customerPartyFieldMappingEntity=new ArrayList<CustomerPartyFieldMappingEntity>();
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id",referencedColumnName="id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerPartyOtherDocumentEntity> otherDocumentsList=new
ArrayList<>();
#OneToOne( cascade={ CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "customer_name_screening_id", referencedColumnName="id")
private CustomerNameScreeningEntity customerNameScreeningEntity;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL )
#JoinColumn(name = "customer_party_mapping_id")
#Fetch(value = FetchMode.SUBSELECT)
private List<CustomerDocInfoTrackingEntity> customerDocInfoTrackingList=new
ArrayList<CustomerDocInfoTrackingEntity>();
}
And I am calling
List<CustomerPartyMappingEntity> customerPartyMappingEntityList = customerPartyMappingRepository.findByCustmerId(customerid);
It gets all the nested mapped list of entities wheras I need only CustomerPartyMappingEntity and its list of customerPartyFieldMappingEntity nested object.
Any help will be appreciated.
First use FetchType.LAZY for nested entity.
Then you can use #EntityGraph to fetch nested entity by name and their nested entity using their name with . in the repository. You use to just specify the nested property in attributePaths like
#EntityGraph(attributePaths = {"customerPartyBookingLocation"})
And the nested property of customerPartyBookingLocation like
#EntityGraph(attributePaths = {"customerPartyFieldMappingEntity.subField"})
Example:
#EntityGraph(attributePaths = {"customerPartyBookingLocation", "customerPartyFieldMappingEntity.subField"})
List<CustomerPartyMappingEntity> findByCustmerId(Integer customerid);
Note: You can't use #EntityGraph with #Query annotation
If your entities are really setup correctly, see for instance the subselect example here and remove your EAGER (you are currently instructing hibernate to fetch all these fields upon entity initialization). It should work.
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 {
I'm writing an API using Spring Boot and Hibernate where my persisted entity objects are also used as DTOs sent to and from the client. This is a simplified version of a typical entity I use:
#Entity
#Table(name = "STUDENT")
public class Student {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ElementCollection
#CollectionTable(name = "GROUP_STUDENT",
joinColumns = #JoinColumn(name = "GROUP_ID"))
#Column(name="STUDENT_ID")
private Set<Long> groupIds;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="GROUP_STUDENT",
joinColumns = #JoinColumn(name="GROUP_ID"),
inverseJoinColumns = #JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}
and this is the associated class:
#Entity
#Table(name = "GROUP")
public class Group {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "groups")
private Set<Student> students = new HashSet<>();
// getters and setters
}
As you can see, there is a #ManyToMany association between Student and Group.
Since I send objects like these to the client, I choose to send only the id's of the associations and not the associations themselves. I've solved this using this answer and it works as expected.
The problem is this. When hibernate tries to persist a Student object, it inserts the groups as expected, but it also tries to insert the groupIds into the mapping table GROUP_STUDENT. This will of course fail because of the unique constraint of the mapping table composite id. And it isn't possible to mark the groupIds as insertable = false since it is an #ElementCollection. And I don't think I can use #Formula since I require a Set and not a reduced value.
This can of course be solved by always emptying either the groups of the groupIds before saving or persisting such an entity, but this is extremely risky and easy to forget.
So what I want is basically a read only groupIds in the Student class that loads the data from the GROUP_STUDENT mapping table. Is this possible? I'm grateful for any suggestions and glad to ellaborate on the question if it seems unclear.
I've managed to solve this by making the id-collection #Transient and populating it using #PostLoad:
#Entity
#Table(name = "STUDENT")
public class Student {
#PostLoad
private void postLoad() {
groupIds = groups.stream().map(Group::getId).collect(Collectors.toSet());
}
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Transient
private Set<Long> groupIds;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="GROUP_STUDENT",
joinColumns = #JoinColumn(name="GROUP_ID"),
inverseJoinColumns = #JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}
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 have some problems with inheritance mapping. Here my database structure:
And associated entities:
AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Id #GeneratedValue(strategy = IDENTITY)
#Column(unique = true, updatable = false, nullable = false)
private ID id;
public ID getId() {
return id;
}
#SuppressWarnings("unused")
public void setId(ID id) {
this.id = id;
}
UserAcitvity entity:
#Entity #Table(name = "user_activity")
#Inheritance(strategy = JOINED)
#AttributeOverride(name = "id", column = #Column(name = "ua_id"))
public abstract class UserActivity extends AbstractEntity<Long> {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "ua_user_id")
private User user;
...
}
Comment entity:
#Entity #Table(name = "comment")
#PrimaryKeyJoinColumn(name = "cm_id")
public class Comment extends UserActivity {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "cm_question_id")
private Question question;
...
}
Question entity:
#Entity #Table(name = "question")
#PrimaryKeyJoinColumn(name = "qs_id")
public class Question extends UserActivity {
...
#OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "question")
private List<Answer> answers = new ArrayList<>();
#OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "question")
private List<Comment> comments = new ArrayList<>();
...
}
Answer entity:
#Entity #Table(name = "answer")
#PrimaryKeyJoinColumn(name = "asw_id")
public class Answer extends UserActivity {
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "asw_question_id")
private Question question;
...
}
and User entity:
#Entity #Table(name = "user")
#AttributeOverride(name = "id", column = #Column(name = "user_id"))
public class User extends AbstractEntity<Long> {
...
#OneToMany(cascade = REMOVE)
private List<Question> questions = new ArrayList<>();
#OneToMany(cascade = REMOVE)
private List<Answer> answers = new ArrayList<>();
#OneToMany(cascade = REMOVE)
private List<Comment> comments = new ArrayList<>();
...
}
Problem:
When I try to save or delete a User I get an exceptions:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [insert into user_question (user_user_id, questions_qs_id) values (?, ?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
and:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper : 147 = user lacks privilege or object not found: USER_ANSWER
Hibernate is trying to create a table: user_question and user_answer which me do not need.
What I should doing for fixes ?
I don't think you can achieve this by mapping the ManyToOne association to User generically in the UserActivity entity. That's probably too confusing for the JPA provider (Hibernate).
Instead, I think you need to map the association to User in each of the Question, Answer and Comment entities. Yes, I know that would be duplicated code, but it looks like the only way you will then be able to qualify the OneToMany mappings in User using the mappedBy reference.
For instance, your Question entity would have an association defined as:
#ManyToOne(cascade = { MERGE, PERSIST }, fetch = LAZY)
#JoinColumn(name = "ua_user_id")
private User questionUser;
Depending on how clever (or not) Hibernate is about the above association, you may need to specify the table="USER_ACTIVITY" in the JoinColumn annotation.
Then the User would have the OneToMany as:
#OneToMany(mappedBy="questionUser", cascade = REMOVE)
private List<Question> questions = new ArrayList<>();
Similarly for each of Answer and Comment.
Of course, I haven't tried this, so I could be wrong.
It's probably happening because when you set the #OneToMany mapping then the hibernate will create an auxiliary table that will store the id from the entities on the relationship.
In this case you should try the following:
#OneToMany(cascade = REMOVE)
#JoinColumn(name = "answer_id")
private List<Answer> answers = new ArrayList<>();
The #JoinColumn annotation will map the relationship without the creation of the auxiliary table, so it's pretty likely this solution will help you in this situation.
Try this mapping, this should work as you expect according to section 2.2.5.3.1.1 of the documentation:
#Entity
public class User {
#OneToMany(cascade = REMOVE)
#JoinColumn(name="user_fk") //we need to duplicate the physical information
private List<Question> questions = new ArrayList<>();
...
}
#Entity
public class Question {
#ManyToOne
#JoinColumn(name="user_fk", insertable=false, updatable=false)
private User user;
...
}
The reason why the auxiliary association is created, is that there is no way for Hibernate to know that the Many side of the relation (for example Question) has a foreign key back to User that corresponds to the exact same relation as User.questions.
The association Question.user could be a completely different association, for example User.questionCreator or User.previousSuccessfulAnswerer.
Just by looking at Question.user, there is no way for Hibernate to know that it's the same association as User.questions.
So without the mappedBy indicating that the relation is the same, or #JoinColumn to indicate that there is no join table (but only a join column), Hibernate will trigger the generic one-to-many association mapping solution that consists in creating an auxiliary mapping table.
The schema misses such association tables, which causes the error that can be solved with the mapping above.
If you want unidirectional one-to-many usage in your entity relationship.
Try with..JoinTable
#OneToMany(cascade = REMOVE)
#JoinTable(name = "user_question", joinColumns = {
#JoinColumn(name = "user_id")}, inverseJoinColumns = {
#JoinColumn(name = "qs_id")})
private List<Question> questions = new ArrayList<>();