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.
Related
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.
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.
I am quite new to hibernate and programming with databases in general to be honest...
I've tried to save some graph-like structure to database.
Suppose I have Java class like this:
public class User {
#OneToMany(cascade=CascadeType.ALL)
private Collection<User> followers = new ArrayList<>();
#OneToMany(cascade=CascadeType.ALL)
private Collection<User> friends = new ArrayList<>();
#Id
private String name;
.....
}
The problem is I want to save it to PostgeSQL database using Hibernate. However I found it quite nontrivial. The one problem for example is:
Suppose I do:
User user1 = new User("user1");
User user2 = new User("user2");
user1.getFollowers().add(user2);
user1.getFriends().add(user2);
Now if I do merge on user1 object there will be issue with key uniqueness constraint. I wonder if this is the issue because I misconfigured Hibernate annotations to save my structure or it is entirely wrong approach to represent graph in such a way using Hibernate ? Any help much appreciated.
Personally I would go for a generated ID column, and would not use the user's name for that. Put a constraint on that name column in the database rather.
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column
private long id;
Otherwise I cannot see anything wrong with your approach. Do you get any exceptions while running your code?
Say I have these classes:
public class Loan {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "lender_id")
private User lender;
}
public class User {
#Id
private Long id;
#Column
private String userName;
#OneToMany
private List<Loan> loans;
}
Now, let's say I have the user (lender) id and in the DAO layer, I want to create a Loan based on the id of the lender?
I realize that I can do the following:
User u = userDao.getUserById(1234L);
loanDao.createLoan(u, "someLoan");
But I'm wondering if it's possible to do it without pre-loading the User record?
There isn't a good way to do that, in part because it would fundamentally lead to incorrect ORM code. You the programmer are responsible for managing the in memory state of the Entities and keeping them correct. If you create a new Loan and say it belongs to a User, and a User has a collection of Loans, it is your responsibility to add that Loan to the User! (This has real consequences as soon as the caches get involved.)
You're using ORM, you need to think in terms of the objects and not in terms of the database. Adding a number in a foreign key column isn't what's important, setting up the correct in-memory representation of the object Model is what's important for you. The database is hibernate's problem.