Ebean ManyToMany with bridge table find all query - java

I have three tables: users(id, name, login, password), roles(id, name), user_roles(id, user_id, role_id)
This is my code
#Entity
#Table(name = "users")
public class User extends Model {
#Id
public Long id;
public String name;
public String login;
public String password;
#ManyToMany
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
public Set<Role> roles;
public static Finder<Integer, User> find = new Finder<>(User.class);
}
#Entity
#Table(name = "roles")
public class Role extends Model {
#Id
public Long id;
public String name;
#ManyToMany(mappedBy = "roles")
public List<User> users;
public static Finder<Integer, Role> find = new Finder<>(Role.class);
}
I want to display all users with roles, example: {"id":1, "name":"My Name", "login":"My Login", "password":"My Password", roles: [{"name":"ADMIN"}, {"name":"USER"}]}
How can I do this? I'm new in Ebean and ORM. Thanks for any help.
Update
public Result all() {
List<User> users = User.find.all();
return ok(toJson(users));
}
But now I getting stackoverflow error infinite recursion.

Make users.role = null and then return Json

Related

JPA (Hibernate) - ManyToMany - setter

I am not sure if I am doing something wrong, but when I have these two entities:
#Setter #Getter #NoArgsConstructor #FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
#Table(name = "GROUP_OF_USERS")
public class Group {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String name;
#ManyToOne
User administrator;
#ManyToMany(mappedBy = "groups", cascade = CascadeType.ALL)
Set<User> users = new HashSet<>();
public void setAdministrator(User user){
this.administrator = user;
addUser(user);
}
public void addUser(User user){
users.add(user);
}
}
#Entity
#Setter #Getter #NoArgsConstructor #FieldDefaults(level = AccessLevel.PRIVATE)
#Table(name = "APP_USER")
public class User {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String name;
#Column(unique = true)
String email;
String password;
String bankAccount;
boolean isActive = false;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "GROUP_USER_RELATIONSHIP",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "group_id"))
Set<Group> groups = new HashSet<>();
public void addGroup(Group group){
groups.add(group);
}
}
and service method like this:
#Override
public Group crateGroup(String userName, String groupName) {
var userInDb = userRepository.findUserByEmailIgnoreCase(userName).orElseThrow(() -> new UserNotFoundException(userName));
userInDb.getGroups().stream()
.filter(group -> group.getName().equals(groupName))
.findAny()
.map(Group::getName)
.ifPresent(name -> {throw new GroupUniqueConstrainException(name);});
var newGroup = new Group();
newGroup.setName(groupName);
newGroup.setAdministrator(userInDb);
return groupRepository.save(newGroup);
}
than I am surprised that administrator is set in DB, but join table doesn't contain any record. Could someone help me here to understand where could be a problem? I found out that I could update the addUser method so it would also add group to the user. But I would need to do this also for addGroup method in User class so when calling that it would create infinite loop.

Selecting specific columns in many to many relationship with JPA

I have to Models for two Entities in Database. Model User and Model Role. These two entities have a many-to-many relationship. In the model files I have class User.java:
#Entity
#Table(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String username;
private String password;
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "role_user",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private Set<Role> roles;
//getters and setters
}
and I have class Role.java:
#Entity
#Table(name = "Role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private int id;
private String role;
#ManyToMany(mappedBy = "roles", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<User> users;
//getters and setters
}
I also have the repositories/DAO for them:
public interface UserRepository extends JpaRepository<User, Integer>{}
and
public interface RoleRepository extends JpaRepository<Role, Integer> {}
Now when I fetch all results from class Role it returns all the columns from Entity User, how can I specify that I want only the column name from User Entity? Should I build another model only with the columns I need or there is any simple way to select specific columns?

JPA Entity structure

I have a login page where the data will be retrieved from database
User
#Setter
#Getter
#Entity
#Table(name = "USER_DETAILS")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID")
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "USER_PASSWORD")
private String userPassword;
#Transient
private Set<Role> roles;
#ManyToMany
#JoinTable(name = "USER_ROLE", joinColumns = #JoinColumn(name = "USER_ID"), inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role
#Setter
#Getter
#Entity
#Table(name = "USER_ROLE")
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ROLE_ID")
private Long id;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "ROLE_NAME")
private String roles;
#Transient
private Set<User>users;
#ManyToMany(mappedBy = "roles")
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
UserDetailServiceImpl
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
System.out.println("Name "+user.getUserName());
System.out.println("role is "+user.getRoles());
if(null == user) {
throw new UsernameNotFoundException("No user present with username: " + username);
}
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Role role : user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(role.getUserName()));
}
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getUserPassword(), grantedAuthorities);
}
}
Table
Role
ROLE_ID | USER_NAME | ROLE_NAME
1 John Admin
User
USER_ID | USER_NAME | USER_PASSWORD | USER_ROLE
1 John pass Admin
Output
Name John role is null 2018-03-12 00:52:06.362 ERROR 12563 ---
[nio-8088-exec-8] w.a.UsernamePasswordAuthenticationFilter : An
internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException:
null
I can't get the role value. What's wrong with the database structure ?
For #ManyToMany to work, you need 3 tables not 2. The two parent tables are typically linked through a third table containing two foreign keys. So here, there should be a third table which would have role_id and user_id.
Also, instead of adding #ManyToMany annotation to the getters, try adding it at the time of declaration itself. Something like this in User class,
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
And the same modification in Role class:
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
Here, user_role is the third table.

Hibernate create alias on many to many list

