Hibernate : many to many linker table, best OO design - java

If I have 3 tables, with the expected normal columns : Customer, CustomerProductLinker and Product.
And I want in my Java code to do this :
Customer customer = myService.getCustomer(id); //calls hibernate session
List<Product> customerProducts = customer.getProducts();
What would my 3 entities look like and the respective collections within each, specifically the getProducts() method ? Or is it better to use HQL and a named query for this ?
I am creating the databse tables from the java code (using the create option in hibernate conf), so the table desgin can be altered if preferred.

Try #ManyToMany relationship using #JoinTable. A customer has a set (or a list) of products. A product has a set (or a list) of customers.
#Entity
public class Customer {
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="customer_id")},
inverseJoinColumns={#JoinColumn(name="product_id")})
private Set<Product> products = new HashSet<Product>();
...
#Entity
public class Product {
#ManyToMany
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="product_id")},
inverseJoinColumns={#JoinColumn(name="customer_id")})
private Set<Customer> customers = new HashSet<Customer>();
...

I would set up the entities like wannik suggested. Try to keep it simple. If you start using named queries you are doing more work and you are just covering an specific case.

Related

How to map a one-to-many result to a Hibernate entity which is joined using a non-primary key?

Suppose there are two classes, Manager and Engineer. Conceptually, an engineer can belong to only one manager and a manager can have multiple engineers but schematically they can be linked by the department id. However, there are no foreign key association between the classes.
public class Manager {
.....
private Long deptId;
}
public class Engineer {
.....
private Long deptId;
}
The following query works in mySQL:
select * from manager m left join engineer e on m.dept_id = e.dept_id.
It returns multiple rows corresponding to the different employees sharing the same department as the manager. I'm not able to map the result of this query to the entities. I would like to do something like the following:
public class Manager {
.....
private Long deptId;
#OnetoMany
private List<Engineer> engineers;
}
#ManytoOne and #OnetoMany annotations expect a foreign key to use for mapping so the above isn't working. Are there other ways to achieve this?
You need to specify #JoinColumn manually. Check out this article for details

Creating missing subclass entity when super exists

I have 2 tables S and I on the database (with a 1:1 relationship), they both have the same id as pk and the hibernate classes I've created are like these:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class S {
#Id
#Column(name = "id")
#GeneratedValue(...)
#SequenceGenerator...
private long id;
....
}
#Entity
#PrimaryKeyJoinColumn(name = "id")
public class I extends S {
....
}
Because of historical reasons, in the database there are objects of type S but not the associated objects of type I. I want to create those I type objects using hibernate. How can I do that? Can I create an I type object from an left join HQL query like this?
select i from I i right join i.id s where s.id = :id
If I try to create a new I entity (new I()) and then persist it, I only managed to get some exceptions as it tries to create an already existing S record. I can't do a simple read/load for I entity as I record does not exist yet. How can I do to create this missing I part entity?
PS I will adjust the question if you point me the unclear things
One approach that will certainly work for you (while is isn't clean one) is to create I records with SQL inserts directly: insert into I_table values (...).
When there are corresponding records in I_table, ORM will start load your objects with I type.
If you have to stay with your ORM and you can delete S records then you can
Load S by id
Delete S (flush? based on your flush mode)
Create I
Copy S values into I
Save I
What you're trying to create is an entity hierarchy. So have to map the entities correctly. The following is probably what you need:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.CHAR)
#DiscriminatorValue("S")
public class S {
#Id
//........
private long id;
....
}
#Entity
#DiscriminatorValue("I")
public class I extends S {
....
}
With this setting the table S will contain a column named DTYPE (for discriminator type) which identifies whether a row belongs to S or I; this is the default; if you don't want that you have to give a name for the DiscriminatorColumn annotation.
Create an instance of S and save
Create an instance of 'I' by populating the inherited properties (i.e., the properties of S) and its own properties, and save.
When you create a query targeting I, you'll get only instances of I, but if your query targets the S, you'll get instances of both entities.

JPA: how to persist many to many relation

I have these 2 entity with many to many relationship.
#Entity
public class User {
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<User> users = new ArrayList<User>();
}
#Entity
public class Language {
#ManyToMany(mappedBy = "languages")
private List<User> users = new ArrayList<User>();
}
I already have 20 languages saved in my language table. Now, I want to create a user and give relate that user with first language in the language table. So I did something like this
Language selectedLanguage = languageService.findById(1);
stammdaten.getLanguages().add(selectedLanguage);
stammdatenService.save(stammdaten);
But this gives me error org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.outgoing.Entity.Language. So how can I save this many to many relation. One thing to note here: I don't want to add new language. I want to add new user with already created languages.
Replace CascadeType.ALL with CascadeType.MERGE.
Also, add setters on both entities.
I define many to many relation through #ManyToMany annotation in JPA.
I have written a code example to insert.
I think this image will help you understand

Deep eager load in J2EE entities

I have a 3 layers model in my J2EE using EJB application: Cart, which has many LineItems, each having many Books (Book doesn't necessarily refers to a Line Item, it's not 2-directional).
Cart(1) <--> (M) LineItem (1) --> (M) Book
I wish to have it all eager loaded, i.e. when I extract the Cart it should also load all its Line Items and all of those Books with minimal number of SQL queries (I'm using a relational DB, e.g. MySQL). It can be done with 3 queries, one for each type of object. Setting "FetchType.EAGER" cause all objects to be loaded, however it has "2+n" calls: 1 query for the cart (obviously), another query for the Line Items, but then had to go on n queries for books, where n is the number of line items.
I used to work with Ruby on Rails, where using eager load (using includes) would do what I need. Can I do it also with J2EE?
(note: join might be an option, but I wish the entities to be populated automatically from the query, although I think the join is less comfortable).
Sample of my code:
#Entity
public class Cart implements Serializable {
#OneToMany(cascade=ALL, mappedBy="cart", fetch = FetchType.EAGER)
private List<LineItem> lineItems;
}
#Entity
public class LineItem implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="cart_id", referencedColumnName = "id")
private Cart cart;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="book_id", referencedColumnName = "id")
private Book book;
}
#Entity
public class Book implements Serializable {
...
}
Here is an example of the SQL queries where the Cart has 3 Line Items:
SELECT id, name FROM carts WHERE (id = 19)
SELECT id, quantity, book_id, cart_id FROM line_items WHERE (cart_id = 19)
SELECT id, description, name, price FROM books WHERE (id = 4)
SELECT id, description, name, price FROM books WHERE (id = 3)
SELECT id, description, name, price FROM books WHERE (id = 1)
Standard JPA provides join fetch, which marks a relation to be fetched eagerly, as if it was marked eager via annotation. In your case, it is only necessary to join fetch lineItems, as book will be eagerly loaded with each LineItem in single query.
With JPA 2.1, you may use Entity graph - you don't need to modify your query, you just attach a descriptor to your query that defines which relations should be eagerly fetched.
If you want to optimize to the smallest amount of queries possible, you might want to use batch fetching, which is available in some JPa providers. But beware, there is no standardized way to turn this on - I just linked to how to do it with EclipseLink.

Hibernate many to many - fetch method eager vs lazy

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();

Categories

Resources