Spring data JPA avoid multiple select in many to one relationship - spring

I'm using Spring Data JPA and have a many to one relationship from Child to Parent Class using hibernate. I'm writing a search API which would search on child table using some child table columns and return list of child objects along with some data from Parent class for each child object. I'm doing by default eager fetch for many to one relationship. The problem i'm facing is lets say after searching child table 10 entries are returned then hibernate is doing 10 different select queries on parent class to get Parent object for each child object. Is there a way to optimize this ? There is a solution given to similar problem here but it is for one to many case. I could not find anything helpful regarding this on web also. Any ideas ?

as you didn't show any codes in the question, it's a little hard to solve it but I think if you specify join column (#JoinColumn annotation) and use #OneToMany annotation in parent class(with specifying fetch type) and #ManyToOne inside child you should not have any problem:
#Entity(name ="Parent")
public class Parent {
#Id
#Column
private int id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name= "paren_id")
private Set<Child> children;
//getters and setters
}
#Entity(name ="Child")
public class Child{
#Id
#Column
private int id;
#ManyToOne
private Parent parent;
//getters and setters
}

Related

Spring Data + View with Union return duplicate rows

i'm using Spring Boot 2.4.2 and Data module for JPA implementation.
Now, i'm using an Oracle View, mapped by this JPA Entity:
#Entity
#Immutable
#Table(name = "ORDER_EXPORT_V")
#ToString
#Data
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class OrderExportView implements Serializable {
private static final long serialVersionUID = -4417678438840201704L;
#Id
#Column(name = "ID", nullable = false)
#EqualsAndHashCode.Include
private Long id;
....
The view uses an UNION which allows me to obtain two different attributes of the same parent entity, so for one same parent entity (A) with this UNION I get the attribute B in row 1 and attribute C in row 2: this means that the rows will be different from each other.
If I run the query with an Oracle client, I get the result set I expect: same parent entity with 2 different rows containing the different attributes.
Now the issue: when I run the query with Spring Data (JPA), I get the wrong result set: two lines but duplicate.
In debug, I check the query that perform Spring Data and it's correct; if I run the same query, the result set is correct, but from Java/Spring Data not. Why??
Thanks for your support!
I got it! I was wrong in the ID field.
The two rows have the same parent id, which is not good for JPA, which instead expects a unique value for each line.
So, now I introduced a UUID field into the view:
sys_guid() AS uuid
and in JPA Entity:
#Id
#Column(name = "UUID", nullable = false)
#EqualsAndHashCode.Include
private UUID uuid;
#Column(name = "ID")
private Long id;
and now everything works fine, as the new field has a unique value for each row.

Many to one mapping Hibernate

I am doing many to one mapping in hibernate. I am using the existing tables which I created earlier for one to many mapping (customer and order) but when I am trying to map and update those table I couldn't able to I don't know how should I processed? and I would like to insert the data meaning I would like to create some more orders using command line runner for that customer.
Could you please help me with this
Appreciate your help.
Mapping one-to-many and many-to-one association
Both associations are the same association seen from the perspective of the owing and subordinate entities and respectively.
Student one-to-many Address
Address many-to-one Student
#OneToMany annotation can be applied to a field or property value of "one" end entity class for a collection or an array representing the mapped "many" end of the association.
#ManyToONe relationship between two entities is by managing the FK(Foreign key) of the "one" end entity, as a column in the "many" entity table.
> **Bidirectional one-to-many using ```#JoinColumn```**
#Entity
public class Student{
#OneToMany(cascade = CasecadeType.ALL)
#JoinTable(name="Student_FK")
public set<Address> getAddress(){
return address;
}
}
One-to-Many side as the owing side, You have to remove the mappedBy element and set the #ManyToOne #JoinColumn as insertable and updatable to false. This Solution is not optimized and will produce some additional UPDATE Statement.
#Entity
public class Address{
#ManyToOne(cascade = CasecadeType.ALL)
#JoinColumn(name="STUDENT_FK", insertable = false, updatable = false)
public Student student;
}
For more details look at this link Link

Spring data JPA self join on an entity. How do I specify the depth of recursion?

Using Spring Boot JPA, I am doing a self join on a table of "Person" with attributes id, name and parent_id. parent_id is a foreign key referencing Person.id. So, a Person will have zero or one parent. A sample of my domain class is below.
#Entity(name="person")
public class Person {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parent_person_id")
private Person parent;
// constructors, getters, setters, etc
}
This actually works just fine; when I query with CrudRepository.findById() for example, I get a Person object with an embedded Person object (parent), which may have another embedded Person object (grandparent), etc until I get to a Person without a parent.
My question is, how may I retrieve only a Person and their immediate parent without recursing any further (no grandparents, great-grandparents, etc)?
I imagine I could simply avoid the join, and make parent_id a plain #Column, then in the service layer do an additional query to find the parent, but I'm wondering if there is some Jpa magic that could make it easier than that.
Actually, this was not as hard as it seemed. Converting my Person entity to a dto provided me with the opportunity to simply STOP at the parent, and not recurse through the whole tree!

one-way one-to-many throws Hibernate Cannot add or update a child row: a foreign key constraint fails

I have an application that teaches the user how to play various card games. The data model that gets persisted consists of a TrainingSession with a uni-directional one-to-many relationship with the Hands.
[EDIT] To clarify, a Hand has no existence outside the context of a TrainingSession (i.e they are created/destroyed when the TrainingSession is). Following the principals of Data Driven Design, the TrainingSession is treated as an aggregate root and therefore a single spring-data CrudRepository is used (i.e., no repository is created for Hand)
When I try to save a TrainingSession using a CrudRepository, I get: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (blackjack.hand, CONSTRAINT FKrpuxac6b80xc7rc98vt1euc3n FOREIGN KEY (id) REFERENCES training_session (tsid))
My problem is the 'save(trainingSession)' operation via the CrudRepository instance. What I don't understand is why the error message states that FOREIGN KEY (id) REFERENCES training_session (tsid)). That seems to be the cause of the problem but I cant figure out why this is the case or how to fix it. The relationship is uni-directional and nothing in the Hand class refers to the TrainingSession.
The code, minus all the getters and setters, is:
#Entity
public class TrainingSession {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer tsid;
private String strategy;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="id")
private List<Hand> hands;
private int userId;
protected TrainingSession() {
}
public TrainingSession(int userId, Strategy strategy, List<Hand> hands) {
this.strategy = strategy.getClass().getSimpleName();
this.hands = hands;
this.userId = userId;
}
while Hand is
#Entity // This tells Hibernate to make a table out of this class
public class Hand {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private int p1;
private String p1s;
private int p2;
private String p2s;
private int d1;
private String d1s;
private int trials;
private int score;
public Hand() {
}
You need to save your TrainingSession and Hand objects first before saving the adding the hand objects to TrainingSession.
TrainingSession ts1 = new TrainingSession();
trainingSessionManager.save(ts1);
Hand hand1 = new Hand();
handManager.save(hand1);
Hand hand2 = new Hand();
handManager.save(hand2);
ts1.gethands().add(hand1);
ts1.gethands().add(hand2)
trainingSessionManager.save(ts1);
If you check your database you will find 3 tables TrainingSession, Hand and TrainingSession_Hand, The TrainingSession_Hand table references to both TrainingSession and Hand both. Therefore you need to save TrainingSession and hand before saving the relationship.
Found the problem. I was assuming that when spring-data set up the DB tables, it was able to figure out and set up the uni-directional 1-to-many relationship. Apparently that isn't the case. When I configure the relationship as bi-directional everything seems to work.
To fix things I:
removed from TrainingSession the #joincolumn annotation for hands
in Hands I added a TrainingSession field with a #ManyToOne annotation:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "tsid", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private TrainingSession tsession;
I also added in the Hand class the getter/setter for tsession
I can now do a save of the entire aggregate construct using only a TrainingSessionRepository.

JPA Many to many unidirectional

I use spring data jpa and i try to do a many to many unidirectional relation.
#Entity
public class Appartment {
...
#ManyToMany
private List<AppartmentFeatureOption> featureOption;
}
#Entity
public class AppartmentFeatureOption {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long appartmentFeatureOptionId;
private String name;
private BigDecimal value;
}
My database is created at run time, but i get this error
org.hibernate.DuplicateMappingException: Same physical table name [appartment_feature_option] references several logical table names: [AppartmentFeatureOption], [Appartment_AppartmentFeatureOption]
Any idea?
Edit with this code that work
#ManyToMany
#JoinTable(name="appartment_feautre_option_appartment", joinColumns=#JoinColumn(name="appartment_id"), inverseJoinColumns=#JoinColumn(name="appartment_feautre_option_id"))
private List<AppartmentFeatureOption> featureOption;
Is this is actually your real code, maybe the issue is that you are using a ManyToMany relationship between Appartment and AppartmentFeatureOption whereas there is no link to Appartment in the AppartmentFeatureOption.
From my understanding for one Appartment you want to have several AppartmentFeatureOption, which is a OneToMany relationship.

Resources