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

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

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) - Insert into One table, reading the foreign key from a table that contains static data

I am currently working with Spring Data JPA, specifically Postgres. Our database is highly Normalized. In some scenarios we have some tables that contains static data. In that case if I am going to insert into table address for example and table address_type contains the static data, I would need to insert the Primary Key of table address_type in a column of table address, to make a Foreign Key reference from table address to table address_type(static data).
Example:
Database Code
create table address_type(
id Serial Primary Key,
type char(3),
description (100)
);
insert into address_type(id, type, description) values(1, 'PRIMARY', 'Primary description');
insert into address_type(id, type, description) values(2, 'SECONDARY', 'Secondary description');
Relation (1 - N)
create table address(
id Serial Primary Key,
address varchar(50) not null,
address_type_id integer references address_type(id)
);
insert into address(id, address, address_type) values(1, 'address somewhere 1', 1);
insert into address(id, address, address_type) values(2, 'address somewhere 2', 1);
insert into address(id, address, address_type) values(3, 'address somewhere 3', 2);
Spring Data JPA Code
#Table(name = "address_type")
public class AddressType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "type")
private String type;
#Column(name = "description")
private String description;
#OneToMany(mappedBy = "addressType")
private Address address;
// Getters and Setters (Lombok)
}
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "type")
private String address;
#Column(name = "address_type_id")
#JoinColumn(---> logic?????????? <-----)
#ManyToOne(---> logic?????????? <-----)
private AddressType addressType;
// Getters and Setters (Lombok)
}
I guess my question is how should I need to setup the logic inside the #JoinColumn and #ManyToOne annotations in the Address entity?
P.D. The Insert should only happen in table address, Spring Data should only read from table address_type to get the foreign key to be stored in the address table.
I got the answer after a lot of research and try/error.
The configuration inside the annotations are the following:
In class Address
#Column(name = "address_type_id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "address_type_id", referencedColumnName = "id")
private AddressType addressType;
Then in AddressType, you should always have a mapped by reference to the child table.
#OneToMany(mappedBy = "addressType")
private Address address;
One IMPORTANT thing to remember, is that before saving back to the database, you should fetch in the database for the entity you want to refer in your child table, so that Spring Data JPA recognize that this is NOT a new record in AddressType table, BUT just a reference and saves the foreign key in the Address table.

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<>();

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

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)

Hibernate/JPA, field 'accreditation_company_id' doesn't have a default value error

I faced the issue described in the title saving my entity though everything in code and db tables seems ok.
Here is my code:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "company_id", referencedColumnName = "id")
List<CompanyObject> companyObjects;
}
#Entity
public class CompanyObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Enumerated(EnumType.STRING)
ObjectType type;
}
Here is my table definitions:
CREATE TABLE `company` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8
CREATE TABLE `company_object` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`type` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK__company` (`company_id`),
CONSTRAINT `FK__company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Object I'm trying to save contains the following info:
Company(id=32, companyObjects=[CompanyObject(id=null, type=NEW)])
Here is the code I use to save the object:
Controller method:
#RequestMapping(value = "/company/{id}", method = RequestMethod.POST)
public String editCompany(#PathVariable("id") long companyId,
#ModelAttribute("form") CompanyDto form) {
Company company = companyService.getCompanyById(companyId);
companyService.updateCompany(company, form);
return "redirect:/companies";
}
Service method:
#Transactional
public Company updateCompany(Company company, final CompanyDto form) {
company.getCompanyObjects().clear();
company.getCompanyObjects().addAll(form.getCompanyObjects());
return companyRepository.save(company);
}
Am I getting this right that hibernate automatically generate and populate all the missing ids in these objects? If yes what am I missing and why the error appears?
You have some problems here. Firstly, your table definitions are wrong, check your accreditation_object table, you have a foreign key there which references a column that doesn't exist: company_id should be accreditation_company_id.
(or is it just some copy-paste error?)
Secondly, your entities are wrong, try this way:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#OneToMany(mappedBy="company", cascade = CascadeType.ALL)
Set<CompanyObject> companyObjects;
}
#Entity
public class CompanyObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Enumerated(EnumType.STRING)
ObjectType type;
#ManyToOne
#JoinColumn(name = "company_id")
Company company;
}
Note the #ManyToOne annotation in the CompanyObject. If I understand correctly, you want to assign one or more CompanyObject to a Company, thus you have to have a #ManyToOne annotated Company typed field in the CompanyObject.
Now, if you want to save these objects, first save the Company instance, then iterate over the list of CompanyObjects, set the previously saved company instance, and then save the CompanyObjects, something like this:
Company company = new Company();
companyDao.persist(company);
List<CompanyObject> companyObjects = new ArrayList<>();
// populate the list somehow
// ...
for(CompanyObject obj: companyObjects){
obj.setCompany(company);
companyObjectDao.persist(obj);
}
Your updateCompany method is wrong, you should try something like the above code. (I cannot rewrite your example because it looks like something is missing there. What is CompanyDTO?)
Edit: You can use cascade saving (note: I've updated the Company entity), just be sure to set the Company instance to every CompanyObject instance, like:
#Transactional
public Company updateCompany(Company company, final CompanyDto form) {
company.getCompanyObjects().clear();
company.getCompanyObjects().addAll(form.getCompanyObjects());
for(CompanyObject obj : company.getCompanyObjects()){
obj.setCompany(company);
}
return companyRepository.save(company);
}
I think this should work, but I'm not a 100% sure. Give it a try.

Resources