Monday, April 15, 2013

Cloning of entity subgraphs

First of all, I have to introduce
myself. I work as a programmer, analyst and a SW architect since
2003. Past experiences with JPA and Hibernate motivated me to write
this short article about considerable benefits of entity cloning. I also introduce an opensource project JpaCloner. You can find it on the github link https://github.com/nociar/jpa-cloner (released under the Apache License, Version 2.0).

Deep entity clone

The cloning mechanism itself is nothing new or special. Every
semi-skilled programmer knows it (copy constructor in C++ or clone()
method in Java). The cloning of a simple object is common and definitely
not interesting for most of us. Until... we have to deal
with concepts like fetching of entity subgraphs,
defensive copying or the entity serialization. It is advisable in
many situations to use, pass or return a deep copy of an entity. But
the question is: how to define the deep copy? It depends on many
factors but mostly on the actual context and requirements. So to be more precise, we often need to
clone an entity subgraph. Deep copies of entities have many
benefits, some examples:

A deep copy can be considered as a fetched entity subgraph.

A copy is always detached from a persistence manager. Any change of a copy does not affect the original.

A deep copy can be used out of a transaction scope and prevents the throwing of the LazyInitializationException.

A copy allows to get rid of JPA proxy classes. A proxy free copy can be serialized by many frameworks (like GWT RPC).

I found couple of projects which deal with similar concepts like Apache Dozer, Gilead (hibernate4gwt) or JpaEntityManager#copy() in EclipseLink. But non of them fulfills my needs, so I decided to do a small project myself. Following sections describe usage of the project in more details.

Entity subgraph

JPA/ORM mapping can be considered as a graph of POJOs. Entities
are nodes and relations are edges (singular, collection or map). A deep clone of
an entity should also make deep clones of neighboring entities and
their neighboring entities, and so on... Such strategy of transitive
cloning may easily ends with the whole DB cloned and OutOfMemoryError
thrown. We have to clone only a part of the object graph - subgraph. The desired subgraph
can be specified by a set of paths from a root entity. After some time I
found that the common dot notation (e.g. "company.department.boss.address") is not flexible enough to
cover many additional needs like recursive relations and path
splitting and joining. So I introduced the GraphExplorer class which
generates the paths upon a string pattern. String literals inside the
pattern are treated as properties. Operators resemble the syntax of
regular expressions:

Dot "." separates paths.

Plus "+" generates at least one preceding path.

Split "|" divides the path into two ways.

Terminator "$" ends the preceding path.

Parentheses "(", ")" groups the paths.

String literals support wildcards: star "*" and question mark "?".

Some examples follow:

project.devices.interfaces

project.devices.inter??ces

project.devices.*

school.teachers.lessonToPupils.(key.class|value.parents)

company.departments+.(boss|employees).address.(country|city|street)

JPA Cloner

The JpaCloner class allows to clone entity subgraphs specified by the string patterns. After the algorithm is finished, each cloned entity contains basic properties (columns) and cloned relations. Non-cloned relations are left as null. The cloned entities are always created as raw classes (annotated by the @Entity or @Embeddable). So a cloned object will never be a hibernate proxy. The JpaCloner supports relations defined as List, Set, SortedSet, Map and SortedMap. The class contains static methods to shield the programmer from the implementation details. The usage looks like:

Conslusion

Working with JPA and ORM may easily become painful (and in some cases a nightmare). This applies especially when it comes to a persisting and merging of complex structures with higher-level semantics. I found that using clones makes my work with ORM again entertaining. Clones can be returned by any transactional method with fetched subgraphs (I use it in my GWT applications). I hope that the project will be useful not only for me :-)

We do not want to copy this fields bc when we try to persist the entity this exception is throwed "Caused by: org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object ... If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting." We use PropertyFilter filter = PropertyFilters.getAnnotationFilter(Id.class, Transient.class); so the id is null but the error continue to persist since the enhancer controls the pcDetachedState... We cannot set to null this fields in our code bc it is added during compile time...

Using BeanUtils.copyProperties(copy, orig); pcStateManager and pcDetachedState are set to null so it persist well on a single element but not on a graph...

So can we say to jpaCloner using patterns or somethine else to not copy this fields when it clones the objects??