I have a class of users that includes a list of books objects. Once user's record is created all the books that he borrows will added to the list of books in his object. Once user removes his profile the record and history of the borrowed books need to be kept.
Many registered users may frequently borrow books and delete their profiles. To make the information retrieval faster (for example, retrieve a list of active users) I do not want to keep the record of deleted profiles in the same table that I keep records of active profiles.
Currently once user wants to delete his profile I put that object (userObj) in deactiveUserObj and remove the user's record from User table and try to save the deactiveUserObj to keep the record of deactivated record in that table but it throws the following exception.
My options: I know that I can create another table and keep the id of active users there or have an extra column of type boolean for user class and make it false to indicate the record is deleted. However I am not sure which of these three approaches is better or if there is another approach that I have not considered.
User class
#Entity
public class User{
private long id;
private List<Book> books;
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<Book> getBooks() {
return books;
}
....
Book class
#Entity
public class Book{
private long id;
private string name;
...
}
Deleted Object class
#Entity
public class DeactiveUsers{
private long id;
private date deactiveSince;
private List<Book> books;
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<Book> getBooks() {
return books;
}
....
}
MyCode
1) code to retrieve an object of user class goes here
2) code to copy object of user class into deletedObject goes here
3) 3.1 - session.delete(userObj);
3.2 - session.saveOrUpdate(deactiveUsersObj);
The code runs into following error
SEVERE: org.hibernate.ObjectDeletedException: deleted object would be re-saved by
cascade (remove deleted object from associations): [com.project.Book#1]
First: The reason for your error is you have cascade set to ALL on User.getBooks(), which will delete its books when you delete the User. Then, in the same session, you attempt to re-add the deleted objects when you add the DeactivatedUser back (which contains the same Books -- while you don't show your code for constructing a DeactivatedUser from a User, I presume you are making a shallow copy of Books, as you should). Change cascade to SAVE_UPDATE.
Second: I highly recommend keeping them all in the same table with a flag field that indicates if they are deleted or not.
Do not optimize prematurely. Unless you've actually benchmarked your code and found a bottleneck in database queries related to active vs. inactive users, there's no benefit to over-complicating your schema and business logic. Most SQL servers are very efficient, it is unlikely that you will find a bottleneck there for your application.
Doing it this way, an easy way to improve performance is to simply create an index on the deleted flag field.
With Hibernate you will be able to easily select active vs. deleted users via a Criteria.
Additionally, and most importantly, by not having a separate User and DeactiveUser object, you won't have to maintain the two objects in parallel, and you won't have to duplicate code that can operate on both types.
Related
I am implementing one to one (one Employee<-> one Mobile) relationship in hibernate as follows. This code works fine, but as this is one to one relationship, assigning same mobile number to emp1 and emp2 should have created problem (it violates relationship) but code is accepting and adding 2 emps with same mobile (Confirmed from Database tables). Why is hibernates one to one relationship like one mobile<->many employees?
My Code:
#Entity
public class Employee {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#OneToOne
private Mobile mobile;
//...rest of the code
}
#Entity
public class Mobile {
#Id #GeneratedValue
private int id;
private long number;
//...rest of the code
}
Test Client main(...){
Mobile mobile = new Mobile(9999999999L);
Employee emp1 = new Employee("Raja");
Employee emp2 = new Employee("Raja");
emp1.setMobile(mobile);
emp2.setMobile(mobile);// VIOLATING 1-1 RELATIONSHIP
//...REST OF THE COMMON CODE
session.save(mobile);
session.save(emp1);
session.save(emp2);
session.getTransaction().commit();
}
DATABASE SHOWS BOTH EMP RECORDS WITH SAME MOBILE NUMBER (VIOLATION OF 1-1)
For one to one relations, you should always make sure that you have a unique constraint on your database (either generated by hibernate or manually created).
Hibernate won't check it because it would require to collect extra data every time to do the check. The database can do it more efficiently.
To do the check, hibernate would have to do an extra query. And if the database is configured correctly that extra query would cost time and resources without any gain.
If you don't have unique constraints and you define the relation bidirectional, you can get even more trouble.
Hibernate will save the conflicting records without complaining as you already discovered. And it would become impossible for hibernate to use the relation starting from the object on the other side (getting the Employee via the Mobile in your case). If mobile would be configured to get it's related employee eagerly, it would become impossible to get the mobile in memory after both employee's where saved.
So, I have found myself in quite a pickle regarding Hibernate. When I started developing my web application, I used "eager" loading everywhere so I could easily access children, parents etc.
After a while, I ran into my first problem - re-saving of deleted objects. Multiple stackoverflow threads suggested that I should remove the object from all the collections that it's in. Reading those suggestions made my "spidey sense" tickle as my relations weren't really simple and I had to iterate multiple objects which made my code look kind of ugly and made me wonder if this was the best approach.
For example, when deleting Employee (that belongs to User in a sense that User can act as multiple different Employees). Let's say Employee can leave Feedback to Party, so Employee can have multiple Feedback and Party can have multiple Feedback. Additionally, both Employee and Party belong to some kind of a parent object, let's say an Organization. Basically, we have:
class User {
// Has many
Set<Employee> employees;
// Has many
Set<Organization> organizations;
// Has many through employees
Set<Organization> associatedOrganizations;
}
class Employee {
// Belongs to
User user;
// Belongs to
Organization organization;
// Has many
Set<Feedback> feedbacks;
}
class Organization {
// Belongs to
User user;
// Has many
Set<Employee> employees;
// Has many
Set<Party> parties;
}
class Party {
// Belongs to
Organization organization;
// Has many
Set<Feedback> feedbacks;
}
class Feedback {
// Belongs to
Party party;
// Belongs to
Employee employee;
}
Here's what I ended up with when deleting an employee:
// First remove feedbacks related to employee
Iterator<Feedback> iter = employee.getFeedbacks().iterator();
while (iter.hasNext()) {
Feedback feedback = iter.next();
iter.remove();
feedback.getParty().getFeedbacks().remove(feedback);
session.delete(feedback);
}
session.update(employee);
// Now remove employee from organization
Organization organization = employee.getOrganization();
organization.getEmployees().remove(employee);
session.update(organization);
This is, by my definition, ugly. I would've assumed that by using
#Cascade({CascadeType.ALL})
then Hibernate would magically remove Employee from all associations by simply doing:
session.delete(employee);
instead I get:
Error during managed flush [deleted object would be re-saved by cascade (remove deleted object from associations)
So, in order to try to get my code a bit cleaner and maybe even optimized (sometimes lazy fetch is enough, sometimes I need eager), I tried lazy fetching almost everything and hoping that if I do, for example:
employee.getFeedbacks()
then the feedbacks are nicely fetched without any problem but nope, everything breaks:
failed to lazily initialize a collection of role: ..., could not initialize proxy - no Session
The next thing I thought about was removing the possibility for objects to insert/delete their related children objects but that would probably be a bad idea performance-wise - inserting every object separately with
child.parent=parent
instead of in a bulk with
parent.children().add(children).
Finally, I saw that multiple people recommended creating my own custom queries and stuff but at that point, why should I even bother with Hibernate? Is there really no good way to handle my problem relatively clean or am I missing something or am I an idiot?
If I understood the question correctly it's all about cascading through simple 1:N relations. In that case Hibernate can do the job rather well:
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "post", orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
}
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private Post post;
}
Code:
Post post = newPost();
doInTransaction(session -> {
session.delete(post);
});
Generates:
delete from Comment where id = 1
delete from Comment where id = 2
delete from Post where id = 1
But if you have some other (synthetic) collections, Hibernate has no chance to know which ones, so you have to handle them yourself.
As for Hibernate and custom queries, Hibernate provides HQL which is more compact then traditional SQL, but still is less transparent then annotations.
In hibernate we can map entity relationships with one to one, one to many, etc. I am little bit skeptical about using the relationship annotations and I prefer to use individual find methods to retrieve child records. Example,
Lets consider I have two tables, User and Roles. Each user can have one role. So the entities are,
class User {
#Column
private String name;
#OneToOne(mappedBy="role_id")
private Role role;
.... getter/setter....
}
class Role {
...
}
Either we have to make eager fetch or it will lead to lazy initialisation exception if the role is accessed outside of the current session.
Instead of this, shall we have the mapping like this?
class User {
#Column
private String name;
#Column
private Long roleId;
....
}
This way, whenever we need the role details, we can get the role_id from the User object and query the role table? Is this a right approach? Yes, I know the benefit of loading object graph, but I think this approach will avoid the unnecessary eager fetches and will run seamlessly if we do database partitions.
(I always consider databases as just datastore and use use individual queries to retrieve data instead of using joins to avoid load on the DB).
Please let me know your thoughts.
I suggest using
class User {
#Column
private String name;
#OneToOne(mappedBy="role_id")
private Role role;
.... getter/setter....
}
class Role {
...
}
Don't see point in calling database select twice. Database can handel joins good and do select very fast so I don't see point to manually do it. Also when using this approach you can easy save/update/delete objects.
To avoid lazy initialisation exception you can use Hibernate.initialize(Object obj) as explained here How Hibernate.initialize() works.
New to Hibernate.
I have User Group many to many relation.
Three tables : User , Group and UserGroup mapping table.
Entities:
#Entity
#Table(name = "user")
public class User {
#Id
#Column (name = "username")
private String userName;
#Column (name = "password", nullable = false)
private String password;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="usergroup",
joinColumns={#JoinColumn(name="username")},
inverseJoinColumns={#JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
#Entity
#Table(name = "group")
public class Group {
#Id
#Column(name = "groupname")
private String groupName;
#Column(name = "admin", nullable = false)
private String admin;
#ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
Notice that in Group Entity I'm using fetch method EAGER.
Now, when I'm calling my DAO to retrive all the groups in the system
using the following criteria:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
I'm getting all the rows from the mappgin table (usergroup) instead of getting the actual number of groups...
for example if i have
in user table
username password
-----------------
user1 user1
user2 user2
in group table
groupname admin
---------------
grp1 user1
grp2 user2
in usergroup table
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
The result will be the following list - {grp1,grp2,grp2,grp1}
Instead of {grp1,grp2}
If I change in Group Entity the fetch method to LAZY I'm getting the correct results
but hibernate throws me LazyException in another place...
Please assist what fetch method should I use and why ?
Thanks!
Lazy people will tell you to always use FetchType.EAGER counter-intuitively. These are the people who generally don't worry about database performance and only care about making their development lives easier. I'm going to say you should be using FetchType.LAZY for the increased performance benefit. Because database access is usually the performance bottleneck of most applications, every little bit helps.
If you do actually need to get a list of users for a group, as long as your call getUsers() from within a transactional session, you won't get that LazyLoadingException that is the bane of all new Hibernate users.
The following code will get you all groups without populating the list of users for those groups
//Service Class
#Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
The following code will get you all groups with the users for those groups at the DAO level:
//DAO class
#SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
Edit 1: Thanks to Adrian Shum for this code
For more info on the different types of FetchMode's see here
If you don't want to have to write a different DAO method just to access your collection object, as long as you are in the same Session that was used to fetch the parent object you can use the Hibernate.initialize() method to force the initialisation of your child collection object. I would seriously not recommend that you do this for a List<T> of parent objects. That would put quite a heavy load on the database.
//Service Class
#Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
I've not come across a situation where I've had to use the above code, but it should be relatively efficient. For more information about Hibernate.initialize() see here
I have done this in the service layer rather than fetching them in the DAO, because then you only have to create one new method in the service rather than making a separate DAO method as well. The important thing is that you have wrapped the getUsers() call within the transaction, so a session will have been created that Hibernate can use to run the additional queries. This could also be done in the DAO by writing join criteria to your collection, but I've never had to do that myself.
That said, if you find that you are calling the second method far more than you are calling the first, consider changing your fetch type to EAGER and letting the database do the work for you.
Although answer from JamesENL is almost correct, it is lacking of some very key aspect.
What he is doing is to force the lazy-loading proxy to load when the transaction is still active. Although it solved the LazyInitialization error, the lazy loadings are still going to be done one by one, which is going to give extremely poor performance. Essentially, it is simply achieving the same result of FetchType.EAGER manually (and with a even worse way, because we missed the possibilities of using JOIN and SUBSELECT strategy), which even contradict with the concern of performance.
To avoid confusion: Using LAZY fetch type is correct.
However, in order to avoid Lazy Loading Exception, in most case, you should have your repository (or DAO?) fetch the required properties.
The most inefficient way is to do it by accessing the corresponding property and trigger the lazy loading. There are some really big drawbacks:
Imagine what happen if you need to retrieve multiple level of data.
If the result set is going to be big, then you are issuing n+1 SQLs to DB.
The more proper way is to try to fetch all related data in one query (or a few).
Just give an example using Spring-data like syntax (should be intuitive enough to port to handcraft Hibernate Repository/DAO):
interface GroupRepository {
#Query("from Group")
List<Group> findAll();
#Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
Join fetching is equally simple in Criteria API (though seems only left join is available), quoted from Hibernate doc:
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();
Been struggling with this one for a couple of days. I've got 2 models course and student. Each model is used as a form to register courses and students. I've created a separate entity called timetable which has a course object and a student object, instead of having a manytomany relationship. Here are the releavant sections of the code
Objects/lists from the class model
#Id
#GeneratedValue
public Long id;
#OneToMany(mappedBy = "course", cascade=CascadeType.ALL)
public List<Timtable> timetable;
Objects/lists from the student model
#Required
#Email
#Id
public String email;
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private List<Timetable> timetable;
Objects from the timetable model
#ManyToOne
#JoinColumn(name="email")
public Student student;
#ManyToOne
#JoinColumn(name="id")
public Course course;
Now, I "add" a course to the timetable through a form. The form has 2 hidden inputs with the email and the id. The mySQL table updates, but with NULL values instead of the values that are populated in the form. If I change the timetable variables from objects to primitive types it updates correctly, but when I change back the objects, and the manytoone/onetomany relationship, it just has NULL again. Any ideas on why this is happening?
I'm new to RDMS and ORMs. I've trawled a fair few resources to get the above code together. This is a good one: http://uaihebert.com/jpa-mini-book-first-steps-and-detailed-concepts/22/ (where I got the new class called timetable idea from) and this: http://lazylightening-tech.blogspot.co.uk/2013/07/manytomany-ebean-example.html the Beisar dude has also posted some really good stuff on it. I've been over too many of his posts on here and the google group to get to link, but most people have error messages. I'm not getting an error, it just isn't registering what I'm submitting. Can anyone help?
try writting up manual getters and setters..even i encountered this problem..if you are using eclipse then you can do..
right click + source + generate getters and setters
moreover go through this-
http://www.playframework.com/documentation/2.1.0/JavaEbean
PS - Because Ebean class enhancement occurs after compilation, do not expect Ebean-generated getter/setters to be available at compilation time. If you’d prefer to code with them directly, either add the getter/setters explicitly yourself, or ensure that your model classes are compiled before the remainder of your project, eg. by putting them in a separate subproject.