How get User to email? - spring

I need get user to email with jpa
i have this code:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(#Param("email") String email);
User findById(#Param("id") Long id);
}
When i tried this :
User owner = userRepository.findById(user.getId()); //yes
User user=userRepository.findByEmail(email);//no
My class User is:
#Entity
#Table(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = -3009157732242241606L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
public User() {
}
//getters and setters
}
I want to obtain a user through the user's email, if I can obtain it through his id but I also need to obtain it through email

In you Model class "email" is not an unique so that it will return multiple rows to avoid that add
#Entity
#Table(name = "User")
public class User implements Serializable {
.....
#Column(unique=true,...)// .. add your extra
private String email;
.....
}

If you want top 1 result to be returned you could change the method
User findByEmail(#Param("email") String email);
with
User findOneByEmail(String email);

Related

JPA OneToOne and shared primary key need manual assignment

I'm using Springboot and JPA to create two tables sharing the same primary key.
For the first table I write:
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE,
CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
private UserLogin login;
}
For the second table I write:
public class UserLogin implements Serializable
{
#Id
private Long user_id;
#OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH},
fetch=FetchType.LAZY)
#MapsId("user_id")
#JoinColumn(name = "user_id", referencedColumnName = "id")
#Setter(AccessLevel.NONE)
private UserAccount user;
public void setUser(UserAccount user)
{
this.user = user;
this.user_id = user.getId();
}
}
Other stuff are omitted for conciseness. The code works because I manually set the id of UserLogin by writing the statement
this.user_id = user.getId();
otherwise I get the error:
Hibernate error: ids for this class must be manually assigned before calling save():
I guess that the ids can be manually managed but I cannot get the right configuration.
UPDATE:
I found the solution thanks (see the accepted answer). Now I just would get rid of the findById() when setting the user login.
//these methods are defined within a dedicated #Service
#Transactional
public void createLoginInfo(UserAccount user)
{
UserLogin userlogin=new UserLogin();
this.addLoginToUser(userlogin,user);
loginService.save(userlogin);
}
#Transactional
public void addLoginToUser(UserLogin login, UserAccount account)
{
//whit this commented line works
//UserAccount acc= this.findById(account.getId());
login.setUser(account);
account.setLogin(login);
}
//In a transactional test method I first create the user then I call
userService.save(theuser);
userService.createLoginInfo(theuser);
You have a bidirectional relationship, but have mapped it with a few competing options that don't work well together. First, in UserAccount, it isn't clear why you have an ID that is generated, yet try to also map it with the relationship (specifically using a PrimaryKeyJoinColumn). If you want it generated, it can't also be a foreign key value in a reference - and you've already got this relationship setup as the 'other' side via the 'mappedBy' setting. It should just be:
public class UserAccount implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE,
CascadeType.REFRESH}, fetch=FetchType.LAZY)
private UserLogin login;
}
User login then should just be:
public class UserLogin implements Serializable {
#Id
private Long user_id;
#OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH},
fetch=FetchType.LAZY)
#MapsId("user_id")
#JoinColumn(name = "user_id", referencedColumnName = "id")
#Setter(AccessLevel.NONE)
private UserAccount user;
public void setUser(UserAccount user) {
this.user = user;
}
}
Note because you have the mapsId annotation on the user relationship, JPA will set the user_id property for you once the ID is assigned - there is no need to manually set it yourself. You can, but if you do you must insure it was assigned previously - which requires a save/flush on the UserAccount. If you don't actually use the Long user_id property, you don't really even need to map it; you can just mark the user property as the ID:
public class UserLogin implements Serializable {
#Id
#OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH},
fetch=FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "id")
#Setter(AccessLevel.NONE)
private UserAccount user;
public void setUser(UserAccount user) {
this.user = user;
}
}
The Long ID from UserAccount then can be used to lookup UesrAccounts and UserLogin instances.
Try this :
public class UserLogin implements Serializable
{
#Id
private Long user_id;
#OneToOne(fetch=FetchType.LAZY)
#MapsId
#JoinColumn(name = "user_id")
private UserAccount user;
public UserAccount getUser() {
return user;
}
public void setUser(UserAccount user) {
this.user = user;
}
}
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
To persist UserLogin :
EntityManager em;
UserAccount user = em.find(UserAccount.class, 1L)
UserLogin login = new UserLogin();
login.setUser(user);
em.persist(login);

Spring boot UserDetailsService Multi-User with extra fields

