Collection exploration in validator interceptor - java

A question about Java-Hibernate-Validator.
I need to access to a collection attribute when my validation system intercepts an entity insert/update.
I mean, my model defines A class with a set of B elements.
When I call saveOrUpdate(a), the onSave/onFushDirty method of my interceptor is invoked. In that moment I need to know the size of the collection. Is it possible?
Thanks!

Well, according the the docs, your onSave method receives the entity you are persisting. At that point you can cast your entity to what you want. You can either check with instanceof or make overloaded methods that accept your different classes. And from there, access your collection.
Also make sure you have an active session at that point, or that there are no lazy collections.

Related

Pointcut entity load/persist JPA actions?

I'd like to wrap/unwrap my Entity each time it's loaded or persisted. I got to know that I cannot do it using JPA listeners since they can only perform an action on the object and not swap it with the other. The natural solution would be to use an Aspect. But are there particular methods that I can pointcut? The thing is that the entity to be wrapped/unwrapped can be a field of another entity...
EDIT:
I found out that you cannot do this using Spring AOP because EntityManager is not spring-managed (why?) - see here. To make it work I would have to define EntityManager bean explicitely but it is not recommended in that post - again, why? How to do that anyway? On the other hand why does they state that created EntityManager is application-managed? See spring API documentation
Try to pointcut the getters and setter that use the entity class. Methods that retrieve entities from EntityManager, best way to achieve this is create new annotation and put over all the methods that could retrieve a instance for that class, then use the #annotation support pointcut:
#annotation - limits matching to join points where the subject of the
join point (method being executed in Spring AOP) has the given
annotation
Also to obtain the modifying entity you could do something like this.
Get method arguments using spring aop?
Sometimes you need access in the advice body to the actual value that was returned. You can use the form of #AfterReturning that binds the return value

What collections does jpa return?

Does JPA ( Eclipselink in this case) always return IndirectList where Entity have a List?
Is ok that list or It should be converted to another list( maybe linkedlist)?
Analysis
If we look at EclipseLink's IndirectList's API, it says:
To use an IndirectList: declare the appropriate instance variable with type IndirectList (jdk1.1) or Collection/List/Vector (jdk1.2).
TopLink will place an IndirectList in the instance variable when the
containing domain object is read from the datatabase. With the first
message sent to the IndirectList, the contents are fetched from the
database and normal Collection/List/Vector behavior is resumed.
If we view IndirectList sources, we will see, that all the work is delegated to it's original collection, just like API says.
Answers
Does JPA ( Eclipselink in this case) always return IndirectList where Entity have a List?
Yes, it always does return your specified collection wrapped with IndirectList. Since it delegates all its internal work to the wrapped collection, it preserves the way it works.
Is ok that list or It should be converted to another list( maybe
linkedlist)?
Yes, it is okay to use IndirectList. You don't convert, you just define any type of collection you want and don't worry about IndirectList, since it is managed transparently.
Since List is an interface the JPA provider is free to return any implementation. EclipseLink rerurns an IndirectList where a List is used. This is perfectly fine since the IndirectList is a List.
For the record or for future reference, it is generally best practice to use interfaces with JPA.

Hibernate, fetch, HQL and hashCode()

I have a HQL query something ala'
SELECT myclass
FROM
MyClass myclass JOIN FETCH
myclass.anotherset sub JOIN FETCH
sub.yetanotherset
...
So, class MyClass has a property "anotherset" , which is a set containing instance of another class, lets call it MyClassTwo. And, class MyClassTwo has a property yetanotherset which is a set of a third type of class (with no further associations on it).
In this scenario, I'm having trouble with the hashCode implementation. Basically, the hashCode implementation of MyClassTwo, uses the "yetanotherset" property, and on the exact line it accesses the yetanothertest property, it fails with a LazyInitializationException.
org.hibernate.LazyInitializationException: illegal access to loading collection
I'm guessing, this is because the data from "yetanotherset" hasn't been fetched yet, but how do I fix this? I don't particularly like the idea of dumbing down the hashCode to ignore the property.
Additional question, does HQL ignore fetch=FetchType.EAGER as defined in XML or annotations, it seems like it does. But I cant verify this anywhere.
Implementing hashCode() using a mutable field is a bad idea: it makes storing the entity in a HashSet and modifying the mutable property impossible.
Implementing it in terms of a collection of other entities is an even worse idea: it forces the loading of the collection to compute the hashCode.
Choose a unique, immutable property (or set of properties) in your entity, and implement hashCode based on that. On last resort, you have the option of using the ID, but if it's autogenerated, you must not put it in a Set before the ID is generated.
This is hibernate's most famous exception and it is exactly as you described it. The session has been disconnected, transaction closed, and you are attempting to access this collection. JOIN FETCH in your HQL should force EAGER loading to occur regardless of whether than annotation is present.
I suspect that your annotations are malformed, you have missing or out of date jars, or some other problem of that type.
Bump your Hibernate logging level up to generate the SQL hibernate.SQL=debug and investigate exactly what SQL is being executed up to where you see this exception. This should indicate to you whether your hibernate configuration is behaving the way you think its configured.
Post more of your code and the logs and someone might be able to help you spot the error.

DAO, Spring and Hibernate

