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

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.

Related

JPA Mapping issue

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;

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.

why I can't use string as id

I am trying to create a user model with a CrudRepository:
#Entity
public class User {
#Id
#GeneratedValue
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
public interface UserRepository extends CrudRepository<User, String> {
}
However I got an 500 error every time I call findOne():
#Controller
public class UserController {
#Autowired
private UserRepository users;
#Override
#RequestMapping(value="/register", method=RequestMethod.POST)
public #ResponseBody User register(#RequestBody User userToRegister) {
String username = userToRegister.getUsername();
User user = users.findOne(id);
if (user != null) {
return null;
}
User registeredUser = users.save(userToRegister);
return registeredUser;
}
}
However if I just switch to an long type id instead of username itself then everything works. I think it's common to use string as id. So how to make this work?
I use the embedded hsql database. I didn't wrote any sql code.
The problem is that String username; is annotated with both #Id and #GeneratedValue. #Id means that is should be a primary key, fine it can be a String. But #GeneratedValue means that you want the system to automatically generate a new key when you create a new record. That's easy when the primary key is integer, all databases have a notion of sequence (even if the syntax is not always the same). But if you want String automatically generated keys, you will have do define your own custom generator.
Or if you have no reason for the #GeneratedValue annotation, simply remove it as suggested by Bohuslav Burghardt
Use column annotation like below by putting nullable False.
#Id
#GeneratedValue
#Column(name = "username", nullable = false)
private String username;

Spring Security + JPA user schema

