-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Quick deep copy of hughe object graph via DB possible?
PostPosted: Wed May 13, 2009 11:52 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
Hi, I just want to deep copy an object graph loaded from db.
(Haven't implemented clone for all classes yet.)

The idea is to load the object graph and change the root id (composite natural key) and to avoid that only the references are copied, I would like to set all other Id's of this graph to null. So saving the graph to the db will treat them as totally new.

Is this possible without traversing the graph by hand?
Is there any function in hibernate to realise that?

Thanks.

Greetings Michael


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Wed May 13, 2009 1:01 pm 
Regular
Regular

Joined: Wed Jun 20, 2007 1:53 am
Posts: 75
Hi,

I think Hibernate will identify that the object reference is attached with session, os I dont think this will workout...Have you tried this?


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Thu May 14, 2009 3:38 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
Hi, cause I use the session-per-request pattern, the session, which was used to load the object, is already closed, before I try to store within a new session it.

I can write the object with changed id to the db, but cause the id's of the sub-objects are still the same, they aren't written to the database.
(Even @Cascade(value = CascadeType.DELETE_ORPHAN) is not possible)

So what I'm looking for is a function which nulls the ID's for an object graph. Cause hibernate have traverse the object graph anyway, I taught it might be possible to do this with hibernate.

Thanks.

Greetings Michael


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Mon May 18, 2009 10:08 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
Hi,

if the relevant fields and id s would be public, this would work:

Code:
private void nullAllObjectIDs(final Object cm) {
   final Field[] fields = cm.getClass().getDeclaredFields();
   for (final Field f : fields) {
       try {
      nullObjectId(f.get(cm));
       }
       catch (final IllegalAccessException iae) {
      iae.printStackTrace();
       }
   }
    }

Code:
private void nullObjectId(final Object o) throws IllegalAccessException {
   if (o != null) {
       final Field[] fields = o.getClass().getDeclaredFields();
       for (final Field f : fields) {

      final Annotation[] annotations = f.getAnnotations();

      for (final Annotation a : annotations) {
          if (a instanceof javax.persistence.Id) {
         f.set(o, null);
          }
          else {
         try {
             nullObjectId(f.get(o));
         }
         catch (final IllegalAccessException iae) {
             iae.printStackTrace();
         }
          }
      }
       }
   }
    }


In the meantime, I have found that
Code:
f.setAccessible(true);

does the job for private members if no security manager is in use.

Also Lists and Maps are recognized yet.

This is ugly and not surely recommend code (a deep clone infrastructure is much better) , but I will post the update code, as soon as it works for me.

May be you have to care for other collection types.

Thanks.

Greetings Michael


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Tue May 19, 2009 7:53 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
Hi,

as I promised, here is the code that works for me.
I used reflection, if this could be done by hibernate much easier, I would be glad to know it.

But be aware, that it is very error-prone and it was much harder to do, than I taught in the first place.
Especially it was quite hard to stop recursion and to avoid a stack-overflow.

Nevertheless it was a good training in reflection.
But as soon as I have some spare time, I will switch to a clone able hierarchy. Cause I don't fell very well with this solution.

Code:
    private static boolean checkBasicTypes(final Object o) {
   if (!(o instanceof Number) && !(o instanceof Boolean) && !(o instanceof Byte)
      && !(o instanceof Character) && !(o instanceof Void)) {
       return false;
   }
   return true;
    }

    private static boolean checkPrimitives(final Field f) {
   if (f.getType() != Long.TYPE && f.getType() != Boolean.TYPE && f.getType() != Short.TYPE
      && f.getType() != Integer.TYPE && f.getType() != Float.TYPE && f.getType() != Double.TYPE
      && f.getType() != Byte.TYPE && f.getType() != Character.TYPE && f.getType() != Void.TYPE) {
       return false;
   }
   return true;
    }

    private static List<Field> getAllDeclaredFields(final Object o) {
   final List<Field> fields = new ArrayList<Field>();
   if (o != null && !(o instanceof Logger)) {
       Class<?> clazz = o.getClass();
       do {
      try {
          fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
          // System.out.println(clazz.getName());
      }
      catch (final Exception e) {
          e.printStackTrace();
      }
       }
       while ((clazz = clazz.getSuperclass()) != null);
   }
   return fields;
    }

    public static void nullAllObjectIDs(final Object o) throws IllegalAccessException {
   if (o != null) {
       final List<Field> fields = getAllDeclaredFields(o);
       for (final Field f : fields) {
      f.setAccessible(true);

      final Object fieldValue = f.get(o);

      if (fieldValue instanceof Collection || fieldValue instanceof Map
         || (fieldValue != null && fieldValue.getClass().isArray())) {

          if (fieldValue.getClass().isArray()) {
         // System.out.println("Array " + o.getClass().getName()
         // + " " + f.getName());
         for (int i = 0, n = Array.getLength(fieldValue); i < n; i++) {
             final Object data = Array.get(fieldValue, i);
               if (!checkBasicTypes(data) && !(data instanceof String)) {
            nullAllObjectIDs(data);
             }
         }
          }

          if (fieldValue instanceof List) {
         // System.out.println("List " + o.getClass().getName() +
         // " " + f.getName());
         final List<?> list = (List<?>) fieldValue;
         for (final Object l : list) {
             nullAllObjectIDs(l);
         }
          }

          if (fieldValue instanceof Map) {
         // System.out.println("Map " + o.getClass().getName() +
         // " " + f.getName());
         final Map<?, ?> map = (Map<?, ?>) fieldValue;
         if (!(map instanceof EnumMap)) {
             for (final Object l : map.keySet()) {
            if (!(l instanceof String) && !checkBasicTypes(l)) {
                nullAllObjectIDs(l);
            }
             }
         }

         for (final Object l : map.values()) {
             nullAllObjectIDs(l);
         }
          }

          if (fieldValue instanceof Set) {
         // System.out.println("Set " + o.getClass().getName() +
         // " " + f.getName());
         final Set<?> set = (Set<?>) fieldValue;
         for (final Object l : set) {
             nullAllObjectIDs(l);
         }
          }
      }
      else {
          /* Avoid null setting of compound ID's */
          if (!(o instanceof CountryModel)) {
         final Annotation[] annotations = f.getAnnotations();

         for (final Annotation a : annotations) {
             if (a instanceof javax.persistence.Id) {
            System.out.println("ID chnage: " + o.getClass().getName() + "\t"
               + f.getName());
            f.set(o, null);
            break;
             }
         }
          }

          /*
           * Avoids inifitive loop, cause primitives have primitives
           * as fields. Also Number, String and Enum aren't part of
           * any direct relation. So stop unecassry rekursion.
           */
          if (!checkPrimitives(f) && !checkBasicTypes(fieldValue)
             && !(fieldValue instanceof String) && !(fieldValue instanceof Class)
             && !(fieldValue instanceof Enum) && !(fieldValue instanceof StringBuffer)
             && !(fieldValue instanceof Collator) && !(fieldValue instanceof Method)
             && !(fieldValue instanceof Calendar)) {

         try {
             // System.out.println("None Basic Attribute " +
             // o.getClass().getName() + "\t"
             // + f.getName());
             nullAllObjectIDs(fieldValue);
         }
         catch (final IllegalAccessException iae) {
             iae.printStackTrace();
         }
          }

      }

      f.setAccessible(false);
       }
   }
    }


Countrymodel has a compound key, which had be changed before.

Hope this will help someone out here. Even it is not recommend.

Greetings Michael


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Wed May 20, 2009 11:23 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
Hi, just on wquestion left.

If I implement clone as deep-copy, shall I set the id to null or should I leave it unchanged.

Even I guess it makes no difference, cause after object creation, it should be null anyway.
Thanks.

Greetings Michael


Top
 Profile  
 
 Post subject: Re: Quick deep copy of hughe object graph via DB possible?
PostPosted: Tue May 26, 2009 4:58 am 
Regular
Regular

Joined: Thu Apr 14, 2005 10:39 am
Posts: 115
By the way, cause of Item 11 in Effective Java (2nd Ed.), I decided not to use the clone/Cloneable-Infrastructure (except for primitive arrays). Instead I provide a Cloning-Constructor for each class. I also use a marker interface to indicate this. It's a pity for that case, that you can't define constructors in interfaces.

The suggested static factory method isn't so good, I think, cause of inheritance issues. Cause you have to invoke an constructor, you can't return any subtype without reflection.

Greetings Michael


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.