The default entities loading in hibernate is set to lazy. I would like to turn on the eager initalization in Hibernate for just while method calling and then still using lazy loading:
For example i have entity like:
public class Applications implements java.io.Serializable {
private int id;
private String name;
private Set viewses = new HashSet(0); // here entities views
private Set routeses = new HashSet(0); // here antoher entities routes
public Set getViewses() {
return this.viewses;
}
public void setViewses(Set viewses) {
this.viewses = viewses;
}
public Set getRouteses() {
return this.routeses;
}
public void setRouteses(Set routeses) {
this.routeses = routeses;
}
// .... other things
}
I want avoid to iterate all inners objects for completly get all details about object Applications For example:
begin();
List apps = getSession().createQuery("from Applications").list(); // here lazy initalisation not all inner elements are filled from database
commit();
Above code resulting that inner entities like (routes,views) are empty.
When i call application.getRouteses() nothing happens beacuse i have set lazy to true and i call getRouteses() after session close (commit) Only way to set all elementy in entitiy Application throught one transaction and return it completly from method is use iteration and setters or eager initialisation:
begin();
List apps = getSession().createQuery("from Applications").list();
Iterator iter = apps.iterator();
while(iter.hasNext()){
Applications application = (Applications) iter.next();
application.setRouteses(application.getRouteses()); // here i set all routes beacuse i call getRoutes and hibernate will load from db
}
commit();
Now eager inistalistation, below code load all details about object with inner routes and views entities too:
begin();
List apps = getSession().createQuery("from Applications").list(); // here eager all inner object are filled from databasse
commit();
only problem is change lazy to eager but not in hibernate.xml configuration. I would like to know it is possible to trun on eager for some time and then turn on lazy (programatical). Fo example:
// HERE LAZY INITIALISATION
turnOnEager(); // some method ???
// HERE EAGER INITIALISATION
begin();
List apps = getSession().createQuery("from Applications").list(); // here eager all inner object are filled from databasse
commit();
// AND LAZY AGAIN
turnOnLazy(); // some method ???
You may not turn on and off the lazy at runtime. But, you can try to change the hql to fetch the child entities like below using fetch keyword.
HQL
from Applications a join fetch a.someProperty s join fetch s.anotherProperty
From docs:
A "fetch" join allows associations or collections of values to be
initialized along with their parent objects using a single select.
This is particularly useful in the case of a collection. It
effectively overrides the outer join and lazy declarations of the
mapping file for associations and collections.
Ref: http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/queryhql.html
Related
I am having trouble publishing events from an aggregate-root in a Spring Boot application. What I basically want is to publish an "Update" event every time some information about a person is changed.
The code for this is pretty straightforward:
#Entity
public class Person {
#Transient
private final Collection<AbstractPersonRelatedEvent> events = new ArrayList<>();
Person(Person other) {
// copy other fields
other.events.foreach(events::add);
}
// other stuff
public Person updateInformation(...) {
Person updated = new Person(this);
// setting new data on the updated person
if (!hasUpdateEventRegistered()) {
updated.registerEvent(PersonDataUpdatedEvent.forPerson(updated));
}
return updated;
}
void registerEvent(AbstractPersonRelatedEvent event) {
events.add(event);
}
#DomainEvents
Collection<AbstractPersonRelatedEvent> getModificationEvents() {
return Collections.unmodifiableCollection(events);
}
#AfterDomainEventPublication
void clearEvents() {
events.clear();
}
}
I am managing Person instances through a manager:
#Service
#Transactional
class PersistentPersonManager implements PersonManager {
// other methods are omitted
#Override
public Person save(Person person) {
return personRepository.save(person);
}
}
However when I call the manager (manager.save(person.updateInformation(...)) the events seem to go "missing":
upon calling the save() method all events are still present but when Spring invokes getModificationEvents() the collection is empty. The events seem to have vanished somewhere in between (with only Spring-code being executed).
As this is pretty basic, I must be missing something essential but got stuck in a rut.
So how do I get back on track here?
I assume you are using JPA here.
For JPA the save operation actually does a merge on the JPA EnityManager.
For a detached entity merge loads/finds the entity with the same id from the database or the current session and copies all the (changed) fields over. This does ignore transient fields like the events.
You are dealing with detached entities because you are creating a new entity every time you call updateInformation.
So here is what is happening:
You load an entity (e1) from the database. It does not have any events registered.
By calling updateInformation you create a new detached entity (e2). You also register events with e2.
When calling save JPA finds the matching e1 and copies all changes from e2 into it, except the events. So e1 still has no events registered.
Events get triggered, but there aren't any because only e1 is used.
In order to fix this: Do not create new instances of the entity in updateInformation.
I get the an exception when trying to get data, lazily(Exception at the very end)
//application gets data by the following DAO.
public T findById(PK id) {
T result = getHibernateTemplate().get(this.type, id);
getHibernateTemplate().flush();
return result;
}
//Junit test calls a serviceX.getById
#Transactional
public SomeObject getById(int x){
return (SomeObject) aboveDao.findById(x);
}
//Withing the JUnit
SomeObject someObj = serviceX.getById(3);
someObj.getAnotherObject().y.equals("3"); //**Exception** at this line.
//SomeObject class has the following property.
#OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
private AnotherObject anotherObject;
I get the following exception when tryin to access anotherObject in the junit
Methods already tried + extra configuration
We use spring annotation TransactionManager.
<tx:annotation-driven /> specified in the config file.
Also, I tried to add #Transaction(propagation = Propagation.REQUIRED) on top of the JUnit, this did not solve the issue. If I run the application, it works without any issues.
How to solve this type of issue for JUnit?
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role xxxxx , no session or session was closed
Here's what happens
SomeObject someObj = serviceX.getById(3); // #Transactional boundary, no more session
someObj.getAnotherObject().y.equals("3"); // session doesn't exist, can't fetch LAZY loaded object
Because your AnotherObject is LAZY fetched, it doesn't actually get loaded in the getById() method. The Session it was associated with is lost when the #Transactional ends, ie. when execution returns from getById(). Because there is no longer a Session, you get the exception.
You can change your FetchType to EAGER. If you're going to that field of your object, you need to initialize it in your Transaction boundaries.
If you only some times need the anotherObject, a possible solution is to create a #Transactional method that calls the getById and eagerly loads the object.
#Transactional
public SomeObject eagerGetById(int x){
SomeObject obj = getById(x);
obj.getAnotherObject(); // will force loading the object
return obj;
}
Calls this method whenever you need to eagerly load the object.
This is could be useful to you LazyInitilializationException
I have a parent entity that has child entities (A) who in turn have their own child entities (B).
#Entity
public class Parent {
#OneToMany
Set<ChildA> childrenA;
}
#Entity
public class ChildA {
#OneToMany
Set<ChildB> childrenB;
}
I'm trying to display the data via a JSF dataTable. I would like to show the following.
Parent1 | NumberOfRelatedChildrenB
Parent2 | NumberOfRelatedChildrenB
To generate the rows in the dataTable I'm using a MangagedBean which gets a List of the Parents via a ParentFacade.findAll(), but I can't figure out how I can get a List of all the associated ChildBs. I guess I could add a #OneToMany ChildB relationship to the Parent entity, but I was hoping there would be a way to get them via the ChildA relationship?
Thanks in advance & sorry for the poor explanation!
No, I suggest to avoid creating an additional relationship in this case. One way is to create a method in the managed bean that returns the number of related ChildB given an input Parent:
#ManagedBean
public class MyManagedBean {
private List<Parent> parentList;//+getter
private Map<Long, Long> relatedChildrenB = new HashMap<Long,Long>();//+getter
#EJB
private ParentFacade parentFacade;
#PostConstruct
public void init() {
parentList = parentFacade.findAll();
for (Parent parent : parentList) {
relatedChildrenB.put(parent.getId(), parentFacade.getNumberOfRelatedChildrenB(parent));
}
}
and in the facelets page:
<h:dataTable value="#{myManagedBean.parentList}" var="parent">
...
#{myManagedBean.relatedChildrenB[parent.id]}
</h:dataTable>
and implement the corresponding queries in the facade service class.
Note that passing an object using parenthesis () in the previous revision in an EL expression requires EL 2.2 and thus either a Servlet 3.0 compatible container or applying some workaround. This solution does not need method call with parameters.
Finally, note that in my final edit I have followed the wise suggestion of skuntsel to avoid db calls in getter methods.
Simple solution would be to put a method in Parent that returns children count and then use it in dataTable column e.g.
#Transient
public int getAllChildrenCount() {
// iterate through children list and count
}
On view:
#{parent.allChildrenCount}
I'm trying to track changes in JPA OneToMany associations in order to notify subscribers of events that a region (one of these associations) of an object have been changed. I first tried to have a specialization of List which is aware of changes.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "col_name")
private List<SomeType> oneOfManyLists = new CAArrayList<SomeType>();
My decorator looks like this:
public class CAArrayList<T> extends ArrayList<T> {
private boolean changed = false;
public CAArrayList() {
super();
}
public boolean isChanged() {
return changed;
}
// Mutable delegates
#Override
public boolean add( T e) {
changed = true;
return super.add( e );
}
....
}
In the DAO I want to check which of the OneToMany associations have been changed,
Unfortunatly the following line leads to an ClassCastException since Hibernate wraps my CAArrayList this with PersistentBag:
((CAArrayList) obj.getSomeList()).isDirty()
The next one works but is not portable between different implementations of JPA.
((PersistentBag) obj.getSomeList()).isDirty()
How should this be done in a portable way?
I think you're trying to do it at the wrong level (too low).
Rather than trying to track all internal changes to the list of SomeType, encapsulate the functional changes to this list in a specific service, to which all the other services (and the presentation layer) would delegate when this list must be modified. In this centralized service, notify the subscribers when the service changes the list of SomeType.
I have some type an architect(?) question
I develop an application, based on Spring and Hibernate (annotated configuration)
For each table in my database I added 4 fields: createdBy and modifiedBy(String), created and modified (Datetime). Respectively each entity class also has this fields and getter/setter pairs. So I want to find best practice solution for filling this fields instead adding for each DAO extra code. Is it possible?
I'll be glad to any proposal
Certainly. Just add this code to a base class for all your persistent instances and enable annotation processing:
#PrePersist
public void prePersist()
{
if (created == null)
{
created = updated = createCurrentTimestamp();
createdBy = updatedBy = CurrentUser.get();
}
}
#PreUpdate
public void preUpdate()
{
updated = createCurrentTimestamp();
updatedBy = CurrentUser.get();
}
public static java.sql.Timestamp createCurrentTimestamp ()
{
final long now = System.currentTimeMillis();
final java.sql.Timestamp ts = new java.sql.Timestamp (now);
ts.setNanos(((int)(now % 1000)) * 1000000);
return ts;
}
CurrentUser is a ThreadLocal<String> which allows me to specify at the start of an operation which user started it. This way, any object that gets touched will contain the correct information.
Without annotation processing, activate the respective options in your HBM file.
Look at Spring AOP.
You can assign an "interceptor" for your DAO methods, so that the objects are first handled by the interceptor, and then the execution proceeds to the DAO methods.
In the interceptor you can fill the objects with the data you need.
One possibility would be to define a Hibernate EventListener which can fill in these fields just before each entity is flushed to the database