I am creating an app where there are two entities
users
chatrooms
There is a many to many relationship between users and chatrooms.
I created a many to many relationship with a join table named users_chatrooms. The values are getting populated in the join table correctly when I wrote code for a user to join a chatroom.
My issue is that, I need an endpoint that can fetch all the users of a given chatroom. For this, I need the table created by the join (users_chatrooms) as part of Jpa. How to accomplish this in JPA ?
User class
package com.example.chat.models;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="USERS")
public class User {
#Id
#Column(name="userid")
private String username;
#Column(name="pass")
private String password;
#ManyToMany(mappedBy = "users",fetch=FetchType.EAGER,cascade = CascadeType.ALL)
List<Chatroom>rooms;
public List<Chatroom> getRooms() {
return rooms;
}
public void setRooms(List<Chatroom> rooms) {
this.rooms = rooms;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Chatroom class
package com.example.chat.models;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="Chatrooms")
public class Chatroom {
#Id
#Column(name="chatroomId")
private String id;
#Column(name="chatRoomName")
private String name;
#Column(name="chatroomDesc")
private String description;
#ManyToMany(fetch=FetchType.EAGER,cascade = CascadeType.ALL)
#JoinTable(
name = "users_chatrooms",
joinColumns = #JoinColumn(name = "chatroomId"),
inverseJoinColumns = #JoinColumn(name = "userid"))
private List<User>users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
You can simply join the two entities using #JoinColumn
#Entity
#Table(name="Chatrooms")
public class Chatroom {
#Id
#Column(name="chatroomId")
private String id;
#Column(name="chatRoomName")
private String name;
#Column(name="chatroomDesc")
private String description;
#ManyToMany(fetch=FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn(name = "userid", referencedColumnName = "chatroomId")
private List<User>users;
// getters and setters
}
Related
First time working with Spring Boot and Hibernate.
I'm trying to map my User.java and Role.java classes to a MySQL database in NetBeans. Everything works just fine except for one detail.
When I go to the generated tables using NetBeans' database manager interface in the Services tab and try to manually add a row into user_role table (result of #ManyToMany), it lets me, even with no user or role created yet. I would expect it to prompt me with some error stating that the user id I'm inserting does not exist, for example.
Now, I come from a PostgreSQL/Doctrine background and that's maybe why it's a little odd to me because if I were to do this in pgAdmin well, that's a big no no.
Please, if this is default behavior, tell me how to override it, if not, here is my code with the error somewhere.
User.java
package hello;
import java.util.Set;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
#Entity // This tells Hibernate to make a table out of this class
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private String email;
private String username;
private String password;
private String passwordConfirm;
#ManyToMany(targetEntity = Role.class)
#Access(AccessType.FIELD)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
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;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role.java
package hello;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "role")
public class Role {
private Long id;
private String name;
#ManyToMany(targetEntity = User.class, mappedBy = "roles")
#Access(AccessType.FIELD)
private Set<User> users;
#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;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
If you need more info, please ask.
I'm creating Spring Boot Application with JPA PostgreSQL.
When I compile my spring project, got the following error.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: eveserver.core.entity.User.name in eveserver.core.entity.Role.users
Please, help me to understand what i'm doing wrong.
This is my User.java
package eveserver.core.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Set;
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String username;
#JsonIgnore
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
private boolean enabled = false;
#ManyToMany
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
public User(){ }
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public User(String username, String password, String email, Set<Role> roles) {
this.username = username;
this.password = password;
this.email = email;
this.roles = roles;
}
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public void enable(){
enabled = true;
}
public void disable(){
enabled = false;
}
}
Role.java
package eveserver.core.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "role")
public class Role implements GrantedAuthority {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy = "role")
#JsonIgnore
private Set<User> users;
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;
}
#Override
public boolean equals(Object obj){
if (obj instanceof Role){
Role r = (Role)obj;
if (r.getId()==this.getId()){
return true;
}
}
return false;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
#Override
public String getAuthority() {
return getName();
}
}
and this is my tables
In your User class, you have a property called roles:
public Set<Role> getRoles() {
return roles;
}
In your Role class, this:
#ManyToMany(mappedBy = "role")
#JsonIgnore
private Set<User> users;
should be:
#ManyToMany(mappedBy = "roles")
#JsonIgnore
private Set<User> users;
mappedBy = "something" is saying, effectively, "within this other entity, there's a property called something that gets a list of entities of this type (the current type that you're in when you use the #ManyToMany annotation). It is not specifying a type or class name like Role.
I have the next situation. I ahve entity object User:
package models;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
import org.hibernate.annotations.Proxy;
#Entity
#Table(name="users")
#Proxy(lazy=true)
public class User {
private int id;
private String login;
private String password;
private String name;
private String email;
private Integer age;
private String country;
private Set<UserRole> roles = new HashSet<UserRole>();
private UserStatus status;
private Date created;
private Date updated;
public User() {
status=UserStatus.A;
}
public User(String user_login, String user_password, String user_name, String user_email) {
this.login = user_login;
this.password = user_password;
this.name = user_name;
this.email = user_email;
status=UserStatus.A;
}
public User(String user_login, String user_password, String user_name, String user_email, int age) {
this(user_login, user_password, user_name, user_email);
this.age = age;
}
public User(String user_login, String user_password, String user_name, String user_email, int age, String country) {
this(user_login, user_password, user_name, user_email, age);
this.country = country;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_id", unique = true)
public int getId() {
return id;
}
public void setId(int user_id) {
this.id = user_id;
}
#Column(name="user_login")
public String getLogin() {
return login;
}
public void setLogin(String user_login) {
this.login = user_login;
}
#Column(name="user_password")
public String getPassword() {
return password;
}
public void setPassword(String user_password) {
this.password = user_password;
}
#Column(name="user_name")
public String getName() {
return name;
}
public void setName(String user_name) {
this.name = user_name;
}
#Column(name="user_email")
public String getEmail() {
return email;
}
public void setEmail(String user_email) {
this.email = user_email;
}
#Column(name="user_age")
public Integer getAge() {
return age;
}
public void setAge(Integer user_age) {
this.age = user_age;
}
#Column(name="user_country")
public String getCountry() {
return country;
}
public void setCountry(String user_country) {
this.country = user_country;
}
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "users_to_userroles", joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "user_role_id ") })
public Set<UserRole> getRoles() {
return roles;
}
public void setRoles(Set<UserRole> user_roles) {
this.roles = user_roles;
}
#Column(name="user_status")
#Enumerated(EnumType.STRING)
public UserStatus getStatus() {
return status;
}
public void setStatus(UserStatus status) {
this.status = status;
}
#Transient
#Column(name="user_created")
public Date getCreated() {
return created;
}
public void setCreated(Date user_created) {
this.created = user_created;
}
#Transient
#Column(name="user_updated")
public Date getUpdated() {
return updated;
}
public void setUpdated(Date user_updated) {
this.updated = user_updated;
}
}
And JSP page (simple form, not related to question) with the form to create new user and table to show all existing users. I have used binding between form and Entity object User (it is inside controller):
User user = new User();
List<User> users = userService.getAllUsers();//to fill table with users
List<UserRole> userRoles = userRolesService.getAllRoles();//to fill tables with users
model.addAttribute("rolesList", userRoles);
model.addAttribute("users", users);
model.put("adminForm", user);//Here adminForm is the name of form in JSP page
Now what is the problem: as you see User has two fields user_created and user_updated (they are created automatically by Postgres server). They are forwarded withh all other fields to table in JSP page. BUT my form in JSP does not provide these fields (no need - right)))), so they are null when transfered from form to controller. And now Hibernate can not add line on Postgres server because two fields are empty((( So my question is:
can I somehow mark these columns as #Transient but only when I save entity not read it from database.
I know I still can bind separate field in form not the whole object. But still is it possible to do what I ask? With existing configuration, new User is saved but these two fields are not read and JSP table columns are empty(((
You need to set the insertable and updatable properties of your column mapping to false. This will make the field read-only for Hibernate.
#Column(name="user_created", insertable=false, updatable=false)
So If I have a User and a UserRole Table like soo..
User Class
package app.repo.User;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Version
private Long version;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String password;
#OneToMany(mappedBy = "user")
private Set<UserRole> roles;
protected User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public String toString() {
return String.format(
"User[id=%d, username='%s', password='%s']",
id, username, password);
}
public long getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<UserRole> getRoles() {
return roles;
}
public void setRoles(Set<UserRole> roles) {
this.roles = roles;
}
}
UserRole Class
package app.repo.User;
import javax.persistence.*;
#Entity
#Table(name = "user_roles")
public class UserRole {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Column(name = "role_name")
private String roleName;
#ManyToOne
private User user;
public UserRole() {
}
public UserRole(String roleName, User user) {
this.roleName = roleName;
this.user = user;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public void setUser(User user) {
this.user = user;
}
}
The above example has a oneToMany relationship with UserRole and a ManyToOne Relationship with user.
My first question is... is it possible to save User and UserRole in one save like so...
userDao.save(user);
And second question is. How would I set that up in a JSON post call ? and how would this be done. This is what I am doing now
{
"userId":"1",
"userName":"RestMan",
"password":"happy",
"version":"1",
"email":"restman#gmail.com",
"enabled":"1",
"roles": {
{"user":"1","role_name":"ROLE_COOLGUY"}
}
}
Otherwise I am thinking to just create a Model that saves the two separately in one method
Change the annotation like this : #OneToMany(cascade = CascadeType.ALL)
hello i'm trying to learn one to many mapping but i really having trouble with hibernate. I was able to persist to database but when trying to apply one to many relationship it doesn't persist to DB and also doesn't display the relationship when viewing the response body in postman. I really need help been on this problem since yesterday morning. I have looked on tutorials on youtube and on internet but every tutorial seem basic and when applying same idea no success. I have an entity person and another entity organization. A person can belong up to one organization but different persons can belong to the same organization. So my approach was using a one to many relationship.
Below is my entity of Organization:
#Entity
#Table(name="organization")
public class Organization {
#Id
#Column(name="org_Id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name, description;
#OneToMany(/*fetch = FetchType.EAGER, cascade = CascadeType.ALL*/)
#JoinTable(joinColumns = #JoinColumn(name="org_Id"),
inverseJoinColumns = #JoinColumn(name="person_Id"))
// #JsonIgnore
//#JoinColumn(name="org_Id")
private Collection<Person> personCollection = new ArrayList<Person>();
public Collection<Person> getPersonCollection() {
return personCollection;
}
public void setPersonCollection(Collection<Person> personCollection) {
this.personCollection = personCollection;
}
private Address address;
public Organization() {}
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;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
And below is my Person entity:#Entity
#Table(name = "Person")
public class Person {
#Id
#Column(name="person_Id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="Email",unique = true)
private String email;
#Column(name="FirstName")
private String first_name;
#Column(name="LastName")
private String last_name;
#Column(name="Description")
private String description;
//#Embedded
private Address address;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
private Organization organization;
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public Person() {}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
when trying to create person. Below is my create method:
public Person createPerson(String f_name, String l_name, String email, String city, String state,
String zipCode, String street, String description, Long id) {
Person person = null;
//f_name, l_name, email are required parameters if empty return null and throws an exception..
if(f_name.isEmpty() || l_name.isEmpty() || email.isEmpty()) {
return person;
}
else {
Session session = null;
Transaction transaction = null;
try {
session = sessionFactory.openSession();
transaction = session.beginTransaction();
person = new Person();
person.setFirst_name(f_name);
person.setLast_name(l_name);
person.setEmail(email);
person.setDescription(description);
Address address = new Address();
address.setStreet(street);
address.setZipCode(zipCode);
address.setState(state);
address.setCity(city);
person.setAddress(address);
/* checks to see if id of organization exist if so add to list if not don't do anything.*/
if(id!=null) {
Organization organization = session.get(Organization.class, id);
if (organization != null) {
/* adds id of organization to person table and vice versa.*/
person.setOrganization(organization);
organization.getPersonCollection().add(person);
} else {
//do nothing
}
}
session.save(person);
transaction.commit();
} catch (HibernateException ex) {
if (transaction != null)
transaction.rollback();
ex.printStackTrace();
} finally {
if (session != null)
session.close();
}
return person;
}
}
I am able to create both person and organization and persist to database. But when i try to add an organization to a person Row in database i cannot add the relationship(verified when i tried looking up database itself) and also no response as i get a lazy initialization collection error as well. Please has anyone encountered this problem
I just executed the code snippet you gave in hibernate with the following simplified structure which works perfectly fine. You should start from here and modify as per your needs.
Entity
#Table(name="organization")
public class Organization {
#Id
#Column(name="org_Id")
private long id;
private String name, description;
#OneToMany(cascade = CascadeType.ALL)
private Collection<Person> personCollection = new ArrayList<Person>();
public Collection<Person> getPersonCollection() {
return personCollection;
}
public void setPersonCollection(Collection<Person> personCollection) {
this.personCollection = personCollection;
}
public Organization() {}
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;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and
#Entity
#Table(name = "Person")
public class Person {
#Id
#Column(name="person_Id")
private long id;
#Column(name="Email",unique = true)
private String email;
#Column(name="FirstName")
private String first_name;
#Column(name="LastName")
private String last_name;
#Column(name="Description")
private String description;
#ManyToOne()
private Organization organization;
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public Person() {}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
EDIT : The CascadeType.ALL from Person side of the relationship has been moved to the Organization side of the relationship.Because you want when organization is deleted Person should also get deleted, but not the other way round.