Not correct list #OneToMany and #ManyToOne Jpa - spring-boot

I am trying to list from child(Person/Person) to parent(Company/Company).
Previously it had infinite loop problems, but
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
fixed it.
Here is the code:
Empresa.class
package com.demo.clase7.Entity;
import com.fasterxml.jackson.annotation.*;
import com.sun.istack.NotNull;
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Set;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "empresa")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Empresa {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "razon_social")
private String razonSocial;
private String ruc, representante;
#Column(name = "fecha_creacion")
private Date fechaCreacion;
#OneToMany(mappedBy = "empresa", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Persona> personaList;
}
Persona.class
package com.demo.clase7.Entity;
import com.demo.clase7.Entity.Empresa;
import com.fasterxml.jackson.annotation.*;
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "persona")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Persona {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre, email, direccion, telefono;
#ManyToOne()
#JoinColumn(name = "empresa_id")
private Empresa empresa;
}
so when list Empresa (Company's), it gives me this result, which is correct
[
{
"id": 1,
"razonSocial": "oswaldo",
"ruc": "23aaasd2424",
"representante": "silviopd222",
"fechaCreacion": "1995-01-01T00:00:00.000+00:00",
"personaList": [
{
"id": 4,
"nombre": "oswal peña diaz",
"email": "pedi#gmail.com",
"direccion": "las viñas 149",
"telefono": "234982734",
"empresa": 1
},
{
"id": 5,
"nombre": "oswal peña diaz",
"email": "pedi#gmail.com",
"direccion": "las viñas 149",
"telefono": "234982734",
"empresa": 1
}
]
},
{
"id": 2,
"razonSocial": "oswaldo",
"ruc": "23aaasd2424",
"representante": null,
"fechaCreacion": null,
"personaList": []
},
{
"id": 4,
"razonSocial": "silviopd",
"ruc": "232424",
"representante": "silviopd2",
"fechaCreacion": "1992-01-01T00:00:00.000+00:00",
"personaList": []
}
]
so when I list Persona (people), it gives me this result which is wrong.
If you realize in the array it is made up of numbers
[
{
"id": 1,
"nombre": "oswal peña diaz",
"email": "pedi#gmail.com",
"direccion": "las viñas 149",
"telefono": "234982734",
"empresa": null
},
{
"id": 4,
"nombre": "oswal peña diaz",
"email": "pedi#gmail.com",
"direccion": "las viñas 149",
"telefono": "234982734",
"empresa": {
"id": 1,
"razonSocial": "oswaldo",
"ruc": "23aaasd2424",
"representante": "silviopd222",
"fechaCreacion": "1995-01-01T00:00:00.000+00:00",
"personaList": [
4,
{
"id": 5,
"nombre": "oswal peña diaz",
"email": "pedi#gmail.com",
"direccion": "las viñas 149",
"telefono": "234982734",
"empresa": 1
}
]
}
},
5
]
database
CREATE SCHEMA clase7;
CREATE TABLE clase7.empresa
(
id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
fecha_creacion datetime,
razon_social varchar(255),
representante varchar(255),
ruc varchar(255)
) ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARSET = utf8mb4;
CREATE TABLE clase7.persona
(
id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
direccion varchar(255),
email varchar(255),
nombre varchar(255),
telefono varchar(255),
empresa_id bigint,
id_persona bigint NOT NULL,
persona_list_id bigint
) ENGINE = InnoDB
AUTO_INCREMENT = 6
DEFAULT CHARSET = utf8mb4;
CREATE TABLE clase7.rol
(
id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
nombre varchar(255)
) ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARSET = utf8mb4;
CREATE TABLE clase7.usuario
(
id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
password varchar(255),
username varchar(255),
empleado_id bigint
) ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARSET = utf8mb4;
CREATE INDEX `FKrvl5v1dpvx13a46b6wxo0qx0j` ON clase7.persona (empresa_id);
CREATE INDEX `FKgot3jq0eng9lvyja174qoe1a5` ON clase7.persona (persona_list_id);
CREATE INDEX `FK44o5rsj3cs2hsw2gkg3056ivm` ON clase7.usuario (empleado_id);
CREATE INDEX `FK3hkcdmd3tnvqg683r4cf0mgpn` ON clase7.empresa_persona_list (empresa_id);
ALTER TABLE clase7.empresa_persona_list
ADD CONSTRAINT `FK3hkcdmd3tnvqg683r4cf0mgpn` FOREIGN KEY (empresa_id) REFERENCES clase7.empresa (id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE clase7.empresa_persona_list
ADD CONSTRAINT `FK90f46adsww2o9oq1gf4100wdd` FOREIGN KEY (persona_list_id) REFERENCES clase7.persona (id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE clase7.persona
ADD CONSTRAINT `FKgot3jq0eng9lvyja174qoe1a5` FOREIGN KEY (persona_list_id) REFERENCES clase7.empresa (id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE clase7.persona
ADD CONSTRAINT `FKrvl5v1dpvx13a46b6wxo0qx0j` FOREIGN KEY (empresa_id) REFERENCES clase7.empresa (id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE clase7.usuario
ADD CONSTRAINT `FK44o5rsj3cs2hsw2gkg3056ivm` FOREIGN KEY (empleado_id) REFERENCES clase7.persona (id) ON DELETE NO ACTION ON UPDATE NO ACTION;
INSERT INTO clase7.empresa(id, fecha_creacion, razon_social, representante, ruc)
VALUES (1, '1994-12-31', 'oswaldo', 'silviopd222', '23aaasd2424');
INSERT INTO clase7.empresa(id, fecha_creacion, razon_social, representante, ruc)
VALUES (2, null, 'oswaldo', null, '23aaasd2424');
INSERT INTO clase7.empresa(id, fecha_creacion, razon_social, representante, ruc)
VALUES (4, '1991-12-31', 'silviopd', 'silviopd2', '232424');
INSERT INTO clase7.persona(id, direccion, email, nombre, telefono, empresa_id, id_persona, persona_list_id)
VALUES (1, 'las viñas 149', 'pedi#gmail.com', 'oswal peña diaz', '234982734', null, 0, null);
INSERT INTO clase7.persona(id, direccion, email, nombre, telefono, empresa_id, id_persona, persona_list_id)
VALUES (4, 'las viñas 149', 'pedi#gmail.com', 'oswal peña diaz', '234982734', 1, 0, null);
INSERT INTO clase7.persona(id, direccion, email, nombre, telefono, empresa_id, id_persona, persona_list_id)
VALUES (5, 'las viñas 149', 'pedi#gmail.com', 'oswal peña diaz', '234982734', 1, 0, null);
INSERT INTO clase7.rol(id, nombre)
VALUES (1, 'silviopd2');
INSERT INTO clase7.rol(id, nombre)
VALUES (2, 'oswal');
INSERT INTO clase7.rol(id, nombre)
VALUES (4, 'silviopd2');
INSERT INTO clase7.usuario(id, password, username, empleado_id)
VALUES (1, '232424', 'oswal', null);
INSERT INTO clase7.usuario(id, password, username, empleado_id)
VALUES (2, '232424ss', 'silviopd2', 1);
INSERT INTO clase7.usuario(id, password, username, empleado_id)
VALUES (4, '232424ss', 'silviopd2', 4);

I found the solution, apparently the code
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
allowed me not to generate infinite loops, but when making the list from person it was not listed very well.
There is an infinite loop when listing by company which is generated by the "company" object found in the Persona.class and at the same time there is an infinite loop when listing person that is generated by the list "personList" that is in Empresa.class.
To solve both loops do the following:
For the Empresa class we add
#JsonIgnoreProperties("empresa")
For the Persona class we add
#JsonIgnoreProperties("personaList")
I leave here the final code
Entity
Persona.class
package com.demo.clase7.Entity;
import com.fasterxml.jackson.annotation.*;
import lombok.*;
import javax.persistence.*;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "persona")
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Persona {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre, email, direccion, telefono;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "empresa_id", referencedColumnName = "id")
#JsonIgnoreProperties("personaList")
private Empresa empresa;
}
Empresa.class
package com.demo.clase7.Entity;
import com.fasterxml.jackson.annotation.*;
import lombok.*;
import javax.persistence.*;
import java.util.*;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "empresa")
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Empresa {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "razon_social")
private String razonSocial;
private String ruc, representante;
#Column(name = "fecha_creacion")
private Date fechaCreacion;
#OneToMany(mappedBy = "empresa")
#JsonIgnoreProperties("empresa")
private List<Persona> personaList;
}

Related

Why OneToMany JPA association is failing while insert statement executes

Hi below is my schema definition
CREATE TABLE LOANS (
LOAN_ID NUMBER(9,0) PRIMARY KEY,
CORR_ID VARCHAR(5) NULL
);
CREATE TABLE DV_LOAN_PARTICIPANTS (
LOAN_ID NUMBER(9,0) ,
DVP_PARTICIPANT_NAME VARCHAR(50) NOT NULL,
DVP_PARTICIPANT_TYPE VARCHAR(50) NOT NULL,
PRIMARY KEY ("LOAN_ID", "DVP_PARTICIPANT_NAME")
);
LOANS Entity
#Table(name = "LOANS")
#Entity
public class Loans {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "LOAN_ID")
#MapKey(name = "dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
// getter and setters
}
DV_LOAN_PARTICIPANTS Entity
#Table(name = "DV_LOAN_PARTICIPANTS")
#Entity
public class DVLoanParticipants implements Serializable {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
// getters and setters
}
Service Class is
DVLoanParticipants dvLoanParticipants = new DVLoanParticipants();
dvLoanParticipants.setLoanId(Long.valueOf("196801758"));
dvLoanParticipants.setDvpParticipantName("VKP");
dvLoanParticipants.setDvpParticipantType("Developer");
Loans loanInsert = new Loans();
loanInsert.setLoanId(Long.valueOf("196801758"));
Map<String,DVLoanParticipants> partyMap = new HashMap<>();
partyMap.put("VKP",dvLoanParticipants);
loanInsert.setDvLoanParticipantsMap(partyMap);
repository.save(loanInsert);
But when i am executing the save i am getting error as
NULL not allowed for column "LOAN_ID"; SQL statement:
insert into dv_loan_participants (dvp_participant_type, loan_id, dvp_participant_name) values (?, ?,
?)
Git Hub Code
https://github.com/vinoykp/spring-jpa/tree/master/spring-boot-hibernate-crud-demo
I had the similar question
Why Value is not getting assigned in JPA for insert statement
What is the issue in association?

