JPA Mapping issue - spring-boot

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;

Related

Why can’t I insert an entity twice by using Spring Data Jdbc?

my entity:
#Table("user")
public class User {
#Id
private Long user_id;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
public Long getUser_id() {
return user_id;
}
public void setUser_id(Long user_id) {
this.user_id = user_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;
}
}
my repository:
public interface UserRepository extends CrudRepository<User, Long> {
#Query("select * from user where username = :username")
User findByUsername(#Param("username") String username);
}
my sql for creating the user table:
CREATE TABLE `user` (
`user_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` text NOT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `UINQUE_USERNAME`(`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
User userForRegister = new User(username, passwordEncoder.encode(password));
userRepository.save(userForRegister)
If I execute the line of 'userRepository.save(userForRegister)', I will insert an entity successfully the first time.
But, if I want to insert another user entity with different username, i will get an error:
2021-01-08 21:37:38.242 INFO 11180 --- [nio-8080-exec-8] c.k.centre.controller.UserController : Failed to execute DbAction.InsertRoot(entity=com.***.***.Entity.User#65bc9ea1, generatedId=null)
I can insert it until I delete all the data of user table.
Is there any point I missed?
I think that GenerateValue would solve the problem
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long user_id;
Also I think you should map your ther fields to database columns using #Column annotation
#Column(name = "user_id")
private Long user_id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
Try adding #GeneratedValue(strategy = GenerationType.IDENTITY) to your user_id. This tells Hibernate that id is generated by your database. You configured your database primary key as autoincrement Column. Take also a look here.

Hibernate didn't create one of tables

I'm learning spring..., I build a simple application.
I have a problem because hibernate does not want to generate one of the models..
I do not know what's wrong
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name="access_card")
public class AccessCard implements Serializable{
private static final long serialVersionUID = -4015209774835055079L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="key")
private String key;
#Column(name="enabled")
private Boolean enabled;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnore
private User user;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Other classes are written similarly and everything works.
Error:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error
executing DDL "create table access_card (id bigint not null, enabled
bit, key varchar(255), user_id bigint not null, primary key (id))
engine=MyISAM" via JDBC Statement
And properties
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
Follow your comment, The problem pointed out at here
key varchar(255)
The table name is reserved keyword.
See https://hibernate.atlassian.net/browse/HHH-4453
If you still want it, the solution is https://vladmihalcea.com/escape-sql-reserved-keywords-jpa-hibernate/

Creating One to Many relation in JPA Spring

I would like to link a comment table and a film table with a user table. I wish to allow a user to have many comments, and a film have many comments. I then want to display a list of comments in a details page for each film, giving the option for the user who created the comment to delete or update it.
I altered my code in an attempt to create a one to many relation between comment and film, but I get the error:
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column
"FILM_ID"; SQL statement: alter table film add column film_id bigint
not null [23502-196]
It makes me think two things:
1) Set to allow null or figure out why there is a null field. I attempted allow null by adding #Column(name = "film_id", nullable = true) but it said parameter is redundant.
2) Film table has auto incrementing ID already, so by adding #Column(name = "film_id") am I duplicating an ID? As with the error message saying "add column" it made me think so?
My attempt currently stands at:
Film.java
package com.demo.spring.domain;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
#Entity
public class Film {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "film_id", nullable = true)
Long id;
String title;
String director;
String description;
#DateTimeFormat(pattern="yyyy-MM-dd")
Date date;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "film_id", referencedColumnName = "film_id")
List<Comment> comments;
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//rest of getter and setters below//
Comment.java
package com.demo.spring.domain;
import javax.persistence.*;
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "comment_id")
Long id;
String body;
#Column(name = "film_id")
Long filmId;
public Long getFilmId() {
return filmId;
}
public void setFilmId(Long filmId) {
this.filmId = filmId;
}
public Comment(){
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
UPDATE
I have changed Film.java..
From:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "film_id", referencedColumnName = "film_id")
List<Comment> comments;
To:
#OneToMany(cascade = CascadeType.ALL)
List<Comment> comments;
And if I add in Comment.java:
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="film", joinColumns=#JoinColumn(name = "film_id_fk", referencedColumnName = "film_id"))
private Set<Comment> comment = new HashSet<Comment>();
Film film;
I get:
MappingException: Foreign key
(FK5vk85sy54a8be115ye9ra1lyu:film_comments [film_film_id])) must have
same number of columns as the referenced primary key (film
[film_id_fk,comment_comment_id])
If I change private Set<Comment> comment = new HashSet<Comment>(); to List<Comment> comments = new ArrayList<Comment>(); I get:
NULL not allowed for column "FILM_ID"; SQL statement: alter table film
add column film_id bigint not null
And if instead I add:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name = "film_id_fk", referencedColumnName = "film_id")
private Set<Comment> comment = new HashSet<Comment>();
Film film;
I get:
MappingException: Could not determine type for:
com.demo.spring.domain.Film, at table: comment, for columns:
[org.hibernate.mapping.Column(film)]
If I change private Set<Comment> comment = new HashSet<Comment>(); to List<Comment> comments = new ArrayList<Comment>(); I get:
NULL not allowed for column "FILM_ID"; SQL statement: alter table film
add column film_id bigint not null
A primary key can't be null, so you can't make "film_id" nullable. And your #JoinColumn annotation is wrong, that goes on the #ManyToOne side. The name parameter should be the name of the foreign key column in the Comments table (so it can't be the same name as the primary key) and referencedColumnName should be the name of the column that you're referencing in the other table
#Entity
public class Film {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "film_id")
Long id;
String title;
String director;
String description;
#DateTimeFormat(pattern="yyyy-MM-dd")
Date date;
#OneToMany(cascade = CascadeType.ALL)
List<Comment> comments;
//...
}
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "comment_id")
Long id;
String body;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "film_id_fk", referencedColumnName = "film_id")
Film film;
//...
}

Unable to fetch the data from the database when tables are mapped by many to many

I am unable to fetch the users data from the database, and I also wonder how to handle the rest request in many to many cases for the same scenario.
I am using Spring Boot and Spring Data JPA. My code for the database is below:
CREATE TABLE `m3_group` (
`GROUP_ID` bigint(11) NOT NULL AUTO_INCREMENT,
`GROUP_NAME` varchar(30) DEFAULT NULL,
`GROUP_CREATED_DATE` datetime DEFAULT NULL,
`GROUP_ADMIN` varchar(14) DEFAULT NULL,
PRIMARY KEY (`GROUP_ID`)
)
ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `m3_user` (
`USER_ID` bigint(11) NOT NULL AUTO_INCREMENT,
`USER_NAME` varchar(50) DEFAULT NULL,
PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=330 DEFAULT CHARSET=utf8;
CREATE TABLE `m3_user_group` (
`GROUP_USER_ID` bigint(11) DEFAULT NULL,
`GROUP_ID` bigint(11) DEFAULT NULL,
KEY `FK1_GROUP_ID` (`GROUP_ID`),
KEY `FK2_USER_ID` (`GROUP_USER_ID`),
CONSTRAINT `FK1_GROUP_ID` FOREIGN KEY (`GROUP_ID`) REFERENCES `m3_group` (`GROUP_ID`),
CONSTRAINT `FK2_USER_ID` FOREIGN KEY (`GROUP_USER_ID`) REFERENCES `m3_user` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#Entity
#Table(name = "M3_USER")
public class User implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID")
private long userId;
#NotBlank
#Column(name = "USER_NAME")
private String userName;
//many-to-many
#ManyToMany(mappedBy="listOfUsers",fetch=FetchType.EAGER)
private List<Group> listOfGroup=new ArrayList<Group>();
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<Group> getListOfGroup() {
return listOfGroup;
}
public void setListOfGroup(List<Group> listOfGroup) {
this.listOfGroup = listOfGroup;
}
}
#Entity
#Table(name="M3_GROUP")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="GROUP_ID")
private long groupId;
#Column(name="GROUP_NAME")
private String groupName;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
#Column(name="GROUP_CREATED_DATE")
#JsonFormat(locale = "en-IN", shape = JsonFormat.Shape.STRING, pattern =
"yyyy-MM-dd HH:mm", timezone = "GMT+5:30")
private Date groupCreatedDate;
#Column(name="GROUP_ADMIN")
private String groupAdminMobileNumber;
//many-to-many
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name = "M3_USER_GROUP", joinColumns = #JoinColumn(name =
"GROUP_USER_ID") , inverseJoinColumns = #JoinColumn(name = "GROUP_ID") )
private List<User> listOfUsers=new ArrayList<User>();
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Date getGroupCreatedDate() {
return groupCreatedDate;
}
public void setGroupCreatedDate(Date groupCreatedDate) {
this.groupCreatedDate = groupCreatedDate;
}
public String getGroupAdminMobileNumber() {
return groupAdminMobileNumber;
}
public void setGroupAdminMobileNumber(String groupAdminMobileNumber) {
this.groupAdminMobileNumber = groupAdminMobileNumber;
}
public List<User> getListOfUsers() {
return listOfUsers;
}
public void setListOfUsers(List<User> listOfUsers) {
this.listOfUsers = listOfUsers;
}
}
#Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
List<Group> findByGroupId(long groupid);
}
#RestController
public class GroupController
{
#Autowired
GroupRepository groupRepository;
#RequestMapping(value="/find/{groupId}",method=RequestMethod.POST)
public ResponseEntity<List<Group>> find(#PathVariable String groupId)
{
long id=Long.parseLong(groupId);
List<Group> group = groupRepository.findByGroupId(id);
System.out.println(group.toString());
return new ResponseEntity<List<Group>>(group,HttpStatus.OK);
}
}
I have mapped the user and group table with many to many bidirectional and i am trying to fetch the data i.e users associated with the groupId but listOfUsers is showing empty.
My rest request is:
Http ://localhost:5000/find/1
And the response is:
[
{
"groupId": 1,
"groupName": "Om namo raghavendra",
"groupCreatedDate": "2017-05-17 12:48",
"groupAdminMobileNumber": "917676060664",
"listOfUsers":[
]
}
]
listOfUsers is empty, so I want to users by using groupId.

Spring - Hibernate ManyToMany in Restful app

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!

Resources