Parent id is not saved in child table Spring JPA2 #OneToMany relation - java

I have json like as shown below which comes from a third party service which I gets it through open feign client. I am trying to save these details into my MySQL database using Spring JPA2.
[
{
"request_id": 111,
"name": "ABC",
"groups": [
{
"id": 21,
"group": "grp_A"
},
{
"id": 22,
"group": "grp_B"
}
]
},
{
"request_id": 222,
"name": "ABC",
"groups": [
{
"id": 23,
"group": "grp_C"
}
]
},
{
"request_id": 333,
"name": "ABC",
"groups": [
{
"id": 24,
"group": "grp_A"
}
]
},
{
"request_id": 444,
"name": "ABC",
"groups": [
{
"id": 25,
"group": "grp_C"
}
]
},
{
"request_id": 555,
"name": "ABC",
"groups": []
}
]
I have wrote the mappings for the above json like as shown blow
Requests.java
#Entity
#Table(name = "requests")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Requests {
#Id
#Column(name = "request_id")
private String id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Groups> groups = Sets.newHashSet();
// getters and setters
}
Groups.java
#Entity
#Table(name = "groups")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Groups{
#Id
#Column(name = "id")
private String id;
#Column(name = "group")
private String group;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "request_id")
private Requests requests;
// getters and setters
}
now within my service class I have done like as shown below
List<Requests> allRequests = requestFeignClient.getRequests();
requestRepository.saveAll(allRequests);
all details are saved into the database except the request_id like as shown below. My Expected group table is also shown below
Can anyone please tell me why the request_id is not getting saved.

Try replacing this:
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
With this:
#OneToMany(mappedBy = "requests", cascade = CascadeType.ALL, fetch = FetchType.LAZY)

Related

manyToMany does not retrieve all mapped data [duplicate]