I have a spring boot project that has 3 types of users (Admin, Expert, Customer) and the application is for Experts that register on site for giving services like fixing computers to Customers that are asking help in site.
I have an inheritance of different kind of User types as following.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "USER_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private Set<String> roles = new HashSet<>();
// getter & setter...
}
#Entity
#DiscriminatorValue("1")
public class Admin extends User {
}
#Entity
#DiscriminatorValue("2")
public class Expert extends User {
private Byte[] expertPhoto;
private String password;
// some other fields & getter & setter...
}
#Entity
#DiscriminatorValue("3")
public class Customer extends User {
private Long credit;
private Set<CustomerOrder> orders = new HashSet<>();
// some other fields & getter & setter...
}
I want to use spring boot security and implement UserDetailsService, my problem is that how to design when I have different User types (Expert, Customer, etc.)?
I want users to be able to have different roles (admin, expert, customer) with one username.
How should I design my system to solve these requirements?
Your role modal seems a bit off. It is better to have a single type of User and fill it with list of a new Role entity. The new User entity will look like the following:
#Table(name = "user")
#Entity
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "username", unique = true, nullable = false)
private String username;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "user_role",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")},
)
private Set<Role> roles;
// getters and setters & other fields user can have
}
And the Role entity will look like this:
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "role_name", unique = true, nullable = false)
private String roleName;
#ManyToMany(mappedBy = "roles")
private Set<User> users;
}
Then, you need to implement org.springframework.security.core.userdetails.User interface to use as a concrete implementation of spring security class Useron your UserDetailsService. Notice that this class is also called User and is different than the User class on your system.
public class MyUserDetail extends User {
private String otherFieldsLikePhoto; // you can add different fields like this to keep extra information
public MyUserDetail(String username, String password, Collection<? extends GrantedAuthority> authorities, String otherFieldsLikePhoto) {
super(username, password, authorities);
this.otherFieldsLikePhoto = otherFieldsLikePhoto;
}
}
Then, you can create your UserDetailsService by implementing org.springframework.security.core.userdetails.UserDetailsService of spring security.
What you will achieve UserDetailsService is to load the user in the MyUserDetail format we just created. It will be something like this:
public class MyUserDetailsService implements UserDetailsService {
private final UserReadService userReadService; // put your service to get user from db
public MyUserDetailsService(UserReadService UserReadService) {
this.userReadService = UserReadService;
}
#Override
public UserDetails loadUserByUsername(String username) {
User user = userReadService.getByUsername(username); // get user from db
String otherFieldsLikePhoto = getUserPhotoOrAnythingElse(user); // get your extra fields however you want
return new MyUserDetail(
user.getUsername(),
user.getPassword(),
getAuthoritySetOfUser(user), // notice how we embed roles to UserDetail
otherFieldsLikePhoto
);
}
// this function is not necessary but useful to calculate authority set calculation on helper
private Set<SimpleGrantedAuthority> getAuthoritySetOfUser(User user) {
Set<Role> userRoles = user.getRoles(); // get roles of user like ADMIN, EXPERT etc.
Set<SimpleGrantedAuthority> authorities = roles.stream()
.map(rolex -> new SimpleGrantedAuthority(rolex.getRoleName()))
.collect(Collectors.toSet());
return authorities;
}
}

HashSet in mapRow for JDBC template

I would like to make a select from one of my user table. In this table there is a field, which is a HashSet. This Set has "role" objects. I need a unique mapRow method for this operation, but I don't know how to to do it with the Set.
You can see my code below:
#Entity
#Getter
#Setter
#ToString
public class User {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
#Column( nullable=false )
private String password;
#Column( unique=true, nullable=false )
private String email;
private Boolean active;
#ManyToMany()
#JoinTable(name="role_user")
private Set<Role> roles;
}
public class UserMapper implements RowMapper<User> {
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("email"));
user.setActive(rs.getBoolean("active"));
//user.setRoles();
return user;
}
}

How to configure oneToMany Mapping in User -Role-Permission

