how to use bidirectional many to many mapping following error is thrown Cannot add or update a child row: a foreign key constraint fails - spring

I am trying to implement the birectional many to many mapping (question,tags,question_tag) by using the following tables using Spring & Hibernate
TABLE user:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NOT NULL,
`password` varchar(255) DEFAULT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE question:
CREATE TABLE `question` (
`qid` int(11) NOT NULL AUTO_INCREMENT,
`questiontitle` varchar(255) DEFAULT NULL,
`questionbody` varchar(3000) DEFAULT NULL,
`uid` int(11) NOT NULL,
`votes` int(11) NOT NULL,
`created_on` DATETIME NOT NULL DEFAULT NOW(), -- or CURRENT_TIMESTAMP
PRIMARY KEY (`qid`),
CONSTRAINT `fk_user_id` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE tags:
CREATE TABLE `tags` (
`tagid` int(11) NOT NULL AUTO_INCREMENT,
`tag` varchar(255) DEFAULT NULL,
PRIMARY KEY (`tagid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE question_tag:
CREATE TABLE `question_tag` (
`qid` int(11) NOT NULL,
`tagid` int(11) NOT NULL,
PRIMARY KEY (`qid`,`tagid`),
CONSTRAINT `fk_qt_qid` FOREIGN KEY (`qid`) REFERENCES `question` (`qid`),
CONSTRAINT `fk_tags_tagid` FOREIGN KEY (`tagid`) REFERENCES `tags` (`tagid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Model used are
Question.Java
#Entity
#Table(name="question")
#NamedQuery(name="Question.findAll", query="SELECT q FROM Question q")
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int qid;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="created_on")
private Date createdOn;
private String questionbody;
private String questiontitle;
private int uid;
private int votes;
//bi-directional many-to-one association to Answer
#OneToMany(mappedBy="question")
private Set<Answer> answers = new HashSet<>();
//bi-directional many-to-one association to QuestionComment
#OneToMany(mappedBy="question")
private Set<QuestionComment> questionComments = new HashSet<>();
//bi-directional many-to-many association to Tag
#ManyToMany(targetEntity = Tag.class,cascade = { CascadeType.ALL })
#JoinTable(name = "question_tag",
joinColumns = { #JoinColumn(name = "qid") },
inverseJoinColumns = { #JoinColumn(name = "tagid") })
private Set<Tag> tags;
//bi-directional many-to-one association to UserQuestionUpvote
#OneToMany(mappedBy="question")
private Set<UserQuestionUpvote> userQuestionUpvotes = new HashSet<>();
public Question() {
}
}
Tag.Java
#Entity
#Table(name="tags")
#NamedQuery(name="Tag.findAll", query="SELECT t FROM Tag t")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int tagid;
private String tag;
//bi-directional many-to-many association to Question
#ManyToMany(mappedBy = "tags")
private Set<Question> questions ;
public Tag() {
}
}
There is a questionServiceImpl class which call the questionRepository.save method.
#Service
public class QuestionServiceImpl implements QuestionService {
#Autowired
private QuestionRepository questionRepository;
#Override
public void save(Question question) {
// TODO Auto-generated method stub
questionRepository.save(question);
}
}
Now in the controller when the call to QuestionServiceImpl.save is made insert call to the tables question, tags & question_tag is made but Cannot add or update a child row error is thrown
Hibernate: insert into question (created_on, questionbody, questiontitle, uid, votes) values (?, ?, ?, ?, ?)
Hibernate: insert into tags (tag, tagid) values (?, ?)
Hibernate: insert into question_tag (qid, tagid) values (?, ?)
17:43:36.692 [http-bio-9191-exec-5] ERROR o.h.e.j.s.SqlExceptionHelper#129 Cannot add or update a child row: a foreign key constraint fails (`Forum`.`question_tag`, CONSTRAINT `fk_tags_tagid` FOREIGN KEY (`tagid`) REFERENCES `tags` (`tagid`))
Jul 10, 2018 5:43:36 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] 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
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`Forum`.`question_tag`, CONSTRAINT `fk_tags_tagid` FOREIGN KEY (`tagid`) REFERENCES `tags` (`tagid`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:400)
at com.mysql.jdbc.Util.getInstance(Util.java:383)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:973)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3847)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3783)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2447)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2594)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1901)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2113)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2049)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2034)

Related

Primary key violation when inserting child entity