I have four class; UserGroup, UserAccount, Role, UserGroupRoleRelation and my db is IBM DB2
#Entity
#Table(name = "USER_GROUP")
public class UserGroup implements Serializable {
#Id
#Column(name = "USER_GROUP_ID")
#GeneratedValue
private Long id;
......
..
#OneToMany(mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserGroupRoleRelation> userAccountsRole = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "ROLE")
public class Role implements Serializable {
#Id
#Column(name = "ROLE_ID")
#GeneratedValue
private Long id;
......
#OneToMany(mappedBy = "role")
private List<UserGroupRoleRelation> userAccountInGroup = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "USER_GROUP_ROLE_LINE", uniqueConstraints = #UniqueConstraint(columnNames = { "ROLE_ID", "USER_GROUP_ID" }))
public class UserGroupRoleRelation {
#Id
#GeneratedValue
#Column(name = "RELATION_ID")
private Long relationId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_ACCOUNT_USER_GROUP_ROLE_LINE", joinColumns = { #JoinColumn(name = "RELATION_ID") }, inverseJoinColumns = { #JoinColumn(name = "USER_ID") }, uniqueConstraints = #UniqueConstraint(columnNames = { "USER_ID", "RELATION_ID" }))
private List<UserAccount> userAccountList = new ArrayList<UserAccount>();
#ManyToOne
#JoinColumn(name = "USER_GROUP_ID")
private UserGroup userGroup;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
}
#Entity
#Table(name = "USER_ACCOUNT")
public class UserAccount implements Serializable {
#Id
#Column(name = "USER_ID")
#GeneratedValue
private Long id;
.....
#ManyToMany(mappedBy = "userAccountList", cascade = CascadeType.ALL)
private List<UserGroupRoleRelation> rolesInGroup = new ArrayList<UserGroupRoleRelation>();
}
I wanna find usergroups of a useraccount and i prepared a method with criteria. its like;
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.add(Restrictions.eq("userAccountsRole.userAccountList", userAccount));
return criteria.list();
}
But when i try to get result of that method, DB2 gives to me DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=3.63.75
Probably its about creating alias on many to many relation. I dont know what should i do to create alias on many to many. How can I get result of that function?
Thank
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.createAlias("userAccountsRole.userAccountList", "userAccountList");
criteria.add(Restrictions.eq("userAccountList.id", userAccount.getId()));
return criteria.list();
}
It works for me. I mean criteria on "id". But I don't understand why I cant check equality on object instead of id when there is ManyToMany list
It is not of creating alias. You are passing an object to hibernate on which it can not make any criteria. You need to create bidirectional mapping for that.Or else if you your requirement is just to fetch the the list of UserAccountList of particular UserGroup class you can follow the below code.
#Override
#Transactional
public List<UserGroup> findUserGroupOf(long userGroupId) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.add(Restrictions.eq("id",userGroupId));
criteria.createAlias("userAccountsRole", "uar");
criteria.setFetchMode("uar.userAccountList",FetchMode.JOIN);
return criteria.list();
}

How to create join table with JPA annotations?

I need to create a join table in my database using JPA annotations so the result will be this:
So far I just implemented 2 entities:
#Entity
#Table(name="USERS", schema="ADMIN")
public class User implements Serializable {
private static final long serialVersionUID = -1244856316278032177L;
#Id
#Column(nullable = false)
private String userid;
#Column(nullable = false)
private String password;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
private static final long serialVersionUID = -7274308564659753174L;
#Id
#Column(nullable = false)
private String groupid;
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
}
Should i create another entity called USER_GROUP or i can just add some annotations, so the join table will be created automatically when i run create tables from entities(ORM)?
How should i annotate my entities to achieve the same as in the image?
You definitely shouldn't create User_Group entity as it's more the underlying database representation than the object oriented one.
You can achieve the join table by defining something like:
#Entity
#Table(name="USERS", schema="ADMIN")
public class User implements Serializable {
//...
#ManyToOne
#JoinTable(name="USER_GROUP")
Group group;
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
//...
#OneToMany(mappedBy="group")
Set<User> users;
Edit: If you want to explicitly set the names of the columns you could use #JoinColumn elements as shown below:
#ManyToOne
#JoinTable(name="USER_GROUP",
joinColumns = #JoinColumn(name = "userid",
referencedColumnName = "userid"),
inverseJoinColumns = #JoinColumn(name = "groupid",
referencedColumnName = "groupid"))
Group group;
I would implement it this way:
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
#OneToMany
#JoinTable(name = "USER_GROUP",
joinColumns = #JoinColumn(name = "groupid"),
inverseJoinColumns = #JoinColumn(name = "userid"))
private List<User> users;
}
Solution suggested by #PedroKowalski should work too, but then you'll have to keep a reference to Group entity in your User entity which is not always possible.
To have the same annotations like in your diagram you can do this in your User class:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_GROUP",
joinColumns = { #JoinColumn(name = "userid") },
inverseJoinColumns = { #JoinColumn(name = "groupid") })
private List<Group> grups;
in your group class
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_GROUP",
joinColumns = { #JoinColumn(name = "groupid") },
inverseJoinColumns = { #JoinColumn(name = "userid") })
private List<User> users;
I'm wondering what is the point to create a Join Table in this way, considering that we can't access directly for queries?
JPA doesn't allow to make queries directly to the Join Table, so if the user want to do an operation on USER_GROUP, he has to creare a normal join query between users and groups; due to this, the join table USER_GROUP is useless.

Categories