Circular reference in entity JPA with Jackson and Spring Boot - java

I have two web services: "Proprietario" and "Veiculo", the "Proprietario" contains a list of "Veiculo" and "Veiculo" contains a "Proprietario".
The problem is that when I make a request calling the findAll method of "Proprietario", when trying to serialize, Jackson goes into infinite loop throwing exception. The same happens when I try to call the findAll method of "Veiculo".
I would like it when I call you to call the findAll of the "Veiculo", bring along the "Proprietario", but do not bring the "Veiculo" list inside the "Proprietario". The opposite of when I call the findAll method of "Proprietario", I'd like to bring the "Veiculo" list, but do not bring the "Proprietario" into the "Veiculo".
I tried to use some Jackson annotations, but none solves the conflict on both sides.
#Getter
#Setter
#Entity
#EqualsAndHashCode(of = "id")
public class Veiculo {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 10)
private String placa;
#Column(nullable = false)
private Integer ano;
#ManyToOne
#JoinColumn
private Proprietario proprietario;
}
#Getter
#Setter
#Entity
#EqualsAndHashCode(of = "id")
public class Veiculo {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 10)
private String placa;
#Column(nullable = false)
private Integer ano;
#ManyToOne
#JoinColumn
private Proprietario proprietario;
}

Try using these two annotations
#JsonManagedReference and #JsonBackReference
see http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

Related

Spring REST + JPA + h2: How to instantiate dummy data for a many to many relationship

I'm trying to build a little project-management tool as my first Spring Boot / JPA / H2 / REST Application using lombok annotations for avoiding boilerplate-code. I followed several promising tutorials. But I'm failing at the very end, when I try to intantiate some dummy data to test the database and start the service.
Till now it had two tables: "T_PROJECT" & "T_EMPLOYEE"
But I also want to be able to visualize, in which period an employee works for a specific project. So I need a third table "T_EMPLOYEE_ACTIVITY" with two extra columns: "START_DATE" & END_DATE".
I made an
ER-Diagram that should help to understand how these tables must work together.
I found already this one here:
JPA 2.0 many-to-many with extra column
... and tried to build it the same way:
The Project entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_PROJECT")
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROJECT_ID")
private Long id;
private String name;
#Column(name = "START_DATE")
private String startDate;
#Column(name = "END_DATE")
private String endDate;
private Status status;
#OneToMany(mappedBy = "project")
#Column(name = "EMPLOYEE_ACTIVITIES")
private Set<EmployeeActivity> employeeActivities;
}
The Employee entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_EMPLOYEE")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "EMPLOYEE_ID")
private Long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
private String role;
#Column(name = "HOURS_PER_WEEK")
private BigDecimal hoursPerWeek;
#OneToMany(mappedBy = "employee")
#Column(name = "EMPLOYEE_ACTIVITIES")
private Set<EmployeeActivity> employeeActivities;
}
EmployeeActivity entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "T_EMPLOYEE_ACTIVITY")
public class EmployeeActivity implements Serializable {
#Id
#Column(name = "EMPLOYEE_ACTIVITY_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "PROJECT_ID")
private Project project;
#ManyToOne
#JoinColumn(name = "EMPLOYEE_ID")
private Employee employee;
#Column(name = "START_DATE")
private String startDate;
#Column(name = "END_DATE")
private String endDate;
}
In the Application.java (with main & run method), I tried to intantiate it like this and failed:
#Override
public void run(String... args) throws Exception {
Project project = new Project(null, "BERLIN_AIRPORT", "2006-05-01", "2020-10-31", Status.COMPLETED, Set.of( ??? ));
projectRepository.save(project);
Employee employee = new Employee(null, "Jim", "Beam", "Architect", BigDecimal.valueOf(40d), Set.of( ??? ));
employeeRepository.save(employee);
EmployeeActivity employeeActivity = new EmployeeActivity();
employeeActivity.setProject(project);
employeeActivity.setEmployee(employee);
employeeActivity.setStartDate("2006.05.01");
employeeActivity.setEndDate("2010.12.12");
employeeActivityRepository.save(employeeActivity);
}
So both - Project and Employee - have an attribute "employeeActivities", that needs some value, when I make a new Object.
But at this point, there is no EmployeeActivity-Object that i could use.
How do I manage this?
Thanks a lot & have nice day!
NicerDicer
I meanwhile found the solution. The problem was, that I tried to use the AllArgsConstructor that has been generated via lombok.
The AllArgsConstructor expects of course all attributes that I declared in the entitties.
The solution is to use setters (in my case auto-generated by the lombok #Data annotation) and to not set the id and employeeActivities from project & employee.
(Alternatively you can of course write your own constructor.)
#Override
public void run(String... args) throws Exception {
Project project_1 = new Project();
project_1.setName("BERLIN_AIRPORT");
project_1.setStartDate("2006-05-01");
project_1.setEndDate("2020-10-31");
project_1.setStatus(Status.COMPLETED);
projectRepository.save(project_1);
Employee employee_1 = new Employee();
employee_1.setFirstName("Jim");
employee_1.setLastName("Beam");
employee_1.setRole("Architect");
employee_1.setHoursPerWeek(BigDecimal.valueOf(40d));
employeeRepository.save(employee_1);
EmployeeActivity employeeActivity = new EmployeeActivity();
employeeActivity.setProject(project_1);
employeeActivity.setEmployee(employee_1);
employeeActivity.setStartDate("2019-05-01");
employeeActivity.setEndDate("2022-12-12");
employeeActivityRepository.save(employeeActivity);
}

Spring Boot: Could not locate field name [companyId] on class [...Entity]

I have a question:
Here is my entity:
public class CompanyBindingEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "BINDING_ID", nullable = false)
private int companyBindingId;
#NotNull
#Embedded
#Valid
private CompanyEntity company;
....
}
Here is CompanyEntity:
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
#Builder
#Data
public class CompanyEntity {
#Id
#NotNull
#Column(name = "COMPANY_ID")
private Integer companyId;
#NotNull
#Column(name = "COMPANY_NAME")
private String companyName;
}
And I want to implement
findByCompanyId(int companyId)
method in my service and repository. But I am receiving this error:
Could not locate field name [companyId] on class [...Entity]
because the companyId is inside CompanyEntity, not in CompanyBindingEntity. I need to find a way about how to solve this. Am I missing a special annotation to search for a nested element?
Thanks a lot for reading!
First, you need to define an association between CompanyBindingEntity and CompanyEntity, for example:
#NotNull
#Embedded
#Valid
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetchType = FetchType.EAGER)
#JoinColumn(name = "COMPANY_ID")
private CompanyEntity company;
Then in the table that holds CompanyBindingEntity define a foreign key to bind "COMPANY_ID" to the ID for CompanyEntity.

