Skip to main content

jpa and object duplication/cloning

12 replies [Last post]
pepe
Offline
Joined: 2003-06-10

Hello.
I have a request where i need to read a JPA object tree from a persistence context (a database on a machine) and persist it to an other (database on an other machine).
Objets i read from first machine have their IDs and when persisting to second machine, they overwrite objects with same IDs.
How can i persist a complete object tree without having to manually and recursively erase all IDs ? Not only it is tedious, but it is sometimes almost impossible to do. I would really love to get a more direct solution.

Thank you.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Lindberg713
Offline
Joined: 2013-08-23

I SOLVED THIS.

I created a component that makes this whole process for you based on the annotations of the package (javax.persistence).

Component already sets the id of the entity to null. He does all the analysis of the algorithm to be applied based on the type of each attribute relationship @ OneToMany @ OneToOne or @ ManyToMany.

Example

Person person = personDAO.find(1);
PersistenceCloner cloner = new PersistenceCloner(person);
Pessoa personCopy = cloner.generateCopyToPersist();

Download JAR and SOURCES: https://lindbergframework.googlecode.com/files/lindbergframework-cloner-...

whartung
Offline
Joined: 2003-06-13

I'm going to effectively disagree with you that the JPA should handle the cloning of objects across EntityManagers. I'd say that it's spectacularly unqualified to support that, and there's a sound reason for it to do so.

Many folks, in many languages and environments, lament that there's rarely any kind of system provided "deep copy" functionality. It seems so easy, but a deep copy is just rampant with edge case, black hole, and implementation defined details that are effectively very difficult, if not impossible to capture, especially declaratively. Java has its Clonable interface, but it's still a shallow copy. The closest we really have (as I mentioned) is serialization, and many folks complain about that simply because it's too eager, for example.

You're asking the JPA to somehow have intimate knowledge of your domain and the context under which you're are cloning your object, and the general case and use cases for that are simply to wide and to varied.

In my experience, I've rarely had the need copy and duplicate object trees, particularly from one DB to another. Why I would want to duplicate all of the leaves of a tree thats nominally going to be stored in a normalized relational DB is, frankly, foreign to me.

So, I basically don't think any effort to solve this problem in some automatic or general way should be spent on the JPA. All of the cases are special cases, even the most basic.

pepe
Offline
Joined: 2003-06-10

We are not talking about the same thing.

Cloning and deep copy are responsibility of the program, JPA does not have to mess with this. We agree.

Nevertheless, just as JPA handles the merging and persisting of entities by setting IDs, it should also handle erasing of those IDs. JPA does not need to know anything more about my data to erase those IDs than when it sets them. What i'm just wishing is that JPA is able to cleanup its own data. IDs are not part of my domain but his, i live without using or even knowing them (ok, mostly. server might use them but client does not), they are here to satisfy JPA's needs.

Everyone's needs are different and i NEED to be able to pass object trees from a JPA context to an other without erasing what's on second context. I also need to be able to duplicate object trees on same context and JPA has the responsibility (imho) to let me do that but actually it ties me to handcoded and/or dirty solutions.
My case is a set of persons handling configuration data for hundreds of servers (about 400 when at max), all at the same time, eventually taking data from one to an other. Data can be pretty deep and complex object trees, complex enough for me to want JPA to handle its own data.

martinstraus
Offline
Joined: 2003-08-22

Just write a class that inspects the annotations in the object tree, identify the @Id and nullify the attribute.

pepe
Offline
Joined: 2003-06-10

what if attribute is private?

whartung
Offline
Joined: 2003-06-13

Reflection can set private attributes. There's a flag on the field, isAccessible, that you need to reset, but reflection will let you do that. Really straightforward.

whartung
Offline
Joined: 2003-06-13

You have the same problems that you would have merging any detached object, and the issue isn't so much with the IDs.

Properly configured with Cascades and what not, the em.merge(rootObject) should Just Work(tm).

