Узнать различия между двумя java bean-компонентами для отслеживания версий
Автор вопроса: Erhan BagdemirОтветов: 4
скажем, у меня есть java bean/объект со 100 полями (унаследовано или нет, в данном случае это не имеет значения). После операций обновления - в транзакции я хочу определить, какие поля изменены, чтобы отслеживать обновления, такие как CVS. Как это сделать проще всего? Любое предложение Framework? Должен ли я сделать два экземпляра этого объекта и перебирать все поля и сопоставлять значения полей? Как в таких ситуациях выглядел бы метод наилучших равных? Следующий equals() кажется очень неудобным:
Вы можете использовать Apache Commons Beanutils. Вот простой пример:
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 · рейтинг 18 · Paweł Szymczyk
Эй, взгляните на Javers, это именно то, что вам нужно - аудит объектов и структура сравнения. С помощью Javers вы можете сохранять изменения, внесенные в объекты вашего домена, одним вызовом javers.commit() после каждого обновления. Когда вы сохраняете некоторые изменения, вы можете легко прочитать их с помощью javers.getChangeHistory, например.
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()));
}
и вывод:
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
Ответ 3 · рейтинг 5 · Damian Leszczyński - Vash
Вы можете использовать отражение для загрузки полей, а затем вызывать их для каждого объекта и сравнивать результат.
Пример исходного кода может выглядеть так:
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
}
}
К вашему сведению, этот код будет работать только с общедоступными полями, объявленными для класса.
Ответ 4 · рейтинг -2 · Vladimir Ivanov
Вы можете использовать Apache BeanUtils для проверки свойств.