This question already has an answer here:
the manytomany does not retrieve the mapped data
(1 answer)
Closed 3 months ago.
Spring boot 2.5.6 (I can't mount a version)
(1) LAZY
Profil.java
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#FieldDefaults(level = AccessLevel.PRIVATE)
#Table(name = "t_profil")
public class Profil {
...
...
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}, fetch = FetchType.LAZY)
#JoinTable(
name = "t_profils_fonctionnalites",
joinColumns = { #JoinColumn(name = "profil_id") },
inverseJoinColumns = { #JoinColumn(name = "fonctionnalite_id") }
)
public Set<Fonctionnalite> fonctionnalites = new HashSet();
}
Fonctionnalite.java
#jakarta.persistence.Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#FieldDefaults(level = AccessLevel.PRIVATE)
#Table(name = "t_fonctionnalite")
public class Fonctionnalite {
...
...
#ManyToMany(mappedBy = "fonctionnalites", fetch = FetchType.LAZY)
private Set<Profil> profils = new HashSet();
I launch the project, the intermediate table is created: "t_profils_fonctionnalites"
I insert data into this table :
profil fonctionnalite
1 1
1 2
2 1
2 4
controller
...
...
List<Profil> profilList= profilDao.findAll();
profilList.forEach(profil -> { profil.getFonctionnalites(); });
return new ResponseEntity<>(profilList, HttpStatus.OK);
I get this data with postman :
[
{
"id": 1,
"code": "CODEP1",
"label": "defaut",
"description": "defaut description1",
"fonctionnalites": [] <--------- no fonctionnalites data
},
{
"id": 2,
"code": "CODEP2",
"label": "label2",
"description": "description2",
"fonctionnalites": [
{
"id": 1,
"code": "codeF1",
"label": "labelF1",
"description": "descriptionF1",
"profils": []
},
{
"id": 4,
"code": "codeF4",
"label": "labelF4",
"description": "descriptionF4",
"profils": []
}
]
},
"fonctionnalites for profil id = 1 is empty ! why ?
(2)
I put the fetch in EAGER on Profil :
Profil.java
#ManyToMany(cascade = {
CascadeType.ALL,
}, fetch = FetchType.EAGER)
#JoinTable(
name = "t_profil_fonctionnalite",
joinColumns = { #JoinColumn(name = "profil_id") },
inverseJoinColumns = { #JoinColumn(name = "fonctionnalite_id") }
)
public Set<Fonctionnalite> fonctionnalites = new HashSet();
This time I get different data, some profiles have features. sometimes just one when there are 2.
I do not understand what is going on. Thank you for your help
Why this code snippet is needed? profilList.forEach(profil -> { profil.getFonctionnalites(); });
Can you try this:
...
...
List<Profil> profilList= profilDao.findAll();
return new ResponseEntity<>(profilList, HttpStatus.OK);

Jackson Ignor From Another Java Class

I want the Employee object to only display the id property when adding a CV. When I put #JsonIgnore on the employee object in the CV, the id doesn't come either. How can I hide property that I do not want to appear in employee class (password, national identity) on the CV class?
Cv Class :
#Entity
#Table(name = "cvs")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CV {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#OneToOne
#JoinColumn(name = "employee_id")
private Employee employee;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Education> educations;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Work> works;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Languege> langueges;
#OneToMany(mappedBy = "cv", cascade = CascadeType.ALL, orphanRemoval = true)
List<Technology> technologies;
#Column(name = "github")
private String github;
#Column(name = "linkedin")
private String linkedin;
#NotNull
#NotBlank
#Column(name = "cover_letter")
private String coverLetter;
#Column(name = "photo")
private String photo;
}
Employee Class :
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "employees")
#Entity
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "id")
public class Employee extends User {
#NotNull
#NotBlank
#Column(name = "first_name", length = 50)
private String firstName;
#NotNull
#NotBlank
#Column(name = "last_name", length = 50)
private String lastName;
#NotNull
#NotBlank
#Column(name = "national_identity", length = 11)
private String nationalIdentity;
#NotNull
#Column(name = "year_of_birth")
private int yearOfBirth;
#JsonIgnore
#OneToOne(mappedBy = "employee")
private CV cv;
}
I read data this format;
{
"id": 0,
"employee": {
"id": 0, //visible
"email": "string", //not visible
"password": "string", //not visible
"firstName": "string",
"lastName": "string",
"nationalIdentity": "string", //not visible
"yearOfBirth": 0 //not visible
},
"educations": [
{
"id": 0,
"schoolName": "string",
"department": "string",
"startingDate": "2022-02-11",
"graduationDate": "2022-02-11"
}
],
"works": [
{
"id": 0,
"workplace": "string",
"job": {
"id": 0,
"jobName": "string"
},
"startingDate": "2022-02-11",
"endDate": "2022-02-11"
}
],
"langueges": [
{
"id": 0,
"languege": "string",
"level": 0
}
],
"technologies": [
{
"id": 0,
"technology": "string"
}
],
"github": "string",
"linkedin": "string",
"coverLetter": "string",
"photo": "string"
}
You can use a custom serializer that you would use on your CV employee field : #JsonSerialize(using = CustomEmployeeSerializer.class)
public class CustomEmployeeSerializer extends SerializerBase<Employee> {
public CustomEmployeeSerializer() {
super(Employee.class, true);
}
#Override
public void serialize(Employee employee, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(employee.getId());
}

many to many spring jpa not returning all data in JSON

I have created a many to many relationship. It is returning the data correctly. However, I noticed that if the data exist in canUse section of the json and has been called before by another Player, it does not print the data again only the new values are printed.
JOSN
[
{
"id": 12,
"firstname": "asdfghjkl ",
"surname": "asda",
"canUse": [
{
"id": 0,
"techniqueName": "JAVA"
},
{
"id": 1,
"techniqueName": "PHP"
},
{
"id": 2,
"techniqueName": "PYTHON"
}
]
},
{
"id": 49,
"firstname": "asc",
"surname": "as",
"canUse": []
},
{
"id": 90,
"firstname": "LOL",
"surname": "LOL",
"canUse": []
}*,
{
"id": 91,
"firstname": "Kareem",
"surname": "LOL",
"canUse": [
1,
2,
{
"id": 3,
"techniqueName": "HASKELL"
}
]
},*
{
"id": 92,
"firstname": "Omar",
"surname": "LOL",
"canUse": []
}
]
Player entity
#Entity
#Table(name = "players")
#EntityListeners(AuditingEntityListener.class)
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Players {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="player_id")
private int id;
private String firstname;
private String surname;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "able_to_use",
joinColumns = {#JoinColumn(name = "player_id", referencedColumnName = "player_id")},
inverseJoinColumns = {#JoinColumn(name = "technique_id", referencedColumnName = "id")}
)
private List<Technique> canUse;
//Getters and Setters
Technique entity
#Entity
#Table(name = "technique")
#EntityListeners(AuditingEntityListener.class)
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Technique {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "technique_name")
private String techniqueName;
#JsonIgnore
#ManyToMany(mappedBy = "canUse", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Players> ableToUse;
//Getters and Setters
Thank you in advance, the problem is occurring for player with ID 91. this player should also print PHP and PYTHON but only the ID is printed.

How to combine two lists from the same relation in Java-8

I have below entities Manager and Colleague
Manager Entity
#Entity
#Table(name = "Manager")
#Data
public class Manager implements java.io.Serializable {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany
#JoinColumn(name = "id")
private List<Colleague> colleagues;
}
Colleague Entity
#Entity
#Table(name = "Colleague")
#Data
public class Colleague implements java.io.Serializable {
#Id
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
}
Above relation can be represented in JSON as
[
{
"id": "101",
"name": "manager1",
"colleagues": [
{
"id": "101",
"name": "colleague1"
},
{
"id": "101",
"name": "colleague2"
}
]
},
{
"id": "101",
"name": "manager2",
"colleagues": [
{
"id": "101",
"name": "colleague3"
},
{
"id": "101",
"name": "colleague4"
}
]
}
]
I am retrieving the result on managerReposiotry.findAll() as
List<Manager> managerList = managerReposiotry.findAll();
I want to create a super list of all names from Manager and Colleague
What I am currently doing is
List<String> names = new ArrayList<>();
managerList.stream()
.forEach(manager -> {
List<String> nameList =
manager.getColleagues().stream()
.map(colleague -> colleague.getName())
.collect(Collectors.toList());
names.addAll(nameList);
}
);
Is there any other way in Java-8 to improve the above code?
Thank you !!
You can use flatMap to flatten all Colleague of Manager then map Colleague name only and collect as list.
List<String> names =
managerList.stream() // ...Stream<Manager>
.flatMap(m -> m.getColleagues().stream()) // ...Stream<Colleague>
.map(c-> c.getName()) // ...Stream<String>
.collect(Collectors.toList());
But the better way is directly fetched from the database if all colleagues have manager.
#Query("select c.name from Colleagues c")
List<String> findAllColleagueName();

Hibernate mapping list inside list

I have 2 tables email_address and group_email
I want to get a list of email addresses and if it is group also list the emails of group. Here is my hibernate dom object:
#Entity
#Table(name = "email_address")
public class EmailAddressDom {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "email_address_id_seq")
#SequenceGenerator(name = "email_address_id_seq", sequenceName = "email_address_id_seq")
private int id;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "is_group")
private boolean isGroup;
#Column(name = "status")
private boolean status;
// Getters and setters
And here is the implementation how I am getting the list
public List<EmailAddressDom> getEmailAddresses() {
Session session = getCurrentSession();
Criteria criteria = session.createCriteria(EmailAddressDom.class);
List<EmailAddressDom> emailAddresses = criteria.list();
return emailAddresses;
}
And the result is
[
{
"id": 451,
"name": "HS",
"isGroup": true,
"status": true
},
{
"id": 452,
"name": "test4",
"email": "test4#mail.com",
"isGroup": false,
"status": true
},
{
"id": 450,
"name": "test3",
"email": "test3#mail.com",
"isGroup": false,
"status": true
},
{
"id": 402,
"name": "test2",
"email": "test2#mail.com",
"isGroup": false,
"status": true
},
{
"id": 401,
"name": "test1",
"email": "test1#mail.com",
"isGroup": false,
"status": true
}
]
I want to get result like this
[
{
"id":451,
"name":"HS",
"isGroup":true,
"status":true,
"groupEmails":[
{
"id":450,
"name":"test3",
"email":"test3#mail.com",
"isGroup":false,
"status":true
},
{
"id":452,
"name":"test4",
"email":"test4#mail.com",
"isGroup":false,
"status":true
}
]
},
{
"id":402,
"name":"test2",
"email":"test2#mail.com",
"isGroup":false,
"status":true
}
]
Which mapping should I use in EmailAddressDom entity?
Updated accordingly to comments
I have written a simple application mapping following way and it is working fine
#ManyToMany(fetch= FetchType.EAGER, cascade={CascadeType.ALL})
#JoinTable(name="group_email",
joinColumns={#JoinColumn(name="group_id")},
inverseJoinColumns={#JoinColumn(name="email_addr_id")})
private List<EmailAddressDom> groupEmails;
But I have confused setting it in my web application, it throws following exception:
Association has already been visited: AssociationKey(table=group_email, columns={email_addr_id})
What can be the reason?
Either Do Eager loading
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "group_email",
joinColumns = #JoinColumn(name = "email_addr_id", referencedColumnName = "id"),
inverseJoinColumns=#JoinColumn(name="group_id", referencedColumnName="id" ))
private List<EmailAddressDom> emailAddressDoms;
OR
//explicitly join in criteria
public List<EmailAddressDom> getEmailAddresses() {
Session session = getCurrentSession();
Criteria criteria = session.createCriteria(EmailAddressDom.class);
criteria.setFetchMode("emailAddressDoms", fetchMode.JOIN);
List<EmailAddressDom> emailAddresses = criteria.list();
return emailAddresses;
}
To achieve self join, in your EmailAddressDom class add reference to itself like:
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "group_email",
joinColumns = #JoinColumn(name = "email_addr_id", referencedColumnName = "id"),
inverseJoinColumns=#JoinColumn(name="group_id", referencedColumnName="id" ))
private List<EmailAddressDom> emailAddressDoms;
In group_email table you do not require id column as email_addr_id and group_id combination would be unique or do you allow same email more than once in a group?
Update after comment from OP:
To group by email groups, change the mapping like shown below.
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "group_email",
joinColumns = #JoinColumn(name="group_id", referencedColumnName="id" ),
inverseJoinColumns=#JoinColumn(name = "email_addr_id", referencedColumnName = "id"))
private List<EmailAddressDom> emailAddressDoms;

Categories