JPA #ForeignKey(value = ConstraintMode.NO_CONSTRAINT) not working with #ManyToMany

I have two entities with ManyToMany Relationship. Goal here is to create schema when application start with no foreign key
1). Job.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "job")
public class Job extends BasicEntity {
#Column(name = "icon")
private String icon;
#ManyToMany
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id"),
inverseJoinColumns = #JoinColumn(name = "city_id"),
foreignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT),
inverseForeignKey = #ForeignKey(value = ConstraintMode.NO_CONSTRAINT)
)
private List<City> cities;
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
2). City.java
package com.govjobportalbackend.entity;
import java.util.List;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
#Entity
#DiscriminatorValue(value = "city")
public class City extends JobMetadata {
#ManyToMany(mappedBy = "cities")
private List<Job> jobs;
#Override
public List<Job> getJobs() {
return jobs;
}
#Override
public void setJobs(List<Job> jobs) {
this.jobs = jobs;
}
}
Below property is set in application.properties file
spring.jpa.hibernate.ddl-auto=update
When run the application, it is logging below SQLs in logs and is creating two foreign keys
Hibernate: create table job (id int4 not null, name varchar(255), icon varchar(255), primary key (id))
Hibernate: create table job_city (job_id int4 not null, city_id int4 not null)
Hibernate: create table job_metadata (type varchar(31) not null, id int4 not null, name varchar(255), primary key (id))
Hibernate: alter table if exists job_city add constraint FKiksm0d31mc3osxut4ciaf4uof foreign key (job_id) references job
Hibernate: alter table if exists job_city add constraint FKknw4pf63xt1tvnqrmrjrm5hqq foreign key (city_id) references job_metadata
If I annotate as per below in City.java then it works as expected but as per my "little" research, this bug is fixed in hibernate (so mapped entity is not required to be annotated with depreciated annotation), or may be I am wrong.
#ManyToMany(mappedBy = "cities")
#org.hibernate.annotations.ForeignKey(name = "none")
private List<Job> jobs;
Environment I am using is below;
Java 11
Hibernate 5.4.28.Final (spring-boot-starter-web)
As SimonMartinelli pointed out, this is most definitely a Hibernate bug. The version that worked for me was:
#JoinTable(
name="job_city",
joinColumns = #JoinColumn(name = "job_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)),
inverseJoinColumns = #JoinColumn(name = "city_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
)
I found that the functionality breaks when you either (1) use #JoinTable.foreignKey instead, or (2) omit the name parameter.

