G
G
getatonheim2015-06-27 11:59:50
Java
getatonheim, 2015-06-27 11:59:50

How to make a complete copy of an object, with all descendants in hibernate?

Hello! The system is based on Jboss Seam version 2.3.1. The system uses Java version 6, as well as Apache Tomcat acting as a server. Instead of EJBs, Spring Beans are used to run the application in a servlet container (Tomcat) instead of an application server (TomEE, GlassFish, Weblogic), and so on. Why this is done is not the point.
Versions of components used:
Jboss Seam 2.3.1
Spring Web 3.2.2
Spring Bean 3.2.2
Spring Remote
Hibernate 4.1.4
The problem is this. The entity save method (let's call it saveEntity()) receives a persistent object. I need in this method to make a copy of this object. The object has descendants, that is, it is also necessary to make a deep copy so that all links are saved. Roughly speaking, I need to save the same object, but with a different ID. After several days of reading answers on Google, and on StackOverflow in particular, I used several methods.
1) Use the following method as advised here :

public static <T> T clone(Class<T> clazz, T dtls) { 
        T clonedObject = (T) SerializationHelper.clone((Serializable) dtls); 
        return clonedObject; 
  }

2) Use a self-written method, the essence of which is to unproxy the object (unproxy) and then copy it:
public static <T> T initializeAndUnproxy(T entity) {

        if (entity == null) {

            return null;
        }

        try {
            Hibernate.initialize(entity);

        }
        catch (Exception e) {

            logger.error("Exception occured:", e);
        }

        if (entity instanceof HibernateProxy) {
            entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
        }

        return entity;
    }

public static Object clone(Object obj) {
        
        try {
            
            ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            byte buf[] = baos.toByteArray();
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            ObjectInputStream ois = new ObjectInputStream(bais);

            Object newObject = ois.readObject();
            ois.close();

            return newObject;
        }

The entity object (to be copied) has an Author field, with a type that implements UserDetails (Spring Security). The field corresponds to the system user who saves the record. The problem is, when using any of the above methods, the following exception is thrown:
xxx.yyy.zzz.User cannot be cast to org.jboss.seam.intercept.Proxy
When using a self-written method, it falls specifically here:
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj); <--- тут валится

I'm not an expert in Spring Security, but I suspect it's preventing me from making a copy, but I'm not sure. In the JBoss documentation, there is nothing on the org.jboss.seam.intercept.Proxy interface. Google is also silent on this issue. The entity type implements Serializable. Thanks in advance for any help.
PS With the help of a dirty hack, it is possible to save the entity under a different id, but this caused other problems, so it was temporarily abandoned
this.getSessionFactory().getCurrentSession().evict(entity);
((AEntity) entity).setEntryKey(null);
this.getSessionFactory().getCurrentSession().merge(entity);

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vladimir Smirnov, 2015-06-29
@bobzer

I will not give an exact answer, but there is something to say about this:

  1. The problem with Proxy usually occurs on child collections, especially when they are marked as Lazy. Hibernate substitutes its proxy object into the collection and, when accessing the collection, reads from the database.
  2. Apparently, Hibernate.initialize(entity) in your case does absolutely nothing. This method should be applied not to the entity itself, but to all Lazy collections of the entity, individually for each. If the entity is not detached (not "torn off" from the session in which it was read from the database), then Hibernate.initialize is not required at all, proxy collection objects (from paragraph 1) themselves consider everything from the database when accessing list in code.
  3. Your hack is the first thing that came to my mind at the beginning of reading the question (if EntryKey is the entity's primary key). As soon as the entity "loses" the primary key, it becomes a candidate for insert, if there is a primary key, then Hibernate will do an update. If you carefully "beat" such manipulations, taking into account this rule, and on child objects, then why not?
  4. The issues of object cloning is a separate large and difficult topic, even without linking it to database entities (which exacerbates this problem even more). Two main cloning options: 1 - use specific libraries; 2 - write everything by hand (100500 set(get())-s). The most reliable and productive option is 2, but it generates a lot of extra code, which also needs to be maintained. Nevertheless, this approach is "more alive than all the living", especially if the number of entities for cloning can be counted on the fingers.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question