OptimisticLocking and #OneToMany(mappedBy=...) handling? - java

I have an AbstractEntity class as superclass for all my entites that defines an optimistic lock column like this:
#Version
private long lockVersion;
Now I often get OptimisticLockingExceptions on entities, that are only changed in one the mappedBy relations similar to the following:
#OneToMany(mappedBy = Property.PROPERTY_DESCRIPTOR, cascade = { CascadeType.REMOVE })
private Set<Property> properties = new HashSet<Property>();
Is it possible to exclude those collections from Hibernate optimistic locking? The entity is not changed in the database at all... only others referencing it.

You can exclude a particular property (and / or collection) from increasing the version number if it's dirty by explicitly excluding it via #OptimisticLock annotation:
#OptimisticLock(excluded=true)
#OneToMany(mappedBy = Property.PROPERTY_DESCRIPTOR, cascade = { CascadeType.REMOVE })
private Set<Property> properties = new HashSet<Property>();
Be aware that it's a Hibernate extension to JPA standard.

i think the accepted answer in this question should help you:link
I havn't tried it myself though, but it could be possible to detect changes not requiring version update and not increment the version.

Related

How to audit class that has OneToMany unidirectional relationship?

I'm using Spring Data JPA for Auditing. There's a unidirectional relationship between classes Article and File. The Article class looks like this:
#Getter
#Entity
#SuperBuilder(toBuilder = true)
#Table(name = "article")
public class Article extends AuditEntity {
...
#Builder.Default
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true)
#JoinTable(name = "article_additional_file",
joinColumns = #JoinColumn(name = "article_id"),
inverseJoinColumns = #JoinColumn(name = "additional_file_id"))
private List<File> additionalFiles = new ArrayList<>();
...
}
The problem is, when changes occur in the file list (owned files get deleted or added), the modifiedDate field (which is in AuditEntity class and it's annotated with #LastModifiedDate annotation) is not updated (it works with all other fields). And I cannot make it a bidirectional relationship since other classes own the File class as well. So my question is, how to trigger the update of field modifiedDate when changes occur in the file list?
EDIT
I'd prefer not to use Enver, if that's possible. I need to use as little additional libraries as possible
Instead of using #JoinTable use #AuditJoinTable
Info from the hibernate documentation:
When a collection is mapped using these two annotations (#OneToMany + #JoinColumn), Hibernate doesn't generate a join table. Envers, however, has to do this, so that when you read the revisions in which the related entity has changed, you don't get false results.
To be able to name the additional join table, there is a special annotation: #AuditJoinTable, which has similar semantics to JPA's #JoinTable.

When updating an ElementCollection does it trigger the #PostUpdate of the containing object?

I have an #ElementCollection Map<User, Long> permissions in a class.
#ElementCollection
#CollectionTable(name = "als_permission", joinColumns = #JoinColumn(name = "File"))
#MapKeyJoinColumn(name = "User")
#Column(name = "Permission")
#JsonIgnore
private Map<User, Integer> permissions = new HashMap<>();
I made some changes on that collection only, and invoke repo.save(entity). I see the record does get updated, but my #PostUpdate handler which is defined in #EntityListeners does not appear to be called.
Is there something I have to put to indicate it? I am thinking I may need to have some sort of cascade somewhere.
Short Answer: No.
I had the same issue came to the conclusion that this is not possible at the moment. Mainly because intended or not implemented yet, see: https://github.com/eclipse-ee4j/jpa-api/issues/167
For the testing I created a repository to find any event I could consumer for the given purpose: https://github.com/HannesRakete/so-jpa-element-collection-events
Workaround 1
Add a #Version for optimistic locking on the parent entity.
Workaround 2
Migrate to another association-type, see https://thorben-janssen.com/hibernate-tips-query-elementcollection/

Hibernate Criteria taking off associations

I have a class Usuario. User have association with UsuarioPerfil:
public class Usuario{
/*Attributes*/
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_usuario_perfil", referencedColumnName = "id", foreignKey = #ForeignKey(name = "fk_usuario_id_usuario_perfil"))
#Getter
#Setter
private UsuarioPerfil usuarioPerfil;
}
public class UsuarioPerfil{
/*Attributes*/
}
I am performing queries using the Criteria, as follows:
Session sessao = this.getEntityManager().unwrap(Session.class);
sessao.createCriteria(Usuario.class).list();
However, in some cases wish list does not come in the data UsuarioPerfil entity, only the User. How can I accomplish this using Hibernate Criteria?
Note: I know this is possible using Hibernate or JPA Query
I don't believe you can explicitly do what you are asking with the Hibernate Criteria API because it is generally accepted practice to make associations LAZY and set them to EAGER on a case-by-case basis.
Therefore, change your mapping to use fetch = FetchType.LAZY and then in the cases where you need the association in your query, specify it as:
criteria.setFetchMode("usuarioPerfil", FetchMode.JOIN);
If you have access to JPA's EntityGraph annotations, I would strongly suggest you look into those as well. At a minimum, you can at least look at Hibernate's FetchProfile concept because those go along way to defining fetch strategies by name which helps keep code and queries much cleaner.

Getting Javassist types instead of actual Hibernate entity types

I have stumbled upon a really annoying situation: I am using Hibernate & Spring as backend for my app and it seems that in some cases, the entities that are in a relationship with a particular entity are not fetched as normal entity objects from the DB, but as Javassist types. E.g.:
I have the Campaign entity with the following relationships:
#Entity
#Table(name = "campaign")
public class Campaign implements Serializable {
[..]
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(uniqueConstraints = #UniqueConstraint(columnNames = {
"campaign_id", "dealer_id" }), name = "campaign_has_dealer", joinColumns = { #JoinColumn(name = "campaign_id", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "dealer_id", nullable = false) })
private List<Dealer> dealers = new ArrayList<Dealer>();
#ManyToMany
// (fetch = FetchType.LAZY)
#JoinTable(uniqueConstraints = #UniqueConstraint(columnNames = {
"campaign_id", "sales_area_id" }), name = "campaign_has_sales_area", joinColumns = { #JoinColumn(name = "campaign_id", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "sales_area_id", nullable = false) })
private List<SalesArea> salesAreas = new ArrayList<SalesArea>();
}
Upon retrieving the salesAreas connected to this Campaign, I get a list of SalesArea_$$_javassist_56, while for the dealers, I get normal Hibernate entities. Since the client part is based on GWT, we use RequestFactory for retrieving stuff. I initially thought it was a problem with the proxies, locators and so on but I have set a breakpoint in the service where these are retrieved and they are Javassist objects directly after selecting them. It seems that even removing the FetchType.LAZY annotation (although definitely not a desirable solution), the same thing happens. This happened also with other types of relationships, not only #ManyToMany.
We are using GWT 2.3, Spring 3, Hibernate 3.6.3 and JPA 2.0 for annotations.
Any suggestions would be appreciated.
Thanks in advance
As far as I can see the big problem that you're having is not so much the fetch type of your association, but rather that the proxied types don't work well with RequestFactory.
Yes, it could be solved by changing the fetch strategy but that sounds rather like a weak workaround that may break upon weird circumstances.
I don't remember exactly how to solve it, but I did, and as far as I remember there was an extension point in the ServiceLayerDecorator class. Basically there you check if the object you're returning is a Hibernate proxy (check Hibernate and HibernateProxy classes) and then return the non-proxy type instead in ServiceLayerDecorator. (http://code.google.com/p/google-web-toolkit/issues/detail?id=6767)
As for your fetch strategy, I'd largely recommend #BatchSize(N) where N is big (maybe 1000), but this is an independent subject.
Good luck!
If you call to the static method:
HibernateProxyHelper.getClassWithoutInitializingProxy(entity);
you get the class of the proxied entity and the class itself if it wasn't proxied.
With Hibernate's proxy model and now with it's use of Javassist to help avoid the slower traditional Hibernate run time reflection operations things will never quite be as elegant as the clean, intuitive experience people who use full bytecode enhancement solutions like JDO implementations (eg DataNucleus) enjoy.
Personally I can never see the sense in persisting (pardon the pun) with solutions that cause so many problems and fill the web with questions about broken code that requires strange, unintuitive workarounds but still people do...
However, back to the question: one solution to your problem, if you're using JPA, is to use DataNucleus/JPA which brings many of the benefits of DataNucleus/JDO (clean underlying implementation - no proxies, no Javassist classes etc.,) in a JPA compliant implementation - i.e. you don't need to change your existing source code to start using it.

Version of all children is incremented in OneToMany when merging parent entity

I have a OneToMany relationship defined like this:
#Entity
Parent extends BaseEntity {
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE})
private List<Child> childList;
// ...
}
#Entity
Child extends BaseEntity {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PARENT_ID")
private Parent parent;
// ...
}
The #Version annotation is defined in the BaseEntity class. The entities are converted to DTOs and changed by the client. Now when the client changes one of the child elements, the parent and its children are converted back to entities and a merge is done by executing em.merge(parent), the version of ALL children is incremented by one! I expected that the version of the changed child is incremented only. First I thought it is because of my EntityCallbackListener which is intercepting the merge with #PreUpdate. But if I comment out the callback method the version fields of the other children are still incremented. Does anyone have an explanation for this behaviour?
I'm using OpenJPA 1.2.3.
Okay, RTFM sometimes helps... :-/
Increasing the version on all children is OpenJPA default:
This lock manager does not perform any exclusive locking, but instead
ensures read consistency by verifying that the version of all
read-locked instances is unchanged at the end of the transaction.
Furthermore, a write lock will force an increment to the version at
the end of the transaction, even if the object is not otherwise
modified. This ensures read consistency with non-blocking behavior.
This is the default openjpa.LockManager setting in JPA.
This setting can be overriden by using the pessimistic lock manager and its properties:
The pessimistic LockManager can be configued to additionally perform
the version checking and incrementing behavior of the version lock
manager described below by setting its VersionCheckOnReadLock and
VersionUpdateOnWriteLock properties.
So I configured OpenJPA to not change the version on update:
<property name="openjpa.LockManager" value="pessimistic(VersionCheckOnReadLock=true,VersionUpdateOnWriteLock=false)"/>
But it does not work. The version fields of all children are still incremented. Do I miss something? What do I have to configure in order to have OpenJPA update the changed entities' version field only?
The problem was a incorrect equals() and hashcode() implementation so the EntityManager assumed all entities in the list had been changed.

Categories

Resources