How to handle two database insertions fom a single form in spring/ hibernate? - spring

I have 3 model classes,
User.java
public class UserData {
private Integer userID;
private String userName;
private String userPassword;
//getters and setters..
and Permission class like..
Permission.java
public class PermissionsData {
private Integer permissionID;
private Integer userID;
private Integer moduleID;
private boolean permissionIsReadOnly;
private boolean permissionIsModify;
private boolean permissionIsFull;
//getters and setters
amd module class like,
module.java
public class ModuleData {
private Integer modId;
private String modName;
I have some modules in my database. When i am creating a new user, i had listed those modules and had added checkboxes to set permissions. When submitting the form, i need to insert user data to user table and his permissions for each module to the Permission table.
Now i had implemented only inserting roles to the database.. my controller is like..
#RequestMapping(value = "/addRole")
public ModelAndView addNewRole()
{
ModelAndView mav = new ModelAndView("addNewRole");
RoleData role = new RoleData();
mav.getModelMap().put("roleDataObj", role);
List<ModuleData> moduleList = moduleService.getAllModules();
mav.getModelMap().addAttribute("ModuleList", moduleList);
return mav;
}
How can i achieve to add data to two tables on one submit?? i would also like to know about the mapping betwewen modules and permission
I am really new to this spring and hibernate. So plz guide me with sample codes..
Thanks in advance.

if you have set cascading in the mapping
#Cascade(Cascade.all)
private List<Permission> permissions;
it is as simple as
public void saveNewUser(...)
{
User user = new User();
// fill properties
Permission p = new Permission();
// fill properties
user.getPermissions().add(p);
session.save(user);
}
Update: the code above would be possible using a class structure like. the important part is the collection/map of permissions each user has per modul
public class Permission {
private boolean IsReadOnly;
private boolean IsModify;
private boolean IsFull;
}
public class Module {
private Integer id;
private String name;
}
public class User {
private Integer id;
private String name;
private String password;
private Map<Module, Permission> permissions;
}
and mapping
<class name="User">
...
<map name="permissions" cascade="all">
<key column="UserId"/>
<index-many-to-many column="ModuleId" class="Module"/>
<composite-element class="Permission">
<property name="IsReadOnly"/>
<property name="IsModify"/>
<property name="IsFull"/>
</composite-element>
</map>

Related

How do I insert data into 2 tables in the same database using Spring Boot Crudrepository?

I want to be able to create a new account for my application. I have an account class which represents one entity and another class that represents personal information of the account. In order to create the new account and have it be in the database I want to add some information into the account table and some information into the PersonalInfo table as detailed in the classes below. How do I do this with a CrudRespository interface. As I understand it, the crudrepository can interact with one table in the database. In my example that would be Accounts. This is fine because most of my checking and communicating will be with the accounts table. But for when I am creating a new account I need to add the data that will be given into two tables. Do I have to make manual queries and add it as a method in there?
#Entity
#Component
public class Account {
#Id
private int accountNum;
private String accountType;
private int accountBalance;
private String accountStatus;
#Entity
#Component
public class PersonalInfo {
#Id
private int accountNum;
private String firstName;
private String lastName;
private String SSN;
private String streetName;
private String city;
private String state;
private String zipcode;
#RepositoryRestResource(collectionResourceRel="accounts",path="accounts")
public interface AccountsDB extends CrudRepository<Account, Integer>{
}
Just create a repository for PersonalInfo and invoke two save() methods (of the two different repositories respectively) with the two created entities respectively.
Just make sure to set the identical ids (accountNum) for these two entities.
Or, you could create a service to do it for you, like so:
public interface AccountAndPersonalInfoService {
void save(Account account, PersonalInfo personalInfo);
}
#Service
public class AccountAndPersonalInfoServiceImpl implements AccountAndPersonalInfoService {
#Autowired
private AccountsDB accountsDB;
#Autowired
private PersonalInfoDB personalInfoDB;
#Override
void save(Account account, PersonalInfo personalInfo) {
if (account.getAccountNum() == personalInfo.getAccountNum()) {
accountsDB.save(account);
personalInfoDB.save(personalInfo);
} else throw new IllegalArgumentException("The ids of the entities do not match.");
}
}

Object is not an instance of a persistable class warning with Spring boot and Neo4j

I have a pretty straightforward class called User which is supposed to create user objects containing user information and login details.
package com.example.domain;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import java.util.HashSet;
import java.util.Set;
#NodeEntity
public class User {
public User() {}
#GraphId
private Long id;
private String username;
private String password;
private String name;
private String email;
private String Role;
#Relationship(type="BELONGS_TO", direction = Relationship.INCOMING)
Set<Item> items = new HashSet<>();
public User(String name, String username, String password, String email) {
this.name = name;
this.username = username;
this.password = password;
this.email = email;
}
// Getters and setters below for private fields...
}
The controller creating the object looks like this:
#RequestMapping(value = "/register",method = RequestMethod.POST)
public String register(Model model,
#ModelAttribute(value="name") String name,
#ModelAttribute(value="username") String username,
#ModelAttribute(value="email") String email,
#ModelAttribute(value="password") String password,
#ModelAttribute(value="confirmPassword") String confirmPassword) {
if(!password.equals(confirmPassword)) {
model.addAttribute("error", true);
return "register";
}
User userEntity=new User(name,username,password,email);
userManagementService.save(userEntity); //<------The object is created but the error occures during persistance
return "login";
}
and my user management service looks like this:
public interface UserManagementService {
List<User> listAll();
User save(User user);
User findUser(String username);
}
What makes the User class, not an instance of a persistable class. What are the characteristics of a persistable class and how can I make User a persistable class?
Have you configured the OGM somewhere? Either in a Java configuration or in a ogm.properties file? You'll need to specify the driver type and tell the SessionFactory where to look for your domain objects.
OGM config reference: https://neo4j.com/docs/ogm-manual/2.1/reference/#reference:configuration
SessionFactory config reference: https://neo4j.com/docs/ogm-manual/2.1/reference/#reference:connecting:session-factory

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;

Returning returned model object to json String using spring data jpa with hibernate

I am using spring data jpa with hibernate
This is my dao interface
#Repository
public interface IUserDAO extends JpaRepository<User, Integer>{
User findByUsername( final String username );
}
This is my User class
Entity
#Table(name="USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="ID", nullable = false)
private int id;
#Column(name="USERNAME", nullable = false)
private String username;
#Column(name="NAME", nullable = false)
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This is my UserImplClass
This is my UserImplClass{
#Autowired
private IUserDAO iUserDAO;
public String findUserByUserName(String username) {
User user =iUserDAO.findByUsername(username);
Convert user to json object from framework level automatically
// i can add my one implemenation of converting user to json here ,but i want to achieve it from framework so that my code is not scattered on every service level
return "jsonStringOfUserObject"
}
Is it possible with spring data jpa with hibernate so that i do not have to write code for converting java object to json string in every service level?
I am using spring ,therefore i want to achieve it from spring .
You have two options to do what you want:
1) If you plan on returning this Object as an HTTP Response, and you use Spring MVC with Controllers you can annotate your controller method as follows:
public #ResponseBody User getUser(){
return userImplClass.findUserByUserName("yourusername");
}
2) If you want the UserImplClass itself to return a JSON String (which I do't recommend, but I leave you the decision), you can use Jackson Object Mapper to do it for you (you can inject it if you declare it as a bean on your configuration xml, or create a new instance of it, I personally prefer injecting it with #Autowired)
public String findUserByUserName(String username) {
User user =iUserDAO.findByUsername(username);
ObjectMapper mapper = new ObjectMapper(); // no need to do this if you inject via #Autowired
return mapper.writeValueAsString(user);
}

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;
}
}

Resources