I got two classes:
package com.educationalcenter.demo.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.*;
import lombok.experimental.FieldDefaults;
import javax.persistence.*;
import java.util.List;
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Setter
#Getter
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
#Table(name = "bankworker")
public class Bankworker {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long bankworkerId;
#Column(nullable = false)
String Fname;
#Column(nullable = false)
String Lname;
#Column(nullable = false, unique = true)
String Login;
#Column(nullable = false)
String Password;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "branch_id", referencedColumnName = "branchId")
#JsonManagedReference
Branch branch;
#OneToOne(mappedBy = "managerId")
#JsonBackReference
Branch branchOneToOne;
#OneToMany(mappedBy = "creditorId")
#JsonBackReference
List<CreditRequest> creditRequests;
}
Bankworker enitity must show: bankworker_id, Fname, Lname, Login, Password, branch_id
package com.educationalcenter.demo.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.*;
import lombok.experimental.FieldDefaults;
import javax.persistence.*;
import java.util.List;
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Setter
#Getter
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
#Table(name = "branch")
public class Branch {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long branchId;
#Column(nullable = false, unique = true)
String address;
#Column(nullable = false)
String city;
String country;
#OneToOne
#JoinColumn(name = "manager_id", referencedColumnName = "bankworkerId", unique = true)
#JsonManagedReference
Bankworker managerId;
#OneToMany(mappedBy = "branch")
#JsonBackReference
List<Bankworker> bankworkers;
}
Branch entity must show: branch_id, address, city, country, manager_id.
So, when I try to get with jpa findAll() methods all branches or bankworkers, it gives this:
[
{
"bankworkerId": 1,
"branch": {
"branchId": 1,
"address": "Lev Tolstoy 3, 17",
"city": "Bishkek",
"country": "Kyrgyzstan",
"managerId": {
"bankworkerId": 1,
"branch": {
"branchId": 1,
"address": "Lev Tolstoy 3, 17",
"city": "Bishkek",
"country": "Kyrgyzstan",
"managerId": {
"bankworkerId": 1,
"branch": {
"branchId": 1,
"address": "Lev Tolstoy 3, 17",
"city": "Bishkek",
"country": "Kyrgyzstan",
"managerId": {
"bankworkerId": 1,
etc.
In short, it calls data about itself, so it never stops.
I'd like to know how I can handle this without sacrificing any fields
Related
For example: I want to find list book and count categories > 3. How to resolve it and don't use annotation Query in interface Repository?
package com.web.dacn.entity.book;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.web.dacn.entity.user.Author;
import com.web.dacn.entity.user.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Entity
#Table(name = "book")
#AllArgsConstructor
#NoArgsConstructor
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name",
nullable = false,
columnDefinition = "NVARCHAR(100)")
private String name;
#Column(name = "thumbnail", columnDefinition = "TEXT")
private String thumbnail;
#Column(name = "view",
columnDefinition = "INTEGER DEFAULT 1",
nullable = false)
private Integer view;
#Column(name = "price")
private Double price;
#Column(name = "vip")
private Boolean vip;
#Column(name = "description",
columnDefinition = "TEXT",
nullable = false)
private String description;
#Column(name = "slug",
columnDefinition = "VARCHAR(2000)",
nullable = false)
private String slug;
#Column(name = "meta_title",
columnDefinition = "NVARCHAR(100)")
private String metaTitle;
#Column(name = "meta_description", columnDefinition = "TEXT")
private String metaDescription;
#Column(name = "status",
nullable = false,
columnDefinition = "INTEGER DEFAULT 1")
private Integer status;
#Column(name = "mod_time", nullable = false)
private Date mod_time;
#ManyToOne(cascade = CascadeType.ALL, targetEntity = User.class)
#JoinColumn(name="mod_user_id")
#JsonIgnore
private User user;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "book_author",
joinColumns = #JoinColumn(name = "book_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "author_id", nullable = false))
#JsonIgnore
private List<Author> authors = new ArrayList<>();
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "book_bookcategory",
joinColumns = #JoinColumn(name = "book_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "category_id", nullable = false))
#JsonIgnore
private List<BookCategory> categories = new ArrayList<>();
}
I am not sure there is another way to do it apart from using custom query. Instead, you can use stream api by doing this:
List<Book> books = repository.findAll();
List<Book> booksWithCategories = books.stream()
.filter(book -> book.getCategories().size() > 3)
.collect(Collectors.toList());
I tried to test the API, but I get a 404 status. Any advice?
After testing:
{
"timestamp": "2022-03-29T01:50:58.126+00:00",
"status": 404,
"error": "Not Found",
"path": "/api/v1/users"
}
I'm not sure if the problem is in the database itself or something went wrong with my code for the controller?
User Table:
package com.infosys.models;
import com.infosys.role_type.RoleType;
import lombok.Data;
import javax.persistence.*;
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "email" )
private String email;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne
#JoinColumn(name = "role_id", nullable = false)
private RoleType role;
}
Role Table
package com.infosys.models;
import com.infosys.role_type.RoleType;
import lombok.Data;
import javax.persistence.*;
#Entity
#Table(name="role")
#Data
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "role_id")
private RoleType role_type;
}
Employee Table:
package com.infosys.models;
import com.infosys.role_type.RoleType;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalTime;
import java.util.*;
#Entity
#Table(name="employee")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "role_id", nullable = false)
private RoleType role;
#ManyToOne
#JoinTable(name = "employee_has_manager",
joinColumns = #JoinColumn(name = "employee_id"),
inverseJoinColumns = #JoinColumn(name = "manager_id")
)
private Set<Manager> manager = new HashSet<>();
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User users;
#Column(name = "start_time")
private LocalTime startTime;
#Column(name = "end_time")
private LocalTime endTime;
#Column(name = "total_hrs")
private Long totalHrs;
}
Manager Table:
package com.infosys.models;
import com.infosys.role_type.RoleType;
import lombok.Data;
import javax.persistence.*;
import javax.persistence.Table;
import java.util.*;
#Entity
#Table(name="manager")
#Data
public class Manager {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User users;
#ManyToOne
#JoinColumn(name = "role_id", nullable = false)
private RoleType role;
#OneToMany(mappedBy = "manager")
private Set<Employee> employee = new HashSet<>();
#Column(name = "employee_review")
private String employeeReview;
}
User Repository:
package com.infosys.repositories;
import com.infosys.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
User Controller:
package com.infosys.controllers;
import com.infosys.models.User;
import com.infosys.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
#RestController
#RequestMapping("/api/v1/")
public class UserController {
#Autowired
private UserRepository userRepository;
// get all users
#GetMapping("/users")
public String getAllUsers() {
return "Test";
}
}
Spring App:
package com.infosys;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
#ComponentScan("com.infosys.repositories")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have three entities namely
product
product details
stock
category
reference is given below
when I try to get the details of product it works fine when I try to save it shows below error in the console
2020-08-12 13:17:22.279 WARN 18612 --- [nio-9002-exec-1] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.eMart.main.entity.Product]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot handle managed/back reference 'defaultReference': back reference type (java.util.List) not compatible with managed type (com.eMart.main.entity.Product)
My question is how to add the product into the database and how did I need to optimize my entities
Input
{
"skuId": "2",
"category": {
"categoryId": 2,
"categoryName": "food"
},
"description": "cow milk",
"stock": {
"stockId": 1,
"inventoryCount": 5,
"selfCount": 5
},
"productDetails": [
{
"productDetailId": 1,
"cost": 10.0,
"currency": "inr",
"expiryDate": "2020-08-11T18:30:00.000+00:00",
"supplierCode": 1
}
]
}
controller method
#PostMapping(value = "/test")
public ResponseEntity<Product> test(#RequestBody Product product) throws Exception {
productRepositry.save(product);
return new ResponseEntity(productRepositry.findAll(),OK);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class Product {
#Id
#Column(name = "SKU_ID")
String skuId=null;
#ManyToOne
#JoinColumn(name = "category_id")
#JsonManagedReference
Category category;
#Column(name = "description")
String description=null;
#JsonManagedReference
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "stock_id", referencedColumnName = "id")
Stock stock=null;
#JsonManagedReference
#OneToMany(mappedBy = "product", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
Set<ProductDetails> productDetails;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class Stock {
#Id
#Column(name = "id")
Integer stockId;
#Column(name = "inventory_count")
Integer inventoryCount;
#Column(name = "self_count")
Integer selfCount;
#JsonBackReference
#OneToOne(mappedBy = "stock",cascade = CascadeType.ALL)
Product product;
}
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Category {
#Id
#Column(name = "category_id")
Integer categoryId;
#Column(name = "category_name")
String categoryName;
#OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
#JsonBackReference
List<Product> product;
#Override
public String toString() {
return "Category{" +
"categoryId=" + categoryId +
", categoryName='" + categoryName + '\'' +
", product=" + product +
'}';
}
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class ProductDetails {
#Id
#Column(name = "id")
Integer productDetailId;
#Column(name = "cost")
Double cost;
#Column(name = "currency")
String currency;
#Column(name = "expiry_date")
Date expiryDate;
Integer supplierCode;
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
Product product;
}
I think you have missed adding #JoinColumn on Product in ProductDetails entity
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SKU_ID")
Product product;
Also, you can try be removing #JsonManagedReference from Category or Stock
I am trying to retrieve all Many To One relationship data from Postgresql database with Rest Controller with foreign key data. Like for Course and Instructor :
{
{
"id": 1,
"course_name": "IT 101",
"instructor":{
"instructor_name":"Jack King"
}
},
{
"id": 2,
"course_name": "CS 101",
"instructor":{
"instructor_name":"Homer Love"
}
},
{
"id": 3,
"course_name": "DB 101",
"instructor":{
"instructor_name":"Jack King"
}
}
}
My Code as follows:
Database
create table instructor
(
id integer generated always as identity,
instructor_name text,
primary key (instructor_name),
unique (id)
);
create table course
(
id integer generated always as identity,
course_name text not null,
instructor_fk integer references instructor (id),
primary key (course_name, instructor_fk),
unique (id)
);
insert into instructor (instructor_name) values ('Jack King');
insert into instructor (instructor_name) values ('Homer Love');
insert into author (course_name, instructor_fk) values ('IT 101', 1, 1);
insert into author (course_name, instructor_fk) values ('CS 101', 2, 2);
insert into author (course_name, instructor_fk) values ('DB 101', 3, 1);
Entities
I am using Project Lombok annotations so instead of constractions, getters and setters for less boilerplate code.
Course
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
#Entity
#Table(name = "course")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String course_name;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} )
#JsonIgnore
#JoinColumn(name = "instructor_fk", nullable = false)
private Instructor instructor;
}
Instructor
#Entity
#Table(name = "instructor")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Instructor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(unique = true)
private String instructor_name;
#OneToMany(
mappedBy = "instructor",
cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
)
private List<Course> courses;
}
Repository
#Repository
public interface CourseRepository extends CrudRepository<Author, Integer> {
}
Service
#Service
public class CourseService {
#Autowired
private CourseRepository courseRepository;
public List<Author> getAllCourses() {
return (List<Author>) courseRepository.findAll();
}
}
Controller
#RestController
public class CourseController {
#Autowired
private CourseService courseService;
#GetMapping("/courses")
private List<Author> getAllCourses() {
return courseService.getAllCourses();
}
}
What I get:
{
{
"id": 1,
"course_name": "IT 101"
},
{
"id": 2,
"course_name": "CS 101"
},
{
"id": 3,
"course_name": "DB 101"
}
}
#JsonIgnore annotation should be on One To Many relation, which means it should be on Instructor entity not on Course entity:
Course
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
#Entity
#Table(name = "course")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String course_name;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} )
#JoinColumn(name = "instructor_fk", nullable = false)
private Instructor instructor;
}
Instructor
#Entity
#Table(name = "instructor")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Instructor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(unique = true)
private String instructor_name;
#OneToMany(
mappedBy = "instructor",
cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}
)
#JsonIgnore
private List<Course> courses;
}
below is my 2 entities:
package dev.proj.project.application.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "user")
public class User {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
public int id;
#NotNull
#Column(name="firstname")
private String firstname;
#NotNull
#Column(name="lastname")
private String lastname;
#NotNull
#Column(name="email")
private String email;
#NotNull
#Column(name="password")
private String password;
#JsonIgnore
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Address> address;
}
package dev.proj.project.application.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "address")
public class Address {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private int id;
#NotNull
#Column(name="street")
private String street;
#NotNull
#Column(name="house")
private String house;
#NotNull
#Column(name="flat")
private String flat;
#NotNull
#Column(name="code")
private String code;
#NotNull
#Column(name="city")
private String city;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id",nullable = false,updatable = true,insertable = true)
private User user;
}
I want to create User->Address One-to-many relationship, but if I made post request to /address:
{
"street": "ulicanewnew",
"house": "5",
"flat": "2",
"code": "20-001",
"city": "warsaw",
"user_id": 1
}
user_id is null in my address table:
Why foreign key in address table is always null?
Do you see what is wrong here?
With direct sql inserts to db - everything works fine
I changed request to:
{
"street": "dyr",
"house": "5",
"flat": "2",
"code": "20-001",
"city": "warsaw",
"user": {
"id": 50
}
}
and it works, but question for now: how to use
"user_id" : 50
in my request, instead of
"user": {
"id": 50
}
?
Try adding below changes to your address entity.
#JoinColumn(name = "user_id")
#ManyToOne(targetEntity = User.class, fetch = FetchType.LAZY)
private User user;
import java.util.List;
import java.util.Set;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
#Entity
#Table(name = "customer")
public class Customer {
...
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<DatabaseTable> databaseTables;
...
}
...
...
import org.hibernate.validator.constraints.Length;
import javax.persistence.*;
#Entity
#Table(name = "databasetable")
public class DatabaseTable {
...
#ManyToOne
#JoinColumn(name = "user_idFK")
private Customer customer;
....
}
Something like that works for me.