Duplicate or Null error when trying to send POST request for JSON Object with Foreign Key using Spring JPA - spring

Here's my parent entity:
#Entity(name = "DrivingInstructor")
#Table(name = "driving_instructor")
#Getter
#Setter
#NoArgsConstructor
public class DrivingInstructor {
#Id
#Column(name = "driving_instructor_id")
private long drivingInstructorId;
#Column(name = "driving_instructor_name")
#Size(max = 128)
private String drivingInstructorName;
#Column(name = "specialization")
#Size(max = 200)
private String specialisation;
}
And here's my supposed child entity:
#Entity(name = "DrivingStudent")
#Table(name = "driving_student")
#Getter
#Setter
#NoArgsConstructor
public class DrivingStudent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "driving_student_id")
private long drivingStudentId;
#Column(name = "driving_student_name")
#Size(max = 128)
private String drivingStudentName;
#ManyToOne(cascade = CascadeType.ALL, targetEntity = DrivingInstructor.class)
#JoinColumn(name = "driving_instructor_id", referencedColumnName = "driving_instructor_name", insertable = false, updatable = false)
private DrivingInstructor drivingInstructor;
}
Here's the relevant chunk of my service class for inserting/saving an instance of a DrivingStudent into the database:
#RequestMapping(path = "api/v0/driving-school")
#RestController
#AllArgsConstructor
public class DrivingStudentRestController {
private final DrivingStudentServiceImpl drivingStudentServiceImpl;
#PostMapping
Long insertOrUpdateDrivingStudent(#Valid #RequestBody DrivingStudent drivingStudent) {
return drivingStudentServiceImpl.insertOrUpdateDrivingStudent(drivingStudent);
}
}
DrivingStudentServiceImpl is just an abstraction layer for Repository class that extends JpaRepository<DrivingStudent, Long>, so insertOrUpdateDrivingStudent() is practically just using the save() method from CrudRepository.
An instance of DrivingInstructor is already pre-inserted with drivingInstructorId of 1, and so I tried to execute a POST request via Postman using this JSON object:
{
"drivingStudentName": "Peter Parker",
"drivingInstructor": {"drivingInstructorId": 1}
}
And I'm getting this exception:
2021-08-27 20:03:37.554 ERROR 16108 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper :
ERROR: duplicate key value violates unique constraint "driving_instructor_pkey"
Detail: Key (driving_instructor_id)=(1) already exists.
2021-08-27 20:03:37.590 ERROR 16108 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a];
constraint [driving_instructor_pkey];
nested exception is org.hibernate.exception.ConstraintViolationException:
could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "driving_instructor_pkey"
Detail: Key (driving_instructor_id)=(1) already exists.
I also tried revising my RestController's PostMapping to look like this, but still nothing changes:
#RequestMapping(path = "api/v0/driving-school")
#RestController
#AllArgsConstructor
public class DrivingStudentRestController {
private final DrivingInstructorRepository drivingInstructorRepository;
private final DrivingStudentServiceImpl drivingStudentServiceImpl;
#PostMapping
Long insertOrUpdateDrivingStudent(#Valid #RequestBody DrivingStudent drivingStudent) {
Optional<DrivingInstructor> drivingInstructor = drivingInstructorRepository.findById(drivingStudent.getDrivingInstructor().getDrivingInstructorId());
if (drivingInstructor.isPresent()) {
drivingStudent.setDrivingInstructor(drivingInstructor.get());
return drivingStudentServiceImpl.insertOrDrivingStudent(drivingStudent);
}
return null;
}
}
The error I am getting then changed to:
2021-08-27 21:36:58.622 ERROR 11388 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper :
ERROR: null value in column "driving_instructor_number" of relation "driving_student" violates not-null constraint
Detail: Failing row contains (Peter Parker, null).
2021-08-27 21:36:58.632 ERROR 11388 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed;
nested exception is org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a];
constraint [driving_instructor_number" of relation "driving_student];
nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: null value in column "driving_instructor_number" of relation "driving_student" violates not-null constraint
Detail: Failing row contains (Peter Parker, null).
There are stuff I've tried but most exceptions simply end up with either of those two. All I really wanted to do was insert an instance of DrivingStudent into the database using POST request, with a foreign key connecting it to a DrivingInstructor instance, and then of course, be able to retrieve those data.
I am able to do insert data manually into the database using the statement:
INSERT INTO driving_student VALUES ('Peter Parker', 1);
And I am able to retrieve that data in JSON format using GET method. So far, my only problem really is how to deal with the POST method.