Correct me if anything is wrong.
Now when we use Spring DAO for ORM templates, when we use #Transactional attribute,
we do not have control over the transaction and/or session when the method is called externally, not within the method.
Lazy loading saves resources - less queries to the db, less memory to keep all the collections fetched in the app memory.
So, if lazy=false, then everything is fetched, all associated collections, that is not effectively, if there are 10,000 records in a linked set.
Now, I have a method in a DAO class that is supposed to return me a User object.
It has collections that represent linked tables of the database.
I need to get a object by id and then query its collections.
Hibernate "failed to lazily initialize a collection" exception occurs when I try to access the linked collection that this DAO method returns.
Explain please, what is a workaround here?
Update: All right, let me ask you this. DAO is an abstract layer, so a method "getUserById(Integer id)" is supposed to return an Object.
What if in some cases I need these linked collections of the User object and in other situation I need those collections.
Are there only two ways:
1) lazy loading = false
2) create different methods: getUserByIdWithTheseCollections(), getUserByIdWithOtherCollections() and inside those methods use your approach?
I mean are there only 2 ways and nothing better?
Update 2: Explain please, what would give me the explicit use of SESSIONFACTORY?
How does it look in practice? We create an instance of DAO object,
then inject it with session factory and this would mean that two consequent
method calls to DAO will run within the same transaction?
It seems to me that anyway, DAO is detached from the classes that make use of it!
The logic and transactions are encapsulated within DAO, right?
You can get the linked collection in transaction to load it while you're still within the transaction:
User user = sessionFactory.getCurrentSession().get(User.class, userId);
user.getLinkedCollection().size();
return user;
As BalusC has pointed out, you can use Hibernate.initialize() instead of size(). That's a lot cleaner.
Then when you return such an entity, the lazy field is already initialized.
Replying to your PS - is using transactions on service level (rather than DAO) level feasible? It seems to be, as doing each DAO call in separate transaction seems to be a waste (and may be incorrect).
I find that it's best to put #Transactional at the service layer, rather than the DAO layer. Otherwise, all your DAO calls are in separate hibernate sessions - all that object equality stuff won't work.
In my opinion best way to solve this problem will be to design application in a session-per-request model. Then, if you even have an object taken from DAO, until your OSIV pattern works you can use the object safely anywhere in application, even in views without bothering this stuff. This is probably better solution that those proposed because:
Hibernate.initialize() or size is a very artificial workaround - what if you want to have User with different collection initialized, would you write another method for getting user?
Service layer transactional model is OK, but the same problem comes when you want to get object extracted from the service layer to use it in controller or view
You could do something like following:
public User getByUserId(Long id, String ... fetch) {
Criteria criteria = createCriteria();
if (fetch != null) {
for (String fieldName : fetch) {
criteria.setFetchMode(fieldName, FetchMode.JOIN); // fetch these fields eagerly
}
}
return criteria.add(Restrictions.eq("id", id)).list();
}

In Hibernate, is there any difference between session.get() and session.load() besides how bad IDs are handled?

An application I'm working on does all its queries by identifier using session.load(). Given the chaos it causes when it can't find the identifier and throws an exception, I'm thinking of swapping it over to session.get(). But before I do that, it's such a fundamental part of the system that I want to make sure there's absolutely no other difference between the two methods. Is there any reason you can think of why the original developers would have chosen load() over get() aside from the handling of invalid IDs?
EDIT: As stated above, I'm fully aware that get returns false and load throws an exception. I'm asking if there's any OTHER way that they differ.
Isn't it so that Get never returns a proxy whereas Load does ?
http://ayende.com/Blog/archive/2009/04/30/nhibernate-ndash-the-difference-between-get-load-and-querying-by.aspx
I think that this is important:
Why is this useful? Well, if you know
that the value exist in the database,
and you don’t want to pay the extra
select to have that, but you want to
get that value so we can add that
reference to an object, you can use
Load to do so:
The code above will not result in a
select to the database, but when we
commit the transaction, we will set
the CustomerID column to 1. This is
how NHibernate maintain the OO facade
when giving you the same optimization
benefits of working directly with the
low level API.
From the NH 2.0 ref documentation:
Note that Load() will throw an
unrecoverable exception if there is no
matching database row. If the class is
mapped with a proxy, Load() returns an
object that is an uninitialized proxy
and does not actually hit the database
until you invoke a method of the
object. This behaviour is very useful
if you wish to create an association
to an object without actually loading
it from the database.
If you are not certain that a matching
row exists, you should use the Get()
method, which hits the database
immediately and returns null if there
is no matching row.
As usual, the best reference for this is the documentation:
Session.get():
Return the persistent instance of the given entity class with the given identifier, or null if there is no such persistent instance. (If the instance, or a proxy for the instance, is already associated with the session, return that instance or proxy.)
Session.load():
Return the persistent instance of the given entity class with the given identifier, assuming that the instance exists.
You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
So, the difference is the way that non-existing instances are treated.
A good approach is shown as follows
If you need to call a getter method, then use get method. get method hits the database.
public class AccountServiceImpl implements AccountService {
private SessionFactory sessionFactory;
public BigDecimal getBalance(Integer acountId) {
// You need to know your balance
// So you need to use get method to access the database
Account account = (Account) sessionFactory.getCurrentSession().get(Account.class, accountId);
return account.getBalance();
}
}
If you need to call both getter and setter method, use get method.
In response to ChssPly's comment:
JPA with Hibernate book says about load method
The load() method always tries to return a proxy, and only returns an initialized object instance if it’s already managed by the current persistence context.
And
It hits the database as soon as you try to access the returned placeholder and force its initialization
Thus, he is right when you set up a single property.
But There is the following scenario shown in JPA with Hibernate book
It’s common to obtain a persistent instance to assign it as a reference to another instance. For example, imagine that you need the item only for a single purpose: to set an association with a Comment: aComment.setForAuction(item).
If this is all you plan to do with the item, a proxy will do fine; there is no need to
hit the database. In other words, when the Comment is saved, you need the foreign key value of an item inserted into the COMMENT table.
The proxy of an Item provides just that: an identifier value wrapped in a placeholder that looks like the real thing.
regards,

Categories