How do I properly update tables in a One to Many Hibernate relationship (by one request)?

I am using Postgresql via Hibernate, there are three tables: users, products, user_products. Here are their mappings
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
#NotBlank
private String name;
#OneToMany(cascade = CascadeType.ALL)
private List<Products> product;
}
#Entity
public class Product{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
#NotBlank
private String name;
#Column(nullable = false)
private Integer price;
}
i know that i can get user by id, then update its field "products" and than save user back. But is it possible to do all this stuff by one request via Hibernate (or using raw sql query)?
I would create a separate Entity UserProduct and save it.
Is there some reason this won't work?

Multi-level sub-menu JSON in Spring boot

I have a some records in the table having parent child relations, screenshot below:
How do I write a JPA Entity to retrieve those records with respect to Parent-Child relation. Your help is appreciated.
The code that did not help me well is as below:
#Entity
#Table(name = PlatformConstant.TABLE_MENU)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "url", nullable = false)
private String url;
#Column(name = "description")
private String description;
#Column(name = "qr_code")
private Blob qrCode;
#JsonManagedReference
#OneToMany(mappedBy = "parent")
private Set<Menu> children;
#ManyToOne
#JsonBackReference
private Menu parent;
}
My code above has the following wrong output:
Using the JpaRepository find all, and applying the answer from #lucid, the new output is as below:
the code:
#Autowired
private MenuService menuService;
#CrossOrigin
#GetMapping("/all")
#ResponseBody
public List<Menu> getMenus() {
return (List<Menu>) menuService.findAll().stream()
.filter (menu-> Objects.isNull(menu.getParent()).collect(Collectors.toList()));
}
the output:
Thank you.
Jackson provides these annotations to control the parent-child relationships.
#JsonBackReference: skips property during the serialization process
#JsonManagedReference: forward reference and serialized annotated property
In your case, you don't want parent object to be serialized inside your child reference, you can annotate it with #JsonBackReference
#JsonManagedReference
#OneToMany(mappedBy = "parent")
private Set<Menu> children;
#ManyToOne
#JsonBackReference
private Menu parent;
Now, to remove child objects from response, we can filter that
Like this
menuService.findAll().stream()
.filter(menu-> Objects.isNull(menu.getParent()))
.collect(Collectors.toList());

How can i get object while sending as json from Spring MVC without infinity loops

So, i'm trying to get my Student Object to my androidClient from server, but i have ifinity loops. If i use #JsonBackReference/#JsonManagedReference or #JsonIgnore i won't get Object like in case of infinity loop, so i have a question how to do this? Here my classes:
Student.java
#Entity
#Table(name ="student")
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#GenericGenerator(name="increment", strategy = "increment")
private int id_student;
#Column(name = "studentName", nullable = false)
private String studentName;
#Column(name = "formOfAducation")
private String formOfAducation;
#ManyToOne
#JoinColumn(name ="id_group")
#JsonBackReference
private Group group;
#Column(name = "studentCourse")
private String studentCourse;
#Column(name = "studentSpecializatio")
private String studentSpecializatio;
#Column(name = "studentBookName")
private String studentBookName;
#Column(name = "studentGender")
private String studentGender;
public Student(){
}
//getter-setters
}
Group.java
#Entity
#Table(name = "party")
public class Group {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#GenericGenerator(name="increment", strategy = "increment")
private int id_group;
#Column(name = "groupName", nullable = false)
private String groupName;
#OneToMany(targetEntity = Student.class, mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonManagedReference
private List<Student> student;
public Group(){
}
On client i have the same classes, and when i'm trying to pass only id from group i cant deserialize it on client. Hope you'll help me. Or there's no way to do this, so how can i edit Student object from client?
There are two possible ways.
The first one is to create DTO objects and initialize them from the entities. Thus you manually stop on desired level without loops.
The second way is to unproxy the entities to break lazy collection loading which leads to the loops.

Categories