Spring Boot 2, Spring 5 JPA: Dealing with multiple OneToMany JoinTables

Not sure the best approach to implementing the CrudRepository for an Entity that has multiple
#OneToMany associations with a #JoinTable
#Entity
#Table(name = "contact", uniqueConstraints = {#UniqueConstraint(columnNames ={"first_name","last_name"})})
#SuppressWarnings("PersistenceUnitPresent")
public class Contact extends Auditable implements Serializable {
#Id
#Column(name = "contact_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "contact_generator")
#SequenceGenerator(name = "contact_generator", sequenceName = "contact_seq", allocationSize = 50)
private Long contactId;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "middle_name", nullable = true)
private String middleName;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_phone"
)
private List<Phone> phoneNumbers = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(name = "contact_email")
private List<EmailAddress> emailAddresses = new ArrayList<>();
public interface ContactRepo extends CrudRepository<Contact, Long> {
List<Contact> findByLastNameContainingIgnoreCase(String lastName);
}
I have the FetchType.LAZY so I don't get the MultipleBagFetchException from 2 cartesian products.
So I know I need to split the 2 joins up which is where I am stuck as to the best solution.
Put in a custom repo and customImpl class that has can access the EntityManager and code out the 2 joins?
I am not crazy and letting Java take care of the cartesian via a Set, nor the one having FetchType.EAGER and dealing with the other with another query??
Generates:
create table contact (
contact_id bigint not null,
create_tm timestamp not null,
created_by varchar(255) not null,
updated_tm timestamp not null,
updated_by varchar(255) not null,
first_name varchar(255) not null,
last_name varchar(255) not null,
middle_name varchar(255),
primary key (contact_id)
)
create table email_address (
email_id bigint not null,
email_addr varchar(255) not null,
email_type varchar(255),
primary_addr boolean default false,
primary key (email_id)
)
create table contact_email (
Contact_contact_id bigint not null,
emailAddresses_email_id bigint not null
)
create table phone (
phone_id bigint not null,
phone_nbr varchar(255) not null,
phone_type varchar(255),
primary_ph boolean default false,
primary key (phone_id)
)
create table contact_phone (
Contact_contact_id bigint not null,
phoneNumbers_phone_id bigint not null
)
The strange think is my JpaDataTests worked find. The find all and findByLastNameContainingIgnoreCase return the phone numbers and email addresses.
However, The Service does not.
#Autowired
private ContactRepo contactRepo;
#Override
public List<Contact> findAllContacts() throws GcaServiceException {
try {
Iterable<Contact> iter = contactRepo.findAll();
return IteratorUtils.toList(iter.iterator());
} catch(DataAccessException e) {
throw new GcaServiceException(e.getMessage());
}
}
#Override
public List<Contact> findByLastName(String lastName) throws GcaServiceException {
try {
return contactRepo.findByLastNameContainingIgnoreCase(lastName);
} catch (DataAccessException e) {
throw new GcaServiceException(e.getMessage());
}
}
[
{
"createTm": "2021-01-11T16:27:19.995011",
"createdBy": "UncleMoose",
"updatedBy": "UncleMoose",
"updateTm": "2021-01-11T16:27:19.995011",
"contactId": 1,
"firstName": "Bruce",
"lastName": "Randall",
"middleName": null,
"phoneNumbers": [],
"emailAddresses": []
},
{
"createTm": "2021-01-11T16:27:19.996009",
"createdBy": "UncleMoose",
"updatedBy": "UncleMoose",
"updateTm": "2021-01-11T16:27:19.996009",
"contactId": 51,
"firstName": "Boss",
"lastName": "Randall",
"middleName": null,
"phoneNumbers": [],
"emailAddresses": []
}
]
Part of the mystery of DataJpaTest vs manual integration testing differences was I decided to look at a map and make sure I wasn't hiking down the wrong Google trail. I turned the H2 console on and found the Join Tables empty even though the insert occurred? However, I notice I was getting different Join Table column names between live and automated testing.
Solution was to explicitly name the Join Table columns. It appears Spring has handled the MultipleBagFetchException issues with multiple OneToMany JoinTable attributes in an Entity.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_phone"
,joinColumns = #JoinColumn(name = "contact_id")
,inverseJoinColumns = #JoinColumn(name = "phone_id")
)
private List<Phone> phoneNumbers = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_email"
,joinColumns = #JoinColumn(name = "contact_id")
,inverseJoinColumns = #JoinColumn(name = "email_id")
)
private List<EmailAddress> emailAddresses = new ArrayList<>();

