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
Related
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
getting
com.microsoft.sqlserver.jdbc.SQLServerException: Invalid column name
'partnerIdPartner'.
application.properties:
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Table creation:
CREATE TABLE [partnersystem] (
[idPartnerSystem] INT IDENTITY(1,1) ,
[partner_idPartner] INT NOT NULL DEFAULT NULL ,
[Name] NVARCHAR(45) NULL DEFAULT NULL ,
[Domain] NVARCHAR(45) NULL DEFAULT NULL ,
[Code] NVARCHAR(45) NULL DEFAULT NULL ,
[PartnerSystem_idSystem] INT NOT NULL DEFAULT NULL ,
[UpdateUser] NVARCHAR(45) NULL DEFAULT NULL ,
[UpdateDT] DATETIME NULL DEFAULT NULL ,
CONSTRAINT [partnersystem_PRIMARY] PRIMARY KEY CLUSTERED ([idPartnerSystem]), CONSTRAINT [partnersystem_fk_PartnerSystem_partner] FOREIGN KEY ("partner_idPartner") REFERENCES "partner" ( "idPartner" ) ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT [partnersystem_fk_PartnerSystem_System] FOREIGN KEY ("PartnerSystem_idSystem") REFERENCES "system" ( "idSystem" ) ON UPDATE NO ACTION ON DELETE NO ACTION);
CREATE INDEX [partnersystem_fk_PartnerSystem_partner] ON [partnersystem] ([partner_idPartner]);
CREATE INDEX [partnersystem_fk_PartnerSystem_System] ON [partnersystem] ([PartnerSystem_idSystem]);
JPA Entity:
#Entity
#Table(name = "partnersystem")
public class PartnerSystem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idPartnerSystem")
private int idPartnerSystem;
#Column(name = "partner_idPartner" )
private int partnerIdPartner;
#Column(name = "Name")
private String name;
#Column(name = "Domain" )
private String domain;
#Column(name = "Code" )
private String code;
#Column(name = "PartnerSystem_idSystem" )
private int partnerSystemIdSystem;
#Column(name = "UpdateUser" )
private String updateUser;
my repository:
#Repository
public interface PartnerSystemRepository extends JpaRepository<PartnerSystem,
Integer>{
public PartnerSystem findByPartnerIdPartner(int partnerIdPartner);
}
executing simple query throws an error.
public List<Object[]> findAllPartnerSystem(int id) {
String test =
"SELECT idPartnerSystem, partnerIdPartner, name, domain, code, partnerSystemId" +
" FROM PartnerSystem " +
"WHERE partnerIdPartner = ?"
;
Query query = em.createNativeQuery(test);
query.setParameter(1, id);
List<Object[]> results = query.getResultList();
for (Object[] row : results) {
}
return results;
}
In native queries you have to use the column name not the property name:
"SELECT idPartnerSystem, partner_idPartner, name, domain, code, PartnerSystem_idSystem" +
" FROM partnersystem " +
"WHERE partner_idPartner=
But I suggest using JPQL queries and not native queries.
I have the following tables
CREATE TABLE APPUSERS (
APPUSERS_ID INT IDENTITY(1,1),
USERNAME VARCHAR(254) NOT NULL,
PASSWORD VARCHAR(100) NOT NULL,
PRIMARY KEY (USERNAME)
);
CREATE TABLE ALL_ROLES (
ROLE_ID INT IDENTITY(1,1),
ROLENAME VARCHAR(100) NOT NULL,
PRIMARY KEY (ROLENAME)
);
CREATE TABLE USER_ROLES(
USER_ROLE_ID INT IDENTITY(1,1),
USERNAME VARCHAR(254) NOT NULL,
CONSTRAINT FK_USERNAME FOREIGN KEY (USERNAME)
REFERENCES APPUSERS (USERNAME),
ROLENAME VARCHAR(100) NOT NULL,
CONSTRAINT FK_ROLENAME FOREIGN KEY (ROLENAME)
REFERENCES ALL_ROLES (ROLENAME),
PRIMARY KEY (username,rolename)
)
I have created the corresponding Entities(See below) and Repositories
#Entity
#Table(name = "appusers")
public class User {
private Long id;
private String username;
private String password;
private String passwordConfirm;
private Set<Role> roles;
#Id
#Column(name="APPUSERS_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Transient
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
#ManyToMany
#JoinTable(name = "USER_ROLES", joinColumns = #JoinColumn(name = "USERNAME"), inverseJoinColumns = #JoinColumn(name = "ROLENAME"))
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
And
#Entity
#Table(name = "USER_ROLES")
public class Role {
private Long id;
#Column(name="USERNAME")
private String name;
private Set<User> users;
#Id
#Column(name="USER_ROLE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
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;
}
#ManyToMany(mappedBy = "roles")
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
When I start the application I get the following error
Foreign key (FKrs04la1w0u7vtog85q1hxlse9:user_roles [rolename])) must have same number of columns as the referenced primary key (user_roles [username,rolename])
I am not able to figure what what is the issue here. Any help is greatly appreciated.
I think the table mappings are all correct but not sure why this error is occurring.
There are a couple of issues on your code, let me explain for steps:
Relationship many to many, you need to create an intermediate table in order to do that so you need to fix these following aspects:
User entity
#Id
#Column(name = "APPUSERS_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(mappedBy = "users")
private Set<Role> roles;
Role entity
#Id
#Column(name = "USER_ROLE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany
#JoinTable(name = "role_user",
joinColumns = { #JoinColumn(name = "role_id") },
inverseJoinColumns = { #JoinColumn(name = "user_id") })
private Set<User> users;
If you want these entities are generated on database by JPA hibernate just put the following property configuration(you just need to create database with name).
spring.jpa.hibernate.ddl-auto=update
Else I leave you here scripts to execute on database.
-- Table: public.appusers
-- DROP TABLE public.appusers;
CREATE TABLE public.appusers
(
appusers_id bigint NOT NULL,
password character varying(255) COLLATE pg_catalog."default",
password_confirm character varying(255) COLLATE pg_catalog."default",
username character varying(255) COLLATE pg_catalog."default",
CONSTRAINT appusers_pkey PRIMARY KEY (appusers_id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.appusers
OWNER to postgres;
-- Table: public.role_user
-- DROP TABLE public.role_user;
CREATE TABLE public.role_user
(
role_id bigint NOT NULL,
user_id bigint NOT NULL,
CONSTRAINT role_user_pkey PRIMARY KEY (role_id, user_id),
CONSTRAINT fkma2afyyxc0mraogwivmj0klfe FOREIGN KEY (role_id)
REFERENCES public.user_roles (user_role_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fkmhbomge36ygro6rth9negs1ye FOREIGN KEY (user_id)
REFERENCES public.appusers (appusers_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.role_user
OWNER to postgres;
-- Table: public.user_roles
-- DROP TABLE public.user_roles;
CREATE TABLE public.user_roles
(
user_role_id bigint NOT NULL,
username character varying(255) COLLATE pg_catalog."default",
CONSTRAINT user_roles_pkey PRIMARY KEY (user_role_id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public.user_roles
OWNER to postgres;
i have my spring app running with two entities Article and Category.
I integrated RestResponses and it works all fine. Next i added a ManyToMany Relationship to these entities and my rest responses return 404.
I will show you my configuration:
DROP TABLE IF EXISTS `articles`;
create table `articles` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
DROP TABLE IF EXISTS `categories`;
create table `categories` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
DROP TABLE IF EXISTS `categories_articles`;
CREATE TABLE IF NOT EXISTS `categories_articles` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`categoryID` int(11) UNSIGNED NOT NULL,
`articleID` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categoryArticleID` (`categoryID`, `articleID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
ALTER TABLE `categories_articles`
ADD CONSTRAINT `categories_articles_fk_1` FOREIGN KEY (`categoryID`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `categories_articles_fk_2` FOREIGN KEY (`articleID`) REFERENCES `articles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
Article.java:
#Entity
#Table(name = "articles")
public class Article {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="name")
private String name;
private List<Category> categories = new ArrayList<Category>();
public Article() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#ManyToMany(mappedBy = "articles")
public List<Category> getCategories() {
return categories;
}
public void setCategories(List<Category> categories) {
this.categories = categories;
}
}
Category.java:
#Entity
#Table(name = "categories")
public class Category {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="name")
private String name;
private List<Article> articles = new ArrayList<Article>();
public Category() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "categories_articles",
joinColumns = #JoinColumn(name = "categoryID", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "articleID", referencedColumnName = "id"))
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
}
}
And my RestController:
#GetMapping("/rest/categories")
public RestResponse<Category> list() {
List<Category> data = categoryService.list();
RestResponse<Category> restResponse = new RestResponse<Category>(true, data.size(), data);
return restResponse;
}
where RestResponse is just a simple PoJo:
public class RestResponse<T> {
private Boolean success;
private Integer count;
private List<T> data;
public RestResponse(Boolean success, Integer count, List<T> data) {
this.success = success;
this.count = count;
this.data = data;
}
// getters and setters
}
Sooo, as soon as i comment the many-to-many part and load my app, it all works fine.. but when i uncomment the many-to-many part, i get 404..
I have no idea why, could anybody help me with this issue?
thanks and greetings!
I'm new to Spring. Try to save following model to mysql using JPA
#Table(name = "PRODUCTORDER")
public class Order extends AbstractPersistable<Long> {
#Length(min = 4, max = 30)
private String name;
#Length(min = 4, max = 50)
private String address;
#NotEmpty
private String[] items; // error for this
// only items setter and getter shown
public String[] getItems() {
return items;
}
public void setItems(String[] items) {
this.items = items;
}
}
I have a table in mySQL as :-
CREATE TABLE PRODUCTORDER (
id int(6) NOT NULL AUTO_INCREMENT,
name varchar(30) NOT NULL,
address varchar(50) NOT NULL,
items varchar(50) not null,
PRIMARY KEY(id)
)
ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
When I tried to save data I get this error:-
org.springframework.orm.jpa.JpaSystemException:
org.hibernate.exception.GenericJDBCException: Incorrect string value: '\xAC\xED
\x00\x05ur...' for column 'items' at row 1; nested exception is
javax.persistence.PersistenceException:org.hibernate.exception.GenericJDBCException:
Incorrect string value: '\xAC\xED\x00\x05ur...' for column 'items' at row 1