Spring JPA #ManyToOne and #OneToMany delete - spring-boot

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.

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

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

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.

How to delete entity related to another entity

I have this entities:
Batch, Malt, Country - batch contains malts (#ManyToMany), malt came from some country (#ManyToOne).
Relations are as following:
Batch:
#JoinTable(name="batch_malt",
joinColumns = #JoinColumn(name="batch_id"),
inverseJoinColumns = #JoinColumn(name="malt_id"))
private Set<Malt> malts = new HashSet<>();
Malt:
#NotNull
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="country_id")
private Country country;
#ManyToMany(mappedBy="malts")
private Set<Batch> batches;
Country:
No mappinge since it is #ManyToOne from Malt.
Country Service:
#Override
public void deleteById(Long countryIdToDelete) {
Set<Malt> malts = maltRepository.findByCountry_id(countryIdToDelete);
if (malts != null) {
for (Malt tempMalt : malts) {
log.debug("Deleting country from malt number: " + tempMalt.getMaltName());
tempMalt.setCountry(null);
}
maltRepository.deleteById(countryIdToDelete);
}
}
I want to delete Country (I don't want to delete Malt - null shoul be instead)
When I'm trying do delete Country I get:
There was an unexpected error (type=Internal Server Error, status=500).
could not execute statement; SQL [n/a]; constraint ["FKM636T1NDS3GNKJC6WG8MCG21L: PUBLIC.BATCH_MALT FOREIGN KEY(MALT_ID) REFERENCES PUBLIC.MALT(ID) (2)"; SQL statement: delete from malt where id=? [23503-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FKM636T1NDS3GNKJC6WG8MCG21L: PUBLIC.BATCH_MALT FOREIGN KEY(MALT_ID) REFERENCES PUBLIC.MALT(ID) (2)"; SQL statement:
delete from malt where id=? [23503-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
Caused by: org.h2.jdbc.JdbcSQLException: Naruszenie więzów integralności: "FKM636T1NDS3GNKJC6WG8MCG21L: PUBLIC.BATCH_MALT FOREIGN KEY(MALT_ID) REFERENCES PUBLIC.MALT(ID) (2)"
Referential integrity constraint violation: "FKM636T1NDS3GNKJC6WG8MCG21L: PUBLIC.BATCH_MALT FOREIGN KEY(MALT_ID) REFERENCES PUBLIC.MALT(ID) (2)"; SQL statement:
delete from malt where id=? [23503-197]
What should I change to be able to delete Country?
Link to whole repo: https://github.com/fangirsan/maruszka-new

JSP Error messages don't show when using Spring Data JPA

This is my repository:
#Repository
public interface ProductRepo extends CrudRepository<Product,Integer> {
public List<Product> findAll();
public Product findById(int id);
}
My Entity Bean:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Integer id;
#NotEmpty
#Column(name = "product_name")
private String productName;
Controller:
#PostMapping("/add-product")
String addProduct(#Validated #ModelAttribute("product")Product product, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
System.out.println("has errors: " + bindingResult.toString());
}
System.out.println( "adding Product ..." );
productService.save(product);
return "redirect:/products/success";
This is my view
<form:form action="add-product" method="post" modelAttribute="product">
<label for="productName">Product Name</label>
<form:input path="productName" id="productName" type="text" placeholder="Add product name"/>
<form:errors path="productName" />
...
It just works fine when using Hibernate SessionFactory to store to database, like this:
// A shorter way to save customer
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(customer);
but when replacing it with Spring Data JPA, it starts throwing exceptions and return 500 html pages instead of just rendering the error field as it used to be.
There are 4 exceptions thrown:
javax.validation.ConstraintViolationException: Validation failed for classes [com.luv2code.springdemo.entity.Product] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must not be empty', propertyPath=productName, rootBeanClass=class com.luv2code.springdemo.entity.Product, messageTemplate='{javax.validation.constraints.NotEmpty.message}'}
]
javax.persistence.RollbackException: Error while committing the transaction
org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:77)
org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71)
org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
org.springframework.web.util.NestedServletException: 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
Previously, without using JPA, the validation error message shows in the view <form:errors />, not thrown as an exception with 500 http error code. What do I miss here?
The behavior you see has nothing to do with Spring Data JPA but the fact that you switched from plain hibernate (judging from your code snippets) to JPA.
When using JPA and using JSR-303 those will work together to prevent invalid entities from being entered in the database by throwing the validation exception. When using plain Hibernate this doesn't happen (at least the exceptions don't propagate).
Which was due to you have written your request handling method. In case of errors in the model you just do a System.out and happily continue the method, what you should have been doing there is return to the original view instead (I assume products/add-product).
#PostMapping("/add-product")
String addProduct(#Validated #ModelAttribute("product")Product product, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
System.out.println("has errors: " + bindingResult.toString());
return "products/add-product";
}
System.out.println( "adding Product ..." );
productService.save(product);
return "redirect:/products/success";
}
Basically you failed at handling the case of errors properly leading to exceptions being thrown due to the JPA and javax.validation working in a united fashion.

sequence does not exist, hibernate and JPA 2.1

I am getting an error saying
`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
java.sql.SQLSyntaxErrorException: ORA-02289: sequence does not exist`
This error happens when I try to create a user.
#RequestMapping(method = POST)
public UserDto createUser(#RequestBody userDto user) {
Preconditions.checkNotNull(user);
return Preconditions.checkNotNull(service.create(user));
}
I am however able to delete and get just not create nor update. What is also frustrating is I get no error when trying to update, it just doesn't so it.
I am not getting any real lead on where to look. I have tried many different methods to resolve this with no avail.
I found a post that had this:
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQUENCE1")
#SequenceGenerator(name="SEQUENCE1", sequenceName="SEQUENCE1", allocationSize=1)
private int user_id;
At this link: SOF link
It is complaining about this entity which I generated with netbeans and I am currently using Intellij. Any advice would be appreciated.
The code that creates new Campaign entity seems to be incorrect.
public CampaignDto create(CampaignDto campaignDto) {
Campaign campaign = mapper.mapReverse(campaignDto);
System.out.println(campaign.toString());
// Following 2 lines must be added to obtain and use managed Shop entity
Shop existingShop = shopRepository.findOne(campaignDto.getShopId());
campaign.setShop(existingShop);
campaign = campaignRepository.save(campaign);
CampaignDto createdCampaign = mapper.map(campaign);
return createdCampaign;
}
It looks like you might not be setting Campaign.shopId field when creating new Campaign.
#JoinColumn(name = "SHOP_ID", referencedColumnName = "SHOP_ID")
#ManyToOne(optional = false)
private Shop shopId;
You might want to rename this field to just shop to make it clear what it holds as it's not just an identifier.
Depending on how you are persisting new objects you might need to add CascadeType.ALL on #ManyToOne to ensure that a new Shop is persisted together with a new Campaign.
#ManyToOne(optional = false, cascade = CascadeType.ALL)
Go to your application property file and put
hibernate.hbm2ddl.auto=true; It might be helpful Hibernate created this sequence and added a new row

Resources