Hibernate mapping list inside list - java

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;

Related

One-Many Relationship in Jpa

I want to get data from my entity with 1-M relationships.
Users have an entity for cv information.With JpaRepo,
Cv class :
#Entity
#Table(name = "cvs")
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "educations", "works", "langueges", "technologies"})
public class CV {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
//ToDo : Employee bilgilerinin görünmesi problemi giderilecek.
#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;
}
This is Education class (work, languege, technology classes same):
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "cv_educations")
public class Education {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#NotNull
#NotBlank
#Column(name = "school_name")
private String schoolName;
#NotNull
#NotBlank
#Column(name = "department")
private String department;
#NotNull
#NotBlank
#PastOrPresent
#Column(name = "starting_date")
#DateTimeFormat(pattern = "yyyy-mm-dd")
private LocalDate startingDate;
#NotBlank
#Column(name = "graduation_date")
#DateTimeFormat(pattern = "yyyy-mm-dd")
private LocalDate graduationDate;
#ManyToOne
#JoinColumn(name = "cv_id")
private CV cv;
}
I tried to build the following structure with jpa, but the constructer takes list parameter. I got an error because I couldn't write it with jpql
public interface CvRepository extends JpaRepository<CV, Integer> {
#Query("select new com.demo.humanresourcesmanagementsystem.Entities.concretes.CV" +
"(employee.firstName, employee.lastName, cv.github, cv.linkedin, cv.coverLetter," +
"educations, works, langueges, technologies)" +
"from CV cv inner join cv.employee employee inner join cv.educations educations " +
"inner join cv.works works inner join cv.langueges langueges " +
"inner join cv.technologies technologies where cv.employee.id =:employeeId")
CV findByCv(int employeeId);
}
I'd like to read about the educations, works, langueges and technologies in this entity. This means that there will be one cv as output, but there may be more than one education object within the cv (such as primary school, high school), and the incoming data will be in the following format, for example:
"firstName": "X",
"lastName" : "X",
"educations" : [
"education1" {
"school" : "x",
"department" : "x" ...},
"education2" {
"school" : "x",
"department" : "x"...}
"works" : [
"work1" {
"workplace" : "x",
"job" : "x" ...
}
]
"github" : "x",
"linkedin" : "x"
How do i set up this structure with the jpa repository? What kind of dto should I write if I'm gonna use it? Thanks.
UPDATE
When i use spring jpa derivered query (findByEmployeeId) i receive data with this format :
{
"success": true,
"message": "string",
"data": {
"id": 0,
"employee": {
"id": 0,
"email": "string",
"password": "string",
"firstName": "string",
"lastName": "string",
"nationalIdentity": "string",
"yearOfBirth": 0
},
"github": "string",
"linkedin": "string",
"coverLetter": "string",
"photo": "string"
}
}
So i cant receive data for education, work, languege and technology.
It seems you're trying to retrieve a CV by its employer.id. In that case, you can really just use the JPA query method containing keywords. In this case it would look like:
CV findByEmployeeId(int employeeId);
This should return the complete CV object as you would expect.
See here for more details on JPA query method keywords.

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 show specific field use ebean relation

...
is my code
#ManyToOne
#JoinColumn(name = "destination_country_id", referencedColumnName = "id", table = "countries", insertable = false, updatable = false)
public Country country;
...
is the result
"id": 139,
"country": {
"id": 1,
"iso": "AU",
"name": "Australia",
},
"country_id": 1
...
I hope the result is
"id": 139,
"country": "australia",
"country_id": 1,
...
add this code and change to private in var country
#Column(name = "name", table = "countries")
public String country_name;
this is a new my code
#ManyToOne
#JoinColumn(name = "destination_country_id", referencedColumnName = "id", table = "countries", insertable = false, updatable = false)
private Country country;
#Column(name = "name", table = "countries")
public String country_name;

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

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)

JSON calls object recursively when posting it

I'm testing my app via Swagger and when trying to post an object, which contains a list of other objects, I get an error java.lang.StackOverflowError. Basically, I'm trying to write an invoice, represented as JSON in Swagger form, and while filling in the data for my invoice I've noticed that invoice item contains the duplicated fields for invoice.
{
"date": "2017-08-27",
"counterparty": {
"address": {
"houseNumber": "string",
"streetName": "string",
"townName": "string",
"zipCode": "string"
},
"bankName": "string",
"bankNumber": "string",
"companyName": "string",
"nip": "string",
"phoneNumber": "string"
},
"invoiceItems": [
{
"amount": 0,
"description": "string",
"id": 0,
"invoice": { //SHOULD NOT BE HERE
"id": 0,
"date": "2017-09-07",
"counterparty": {
"address": {
"houseNumber": "string",
"streetName": "string",
"townName": "string",
"zipCode": "string"
},
"bankName": "string",
"bankNumber": "string",
"companyName": "string",
"nip": "string",
"phoneNumber": "string"
},
"invoiceItems": [
{}
]
},
"numberOfItems": 0,
"vat": "VAT_23",
"vatAmount": 0
}
]
}
#Entity
#Table(name = "invoices")
public class Invoice implements Comparable<Invoice> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private int id;
#Column(name = "date")
private LocalDate date = LocalDate.now();
#OneToOne(mappedBy = "invoice", cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.PERSIST, CascadeType.REFRESH})
private Counterparty counterparty;
#OneToMany(mappedBy = "invoice", cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
private List<InvoiceItem> invoiceItems = new ArrayList<>();
#Entity
#Table(name = "items")
public class InvoiceItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private int id;
private String description;
private int numberOfItems;
private BigDecimal amount;
private BigDecimal vatAmount;
#JoinColumn(name = "vat_code")
#Enumerated(EnumType.ORDINAL)
private Vat vat;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name = "invoice_id")
private Invoice invoice;
I have the very same mapping with Counterparty, but it doesn't show me invoice data in JSON:
#Id
private String nip;
private String companyName;
private String phoneNumber;
private String bankName;
private String bankNumber;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "counterparty", cascade = CascadeType.ALL)
private Address address;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name = "invoice_id")
private Invoice invoice;
Maybe there are some issues with my annotations that invoice is being called recursively?
You should mark your Invoice property on InvoiceItems with the #JsonIgnore annotation which means it won't be serialized into JSON when the InvoiceItems is serialized which should prevent your infinitely recursive JSON issue:
#Entity
#Table(name = "items")
public class InvoiceItem {
...
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name = "invoice_id")
#JsonIgnore
private Invoice invoice;

Categories