Spring JPA one-to-many relationship return null on update

I have a one-to-many relationship between AcademicYear and subject (One AcademicYear has many Subjects).
Here is the model for AcademicYear:
package com.sms.entity;
import javax.persistence.*;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import org.hibernate.annotations.UpdateTimestamp;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.Date;
import java.util.Set;
#Entity
#Setter
#Getter
#Table(name = "academic_years")
#NoArgsConstructor
#AllArgsConstructor
public class AcademicYear {
public AcademicYear(long id, String name, Date updatedAt) {
this.id = id;
this.name = name;
this.updatedAt = updatedAt;
}
#Schema(description = "Unique identifier of the academic year.", example = "1")
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Schema(description = "Name of the Academic Year.", example = "First Year Primary", required = true)
#Column(name = "name")
private String name;
#JsonManagedReference
#OneToMany(mappedBy="academicYear", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Subject> subjects;
#UpdateTimestamp
#Column(name = "updated_at", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date updatedAt;
public Set<Subject> getSubjects() {
return subjects;
}
public void setSubjects(Set<Subject> subjects) {
this.subjects = subjects;
}
}
And the model for Subject:
package com.sms.entity;
import javax.persistence.*;
import com.fasterxml.jackson.annotation.JsonBackReference;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.util.Date;
#Table(name = "subjects")
#Entity
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Subject {
public Subject(long id, String name, Date updatedAt) {
this.id = id;
this.name = name;
this.updatedAt = updatedAt;
}
#Schema(description = "Unique identifier of the subject.", example = "1")
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Schema(description = "name of the subject.", example = "Mathematics-2")
#Column(name = "name")
private String name;
#ManyToOne(cascade = {CascadeType.ALL})
#JsonBackReference
#JoinColumn(name="academic_year_id", nullable=false)
private AcademicYear academicYear;
#UpdateTimestamp
#Column(name = "updated_at", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private Date updatedAt;
}
When I try to update name attribute for AcademicYear, I send a PUT request with the following body:
{
"id": 2,
"name": "Second Year"
}
I got the following response:
{
"id": 2,
"name": "Second Year",
"subjects": null,
"updatedAt": "2020-03-27T18:01:16.163+0000"
}
I have subjects as null. This AcademicYear entity already have records, when I send GET request with 2 as pathvariable to get the entity I get the following response:
{
"id": 2,
"name": "Second Year",
"subjects": [
{
"id": 3,
"name": "english",
"updatedAt": "2020-03-27T17:39:09.000+0000"
},
{
"id": 4,
"name": "physics",
"updatedAt": "2020-03-26T21:45:09.000+0000"
},
{
"id": 5,
"name": "chemistry",
"updatedAt": "2020-03-26T21:45:09.000+0000"
},
{
"id": 2,
"name": "math",
"updatedAt": "2020-03-27T17:39:09.000+0000"
}
],
"updatedAt": "2020-03-27T18:01:16.000+0000"
}
I have fetch type as EAGER, don't know why I get subjects as null when I update the entity name. Any help?
You need to use PATCH instead of PUT for partial updates.
Here is why
Based on RFC 7231, PUT should be used only for complete replacement of representation, in an idempotent operation. PATCH should be used for partial updates.
Based on your input, request set null to subjects.
If you still want to use the PUT then you need to provide the whole request object which you want to update/replace
you can find more details here
Why isn't HTTP PUT allowed to do partial updates in a REST API?