DB Schema (H2 database):
create table book (
id_book bigint auto_increment not null primary key,
title varchar(255) not null,
id_author bigint not null,
id_genre bigint not null
);
create table comment (
id_comment bigint auto_increment not null primary key,
id_book bigint not null,
comment_text varchar(255) not null
);
Domain classes:
public class Book {
#Id
#Column("id_book")
private Long id;
private String title;
#Column("id_author")
AggregateReference<Author, Long> author;
#Column("id_genre")
AggregateReference<Genre, Long> genre;
#MappedCollection(idColumn = "id_book", keyColumn = "id_comment")
List<Comment> comments = new ArrayList<>();
public void addComment(String commentText) {
comments.add(new Comment(commentText));
}
//getters and setters
}
public class Comment {
#Column("id_comment")
private Long id;
#Column("comment_text")
private String text;
public Comment(String text) {
this.text = text;
}
public Comment() {
}
//getters and setters
}
I have the problem when I add a comment to the book.
```java
#Override
#Transactional
public String addComment(long bookId, String commentText) {
var book = bookRepository.findById(bookId);
return book.map(b -> {
b.addComment(commentText);
b = bookRepository.save(b);
return bookConverter.convertToString(b);
}).orElse("Book not found!");
}
It generates SQL like this...
Executing SQL batch update [INSERT INTO "COMMENT" ("comment_text", "id_book", "id_comment") VALUES (?, ?, ?)]
... adds values for id_comment field like 0, 1, 2, 3 and these values intersect with existing ones. So I get Primary Key Violation. Why it adds id_comment field to the INSERT expression?
Why it adds id_comment field to the INSERT expression?
Because you told it to.
The following annotation tells Spring Data JDBC to store the index of the list in the id_comment column.
#MappedCollection(idColumn = "id_book", keyColumn = "id_comment")
Your data model is missing a column for the list index. Add that column and use it as keyColumn in the #MappedCollection annotation

Spring Data JPA + Postgres - Unable to insert data with One to One mapping

I have a UserInfo and AddressInfo entity classes and they have one to one association. UserInfo uses sequence to add primary key but I am getting below error;
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property
UserInfo Entity
#Entity
#Table(name = "userinfo")
public class UserInfo {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "userid_seq")
#SequenceGenerator(name="userid_seq", allocationSize=1)
private Long userId;
private String firstName;
private String lastName;
private String email;
private String username;
private String password;
#OneToOne(mappedBy = "userInfo", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private AddressInfo addressInfo;
//Getters - Setters
}
AddressInfo Entity
#Entity
#Table(name = "addressinfo")
public class AddressInfo {
#Id
private Long addressId;
private String homeAddress;
private String homeCity;
private String homeState;
private String homeZip;
#OneToOne
#MapsId
#JoinColumn(name = "address_id")
private UserInfo userInfo;
//Getters - Setters
}
UserInfo Table
CREATE TABLE userinfo (
user_id INTEGER NOT NULL DEFAULT nextval('userid_seq') PRIMARY KEY ,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
username VARCHAR(20) NOT NULL,
password VARCHAR(20) NOT NULL
);
AddressInfo Table
CREATE TABLE addressinfo (
address_id INTEGER NOT NULL PRIMARY KEY,
home_address VARCHAR(100) NULL,
home_city VARCHAR(100) NULL,
home_state VARCHAR(100) NULL,
home_zip VARCHAR(100) NULL,
CONSTRAINT fk_addinfo
FOREIGN KEY(address_id)
REFERENCES userinfo(user_id)
);
I thought my sequence had some issues but I could see that if I remove the child entity then its is inserting into the UserInfo table successfully but getting above error when I add OneToOne mapping. Seems like I have issues with my Primary key. I see similar questions but I could not find what is going on here. A help would be really appreciated.
Two solutions come to mind:
A single transaction that inserts both Userinfo and Addressinfo. With the second insert using the sequence currval to define the id.
create table addressinfo (
address_id integer default currval('userid_seq') primary key,
home_address varchar(100) null,
home_city varchar(100) null,
home_state varchar(100) null,
home_zip varchar(100) null,
constraint fk_addinfo
foreign key(address_id)
references userinfo(user_id)
);
do $$
begin
insert into userinfo (first_name, last_name, email, username, password)
values ('Jane','Smith','j.smith#thisplace.org','js','psojHvIEJNB');
insert into addressinfo( home_address, home_city, home_state,home_zip)
values ('1 Joe''s Lane','Smithtown', 'NV', '0123456789asdfgh');
end;
$$;
A single statement handling the insert for both tables:
with newuser (user_id) as
(insert into userinfo (first_name, last_name, email, username, password)
values ('Joe','Smith','j.s.smith#thisplace.org','js2','+949+fsrgwDGKJS58')
returning user_id
) --select id from newuser;
insert into addressinfo(address_id, home_address, home_city, home_state,home_zip)
select user_id,'1 Joe''s Lane','Smithtown', 'NV', '0123456789asdfgh'
from newuser;
However neither is a good plan. A 1:1 relationships are always questionable. In this case they are not. What happens when user Jane Smith stores her address, then insists that her husband, Joe, have the same address. You wind up with DUPLICATE addresses. See example here. You might be better off giving addressinfo its own PK sequence and putting address_id as a column and FK into userinfo

how to fix incompatible foreign key constraints spring boot

I have a spring boot application where I have two entities in a relationship. MeetingsSetting and meetingTime. One MeetingSetting can have multiple meetingTimes and one meetingtime belongs to one Meetingsetting.
I generate the databases through sql script and reference the foreing key their for the meetingTime entity. But I am getting the following error:
Caused by: java.sql.SQLException: Referencing column 'meeting_name' and referenced column 'id' in foreign key constraint 'FK1omm6fk51xdsd0kysqbmleweg' are incompatible.
THe funny thing is I am not referencing the id anywhere not in my entity nor in my script. This is how my entity and script looks like:
meetingSetting:
#Entity
#Table(name = "meeting_settings")
#Data
public class MeetingsSetting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name")
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meeting_Name", cascade = CascadeType.ALL)
private Set<MeetingTime> meetingTime = new HashSet<>();
}
MeetingTime:
#Entity
#Table(name = "meeting_times")
#Data
public class MeetingTime {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#Column(name = "meeting_name",insertable = false, updatable = false )
private String meetingName;
#ManyToOne
#JoinColumn(name = "meeting_name", nullable = false)
private MeetingsSetting meeting_Name;
}
and this is my script where I generate my tables:
-- auto-generated definition
create table meeting_settings
(
id bigint auto_increment
primary key,
meeting_name varchar(255) null,
meeting_pw varchar(255) null,
meeting_url varchar(255) null
);
create table meeting_times
(
id bigint auto_increment
primary key,
meeting_date varchar(255) null,
start_time varchar(255) null,
end_time varchar(255) null,
meeting_name varchar(255) null,
constraint fk_meeting_times_meeting_name
foreign key (meeting_name) references meeting_settings (meeting_name)
);
What could cause such an error? because I am not referencing anywhere the id.
UPDATE:
spring.datasource.url=jdbc:mysql://localhost:3306/coorporate_blinddate?createDatabaseIfNotExist=true&useSSL=true&serverTimezone=UTC
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=../generate.sql
spring.jpa.show-sql= true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto = update
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=Test1234##1&
server.port=8081
Hibernate Sequence:
Hibernate: create table meeting_settings (id bigint not null auto_increment, meeting_name varchar(255), meeting_pw varchar(255), meeting_url varchar(255), primary key (id)) engine=InnoDB
Hibernate: create table meeting_settings_meeting_time (meetings_setting_id bigint not null, meeting_name bigint not null, primary key (meetings_setting_id, meeting_name)) engine=InnoDB
Hibernate: create table meeting_times (id bigint not null auto_increment, meeting_date varchar(255), end_time varchar(255), meeting_name varchar(255), start_time varchar(255), primary key (id)) engine=InnoDB
Hibernate: alter table meeting_settings drop index UK_klg4vqmhi7o9qff83ymly598o
Hibernate: alter table meeting_settings add constraint UK_klg4vqmhi7o9qff83ymly598o unique (meeting_name)
Hibernate: alter table meeting_settings_meeting_time drop index UK_jsn83wsxfkpm1xfencvsdkqj1
Hibernate: alter table meeting_settings_meeting_time add constraint UK_jsn83wsxfkpm1xfencvsdkqj1 unique (meeting_name)
Hibernate: alter table meeting_settings_meeting_time add constraint FK9lq62drkkslq6x381b3lieruu foreign key (meeting_name) references meeting_times (id)
Hibernate: alter table meeting_settings_meeting_time add constraint FKglhgb5vgsviqm7t6vtmdx5e7t foreign key (meetings_setting_id) references meeting_settings (id)
Hibernate: alter table meeting_times add constraint FK1omm6fk51xdsd0kysqbmleweg foreign key (meeting_name) references meeting_settings (id)
A table which is generated
meeting_settings_meeting_time
meetings_setting_id
meeting_name
PRIMARY
UK_jsn83wsxfkpm1xfencvsdkqj1
FK9lq62drkkslq6x381b3lieruu
FKglhgb5vgsviqm7t6vtmdx5e7t
UK_jsn83wsxfkpm1xfencvsdkqj1
Looks like your meeting_name column is not unique in order to do the relationship you have done. Add unique = true
#Column(name = "meeting_name", unique = true)
private String meetingName;
And
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(inverseJoinColumns=#JoinColumn(name="meeting_name"))
private Set<MeetingTime> meetingTime = new HashSet<>();

#CollectionTable does not connect

i have the two tables
Users Table
create table users (
usernumber int unsigned not null auto_increment primary key,
username varchar(32) not null,
password varchar(72) not null,
displayname varchar(32) not null,
birthday datetime not null,
email varchar(255) not null,
firstname varchar(32) not null,
lastname varchar(32) not null
);
Users Locked Table
create table users_locked (
idx int unsigned not null auto_increment primary key,
usernumber int unsigned not null,
start_date datetime not null,
end_date datetime not null,
memo varchar(32) default '' not null,
foreign key (usernumber) references users(usernumber) on delete cascade
);
and i used #CollectionTable to create the entity associated with the two table
following...
Users.java
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Entity
#Table(name = "users")
#Access(AccessType.FIELD)
public class Users {
#EmbeddedId
private UsersID usernumber;
#Column(name = "username", unique = true)
private String username;
#Column(name = "password")
private String password;
#Column(name = "displayname")
private String displayname;
#Embedded
private PersonalInformation personal;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "users_locked", joinColumns = #JoinColumn(name = "usernumber"))
private List<LockPeriod> locked;
// ....
}
LockPeriod.java
#Getter
#Builder
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Embeddable
public class LockPeriod {
#Column(name = "start_date")
private Date start;
#Column(name = "end_date")
private Date end;
#Column(name = "memo")
private String memo;
//....
}
but the following error occurs and can not be mapped :
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
did I do something wrong?
I am sorry for the lack of English.

extra NULL item in spring-jpa one-to-many list attribute

spring-jpa with hibernate and mysql 5.7 (dependency managed by spring platform-bom-1.1.4.RELEASE)
Entity class:
#Entity
#Table(name = "activity")
public class ActivityEntity
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
// optional
#Column(name="project_id")
private Integer projectId;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="container_id")
private ActivityEntity container;
#OneToMany(mappedBy="container", cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
#OrderColumn(name="order")
private List<ActivityEntity> activities;
private String name;
private String description;
...
}
DAO class:
public interface IActivityDao extends JpaRepository<ActivityEntity, Integer>
{
List<ActivityEntity> findByContainerId(int containerId);
}
Database table:
CREATE TABLE `activity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`project_id` bigint(20) DEFAULT NULL,
`container_id` bigint(20) DEFAULT NULL,
`name` varchar(128) NOT NULL,
`description` varchar(256) DEFAULT NULL,
`type` varchar(32) NOT NULL,
`mode` varchar(32) NOT NULL,
`tag` varchar(32) DEFAULT NULL,
`script` longtext,
`order` int(11) DEFAULT '1',
`created_user_id` varchar(45) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`updated_user_id` varchar(45) DEFAULT NULL,
`updated_date` datetime DEFAULT NULL,
`projectId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `container_id` (`container_id`),
CONSTRAINT `fk_container_id` FOREIGN KEY (`container_id`) REFERENCES `activity` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=214 DEFAULT CHARSET=utf8;
Database table content:
Unit Test Code:
public class ActivityDaoTest extends AbstractDaoTest
{
#Resource
private IActivityDao activityDao;
#Test
public void testFindOne() throws Exception {
ActivityEntity activity = activityDao.findOne(210);
ActivityEntity root = activity.getContainer();
int level = 0;
printActivity(activity, level);
Assert.assertNotNull(activity);
if (root != null) {
level = 0;
printActivity(root, level);
}
}
...
}
In the above test code, root.getActivities() and activity.getActivities() return 5 and 4 items respectively, with the first item to be null.
Do I miss something in the entity? Thanks for help.
I'll answer my won question (posted too early):
Remove the order attribute from the Entity
The value of the order column in the table starts from 0 (the NULL item is for the index of 0 in the list)
That solves my problem.

Resources