jpaRepository findAll with parent and child use pagable - spring

I'm using spring boot and jpa, I'm trying to get data from parent entity and child entity using jparepository.
Parent Entity:
#Entity
#Table(name = "parent")
public class Parent {
#Id
private int id;
private String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<Child> children;
}
Child Entity:
#Entity
#Table(name = "child")
public class Child {
#Id
private int id;
private String name;
private int parent_id;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private Parent parent;
jpaRepository:
public interface ParentRepository extends JpaRepository<Parent, Integer>, JpaSpecificationExecutor<Parent> {
}
the reason I set the fecth to FetchType.LAZY is that sometimes I just want to get parent without child.
So, here is my question:
when I use
parentRepository.findAll(pagable);
the result only contains parents, not child, but I want the result to contain children, and in some situation I don't want it. how to write it ?

To fetch children collection you can declare an entity graph. Something like this:
#NamedEntityGraph(
name = "parent.withChildren",
attributeNodes = {
#NamedAttributeNode("children")
}
)
And then use it with repository methods:
#EntityGraph("parent.withChildren")
Page<Parent> findWithChidren(Pageable page);

Related

How to use #NamedEntityGraph with #EmbeddedId?

I'm trying to have Spring Data JPA issue one query using joins to eagerly get a graph of entities:
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("positionKey.account"),
#NamedAttributeNode("positionKey.product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
Here's my Spring Data repo:
public interface PositionRepository extends JpaRepository<Position, PositionKey> {
#EntityGraph(value = "PositionKey.all", type = EntityGraphType.LOAD)
List<Position> findByPositionKeyAccountIn(Set<Account> accounts);
}
This produces the following exception:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [positionKey.account] on this ManagedType
I want all of the accounts and products to be retrieved in one join statement with the positions. How can I do this / reference the embedded ID properties?
I would suggest refactoring the entity this way if it possible
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("account"),
#NamedAttributeNode("product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
#MapsId("accountId")
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#MapsId("productId")
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#Column(name = "accountId")
private Long accountId;
#Column(name = "productId")
private Long productId;
}
Such an EmbeddedId is much easier to use. For instance, when you are trying to get an entity by id, you do not need to create a complex key containing two entities.

Springboot add problem in oneTOMany relation

I'm writing 3 tables in the following relation:
Club class:
#Setter
#Getter
#Entity
#Table(name = "Club")
public class Club {
#Id
#GeneratedValue
private Long id;
private String name;
private String type;
private String mainPage;
private String logo;
#OneToMany(mappedBy="clubProductKey.club", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.club", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
Product class:
#Setter
#Getter
#Entity
#Table(name = "Product")
public class Product {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="clubProductKey.product", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.product", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
ClubProduct class:
#Setter
#Getter
#Entity
#Table(name = "ClubProduct")
public class ClubProduct {
#EmbeddedId
private ClubProductKey clubProductKey;
...
ClubProductKey class:
#Setter
#Getter
#Embeddable
public class ClubProductKey implements Serializable {
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "club_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Club club;
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "product_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Product product;
...
ClubProductRepository class:
public interface ClubProductRepository extends JpaRepository<ClubProduct, ClubProductKey> {
public List<ClubProduct> findByClubProductKeyClub(Club club);
public List<ClubProduct> findByClubProductKeyProduct(Product product);
}
I try to save clubProduct like this:
#Service
public class ClubProductServiceImp implements ClubProductService {
#Autowired
private ClubProductRepository clubProductRepository;
...
ClubProduct savedClubProduct = clubProductRepository.save(clubProduct);
return savedClubProduct;
}
However I find that the clubProduct is not saved in the clubProducts list in the club or product entity, the list is null. Must I add lines like club.getClubProducts.add(clubProduct) or is there any other way to make it added automatically?
Thank you.
The #OnetoMany mapping in your Club class uses the attribute mappedby which means that it represents the owning side of the relation responsible for handling the mapping. However, we still need to have both sides in sync as otherwise, we break the Domain Model relationship consistency, and the entity state transitions are not guaranteed to work unless both sides are properly synchronized.
The answer is yes, you have to manage the java relations yourself so that the clubProducts gets persisted. You are using an instance of the repository class club to persist the data so , you should add a setter method like :
public void addClubProduct(ClubProduct clubProduct) {
if (clubProduct!= null) {
if (clubProduct== null) {
clubProduct= new ArrayList<ClubProduct>();
}
clubProducts.add(clubProduct);
clubProduct.setClubProduct(this);
}
}
also a method to remove it from the list and use these method in your code to set the values to the list properly before initiating save . Read related article

Having an issue to save through Spring Data JPA

I have 2 tables Employee & EmployeeAddress joined by one to many relationship with Foreign key. When i am trying to save the Employee object i am getting the below log in my console. It might be this unidirectional joining is not happening properly.
2019-07-26 16:32:19 - SQL Error: 1048, SQLState: 23000
2019-07-26 16:32:19 - Column 'EMP_ID' cannot be null
Below are POJO Classes: These are mapped with the DB tables.I am facing issues with the Join column.
Can any one please help me on this.
Thanks in advance.
Try using:
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
Assuming the issue is with Relationship so please make sure the following is correct
mappedBy is used on the parent Entity and have the same name
reference in Child Entity
use cascade = CascadeType.PERSIST, cascade = CascadeType.ALL or cascade = CascadeType.MERGE (sometime) at Parent Entity
#JoinColumn is used on Child Entity with correct reference column of Parent
Parent.java
#Entity
#Table(name = "parent")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int parentId;
private String name;
#OneToMany(mappedBy="parent",fetch=FetchType.LAZY,cascade = CascadeType.PERSIST)
private List<Child> child = new ArrayList<Child>();
}
Child.java
#Entity
#Table(name = "child")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int childId;
private String account;
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Parent.class)
#JoinColumn(name="parentId", referencedColumnName = "parentId", nullable = false)
private Parent parent;
}
Controller
#RestController
public class RelationshipController {
#Autowired ParentRepository parentRepository;
#Autowired ChildRepository childRepository;
//save Child with Parent at same
#PostMapping(value = "/onetomany")
public String OneToMany(#RequestBody Parent parent)
{
for (Child child : parent.getChild()) {
child.setParent(parent);
}
parent.setChild(parent.getChild());
parentRepository.save(parent);
return "saved";
/*{
"name":"Romil",
"child":[
{"account":"1"},
{"account":"2"}
]
}*/
}
//save Child with Parent's ID
#PostMapping(value = "/onetomanyPID")
public String OneToMany(#RequestBody Child child)
{
child.setParent(child.getParent());;
childRepository.save(child);
return "saved";
/*{
"account":"3",
"parent":{
"parentId":"1",
"name":"Romil"
}
}*/
}
}

can i retrieve child entity containing parent entity id by using Spring Data Jpa one-to-many unidirectional relationship

Here is my parent entity:
#Entity
public class Parent {
#Id
int parentId;
String name;
#OneToMany()
#JoinColumn(name="parent_id")
List<Child> childList;
}
public class Child {
int childId;
//if i am taking this property as non-transient application won't run. but i need parent Id without changing the class structure..
#Transient
int parentId;
// ... some other properties
}
Insertion is successful as two tables are created : parent(id,name),
child(id,name,parent_id).
But when I retrieve the Parent record then in the Child object, the
parentId property remains 0.
i found a way to retrieve the parentid from the child entity by doing a bidirectional mapping of the Parent-child relationship. You can get the ParentId by using a getter method from the child entity that returns the parentId.
Parent Entity
#Entity
#Table(name = "Parent")
public class Parent implements Serializable {
#Column(name = "ID", nullable = false, length = 10)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int Id;
#Column(name = "Name")
public String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<Child> children = new HashSet<>();
public Parent() {
}
//getters and setters omitted for brevity
}
//Child Entity
#Entity
#Table(name = "Child")
public class Child implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int Id;
#Column(name = "Name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parentId", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Parent parent;
public Child() {
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonIgnore
public Parent getParent() {
return parent;
}
//getter method to retrieve the parent id in the child entity
public int getParent_Id(){
return parent.getId();
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
Notice the method getParent_Id() which returns the parent Id, since getter methods are used to return object, the parentId would be returned as part of the child entity anytime it is fetched.
Also note the use of #JsonIgnore on the getParent() method, this to avoid an infinite recursion going on during serialization since Parent refers to Child and Child refer to Parent.

how to add #onetoone mapping for self entity in hibernate

how to add one-to-one mapping for the self entity. Like in this example. I want to have parent-child relationship for the Person itself.
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="personId")
private int id;
#OneToOne
#JoinColumn()
private Person parentPerson;
}
Here is example of bidirectional self mapping #OneToOne (I change column names to SQL notation):
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="person_id")
private int id;
#OneToOne
#JoinColumn(name = "parent_person_id")
private Person parentPerson;
#OneToOne(mappedBy = "parentPerson")
private Person childPerson;
}
But, I don't understand why you want to use #OneToOne in this case.
I am using it like this:
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "PARENT_ID", nullable = true)
private Person parent;
In order to add parent from your service layer, you need to already have at least one Person in the database.
Lets presume you do. Then create a new person. For e.g.:
#Transactional
public void createPerson() {
Person parent = //get your parent
Person child = new Person();
if (parent != null) {
child.setParent(parent);
}
}
If this is what you mean..

Resources