The problem is more related to relationships and keeping those in sync, since those must all be done manually. For example, if the detached object is a parent/child with 3 child instances, and the DB has 4 child instances, you need to specifically em.delete() the removed instance, the JPA merge will not do that for you.

But, you shouldn't be having any issues with IDs.

What issues are you having?

pepe
Offline
Joined: 2003-06-10

I want to persist an object and all objects that it references with completely new database rows, not merge.

Here is an example of my use case:
I have four machines, A, B, C and D.
A and B both have a derby and java program using JPA for persistence.
C and D both have a program that access A and/or B to get marshalled objects, retrived from DB using JPA.
Root object retreived from A or B can be merged back to their source machine. This is no problem.
Nevertheless, i need to be able to transfer objects from A to/from B through C and/or D. When doing this, i absolutely need to write a brand new object graph, not update the eventual other objects having the same ID. This is the case i currently have. When i persist objects gotten from one machine to the other one, objects having the same ID overwrite the ones already present.

This is a simplified scheme. there can be multiple servers and multiple clients and not only we should transfer data from any to any other but also any client can create new objects to be saved.
I do not need to synchronize servers but really to transport data from one to an other.

whartung
Offline
Joined: 2003-06-13

Yea, ok, then you're pretty much stuck with the burden of cleaning up your object graph by hand and removing the primary keys. The JPA isn't going to do that for you.

There's a couple of mechanisms you could use to pull this off.

One, you could simply write several "reset" methods in all of your domain objects that take the responsibility of reseting the primary keys of the objects, and any child objects.

Another is to implement the Externalizable interface on all of your objects, set a flag in a global static (or thread local if that's what you need) that says "don't serialize the primary key", then write serialize the graph out to a ByteArrayStream, and then read it back in. This will readily handle the "graph" nature of your object, dealing with loops and sharing, and such. Otherwise you'd need to track and watch for recursive loops or whatever, depending on your graph complexity.

If it's truly a tree (with link back to parents, but basically a tree), then the "reset" method would be really efficient.

[code]
public void reset() {
id = null;
for(Child c : children) {
c.reset();
}
}
[/code]

If that won't send your graph in to an endless loop, then it's pretty simple, and easy to maintain.

pepe
Offline
Joined: 2003-06-10

>Yea, ok, then you're pretty much stuck with the burden of cleaning up your object graph by hand and removing the primary keys. The JPA isn't going to do that for you.
Which is imho a big miss from the jpa spec. They have everything to do that, it just misses a cleanup() method with some copy/paste from merge(). (okay, i might simplify a bit there....)
Anyone willing to do a bit more than the CRUD tasks is forced to adapt its architecture. One can no longer use POJO.
Being able to do that operation not only gives the possibility to duplicate entries -which is something imho needed by lots of applications- but also to transport data, which is also important.

>One, you could simply write several "reset" methods in all of your domain
Yes, i think i could resort to write some kind of visitor pattern thingy so that i can nullify ids. But i can tell you i'll be doing that with a tear drop. This is really something that should be done by jpa, not us.

>Another is to implement the Externalizable interface on all of your objects...
But that would add lots of burden to write all that code, increase possibilities of problems due to maintenance, ... I certainly do not want that.

Your idea about serializing could nevertheless bring something interesting. If i can parse the buffer and set to null all fields having an @ID annotation before unmarshalling, that could do the trick. That would be a diiiiiiiiiiiiiiiirty trick but would at least not add burden to my code. I'll look into that first.
Mhh, thinking more, maybe i'll have more luck serializing to xml, parsing the data, removing id tags and unmarshalling. Less dirty.

Is there any place where the reset functionnality can be added as a proposal for next jpa spec? I wonder if it would be needed to be a jcp member for that....

mvatkina
Offline
Joined: 2005-04-04

You can send your comments to jsr-317-comments@jcp.org. It's listed on the JCP page: http://jcp.org/en/jsr/detail?id=317.

Regards,
-marina

pepe
Offline
Joined: 2003-06-10

Thanks !
I will do.