Ok, I just changed/simplified the annotations in DrivingStudent's drivingInstructor JoinColumn field from this:
#ManyToOne(cascade = CascadeType.ALL, targetEntity = DrivingInstructor.class)
#JoinColumn(name = "driving_instructor_id", referencedColumnName = "driving_instructor_name", insertable = false, updatable = false)
private DrivingInstructor drivingInstructor;
to this:
#ManyToOne
#JoinColumn(name = "driving_instructor_id")
private DrivingInstructor drivingInstructor;
and it somehow worked... I have no idea why though.

Related

Doing custom query by a one of multiple primary keys throw me errors

My custom #Query of a table with multilple “primary keys” doen'twork.
In (image1) i show the basic structure i used to build my backend.
I start declaring my entity “Absentismos” and a other file to declare their primary keys “AbsentismoPrimaryData” with #Embeddable annotation.
#Entity
#Table(name="absentismo_incidencias")
#Getter
#Setter
public class Absentismos {
#EmbeddedId
public AbsentismoPrimaryData absentismoPrimaryData;
#Column(name="FECHA_INICIO")
public String fechaInicio;
#Column(name="FECHA_FINAL")
public String fechaFinal;
#Column(name="CANTIDAD")
public Integer cantidad;
}
#Embeddable
#Getter
#Setter
public class AbsentismoPrimaryData implements Serializable {
#Column(name="COD_TRABAJADOR")
public String codTrabajador;
#Column(name="CONCEPTO")
public String concepto;
//#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name="FECHA_REGISTRO", insertable = false, updatable = false)
public String fechaRegistro;
}
Second, defined my repository with JPA.
#Repository
public interface AbsentimosRepository extends JpaRepository<Absentismos, AbsentismoPrimaryData> {
#Query(value="SELECT u FROM Absentismos u WHERE to_char( u.fechafiltro ,'yyyymmdd') = ?1",nativeQuery = true)
List<Absentismos> findAbsentismosByFechaRegistro(String fechaRegistro);
List<Absentismos> findByCantidad(Integer fechaRegistro);
}
Third, built my services
#Service
public class AbsentismoService {
#Autowired
private AbsentimosRepository repository;
public List<Absentismos> getAbsentismos(String fechaRegistro){
return repository.findAbsentismosByFechaRegistro(fechaRegistro);
}
public List<Absentismos> getByCantidad(Integer codTrabjador){
return repository.findByCantidad(codTrabjador);
}
}
and the controller.
#RestController
#RequestMapping("/absentismo")
public class AbsentismoController {
#Autowired
private AbsentismoService service;
#GetMapping()
public ResponseEntity<List<Absentismos>> getAbsentismos(#RequestParam("fechaRegistro") String fechaRegistro){
return new ResponseEntity<List<Absentismos>>(service.getAbsentismos(fechaRegistro), HttpStatus.OK);
}
Finally, my object in JSON format have this shape:
{
"absentismoPrimaryData": {
"codTrabajador": "10000576",
"concepto": "1413",
"fechaRegistro": "2009-07-16 00:00:00"
},
"fechaInicio": "2009-07-16 00:00:00",
"fechaFinal": "2009-07-31 00:00:00",
"cantidad": 16
}
In a table with unique primary key #Id, it works good. But, In this case i need to do a query by date with the field “fechaRegistro”.
However, After i try calle findAbsentismosByFechaRegistro function, the console show me this error:
Hibernate: SELECT u FROM Absentismos u WHERE to_char( u.fechafiltro ,'yyyymmdd') = ?
2022-03-29 14:10:38.432 WARN 14308 --- [nio-8082-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 942, SQLState: 42000
2022-03-29 14:10:38.433 ERROR 14308 --- [nio-8082-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ORA-00942: la tabla o vista no existe
2022-03-29 14:10:38.450 ERROR 14308 --- [nio-8082-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
oracle.jdbc.OracleDatabaseException: ORA-00942: la tabla o vista no existe
image1

Q: Transactional Code why does this work so well?

Hello my professionals I have a simple question here that I would like to beg to solve this..
this is an Entity of Member
#Entity
#Getter
#Builder
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#AllArgsConstructor
/*#ToString(of = {"id", "username", "age"})*/
public class Member {
#Id
/*#GeneratedValue(strategy = GenerationType.IDENTITY)*/
#Column(name = "member_id")
private Long id;
private String username;
private int age;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "member")
private List<Team> teams;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "member")
private List<Coach> coachs;
}
And this is an Entity of Coach
#Entity
#AllArgsConstructor
#Getter
#Builder
#Setter
#NoArgsConstructor
#ToString(of = {"id","name","career"})
public class Coach {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name= "coach_id")
private Long id;
#Column
private String name;
#Column
private String career;
#ManyToOne(fetch = FetchType.LAZY,cascade = ALL)
#JoinColumn(name = "member_id")
private Member member;
#OneToOne(fetch = FetchType.LAZY,cascade = ALL)
#JoinColumn(name = "team_id")
private Team team;
}
and This is Controller Code
#GetMapping("/member")
public void createUser(){
Member m = memberService.createMember();
Coach c = m.getCoachs().get(0);
log.info(c.getName());
}
and This is Service Code
private final MemberRepository memberRepository;
#Transactional
public Member createMember(){
return memberRepository.findMemberById(3L);
}
and the last this is RepositoryCode
Member findMemberById(Long id);
So my question is that when i printed out Coach's name at the controller on console
it printed out so well.
but what I know the Transaction is over from the service So the persistence container is closed that means coach name can't be imported cause it's LAZY loading and persistence container is closed but it was printed out well
I want to know the reason why ...
here are the console results Thanks !!
[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO o.a.c.c.C.[.[.[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[2022-01-10 23:27:46.835] [http-nio-9000-exec-2] [] INFO o.s.w.s.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
[2022-01-10 23:27:46.855] [http-nio-9000-exec-2] [] INFO o.s.w.s.DispatcherServlet - Completed initialization in 19 ms
Hibernate:
/* select
generatedAlias0
from
Member as generatedAlias0
where
generatedAlias0.id=:param0 */ select
member0_.member_id as member_i1_1_,
member0_.age as age2_1_,
member0_.username as username3_1_
from
member member0_
where
member0_.member_id=?
[2022-01-10 23:27:47.007] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867007 | took 15ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=?
/* select generatedAlias0 from Member as generatedAlias0 where generatedAlias0.id=:param0 */ select member0_.member_id as member_i1_1_, member0_.age as age2_1_, member0_.username as username3_1_ from member member0_ where member0_.member_id=3;
[2022-01-10 23:27:47.170] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867170 | took 12ms | commit | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
;
Hibernate:
select
coachs0_.member_id as member_i4_0_0_,
coachs0_.coach_id as coach_id1_0_0_,
coachs0_.coach_id as coach_id1_0_1_,
coachs0_.career as career2_0_1_,
coachs0_.member_id as member_i4_0_1_,
coachs0_.name as name3_0_1_,
coachs0_.team_id as team_id5_0_1_
from
coach coachs0_
where
coachs0_.member_id=?
[2022-01-10 23:27:47.200] [http-nio-9000-exec-2] [4c0222d3] INFO p6spy - #1641824867200 | took 12ms | statement | connection 1| url jdbc:mariadb://patrick-lab.cjeq2ffynlc2.ap-northeast-2.rds.amazonaws.com:3306/patricklab?characterEncoding=UTF-8&serverTimezone=UTC
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=?
select coachs0_.member_id as member_i4_0_0_, coachs0_.coach_id as coach_id1_0_0_, coachs0_.coach_id as coach_id1_0_1_, coachs0_.career as career2_0_1_, coachs0_.member_id as member_i4_0_1_, coachs0_.name as name3_0_1_, coachs0_.team_id as team_id5_0_1_ from coach coachs0_ where coachs0_.member_id=3;
[2022-01-10 23:27:47.213] [http-nio-9000-exec-2] [4c0222d3] INFO m.p.l.m.c.MemberController - Coach1
I believe it is because you are using the spring-boot default setting which the spring.jpa.open-in-view is set to true .
This property enables OpenSessionInView pattern which you can simply think that a transaction will be opened automatically for you at the very first beginning when processing any HTTP request (e.g. in the Servlet Filter etc). Because of this , a transaction is actually already open before your service method executes and it is still active after your service method completes. Hence you will not experience any LazyInitializationException even after you access non-initialized properties outside the service method as the transaction is still active.
There is a strong debate about whether or not spring-boot should enable it by default in the past . You can refer this for more details if you are interested. I personally would recommend to turn it off.

Spring JPA #ManyToOne and #OneToMany delete

I use Spring Boot backend for my application. I have 2 entities, Post and Comment. There are multiply comments for my posts. What I want:
If I delete my post, I want to delete my comments as well.
If I delete a comment, I want to delete only that one comment.
Here is my Post entity:
#Entity
public class Post{
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "post")
private Set<Comment> comments;
...
}
Here is my Comment entity:
#Entity
public class Comment{
...
#ManyToOne
#JoinColumn(name = "post_id", nullable = false)
private Post post;
...
}
This is how I tried to delete my comment:
#DeleteMapping("/{commentId}")
#RoleSecured({})
public ResponseEntity<Object> delete(#PathVariable Long commentId) {
commentRepository.deleteById(commentId);
return ResponseEntity.ok().build();
}
And this is how I tried to delete my post:
#DeleteMapping("/{postId}")
#RoleSecured({})
public ResponseEntity<Object> delete(#PathVariable Long postId) {
Post post = postRepository.findById(postId).orElseThrow(() -> new RuntimeException("Post not present in database"));
postRepository.delete(post);
}
This is my last try but I tried a lot of things, can you help me please how can I implement both deletes.
Delete of the post exception:
021-11-24 15:05:48.479 ERROR 23332 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Cannot delete or update a parent row: a foreign key constraint fails (`buildtogether`.`comment`, CONSTRAINT `fk_comment_post_id` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`))
2021-11-24 15:05:48.480 INFO 23332 --- [nio-8080-exec-1] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2021-11-24 15:05:48.588 ERROR 23332 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`buildtogether`.`comment`, CONSTRAINT `fk_comment_post_id` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`))
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117) ~[mysql-connector-java-8.0.27.jar:8.0.27]
Delete of the comment exception: no exception, but not delete the comment.

Spring Data REST getting exception when trying to save the data

I new to using Spring data rest , so even though this might be a very basic thing I would appreciate it if someone could point what is the mistake that i made or point me in the right direction .
I have an EmployeeRepository which contains the employee entities.
public interface EmployeeRepository extends CrudRepository<Employee,Long>{
#Query("select emp from Employee emp where emp.employeeId=?1")
Employee findById(String employeeId);
#Query("select emp from Employee emp where emp.officialEmailId = ?1")
Employee findByOfficialEmailId(String officialEmailId);
}
Below is a part of the employee entity delcaration .
public class Employee extends AbstractAuditingEntity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Size(min = 3, max = 30)
#NotBlank
#Column(unique=true,nullable=false)
private String employeeId;
#Size(min=3,max = 25)
#NotBlank
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
private String lastName;
#Size(min=3,max = 25)
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
#NotBlank
private String firstName;
#Size(min=3,max = 25)
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
private String middleName;
}
In my service class I needed to get list of employee entities based on a condition and set a common property to all those .I tried it and it did not work so I tried something much simpler , I'm trying to set the middle name of all employees to a common value and save it to the db . Here is the code for it
#Transactional
public void tempFunction(){
Iterable<Employee> empList = employeeRepository.findAll();
for(Employee e : empList){
e.setMiddleName("newMiddleName");
employeeRepository.save(e);
}
}
I'm getting the below exception 2016-02-20 17:59:08.545 ERROR 533 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction] with root cause
java.lang.NullPointerException: null
And the stack trace shows the root cause to be a NullPointerException which I know is not present anywhere near where I'm invoking this code from either in my controller or service . Could someone point if there is any mistake in my code , thanks in advance .

JPA CriteriaBuilder: Predicate with a Serializable field throws an exception

I have an entity with a persistent field declared as Serializable.
I would like to build a query with the CriteriaBuilder, that filters the results by the Serializable field.
The database is Oracle, and the field type is RAW(255) as hbm2ddl defined it.
If i write the query with a plain JPQL TypedQuery, everything works fine (the Serializable field is the one with the name "entityId"):
TypedQuery<Change> query = em.createQuery("FROM Change c WHERE c.entityClass = :class AND c.entityId = :id", Change.class);
query.setParameter("class", Person.class.getName());
query.setParameter("id", new Integer(2287));
query.getResultList();
However, the very same query with criteria builder does not work:
final CriteriaBuilder builder = em.getCriteriaBuilder();
final CriteriaQuery<Change> criteriaQuery = builder.createQuery(Change.class);
final Root<Change> from = criteriaQuery.from(Change.class);
final CriteriaQuery<Change> select = criteriaQuery.select(from);
final List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(from.get("entityClass"), Person.class.getName()));
predicates.add(builder.equal(from.get("entityId"), new Integer(2287)));
select.where(predicates.toArray(new Predicate[predicates.size()]));
final TypedQuery<Change> query = em.createQuery(select);
query.getResultList();
It throws the following exception after invoking getResultList():
[2013-05-21 16:12:45,960] [com.mycompany.myproduct.server.Main.startServer(Main.java:56)] [ERROR] [main] - Error starting Server: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: expected BINARY got NUMBER
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
...
Change.java:
#Entity
#Table(name = "T_REVISION_CHANGE")
#SequenceGenerator(name = "seq_revision_change", sequenceName = "SEQ_REVISION_CHANGE", allocationSize = 1)
public class Change {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_revision_change")
private Integer id;
#Column(name = "ENTITY_CLASS")
private String entityClass;
#Column(name = "ENTITY_ID")
private Serializable entityId;
}
I tried to manually serialize the Integer but the same kind of exception was thrown saying that a Serializable instance was expected instead of a byte array... :)
Any comment would be much appreciated.

Resources