I have a spring boot app(Spring boot 2) with spring data jpa.i have 3 MYSQL tables to store user information ,roles and permissions
User will contains basic user details like username ,password firstname,lastname.
Role represent the user roles like Admin ,User,Staff,test (user can have many roles)
Permission has 3 possibilities Read,Write,Customize(each role has many permissions)
UserRole - joint table for User and Roles
RolePermission - joint table for roles and permissions
Am looking for Spring boot service , which will return false if the user is not validated against the User table ,if the user is validated successfully then the response should contains the user roles and permissions .
I was able to build the sample rest service but failed to setup the below
How to configure the entity classes for the oneToMany mapping in this requirement
What would be the corresponding query in the repository interface
Please see the entity tables
#Entity
#Table( name = "TURBINE_USER" )
public class PortalUser {
public PortalUser() {
}
public PortalUser(long userID ,String userName , String password , String firstName, String lastName, String email) {
this.userID = userID;
this.userName = userName;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
#Id
#Column(name="USER_ID",unique=true)
private long userID;
#NotNull
#Column(name="LOGIN_NAME",unique=true)
private String userName;
#NotNull
#Column(name="PASSWORD_VALUE")
private String password;
#NotNull
#ColumnDefault("")
#Column(name="FIRST_NAME")
private String firstName;
#NotNull
#ColumnDefault("")
#Column(name="LAST_NAME")
private String lastName;
#Column(name="EMAIL")
private String email;
#Column(name="CONFIRM_VALUE")
private String confirmValue;
#NotNull
#Column(name="CREATED")
private Timestamp createdDt;
#NotNull
#Column(name="MODIFIED")
private Timestamp modified;
#NotNull
#Column(name="LAST_LOGIN")
private Timestamp lastLogin;
#Column(name="DISABLED")
private char disabled;
#Column(name="OBJECTDATA")
private byte[] objectData;
#NotNull
#Column(name="PASSWORD_CHANGED")
private Timestamp passwordChanged;
// getters and setters
}
#Entity
#Table(name = "TURBINE_ROLE")
public class Role {
#Id
#Column(name= "ROLE_ID",unique = true)
private long roleId;
#Column(name= "ROLE_NAME")
private String roleName;
}
#Entity
#Table(name ="TURBINE_PERMISSION")
public class Permission {
#Id
#Column(name= "PERMISSION_ID")
private long permissionId;
#Column(name= "PERMISSION_NAME")
private String name;
}
#Entity
#Table(name= "TURBINE_USER_GROUP_ROLE")
public class UserRoles {
#Column(name="USER_ID")
private PortalUser user;
#Column(name="ROLE_ID")
private Role roles;
}
#Entity
#Table(name="TURBINE_ROLE_PERMISSION")
public class RolePermission {
#Column(name= "ROLE_ID")
private Role roleId;
#Column(name= "PERMISSIONID")
private Permission permissionId;
}
It is ManyToMany relationship between User and Role, Role and Permission entities. You should not define Join tables as entities, and they are generated/managed by JPA implementation.
For example, ManyToMany annotation for User and Role entities:
public class PortalUser {
...
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "User_Role",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private Set<Role> roles;
}
Tested with CMobileCom JPA.
Disclaimer: I am a developer of CMobileCom JPA, a light weight JPA implementation for Java and Android.
One of the simple way to define the relation among User-Role-Permission is as bellow - Define entities class for Role and Permission and declare the many to many relation inside User entity, Which auto created the two link table user_role and user_permission.
1. Role Entity Class
#Entity
#Table(name = "ROLE")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
.
}
2. Permission Entity Class
#Entity
#Table(name = "PERMISSION")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
.
}
3. User Entity Class
#Entity
#Table(name = "USER")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
.
// Relation with role
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name="USER_ROLE",
joinColumns = {#JoinColumn (name="USER_ID", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="ROLE_ID", referencedColumnName="id")}
)
private List<Role> roles;
// Relation with permission
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name="USER_PERMISSION",
joinColumns = {#JoinColumn (name="USER_ID", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="PERMISSION_ID", referencedColumnName="id")}
)
private List<Permission> permissions;
}

Retrieve an object which has a POJO as a primary key with Spring JPA

I have the following classes: DepartmentMember and Account, mapped by a OneToOne relationship.
This is the DepartmentMember class:
#Entity(name="departmentmember")
#Table(name="departmentmember")
#Embeddable
public class DepartmentMember {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="name", nullable=false)
private String nume;
#Column(name="lastName", nullable=false)
private String prenume;
#OneToOne(mappedBy="departmentMember",cascade=CascadeType.ALL,fetch=FetchType.LAZY, optional=false)
#JsonIgnore
private Account account;
public DepartmentMember() {}
public DepartmentMember(String nume, String prenume, String cNP, String email) {
super();
this.nume = nume;
this.prenume = prenume;
}
//getters and setters
}
And this is the Account class :
#Entity(name="users")
#Table(name="users")
public class Account {
#Id
private int id;
#Column(name="username", unique=true, nullable=false)
private String username;
#Column(name="password", nullable = false)
private String password;
#Column(name="authorities", nullable=false)
private String authorities;
#OneToOne(fetch=FetchType.EAGER)
#MapsId
#Embedded
private DepartmentMember departmentMember;
public Account() {}
public Account(String username, String password, String authorities) {
super();
this.username = username;
this.password = password;
this.authorities = authorities;
}
//getters and setters
}
I have defined an interface AccountRepository which extends the CrudRepository interface provided by Spring JPA.
What I want to do is define a query, which takes as a parameter a DepartmentMember id and retrieves the associated account for that member. Now this is how an Account object looks like:
{
"username": "Maria_Popescu",
"password": "4ec38c6e-2463-4562-99ba-9f6c2b4528c4",
"authorities": "ROLE_USER",
"departamentMember": {
"id": 2,
"nume": "Popescu",
"prenume": "Maria",
}
I tried using the findOne(int id) method, but it didn't work, so which is the correct approach to solve this?
Edit:
In the AccountRepository I have defined the following method :
Account findByDepartmentMemberId(int id) and I still get a not found error.
There was actually another problem in my controller. I managed to get it working by adding
Account findByDepartmentMemberId(#Param("id")int id);
in the AccountRepository

Resources