I'm looking for the best method(in efficientcy of time/database hits/processing/memory load)
to load a User domain class along with mapped associations of sets (but only about 20 objects from each set and a count of the objects from that set)
using Lazy-loading with @OneToMany
------------------------------------
@OneToMany sets lazy-loading------------------------------------
Say I have a User Domain Class with 7 (and ever-growing) Sets Mapped to other domain objects using "@OneToMany" annotation
E.g. User.javaCode:
@Entity
@Table(name = "user", catalog = "myapp")
public class User implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String userName;
.
.
.
.
private Set<Address> address = new HashSet();
private Set<Message> message = new HashSet();
private Set<Friends> friends = new HashSet();
private Set<Refrences> refrences = new HashSet();
private Set<Events> events = new HashSet();
.. then getters and setters for each
@OneToMany(cascade ={CascadeType.ALL})
@JoinColumn(name="xxxxxxx")
public Set<Address> getAddress() {
return address;
}
public void setAddress(Set<Address> address) {
this.address = address;
}
By x7
Same for the rest of the 7 sets “Set<xxxxx>”.
Now all of these "Sets" are lazy-loaded collections.
When I want to access a certain User I search for the username and lazy-load the rest of the domain objects that correspond to that user name.
E.g. UserDaoImpl.javaCode:
@Repository("userDao")
public class UserDaoImpl implements UserDao{
private SessionFactory sessionFactory;
public UserDaoImpl() {}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Transactional(readOnly = true)
public User getUserWithUserName(String userName) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
try {
User user = (User)session.
createQuery("from User as user where user.userName =:userNameText").
setString("userNameText",userName).uniqueResult();
//Initializes the sets for lazy-loading (within the same session)
Hibernate.initialize(user.getRefrences());
Hibernate.initialize(user.getFriends());
Hibernate.initialize(user.getAddress());
Hibernate.initialize(user.getMessages());
Hibernate.initialize(user.getEvents());
return user;
} catch (HibernateException e) {
return (User) null;
}finally{
session.getTransaction().commit();
}
}
}
The Above code works perfectly, but I don't know how efficient it really is.
It loads the collections. But it loads the whole collection.
I only want to load a certain amount of the lazy-loaded collections
and a count for the amount of rows for each collection.
Over time the amount of objects in each collection will grow to much
and it will be a waste to load 7 entire collections to only display about 20 objects from each one.
Question 1: Is There anyway of limiting the amount of objects loaded in a lazy-loaded collection?
I've seen the "@Where" annotation can be used on Sets in Domains to make a clause, but is there a way of limit restricting with it?
----------------------------------------------------------
@ManyToOne sets lazy-loading + Counting Collections----------------------------------------------------------
Now I've added the counting of each collection associated to the User Class.
The result of each count is set in a Transient object within the User Class.
E.g. UserDaoImpl.java + counting code addedCode:
@Repository("userDao")
public class UserDaoImpl implements UserDao{
private SessionFactory sessionFactory;
public UserDaoImpl() {}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Transactional(readOnly = true)
public User getUserWithUserName(String userName) {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
try {
User user = (User)session.
createQuery("from User as user where user.userName =:userNameText").
setString("userNameText",userName).uniqueResult();
//Initializes the sets for lazy-loading (within the same sessio)
Hibernate.initialize(user.getRefrences());
Hibernate.initialize(user.getFriends());
Hibernate.initialize(user.getAddress());
Hibernate.initialize(user.getMessages());
Hibernate.initialize(user.getEvents());
//***********Counting section within same Hibernate Session*************
//Get size of each collcetion
String refrenceSize = session.createFilter(user.getRefrences(), "select count(*)" ).uniqueResult().toString();
String friendSize = session.createFilter(user.getFriends(), "select count(*)" ).uniqueResult().toString();
String addresSize = session.createFilter(user.getAddress(), "select count(*)" ).uniqueResult().toString();
.
. same for Messages and Events.
//Attach the size of each element into a Transient object into the User Domain
user.setRefrenceSize(refrenceSize);
user.setFriendSize(friendSize);
.
.
. same for Address, Message and Events
//***********************************************************************
return user;
} catch (HibernateException e) {
return (User) null;
}finally{
session.getTransaction().commit();
}
}
}
Question 2: Is this the best possible method for counting Collections and adding them into a class
so they can be displayed in the view? Or is there a better way of counting for many collections?
+ Whats the best way of getting them out into the view?(Adding them to Tranisent objects?)
Question 3: How many times did It hit the database?
Within the 1 Hibernate session it loaded the User Domain, and 7 sets assocated with it.
Then counted the size of 7 collections. Does each generated SQL statement mean a Database Hit?
If so I seen 8 hits, 1 for for the user and 7 for counts.
It took some time to count as it was always draging around the collections with each count.
Now I have the User Class with all its lazy-loaded collections and the count for each Collection.
So people I leave it up to your knowledge.
Whats the best possible thing to do here?
Thanks for reading,
Daxon