2011-05-23 5 views
16

decir que tengo un Java Bean/una entidad con 100 campos (heredado o no, no es relevante en este caso). Después de las operaciones de actualización: en una transacción, quiero determinar qué campos se modifican para rastrear actualizaciones como un CVS. ¿Cuál es la forma más fácil de hacer esto? ¿Alguna sugerencia de Marco? ¿Debo hacer dos instancias de este objeto e iterar sobre todos los campos y hacer coincidir los valores de los campos? ¿Cómo sería el mejor método igual en tales situaciones? Los siguientes equals() parece muy incómoda:averiguar las diferencias entre dos beans Java para el seguimiento de versión

return (field1.equals(o.field1)) && 
(field2.equals(o.field2)) && 
(field3.equals(o.field3)) && 
... 
(field100.equals(o.field100)); 

Respuesta

29

Puede usar Apache Commons Beanutils. Aquí hay un ejemplo simple:

package at.percom.temp.zztests; 

import java.lang.reflect.InvocationTargetException; 
import org.apache.commons.beanutils.BeanMap; 
import org.apache.commons.beanutils.PropertyUtilsBean; 
import java.util.Arrays; 
import java.util.HashSet; 
import java.util.Objects; 
import java.util.Set; 

public class Main { 

    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 
     Main main = new Main(); 
     main.start(); 
    } 

    public void start() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 
     SampleBean oldSample = new SampleBean("John", "Doe", 1971); 
     SampleBean newSample = new SampleBean("John X.", "Doe", 1971); 

     SampleBean diffSample = (SampleBean) compareObjects(oldSample, newSample, new HashSet<>(Arrays.asList("lastName")), 10L); 
    } 

public Object compareObjects(Object oldObject, Object newObject, Set<String> propertyNamesToAvoid, Long deep) { 
    return compareObjects(oldObject, newObject, propertyNamesToAvoid, deep, null); 
} 

private Object compareObjects(Object oldObject, Object newObject, Set<String> propertyNamesToAvoid, Long deep, 
     String parentPropertyPath) { 
    propertyNamesToAvoid = propertyNamesToAvoid != null ? propertyNamesToAvoid : new HashSet<>(); 
    parentPropertyPath = parentPropertyPath != null ? parentPropertyPath : ""; 

    Object diffObject = null; 
    try { 
     diffObject = oldObject.getClass().newInstance(); 
    } catch (Exception e) { 
     return diffObject; 
    } 

    BeanMap map = new BeanMap(oldObject); 

    PropertyUtilsBean propUtils = new PropertyUtilsBean(); 

    for (Object propNameObject : map.keySet()) { 
     String propertyName = (String) propNameObject; 
     String propertyPath = parentPropertyPath + propertyName; 

     if (!propUtils.isWriteable(diffObject, propertyName) || !propUtils.isReadable(newObject, propertyName) 
       || propertyNamesToAvoid.contains(propertyPath)) { 
      continue; 
     } 

     Object property1 = null; 
     try { 
      property1 = propUtils.getProperty(oldObject, propertyName); 
     } catch (Exception e) { 
     } 
     Object property2 = null; 
     try { 
      property2 = propUtils.getProperty(newObject, propertyName); 
     } catch (Exception e) { 
     } 
     try { 
      if (property1 != null && property2 != null && property1.getClass().getName().startsWith("com.racing.company") 
        && (deep == null || deep > 0)) { 
       Object diffProperty = compareObjects(property1, property2, propertyNamesToAvoid, 
         deep != null ? deep - 1 : null, propertyPath + "."); 
       propUtils.setProperty(diffObject, propertyName, diffProperty); 
      } else { 
       if (!Objects.deepEquals(property1, property2)) { 
        propUtils.setProperty(diffObject, propertyName, property2); 
        System.out.println("> " + propertyPath + " is different (oldValue=\"" + property1 + "\", newValue=\"" 
          + property2 + "\")"); 
       } else { 
        System.out.println(" " + propertyPath + " is equal"); 
       } 
      } 
     } catch (Exception e) { 
     } 
    } 

    return diffObject; 
} 

    public class SampleBean { 

     public String firstName; 
     public String lastName; 
     public int yearOfBirth; 

     public SampleBean(String firstName, String lastName, int yearOfBirth) { 
      this.firstName = firstName; 
      this.lastName = lastName; 
      this.yearOfBirth = yearOfBirth; 
     } 

     public String getFirstName() { 
      return firstName; 
     } 

     public String getLastName() { 
      return lastName; 
     } 

     public int getYearOfBirth() { 
      return yearOfBirth; 
     } 
    } 
} 
+2

Su código puede terminar con NullPointerException si el valor de la propiedad1 es nulo. –

+1

Por supuesto, hay mucho margen de mejora, esto debe entenderse como una prueba de concepto, no como un código de producción lista. Además, podría haber una excepción no detectada si los tipos de oldObject y newObject no coinciden y así sucesivamente ... – t3chris

+0

desafortunadamente no funciona para objetos complejos :-( – rodrigocprates

1

Puede utilizar Apache BeanUtils a la comprobación de las propiedades.

+0

cuál es el paquete de BeanUtils me da la información que la propiedad se ha cambiado? –

+0

Quise decir que puede iterar a través de las propiedades de la matriz, que BeanUtils devuelve y obtiene los valores. –

5

Puede usar la reflexión para cargar los campos y luego invocarlos en cada objeto y comparar el resultado.

código fuente Ejemplo podría tener este aspecto:

public static <T> void Compare(T source, T target) throws IllegalArgumentException, IllegalAccessException { 

      if(source == null) { 
       throw new IllegalArgumentException("Null argument not excepted at this point"); 
      } 

      Field[] fields = source.getClass().getFields(); 

      Object sourceObject; 
      Object targetObject; 

      for(Field field : fields){ 
       sourceObject = field.get(source); 
       targetObject = field.get(target); 

       //Compare the object 

      } 

     } 

FYI, este código sólo funcionará en campos públicos declarados para la clase.

+2

Tenga en cuenta que siempre puede evitar la limitación del campo público con setAccessable (true); – TheLQ

14

Hey vistazo a Javers que es exactamente lo que necesita - objetos marco de auditoría y dif. Con Javers puede persistir los cambios realizados en los objetos de su dominio con una sola llamada javers.commit() después de cada actualización. Cuando persiste algunos cambios, puede leerlos fácilmente al javers.getChangeHistory, p.

public static void main(String... args) { 
    //get Javers instance 
    Javers javers = JaversBuilder.javers().build(); 

    //create java bean 
    User user = new User(1, "John"); 

    //commit current state 
    javers.commit("author", user); 

    //update operation 
    user.setUserName("David"); 

    //commit change 
    javers.commit("author", user); 

    //read 100 last changes 
    List<Change> changes = javers.getChangeHistory(instanceId(1, User.class), 100); 

    //print change log 
    System.out.printf(javers.processChangeList(changes, new SimpleTextChangeLog())); 
} 

y la salida es:

commit 2.0, author:author, 2015-01-07 23:00:10 
    changed object: org.javers.demo.User/1 
    value changed on 'userName' property: 'John' -> 'David' 
commit 1.0, author:author, 2015-01-07 23:00:10 
    new object: 'org.javers.demo.User/1 
Cuestiones relacionadas