Hibernate is generating wrong insert statement - wrong column binding - for the following entity model with single table inheritance and composite primary key using #IdClass.
Wondering if this is a Hibernate bug.
Stack: Spring Boot 2.7 / H2
Superclass base entity - AbstractMemberEvent:
#Entity
#Table(name = "MEMBER_EVENTS")
#Getter
#FieldDefaults(level = AccessLevel.PRIVATE)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "EVENT_TYPE")
#NoArgsConstructor
#IdClass(MemberEventId.class)
public abstract class AbstractMemberEvent {
#Id
#Column(name = "USID")
String usid;
#Id
#Column(name = "UPDATED")
Long eventTime;
#Id
#Column(name = "EVENT_TYPE", insertable = false, updatable = false )
String eventType;
Subclass entity - NewMemberJoined
#Entity
#Getter
#Setter
#FieldDefaults(level = AccessLevel.PRIVATE)
#DiscriminatorValue("NEWMEMBERJOINED")
#NoArgsConstructor
public class NewMemberJoined extends AbstractMemberEvent{
#Column(name="PAYLOAD")
NewMemberJoinedInfo newMemberJoinedInfo;
The IdClass
#Getter
#FieldDefaults(level = AccessLevel.PRIVATE)
#NoArgsConstructor
#EqualsAndHashCode
public class MemberEventId implements Serializable {
String usid;
Long eventTime;
String eventType;
}
From the logs I see that the wrong values are bound to the parameters
Hibernate:
create table member_events (
event_type varchar(31) not null,
updated bigint not null,
usid varchar(255) not null,
payload varchar(255),
primary key (updated, event_type, usid)
)
...
Hibernate:
insert
into
member_events
(payload, event_type, updated, usid)
values
(?, ?, ?, ?)
2022-11-25 21:52:13.789 DEBUG 527088 --- [ restartedMain] tributeConverterSqlTypeDescriptorAdapter : Converted value on binding : ...
2022-11-25 21:52:13.790 TRACE 527088 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [{"firstName":"Jasper" ...}]
2022-11-25 21:52:13.790 TRACE 527088 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1388534400000]
2022-11-25 21:52:13.790 TRACE 527088 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [NEWMEMBERJOINED]
2022-11-25 21:52:13.790 TRACE 527088 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [VARCHAR] - [3060001234567]
2022-11-25 21:52:13.794 WARN 527088 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 22018, SQLState: 22018
2022-11-25 21:52:13.794 ERROR 527088 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : Data conversion error converting "'NEWMEMBERJOINED' (MEMBER_EVENTS: ""UPDATED"" BIGINT NOT NULL)"; SQL statement:
insert into member_events (payload, event_type, updated, usid) values (?, ?, ?, ?) [22018-214]7'
Related
I have a problem saving a row in the "Orders" table. Each order includes a list of dishes.
When I saved through the code everything was fine. But when I send a JSON file, I see that Spring sends an update command to the DISH table. And I do not understand why, how can he be made not to update the value obtained?
this is the orders class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Entity
#Table(name="Orders")
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int orderId;
#Column(name="customer_name")
private String customerName;
#ElementCollection
#ManyToMany(mappedBy ="Orders",cascade = CascadeType.MERGE)
#JoinColumn(name = "id")
private List<Dish> dish;
#Column(name="order_date")
private Date orderDate;
#Enumerated(value = EnumType.ORDINAL)
#Column(name = "status_id")
private Status status;
#Enumerated(value = EnumType.ORDINAL)
#Column(name = "table_id")
private RTable table;
#ManyToOne
#JoinColumn(name = "waiter_id")
private Waiter waiter;
#Column(name="total_price")
private double totalPrice;
The Dish class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Entity
#Table(name="dishes")
public class Dish{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#Column(name="preperation_Time_By_Minutes")
private double preperationTimeByMinutes;
private double price;
}
part from the trace: (including the unwanted update query)
Hibernate: insert into orders (customer_name, order_date, status_id, table_id, total_price, waiter_id) values (?, ?, ?, ?, ?, ?)
2022-05-23 16:05:20.398 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [null]
2022-05-23 16:05:20.398 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [TIMESTAMP] - [Sun Nov 05 02:00:00 IST 2023]
2022-05-23 16:05:20.398 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [1]
2022-05-23 16:05:20.398 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [INTEGER] - [2]
2022-05-23 16:05:20.399 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [DOUBLE] - [0.0]
2022-05-23 16:05:20.399 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [INTEGER] - [null]
2022-05-23 16:05:20.404 DEBUG 19712 --- [nio-8080-exec-6] org.hibernate.SQL : update dishes set id=? where id=?
Hibernate: update dishes set id=? where id=?
2022-05-23 16:05:20.404 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [14]
2022-05-23 16:05:20.405 TRACE 19712 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [1]
2022-05-23 16:05:20.406 WARN 19712 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2022-05-23 16:05:20.406 ERROR 19712 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '14' for key 'dishes.PRIMARY'
2022-05-23 16:05:20.406 INFO 19712 --- [nio-8080-exec-6] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2022-05-23 16:05:20.409 ERROR 19712 --- [nio-8080-exec-6] 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 [dishes.PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '14' for key 'dishes.PRIMARY'
....
......
.........
Here's my question. I am unable to write the JSON after I send a POST request from POSTMAN to create/add a row into my SQL database. I need the server to send back a response of the added entry in order to retrieve the ID that SQL generates.
I face this issue when the new Case entry is a child of two entities(User and Application) and a grandchild of one entity(Owner).
*User(Owner)
|\
| *Application
|/
*Case
I'm new and currently using the Spring Boot JPA package.
I'm aware that many have asked questions related to the error above. There is none regarding my case to my knowledge. All of them refer to fixes over a HTTP GET method. If you found some please guide me to them. Or please help to answer my query. Any help is appreciated!
I attach my codes here:
User Entity
package com.lightlce.CRUD.Entities;
import javax.persistence.*;
#Entity(name = "User")
#Table(name = "User", schema = "dbo")
public class User {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String staffId;
private String username;
private String role;
public User (){}
public User(Integer id, String staffId, String username, String role) {
this.id = id;
this.staffId = staffId;
this.username = username;
this.role = role;
}
// Getters and Setters
}
Application Entity
package com.lightlce.CRUD.Entities;
import javax.persistence.*;
#Entity(name = "Application")
#Table(name = "InvApplication", schema = "dbo")
public class Application {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String applicationName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ownerId")
private User owner;
public Application(){}
public Application(Integer id, String applicationName, User owner) {
this.id = id;
this.applicationName = applicationName;
this.owner = owner;
}
// Getters and Setters
}
Case Entity
package com.lightlce.CRUD.Entities;
import com.lightlce.CRUD.AuditModel.Auditable;
import javax.persistence.*;
#Entity(name = "Case")
#Table(name = "InvCase", schema = "dbo")
public class Case extends Auditable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "applicationId")
private Application application;
//redacted
public Case() {
}
public Case(Integer id, User user, Application application) {
this.id = id;
this.user = user;
this.application = application;
}
// Getters and Setters
}
Case Controller
package com.lightlce.CRUD.Controllers;
import com.lightlce.CRUD.Entities.Application;
import com.lightlce.CRUD.Entities.Case;
import com.lightlce.CRUD.Entities.User;
import com.lightlce.CRUD.Repository.ApplicationRepository;
import com.lightlce.CRUD.Repository.CaseRepository;
import com.lightlce.CRUD.Repository.UserRepository;
import com.lightlce.CRUD.Services.ApplicationService;
import com.lightlce.CRUD.Services.CaseService;
import com.lightlce.CRUD.Services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
#RestController
public class CaseController {
#Autowired
private CaseService caseService;
#Autowired
private UserService userService;
#Autowired
private ApplicationRepository applicationRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private CaseRepository caseRepository;
#Autowired
private UserController userController;
#RequestMapping("cases")
public Page<Case> getAllCases(Pageable pageable) {
return caseService.getAllCases(pageable);
}
#PostMapping("cases/add")
public Case addCase(#RequestBody Case aCase) {
User staff = userService.searchUser(aCase.getUser()); //Finds the user based on ID provided
Application application = applicationRepository.findById(aCase.getApplication().getId()).get(); //Finds the application based on ID provided
aCase.setUser(staff);
aCase.setApplication(application);
return caseService.addCase(aCase);
}
}
Case Service
package com.lightlce.CRUD.Services;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.lightlce.CRUD.Entities.Application;
import com.lightlce.CRUD.Entities.Case;
import com.lightlce.CRUD.Entities.User;
import com.lightlce.CRUD.Repository.CaseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
#Service
public class CaseService {
#Autowired
private CaseRepository caseRepository;
#Autowired
private UserService userService;
public Page<Case> getAllCases(Pageable pageable){
return caseRepository.customFindAll(pageable);
}
public Case addCase(Case aCase) {
caseRepository.save(aCase);
return aCase;
}
}
Case Repository
package com.lightlce.CRUD.Repository;
import com.lightlce.CRUD.Entities.Case;
import com.lightlce.CRUD.Entities.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
#Repository
public interface CaseRepository extends JpaRepository<Case, Integer>{
Page<Case> findAll(Pageable pageable);
#Query(value = "SELECT c FROM com.lightlce.CRUD.Entities.Case c " +
"JOIN FETCH c.user u " +
"JOIN FETCH c.application a " +
"JOIN FETCH a.owner o",
countQuery = "SELECT COUNT(c) FROM com.lightlce.CRUD.Entities.Case c " +
"JOIN c.user u " +
"JOIN c.application a " +
"JOIN a.owner o")
Page<Case> customFindAll(Pageable pageable);
}
POST http://localhost:8080/cases/add
{
"user": {
"staffId": "TEST123"
},
"application":{
"id": 2
}
}
Expected Response
{
"created_at": "2020-05-13T09:34:04.093+0000",
"modified_at": "2020-05-13T09:34:04.093+0000",
"id": 1
"user": {
"id": 1,
"staffId": "TEST123",
"username": "lightlce",
"role": "admin"
},
"application": {
"id": 2,
"applicationName": "ApplicationDemo",
"owner": {
"id": 1,
"staffId": "TEST123",
"username": "lightlce",
"role": "admin"
}
}
}
Postman Exception
{
"timestamp": "2020-05-14T02:36:40.999+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Could not write JSON: could not initialize proxy [com.lightlce.CRUD.Entities.User#2] - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy [com.lightlce.CRUD.Entities.User#2] - no Session (through reference chain: com.lightlce.CRUD.Entities.Case[\"application\"]->com.lightlce.CRUD.Entities.Application[\"owner\"]->com.lightlce.CRUD.Entities.User$HibernateProxy$QRpaILkJ[\"staffId\"])",
"path": "/cases/add"
}
Springboot Logs
2020-05-14 10:36:38.262 DEBUG 50878 --- [nio-8080-exec-6] org.hibernate.SQL :
select
user0_.id as id1_0_,
user0_.role as role2_0_,
user0_.staffId as staffId3_0_,
user0_.username as username4_0_
from
dbo.InvAllUser user0_
where
user0_.staffId=?
Hibernate:
select
user0_.id as id1_0_,
user0_.role as role2_0_,
user0_.staffId as staffId3_0_,
user0_.username as username4_0_
from
dbo.InvAllUser user0_
where
user0_.staffId=?
2020-05-14 10:36:38.278 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [TEST123]
2020-05-14 10:36:38.808 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([id1_0_] : [INTEGER]) - [1]
2020-05-14 10:36:38.811 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([role2_0_] : [VARCHAR]) - [admin]
2020-05-14 10:36:38.812 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([staffId3_0_] : [VARCHAR]) - [TEST123]
2020-05-14 10:36:38.812 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([username4_0_] : [VARCHAR]) - [lightlce]
2020-05-14 10:36:38.837 DEBUG 50878 --- [nio-8080-exec-6] org.hibernate.SQL :
select
applicatio0_.id as id1_1_0_,
applicatio0_.applicationName as applicat2_1_0_,
applicatio0_.ownerId as ownerId3_1_0_
from
dbo.InvApplication applicatio0_
where
applicatio0_.id=?
Hibernate:
select
applicatio0_.id as id1_1_0_,
applicatio0_.applicationName as applicat2_1_0_,
applicatio0_.ownerId as ownerId3_1_0_
from
dbo.InvApplication applicatio0_
where
applicatio0_.id=?
2020-05-14 10:36:38.839 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [2]
2020-05-14 10:36:39.427 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([applicat2_1_0_] : [VARCHAR]) - [ApplicationDemo]
2020-05-14 10:36:39.427 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicExtractor : extracted value ([ownerId3_1_0_] : [INTEGER]) - [2]
2020-05-14 10:36:39.546 DEBUG 50878 --- [nio-8080-exec-6] org.hibernate.SQL :
insert
into
dbo.InvCase
(created_at, modified_at, applicationId, approverId, caseDesc, caseTitle, caseType, status, userId)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
insert
into
dbo.InvCase
(created_at, modified_at, applicationId, approverId, caseDesc, caseTitle, caseType, status, userId)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-05-14 10:36:39.553 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [Thu May 14 10:36:39 SGT 2020]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [TIMESTAMP] - [Thu May 14 10:36:39 SGT 2020]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [2]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [INTEGER] - [1]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [5] as [VARCHAR] - [ApplicationDemo]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [VARCHAR] - [TEST123]
2020-05-14 10:36:39.555 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [7] as [VARCHAR] - [new]
2020-05-14 10:36:39.556 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [VARCHAR] - [Pending]
2020-05-14 10:36:39.557 TRACE 50878 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [9] as [INTEGER] - [1]
2020-05-14 10:36:40.987 WARN 50878 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy [com.lightlce.CRUD.Entities.User#2] - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy [com.lightlce.CRUD.Entities.User#2] - no Session (through reference chain: com.lightlce.CRUD.Entities.Case["application"]->com.lightlce.CRUD.Entities.Application["owner"]->com.lightlce.CRUD.Entities.User$HibernateProxy$QRpaILkJ["staffId"])]
There are as many problems with your code as i really don't know how to help to solve it.
but as a quick fix try to create a new entity class for user object.
Solved it with this workaround.
Since fooService.save(foo); also updates the foo object with the ID, I can simply return foo.getId() to achieve what I want without sending another find method.
i.e. Case Service
public Integer updateCase(Case aCase) {
caseRepository.save(aCase);
return aCase.getId();
}
in my Spring Boot application I'm trying to save an Entity with a few nested Entities that have a one-to-many relationship. I'm using JPA and Hibernate to save them to a MySQL database.
On the primary keys, I am using #GeneratedValue(strategy = GenerationType.AUTO) on a Long to automatically create values for new entities. This worked for some time, however recently I'm getting an error when trying to save the entity:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '282' for key 'PRIMARY'
This is what my entities look like:
Question.java
#Entity
#Table(name = "question")
public class Question {
private Long id;
private List<Answer> answers;
// other fields
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
#OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Answer> getAnswers() { return answers; }
public void setAnswers(List<Answer> answers) { this.answers = answers; }
}
Answer.java
#Entity
#Table(name = "answer")
public class Answer {
private Long id;
private Question question;
// other fields
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
#ManyToOne
#JoinColumn(name = "question_id")
public Question getQuestion() { return question; }
public void setQuestion(Question question) { this.question = question; }
}
Repositories are simply:
public interface QuestionRepository extends JpaRepository<Question, Long>{ ... }
Now let's say I am adding a new Question via a web form, including 4 Answers. When calling the repository simply like:
questionRepository.save(question);
After some investigation, it appears that Hibernate is using the auto increment value from the question table for the answers entities, which doesn't work because it already exist.
Trace output log:
Hibernate: insert into question (id) values (?)
2019-08-31 18:47:49.652 TRACE 11516 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [BIGINT] - [281]
Hibernate: insert into answer (question_id, id) values (?, ?)
2019-08-31 18:47:49.688 TRACE 11516 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [281]
2019-08-31 18:47:49.688 TRACE 11516 --- [nio-8080-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [282]
2019-08-31 18:47:49.746 WARN 11516 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2019-08-31 18:47:49.746 ERROR 11516 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '282' for key 'PRIMARY'
2019-08-31 18:47:49.748 ERROR 11516 --- [nio-8080-exec-6] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2019-08-31 18:47:49.847 ERROR 11516 --- [nio-8080-exec-6] 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 [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '282' for key 'PRIMARY'
I've verified that the auto_increment value on the answer table is 581.
What could be the cause of this? Is there property/configuration I'm missing?
Since MySQL doesn't support sequences natively so this issue might occur when #GeneratedValue(strategy = GenerationType.AUTO) is used.
Use the below workaround for id generation.
#Id
#GeneratedValue(
strategy= GenerationType.AUTO,
generator="native"
)
#GenericGenerator(
name = "native",
strategy = "native"
)
Please refer https://hibernate.atlassian.net/browse/HHH-11014 for further details.
You can also refer ::: https://vladmihalcea.com/why-should-not-use-the-auto-jpa-generationtype-with-mysql-and-hibernate/
I have the following 'Parent':
#Entity
#Table(name = "messages")
#SequenceGenerator(name = "messageSequence", sequenceName = "message_sequence")
public class Message {
#Id
#Column(name = "message_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "messageSequence")
Long messageId;
#OneToMany(mappedBy = "message", cascade=CascadeType.ALL)
List<Recipient> recipients = new ArrayList<>();
public void addRecipient(Recipient r) {
r.setMessage(this);
recipients.add(r);
}
#Column(name = "message_body")
String body;
// Constructors/Getters/Setters
...
}
And the recipient 'Child':
#Entity
#Table(name = "message_recipients")
public class Recipient {
#EmbeddedId
private RecipientId recipientId;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "message_id")
private Message message;
public void setMessage(Message msg) {
this.message = msg;
}
public Recipient(Message msg, String name) {
this.id = new RecipientId(msg, name);
this.message = msg;
}
#Override
public boolean equals(Object o) { ... }
#Override
public int hashCode(Object o) { return key.hashCode(); }
}
With the composite key:
#Embeddable
public class RecipientId implements Serializable {
#Column(name = "message_id")
Long messageId;
#Column(name = "message_to")
String messageTo;
#Column(name = "message_timestamp")
LocalDateTime messageTimestamp;
public RecipientId(Message msg, String messageTo) {
this.messageId = msg.messageId;
this.messageTo = messageTo;
this.messageTimestamp = LocalDateTime.now();
}
#Override
public boolean equals(Object o) { ... }
#Override
public int hashCode() {
return Objects.hash(messageId, messageTo, messageTimestamp);
}
}
I have the following test code:
#Bean
public void makeMessage(MessageRepository messages) {
Message newMsg = new Message();
newMsg.addRecipient(new Recipient(newMsg, "recipientName"));
newMsg.setBody("This is a test message");
messages.save(newMsg);
}
When executing makeMessage() I can see in the debug output that Message gets a generated MessageId value, but when persisting Recipients, the messageId of the composite key is null, causing an failure of the transaction.
2018-11-27 14:49:54.158 DEBUG 2791 --- [ restartedMain] org.hibernate.SQL :
Hibernate:
values
nextval for amessage_sequence
2018-11-27 14:49:54.190 DEBUG 2791 --- [ restartedMain] org.hibernate.SQL :
Hibernate:
insert
into
messages
(body, message_code)
values
(?, ?)
2018-11-27 14:49:54.191 TRACE 2791 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [This is a test message]
2018-11-27 14:49:54.193 TRACE 2791 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [2922079]
2018-11-27 14:49:54.196 DEBUG 2791 --- [ restartedMain] org.hibernate.SQL :
Hibernate:
insert
into
message_recipient
(timestamp, message_id, message_to)
values
(?, ?, ?)
2018-11-27 14:49:54.196 TRACE 2791 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [TIMESTAMP] - [2018-11-27 14:49:54.141]
2018-11-27 14:49:54.196 TRACE 2791 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [null]
2018-11-27 14:49:54.196 TRACE 2791 --- [ restartedMain] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [RecipientName]
2018-11-27 14:49:54.199 WARN 2791 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: -407, SQLState: 23502
2018-11-27 14:49:54.199 ERROR 2791 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC=TBSPACEID=7, TABLEID=21, COLNO=1, DRIVER=4.19.26
I've exhausted about every possibility I can think of for trying to get this to work, including #IdClass, but its always null. From everything I can find online, this should work. I found one or two similar SO questions, but none addressed the specifics of a Composite Key where one of the composite fields is the parents Id.
I figure I can work around this by persisting the Message and the Recipients in two separate states (persist Message, get the ID and then create and persist the Recipients) but from what I know, this should work...
That is because you need to map the message_id into the class RecipientId with #MapsId("messageId") you can do the following:
#Entity
#Table(name = "message_recipients")
public class Recipient {
#EmbeddedId
private RecipientId recipientId;
#MapsId("messageId")
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "message_id")
private Message message;
public void setMessage(Message msg) {
this.message = msg;
}
public Recipient(Message msg, String name) {
this.id = new RecipientId(msg, name);
this.message = msg;
}
#Override
public boolean equals(Object o) { ... }
#Override
public int hashCode(Object o) { return key.hashCode(); }
}
I am trying to update a one to many relationship but getting a
insert into sw_standard_standard (sw_standard_id, standard_id) values
(?, ?) [23505-192]]; nested exception is
org.hibernate.exception.ConstraintViolationException
Here is my code:
SwStandard
#Entity
public class SwStandard implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#OneToOne
private DeviceType deviceType;
#ElementCollection
#OneToMany
private List<Image> standard;
#ElementCollection
#OneToMany
private List<Image> limited;
#ElementCollection
#OneToMany
private List<Image> exception;
public SwStandard() {}
public SwStandard(DeviceType deviceType, List<Image> standard, List<Image> limited, List<Image> exception) {
this.deviceType = deviceType;
this.standard = standard;
this.limited = limited;
this.exception = exception;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public DeviceType getDeviceType() {
return deviceType;
}
public void setDeviceType(DeviceType deviceType) {
this.deviceType = deviceType;
}
public List<Image> getStandard() {
return standard;
}
public void setStandard(List<Image> standard) {
this.standard = standard;
}
public List<Image> getLimited() {
return limited;
}
public void setLimited(List<Image> limited) {
this.limited = limited;
}
public List<Image> getException() {
return exception;
}
public void setException(List<Image> exception) {
this.exception = exception;
}
}
Image
#Entity
public class Image implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String name;
public Image() {}
public Image(String name) {
this.name = name;
}
public Image(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Update code:
#RequestMapping(value = "/swstandard/update", method = RequestMethod.POST)
public SwStandard updateSwStandard(#RequestBody SwStandard request) {
return swService.save(request);
}
SwService:
#Autowired
private SwStandardRepository swService;
SwStandardRepository:
public interface SwStandardRepository extends CrudRepository<SwStandard, Long> {
SwStandard findByDeviceTypeId(Long id);
}
First I save a SwStandard with 1 standard item. Then I save want to replace the standard item with something else. When I do this it returns the error above.
Edit
Alan Hay help partially helped.
Now I cannot save the same image across a SwStandard for example here is a SwStandard I'll save:
{
"id": 1,
"deviceType": { "id": 1 },
"standard": [{"id": 1}],
"limited": [],
"exception": []
}
and here is another SwStandard I'll save which will fail with the error above:
{
"id": 2,
"deviceType": { "id": 2 },
"standard": [{"id": 1}],
"limited": [],
"exception": []
}
As you can see both SwStandards have the same standard item.
Here is the log output:
2016-11-08 16:19:12.060 INFO 15668 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2016-11-08 16:19:12.060 INFO 15668 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2016-11-08 16:19:12.092 INFO 15668 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 31 ms
2016-11-08 16:19:12.255 WARN 15668 --- [nio-8080-exec-1] org.hibernate.orm.deprecation : HHH90000015: Found use of deprecated [org.hibernate.id.MultipleHiLoPerTableGenerator] table-based id generator; use org.hibernate.id.enhanced.TableGenerator instead. See Hibernate Domain Model Mapping Guide for details.
2016-11-08 16:19:12.255 DEBUG 15668 --- [nio-8080-exec-1] org.hibernate.SQL : select sequence_next_hi_value from hibernate_sequences where sequence_name = 'sw_standard' for update
2016-11-08 16:19:12.256 DEBUG 15668 --- [nio-8080-exec-1] org.hibernate.SQL : insert into hibernate_sequences(sequence_name, sequence_next_hi_value) values('sw_standard', ?)
2016-11-08 16:19:12.258 DEBUG 15668 --- [nio-8080-exec-1] org.hibernate.SQL : update hibernate_sequences set sequence_next_hi_value = ? where sequence_next_hi_value = ? and sequence_name = 'sw_standard'
2016-11-08 16:19:12.262 DEBUG 15668 --- [nio-8080-exec-1] org.hibernate.SQL : insert into sw_standard (device_type_id, id) values (?, ?)
2016-11-08 16:19:12.262 TRACE 15668 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2016-11-08 16:19:12.262 TRACE 15668 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2016-11-08 16:19:12.264 DEBUG 15668 --- [nio-8080-exec-1] org.hibernate.SQL : insert into sw_standard_standard (sw_standard_id, standard_id) values (?, ?)
2016-11-08 16:19:12.270 TRACE 15668 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2016-11-08 16:19:12.271 TRACE 15668 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2016-11-08 16:19:22.162 WARN 15668 --- [nio-8080-exec-2] org.hibernate.orm.deprecation : HHH90000015: Found use of deprecated [org.hibernate.id.MultipleHiLoPerTableGenerator] table-based id generator; use org.hibernate.id.enhanced.TableGenerator instead. See Hibernate Domain Model Mapping Guide for details.
2016-11-08 16:19:22.163 DEBUG 15668 --- [nio-8080-exec-2] org.hibernate.SQL : insert into sw_standard (device_type_id, id) values (?, ?)
2016-11-08 16:19:22.164 TRACE 15668 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [2]
2016-11-08 16:19:22.164 TRACE 15668 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [2]
2016-11-08 16:19:22.165 DEBUG 15668 --- [nio-8080-exec-2] org.hibernate.SQL : insert into sw_standard_standard (sw_standard_id, standard_id) values (?, ?)
2016-11-08 16:19:22.166 TRACE 15668 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [2]
2016-11-08 16:19:22.166 TRACE 15668 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2016-11-08 16:19:22.169 WARN 15668 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23505, SQLState: 23505
2016-11-08 16:19:22.169 ERROR 15668 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Unique index or primary key violation: "UK_QM6PF2T2PB3DLYYLV44TQL40J_INDEX_2 ON PUBLIC.SW_STANDARD_STANDARD(STANDARD_ID) VALUES (1, 1)"; SQL statement:
insert into sw_standard_standard (sw_standard_id, standard_id) values (?, ?) [23505-192]
2016-11-08 16:19:22.172 INFO 15668 --- [nio-8080-exec-2] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2016-11-08 16:19:22.194 ERROR 15668 --- [nio-8080-exec-2] 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 ["UK_QM6PF2T2PB3DLYYLV44TQL40J_INDEX_2 ON PUBLIC.SW_STANDARD_STANDARD(STANDARD_ID) VALUES (1, 1)"; SQL statement:
insert into sw_standard_standard (sw_standard_id, standard_id) values (?, ?) [23505-192]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause