Retrieve data from many-to-many relationship - Hibernate - java

I have a 'User' class:
public class User implements Serializable {
#Id
#GeneratedValue
int id;
String nome;
#Column(unique = true)
String email;
#ManyToMany
#JoinTable (name = "user_roles", joinColumns=
{ #JoinColumn (name = "user_id")}, inverseJoinColumns=
{ #JoinColumn (name = "role_id")})
private List<Role> roles;
I have a 'Role' class:
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
int id;
#Column(unique = true)
String role;
Relationship :An User has roles, and roles has users. I have a many-to-many relationship here. The relation is unidirectional, and my dominant entity is User.
In my database has a table named "user_roles", created automatically. I have a User registered with two roles.
When I retrieve the User from my database using a "UserDao" class, and I try to access the roles of the user, I get NullPointerException. I got all others informations (name, email, etc), but the roles I can not access.
My 'UserDao' class:
public class UserDaoImpl implements UserDao{
private static final transient Logger log = LoggerFactory.getLogger(UserDaoImpl.class);
private final String HIBERNATE_CFG = "data.cfg.xml";
#Override
public int insertUser (User user){
int code = 0;
Session session = new HibernateUtil(HIBERNATE_CFG).getSessionFactory().getCurrentSession();
try {
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
code = 1;
}
catch (Exception e) {
log.error(e.getMessage());
session.getTransaction().rollback();
code = 0;
}
return code;
}
#Override
public List<User> getAll() {
Session session = new HibernateUtil(HIBERNATE_CFG).getSessionFactory().getCurrentSession();
List<User> users = null;
try
{
session.beginTransaction();
String queryString = "SELECT * FROM USER;";
SQLQuery consulta = session.createSQLQuery(queryString);
consulta.setResultTransformer(Transformers.aliasToBean(User.class));
users = (List<User>) consulta.list();
session.getTransaction().commit();
}
catch(ConstraintViolationException e)
{
log.error(e.getMessage());
session.getTransaction().rollback();
}
return users;
}
I tried using iterator:
List roles = user.getRegras();
for (Iterator it = roles.iterator(); it.hasNext();){
Role r = (Role) it.next();
out.println("Role:" + r.getRole());
}
I tried to instantiate the variable "roles" in the 'User' class like below. But every time the variable returns size 0:
#ManyToMany
#JoinTable (name = "user_roles", joinColumns=
{ #JoinColumn (name = "user_id")}, inverseJoinColumns=
{ #JoinColumn (name = "role_id")})
private List<Role> regras = new ArrayList<Role>();
I tried getting the roles from Object User, but I receive a NullPointerException when the method size() is called.
List<Role> roles = (List<Role>)user.getRoles();
out.println("size roles: " + roles.size());
I'm trying to acess my table 'user_roles' to retrieve the data from database, but I can't dothis. Can anyone help me?
Thanks in Advance

You're using a SQL query to load the users, and mapping the result to the entity. So what you get is detached entities, containing only what the SQL query returned.
Learn to use JPQL/HQL, and to load entities, which will then be managed:
String queryString = "SELECT u FROM User u";
Query consulta = session.createQuery(queryString);
users = (List<User>) consulta.list();

Related

Fix adding entites to ManyToMany

I need to check if user's email is already added to list of students and if so, connect this user with course.
java.lang.UnsupportedOperationException
at java.util.Collections$1.remove(Collections.java:4684)
at java.util.AbstractCollection.clear(AbstractCollection.java:436)
at org.hibernate.collection.internal.PersistentSet.clear(PersistentSet.java:318)
at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:581)
at org.hibernate.type.CollectionType.replace(CollectionType.java:757)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:167)
User Entity
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(
name = "Student_Courses",
joinColumns = {#JoinColumn(name = "student_id")},
inverseJoinColumns = {#JoinColumn(name = "course_id")}
)
private Set<Course> availableCourses = new HashSet<>();
Course Entity
#ManyToMany(mappedBy = "availableCourses")
private Set<User> users = new HashSet<>();
UserService
public void bindStudentWithCoursesAfterRegistration(String email) {
User user = userRepo.findFirstByEmail(email);
List<CourseStudentEmails> studentEntriesInCourses = courseStudentEmailsRepo.findAllByEmail(email);
if (studentEntriesInCourses.size() != 0){
for (CourseStudentEmails entry : studentEntriesInCourses) {
Course course = entry.getCourse();
user.getAvailableCourses().add(course);
}
}
userRepo.save(user);//Exception throws here
}
Registration Controller
#PostMapping("/registration")
public String addUser(User user, #RequestParam(value = "checkboxTeacher", required = false) String checkboxValue, Model model) {
[//chek if user is already registered]
userRepo.save(user);
courseService.bindStudentWithCoursesAfterRegistration(user);
return "redirect:/login";
}
I guess you're missing a proper query to find appropriate CourseStudentEmails.
If you keep a collection of String as emails in your entity (CourseStudentEmails), you can query entities based on specific email as this:
#Query("SELECT c FROM CourseStudentEmails c WHERE ?1 member of c.emails")
List<CourseStudentEmails> findAllByEmail(String email);

Fetch Many to Many collection using Hibernate Criteria

I have two entities User and Attribute and they have Many to Many relation. like below.
public class User implements java.io.Serializable {
private Set<Attribute> attributes = new HashSet<>();
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "user_attributes", joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "attribute_id") })
public Set<Attribute> getAttributes() {
return attributes;
}
}
User_Attributes table may or may not contain data. I am trying to fetch user like below
public User getUserByUserNameAndStatus(String userName, int status) {
Criteria criteria = getSession().createCriteria(User.class);
criteria.createAlias("attributes", "attr", JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("roles", FetchMode.JOIN);
criteria.add(Restrictions.eq("attr.status", 1));
criteria.add(Restrictions.eq("userName", userName));
criteria.add(Restrictions.eq("status", status));
User user = (User) criteria.uniqueResult();
return user;
}
This code return me user object only if there is an entry in User_Attributes table otherwise it is returning me null. But what I want to fetch user object also if there is no entry in User_Attributes table.

JPA recursive one-to-one lazy-loaded reference not updated

Using JPA 2.1 and Hibernate 4.3.6.Final, I have the following simple entity:
#Entity
#Table(name = "CONTACT")
public class Contact {
#Id
#Column(name = "ID")
private String id = UUID.randomUUID().toString();
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARTNER_ID")
private Contact partner;
Contact() {
}
public void assignPartner(final Contact other) {
this.partner = Objects.requireNonNull(other);
other.partner = this;
}
public void unassignPartner() {
if (partner != null) {
partner.partner = null;
}
partner = null;
}
}
Notice the lazy-loaded one-to-one recursive association to a partner Contact. Also notice how assignPartner() and unassignPartner() manage the bi-directional relationship.
And the following methods:
private static void assignPartner(final EntityManager entityManager) {
entityManager.getTransaction().begin();
final Contact contact1 = entityManager.find(Contact.class, CONTACT1_ID);
final Contact contact2 = entityManager.find(Contact.class, CONTACT2_ID);
contact1.assignPartner(contact2);
entityManager.getTransaction().commit();
}
private static void unassignPartner(final EntityManager entityManager) {
entityManager.getTransaction().begin();
final Contact contact1 = entityManager.find(Contact.class, CONTACT1_ID);
contact1.unassignPartner();
entityManager.getTransaction().commit();
}
Assuming existing rows for CONTACT1_ID and CONTACT2_ID, after running assignPartner() then unassignPartner(), database state shows that contact1 has a null partner_id and contact2 still has a non-null partner_id.
However, if I change the Contact.partner fetch type to EAGER, after running assignPartner() then unassignPartner(), database state shows that both contact1 and contact2 have null partner_id.
Why is that? Why are changes to the partner entity not flushed to the database?
EDIT 1
Changes to the partner reference through direct field access, e.g. partner.firstName = "DUMPED", are not propagated either.
Changes to the partner reference through method access, e.g. partner.setFirstName("DUMPED"), are propagated.
Neither partner.partner = null or partner.setPartner(null) are propagated.
EDIT 2
As suggested by Rat2000, moving the unassignment logic outside the Contact.unassignPartner() method and inside the unassignPartner(EntityManager) method seems to work properly. So it's really something to do with how Hibernate deals with the contact1.partner proxy, and in particular the contact1.partner.partner proxy.
final Contact contact1 = entityManager.find(Contact.class, CONTACT1_ID);
contact1.getPartner().unassignPartner();
contact1.unassignPartner();
Try this:
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.UPDATE)
#JoinColumn(name = "PARTNER_ID")
private Contact partner;

List ManyToMany related objects to the current user session

I have an existing database modeled the following way:
users - Id(PK), login, password
locales - Id(PK), descripcion, direccion, etc...
users_locales - user_id(K), local_id(K)
and entity classes as follow
User
#Entity
#Table(name="users")
public class User {
#Id
#GeneratedValue
private Integer id;
private String login;
private String password;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_locales", joinColumns = {#JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "local_id") })
private List<Local> userLocales = new ArrayList<Local>();
Local
#Entity
#Table(name="locales")
public class Local {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name="descripcion")
private String descripcion;
#Column(name="direccion")
private String direccion;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "user_locales", joinColumns = {#JoinColumn(name = "local_id") }, inverseJoinColumns = { #JoinColumn(name = "user_id") })
private List<User> localesUser = new ArrayList<User>();
I need to use a option select in a jsp page that displays a list of "locals" partners the user logged in, but i cant deploy it correctly.
This is not fully developed and I'm doing one or more things wrong, because i can't fully understand how hibernate and Spring security works.
Would need to create a service in "UserService" that generated the list and this is what I need help.
I add some code snippets that I think could be needed for this task.
userService
public interface UserService {
public User getUser(String login);
}
localServiceImpl
#Transactional
public List<Local> listLocal() {
return localDAO.listLocal();
}
//This just show a list of all Local objects in database
localDAOImpl
#Repository
public class LocalDAOImpl implements LocalDAO {
#Autowired
private SessionFactory sessionFactory;
public List<Local> listLocal() {
return sessionFactory.getCurrentSession().createQuery("from Local")
.list();
}
}
controller
#RequestMapping(value="/index", method=RequestMethod.GET)
public ModelAndView indexPage( Map<String, Object> map ) {
map.put("localList", localService.listLocal());
return new ModelAndView("home");
}
//Now its pointing to localService and this list all objets in database
The home.jsp file
<select label="Locales" array="Locales" name="Locales">
<c:forEach items="${localList}" var="local">
<option value="${local.localidad}">
<c:out value="${local.descripcion}"/>
</option>
</c:forEach>
</select>
How could i do a list of "locales" related to the current user session? sorry for possible spelling or grammatical mistakes.
At the moment you are selecting the whole table. You need to modify localDAO.listLocal() and pass the login as parameter then modify your query and use that parameter to limit the output records:
public List<Local> listLocal(String userLogin) {
Query query = sessionFactory.getCurrentSession()
.createQuery("select u.userLocales from User u where u.login = ?");
query.setParameter(0, userLogin);
return query.list();
}
Hibernate, Spring or whatever framework you use, don't limit data automatically. You need to take care of limiting retrieved data for current user yourself.

Container managed security in Glassfish

I have followed this blog-tutorial and successfully got it to work:
http://jugojava.blogspot.com/2011/02/jdbc-security-realm-with-glassfish-and.html
I have named my two entities Group and User. The have a bi-directional many-to-many relationship.
Now the reason I have done it as in the blog is because I am making an administrator page where I want to be able to add new users. I also let users have the oppertunity to register them self, and they will have the role user.
#Entity
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String groupName;
#ManyToMany
#JoinTable(joinColumns = {#JoinColumn(name="group_id")}, inverseJoinColumns = {#JoinColumn(name="user_id")})
private List<User> users;
....
}
and
#Entity
#Table(name = "app_user")
public class User implements Serializable {
public static final String ALL = "User.all";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private Integer age;
private String username;
private String email;
private String password;
#ManyToMany(mappedBy = "users")
private List<Group> groups;
....
}
My question is how I would assign the group user to a user when it register without me picking the groups from a list in the view?
This is what I have done in the application code but it binds the code the the id of the group in the database, are there better ways?
Method from EJB
public void persistAsUser(User user) {
Group group = new Group(2L, "user");
user.addGroup(group);
userRepository.persist(user);
}
You may want to define a UNIQUE index on the field groupName. Then, create a Data Access Object for the Group table, which provides a method for getting a Group from a groupName (code not tested):
public class GroupDAO implements Serializable {
#PersistenceContext private EntityManager em;
public Group findByGroupName(String groupName) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Group> cq = cb.createQuery(Group.class);
Root<Group> group = cq.from(Group.class);
cq.where(cb.equal(group.get(Group_.groupName), groupName));
TypedQuery<Group> q = em.createQuery(cq);
return q.getSingleResult();
}
}
If you don't like Criteria Builder, you can use a Named Query. Add this annotation to your Group Entity Class:
#NamedQuery(name = "Group.findByGroupname", query = "SELECT f FROM group f WHERE f.groupname = :groupname")
and build a Named Query as follows:
return em.createNamedQuery("Group.findByGroupname").setParameter("groupname", groupName).getResultList();

Categories

Resources