How #RequestBody works

How to get more details:
I am doing simple rest post request from Postman chrome extension.
My controller is :
#Controller
#RequestMapping("/theme")
public class ThemeController {
#RequestMapping(value = "/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
Status addTheme(#RequestBody Theme theme) {
try {
themeServices.addEntity(theme);
return new Status(1, "Theme added Successfully !");
} catch (Exception e) {
// e.printStackTrace();
return new Status(0, e.toString());
}
}
In Theme.java:
#Entity
#Table(name = "theme", uniqueConstraints = { #UniqueConstraint(columnNames = { "theme_id" }) })
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#NamedQuery(name = "Theme.findAll", query = "SELECT t FROM Theme t")
public class Theme implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "theme_id")
private long themeId;
private String description;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id", nullable=true)
private ThemeCategory themeCategory;
In ThemeCategory.java:
#Entity
#Table(name = "theme_category", uniqueConstraints = { #UniqueConstraint(columnNames = { "category_id" }) })
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#NamedQuery(name = "ThemeCategory.findAll", query = "SELECT t FROM ThemeCategory t")
public class ThemeCategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "category_id")
private long categoryId;
private String description;
private String name;
// bi-directional many-to-one association to Theme
// #OneToMany(mappedBy="themeCategory")
#OneToMany(mappedBy = "themeCategory", fetch = FetchType.EAGER)
#Column(nullable = true)
#JsonManagedReference
private Set<Theme> themes;
// bi-directional many-to-one association to ThemeCategory
#ManyToOne
#JoinColumn(name = "parent_category_id", nullable=true)
#JsonBackReference
private ThemeCategory parentThemeCategory;
// bi-directional many-to-one association to ThemeCategory
// #OneToMany(mappedBy="themeCategory")
#OneToMany(mappedBy = "parentThemeCategory", fetch = FetchType.EAGER)
#Column(nullable = true)
#JsonManagedReference
private Set<ThemeCategory> themeCategories;
Theme Category Table:
CREATE TABLE `theme_category` (
`category_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`parent_category_id` smallint(5) unsigned DEFAULT NULL,
`name` varchar(45) NOT NULL,
`description` varchar(1000) DEFAULT NULL ,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`category_id`),
KEY `idx_parent_category_id` (`parent_category_id`),
CONSTRAINT `fk_parent_category_id` FOREIGN KEY (`parent_category_id`) REFERENCES `theme_category` (`category_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=301 DEFAULT CHARSET=utf8;
Theme Table:
CREATE TABLE `theme` (
`theme_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`category_id` smallint(5) unsigned NOT NULL,
`file_path` varchar(200) DEFAULT NULL,
`description` varchar(1000) DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`theme_id`),
KEY `idx_category_id` (`category_id`),
CONSTRAINT `fk_category_id` FOREIGN KEY (`category_id`) REFERENCES `theme_category` (`category_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=401 DEFAULT CHARSET=utf8;
I am using Postman extension to do a rest post call:
http://localhost:8080/CustomerRegistration/theme/create
Header params:
Content-Type: application/json
Json Body:
{"description":"theme8","name":"theme8","themeCategory":{"categoryId":302, "themes":[],"parentThemeCategory":{}, "themeCategories":[]}}
And tried around 2 hours with multiple ways of body. But it consistently saying:
The server refused this request because the request entity is in a format not supported
by the requested resource for the requested method.
To analyse, I am not getting any thing else. In Eclipse console also not showing anything regarding the this issue.
What is wrong? Is there any tools to create valid requests.

Resources