As per Spring documentation if you need to manage spring security via database you should have some standard schema of tables. for example.
create table users(
username varchar(256) not null primary key,
password varchar(256) not null,
enabled boolean not null
);
create table authorities (
username varchar(256) not null,
authority varchar(256) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
The problem I am facing is following.
1) Not able to understand how could I achieve such schema of tables using JPA?
I have tried something as follows.
#Entity
#Table(name="USERS")
public class UsersPersistence extends Users implements Serializable{
private static final long serialVersionUID = 1009548075747154488L;
public UsersPersistence() {
super();
}
public UsersPersistence(long id, String userName, String password, boolean enabled) {
super(id, userName,password,enabled);
}
#Id
#GeneratedValue
#Column(name="id")
#Override
public long getId() {
return super.getId();
}
#Column(name="username", nullable=false)
#Override
public String getUserName() {
return super.getUserName();
}
#Column(name="password", nullable=false)
#Override
public String getPassword() {
return super.getPassword();
}
#Column(name="enabled", nullable=false)
#Override
public boolean isEnabled() {
return super.isEnabled();
}
}
This table created as per requirement stated in Spring documentation schema.
Problem in understanding is when i am trying to assign a foreign key on username in authorities table.Since JPA assign the foreign key's via the id of parent table (primary key Table) Or may be i do not know how to assign it.
Following is the JPA class which create problem :-
#Entity
#Table(name="AUTHORITIES")
public class AuthoritiesPersistence extends Authorities implements Serializable{
private static final long serialVersionUID = 1L;
public AuthoritiesPersistence() {
super();
}
public AuthoritiesPersistence(long id, UsersPersistence userName, String authority) {
super(id,userName,authority);
}
#Id
#GeneratedValue
#Column(name="id")
#Override
public long getId() {
return super.getId();
}
#Override
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="username", nullable=false)
public UsersPersistence getUserName() {
return (UsersPersistence) super.getUserName();
}
#Column(name="authority", nullable=false)
#Override
public String getAuthority() {
return super.getAuthority();
}
}
This table is created successfully but Spring security authentication is not able to recognize the username because JPA uses the foreign key id than the actual user name.
Any help would be appreciable. I am really stuck in creating a foreign key which will be based on the username rather than the id.
Thanks
You only have to stick to the schema given in Spring Security reference docs, when using a default JdbcDaoImpl as UserDetailsService implementation, which is the case if you have the <jdbc-user-service> tag in your security configuration. Even then it is possible to override the default SQL queries it uses to fetch users and authorities (refer to the namespace appendix).
However if you manage user accounts using hibernate, it would make sense to write your own UserDetailsService implementation, instead of trying to create JPA entities that result in the specific schema required by the JdbcDaoImpl.
The documentation states as well:
If your application does use an ORM tool, you might prefer to write a custom UserDetailsService to reuse the mapping files you've probably already created.
There are a couple of ways you could handle this:
You could write your own AuthenticationProvider using Hibernate for
DB access
You could use the <jdbc-user-service> and overwrite the SQL queries as mentioned by #zagyi. (http://static.springsource.org/spring-security/site/docs/current/reference/appendix-namespace.html#nsa-jdbc-user-service)
Or you can create the your schema to fit the standard <jdbc-user-service>
To use the approach you are interested in you have to know that aside from having the fields username, password and enabled spring security expects the username to be a unique identifier. This means that you can use the username property of your entity as Id for your DB and Hibernate.
If you don't want to do this a way of approaching this is to set a table wihch defines the authorites using an ID/Name and the authority. And then to set up the use a jointable to map them to the users.
Some untested examplecode:
Role:
#Entity
#DynamicUpdate
#Table(name ="authorities")
public class Authority{
private String authority;
#Id
#Column(name="authority")
public String getAuthority() {
return authority;
}
User:
#Entity
#DynamicUpdate
#Table(name = "users", uniqueConstraints={ #UniqueConstraint(columnNames={"username"})})
public class User {
private String username;
private List<Authority> authorities;
#Type(type = "numeric_boolean")
private boolean enabled;
#Id
#Column(name="username")
public String getUsername() {
return username;
}
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "authorities",
joinColumns = #JoinColumn(name = "username"),
inverseJoinColumns = #JoinColumn(name = "rolename")
)
public List<Authority> getauthorities() {
return authorities;
}
#Column(name="ENABLED")
public boolean isEnabled() {
return enabled;
}
When the base is running you can add properties for internal use as u like.
I have figure out the Alternative which is "Configuring the JdbcUserDetailsManager to use custom SQL queries" at least i can create my tables via JPA and Can hope
" users-by-username-query and authorities-by-username-query " would do my work indeed.
To achieve it I have to add following schema .
create table custom_user_authorities (
id bigint identity,
user bigint not null,
authority varchar(256) not null,
);
this schema have id(which will be auto-incremented) which will definitely work for JPA.
I was able to map those tables using the following class definition:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column
private boolean enabled;
#Column
private String firstName;
#ElementCollection
#JoinTable(name = "authorities", joinColumns = {#JoinColumn(name = "email")})
#Column(name = "authority")
private Set<String> roles;
public User() {
}
public Serializable getId() {
return username;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setPassword(String password) {
this.password = password;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<String> getRoles() {
return roles;
}
public void setRoles(Set<String> roles) {
this.roles = roles;
}
}

Hibernate exception - hibernate.internal.QueryImpl cannot be cast

I'm trying to authenticate with a database username. So far the error is:
Your login attempt was not successful, try again.
Reason: org.hibernate.internal.QueryImpl cannot be cast to com.**.**.model.UserEntity
The query in the dao class
#Repository
public class UserEntityDAOImpl implements UserEntityDAO{
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getCurrentSession() {
return this.sessionFactory.getCurrentSession();
}
#Override
public UserEntity getUserByName(String username) {
// TODO Auto-generated method stub
UserEntity userEntity = (UserEntity)
sessionFactory.getCurrentSession().createQuery(
"select u from UserEntity u where u.username = '' + username + ''");
return userEntity;
}
service
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
#Autowired
private UserEntityDAO userEntityDAO;
#Autowired
private Assembler assembler;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
UserDetails userDetails = null;
UserEntity userEntity = userEntityDAO.getUserByName(username);
if (userEntity == null)
throw new UsernameNotFoundException("user not found");
return assembler.buildUserFromUser(userEntity);
}
}
DB table that holds the user details
/*Table structure for table `user` */
CREATE TABLE `user` (
`user_id` INT(11) NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL DEFAULT NULL ,
`password` VARCHAR(45) NOT NULL ,
`username` VARCHAR(45) NOT NULL ,
`active` TINYINT(1) NOT NULL ,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
Model
#Entity
#Table(name = "user", schema = "")
#Component
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "user_id")
private Integer id;
#Column(name = "name")
private String name;
#Basic(optional = false)
#Column(name = "password")
private String password;
#Basic(optional = false)
#Column(name = "username")
private String username;
#Basic(optional = false)
#Column(name = "active")
private boolean active;
#JoinTable(name = "user_role", joinColumns = {
#JoinColumn(name = "user_id")}, inverseJoinColumns = {
#JoinColumn(name = "role_id")})
#OneToMany
private Set <Role> roles;
public UserEntity() {
}
//getters and setters
What I would like insight on is why there is a problem with the query, and why the username is not able to be retrieved from the database.
Edit: After changing the query, the login is still not successful. The login page is returned and there is no error message in the output console other than this:
Hibernate: select userentity0_.user_id as user1_1_, userentity0_.active as
active1_, userentity0_.name as name1_, userentity0_.password as password1_,
userentity0_.username as username1_ from user userentity0_ where
userentity0_.username=?
Hibernate: select roles0_.user_id as user1_1_1_, roles0_.role_id as role2_2_1_,
role1_.role_id as role1_0_0_, role1_.role as role0_0_ from user_role roles0_ inner
join role role1_ on roles0_.role_id=role1_.role_id where roles0_.user_id=?
INFO : com.**.**.controller.ApplicationController - This is the login page {}.
You forgot to execute the query you created. It should be:
sessionFactory.getCurrentSession().createQuery(...).uniqueResult();
Also, use a proper bind variable. As it stands your query is bogus with single quotes, I'm not sure if you made a typo pasting it into stackoverflow, but something like this would be much safer:
sessionFactory
.getCurrentSession()
.createQuery("select u from UserEntity u where u.username = :username")
.setParameter("username", username)
.uniqueResult();

Resources