In here im logging the changes that has been done to a particular Object record. So im comparing the old record and the updated record to log the updated fields as a String. Any idea how can I do this?
How to compare two objects and get the changed fields
Asked Answered
Compare field by field and log the changed ones? –
Snooperscope
Seems your are not happy with comparing each attributes. In this case JAVERS can be an option. –
Ballot
Is your old record stored in an object and you set the new record to it, or you use 2 record object to store this 2 records? –
Crackleware
Well i found a solution as below :
private static List<String> getDifference(Object s1, Object s2) throws IllegalAccessException {
List<String> values = new ArrayList<>();
for (Field field : s1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value1 = field.get(s1);
Object value2 = field.get(s2);
if (value1 != null && value2 != null) {
if (!Objects.equals(value1, value2)) {
values.add(String.valueOf(field.getName()+": "+value1+" -> "+value2));
}
}
}
return values;
}
You may use javers library for this.
<groupId>org.javers</groupId>
<artifactId>javers-core</artifactId>
POJO:
public class Person {
private Integer id;
private String name;
// standard getters/constructors
}
Usage:
@Test
public void givenPersonObject_whenApplyModificationOnIt_thenShouldDetectChange() {
// given
Javers javers = JaversBuilder.javers().build();
Person person = new Person(1, "Michael Program");
Person personAfterModification = new Person(1, "Michael Java");
// when
Diff diff = javers.compare(person, personAfterModification);
// then
ValueChange change = diff.getChangesByType(ValueChange.class).get(0);
assertThat(diff.getChanges()).hasSize(1);
assertThat(change.getPropertyName()).isEqualTo("name");
assertThat(change.getLeft()).isEqualTo("Michael Program");
assertThat(change.getRight()).isEqualTo("Michael Java");
}
Plus other use cases are supported as well.
It does not answer to question. javers only shows difference, but does not provide information about field. –
Masha
Kotlin generic function with reflection
It's not a perfect answer to this question, but maybe someone could use it.
data class Difference(val old: Any?, val new: Any?)
fun findDifferencesInObjects(
old: Any,
new: Any,
propertyPath: String? = null
): MutableMap<String, Difference> {
val differences = mutableMapOf<String, Difference>()
if (old::class != new::class) return differences
@Suppress("UNCHECKED_CAST")
old::class.memberProperties.map { property -> property as KProperty1<Any, *>
val newPropertyPath = propertyPath?.plus('.')?.plus(property.name) ?: property.name
property.isAccessible = true
val oldValue = property.get(old)
val newValue = property.get(new)
if (oldValue == null && newValue == null) return@map
if (oldValue == null || newValue == null) {
differences[newPropertyPath] = Difference(oldValue, newValue)
return@map
}
if (oldValue::class.isData || newValue::class.isData) {
differences.putAll(findDifferencesInObject(oldValue, newValue, newPropertyPath))
} else if (!Objects.equals(oldValue, newValue)) {
differences[newPropertyPath] = Difference(oldValue, newValue)
}
}
return differences
}
Result
{
"some.nested.and.changed.field": {
"old": "this value is old",
"new": "this value is new"
},
...
}
- maybe this method can help you to solve you problem
/**
* get o1 and o2 different value of field name
* @param o1 source
* @param o2 target
* @return
* @throws IllegalAccessException
*/
public static List<String> getDiffName(Object o1,Object o2) throws IllegalAccessException {
//require o1 and o2 is not null
if (o1==null&&o2==null){
return Collections.emptyList();
}
//if only one has null
if (o1 == null){
return getAllFiledName(o2);
}
if (o2 == null){
return getAllFiledName(o1);
}
//source field
Field[] fields=o1.getClass().getDeclaredFields();
List<String> fieldList=new ArrayList<>(fields.length);
//if class is same using this to call
if (o1.getClass().equals(o2.getClass())){
//loop field to equals the field
for (Field field : fields) {
//to set the field access
field.setAccessible(true);
Object source = field.get(o1);
Object target = field.get(o2);
//using jdk8 equals to compare two objects
if (!Objects.equals(source, target)){
fieldList.add(field.getName());
}
}
}else {
//maybe o1 class is not same as o2 class
Field[] targetFields=o2.getClass().getDeclaredFields();
List<String> sameFieldNameList=new ArrayList<>();
//loop o1 field
for (Field field : fields) {
String name = field.getName();
//loop target field to get same field
for (Field targetField : targetFields) {
//if name is equal to compare
if (targetField.getName().equals(name)){
//add same field to list
sameFieldNameList.add(name);
//set access
field.setAccessible(true);
Object source = field.get(o1);
//set target access
targetField.setAccessible(true);
Object target = targetField.get(o2);
//equals
if (!Objects.equals(source, target)){
fieldList.add(field.getName());
}
}
}
}
//after loop add different source
for (Field targetField : targetFields) {
//add not same field
if (!sameFieldNameList.contains(targetField.getName())){
fieldList.add(targetField.getName());
}
}
}
return fieldList;
}
/**
* getAllFiledName
* @param obj
* @return
*/
private static List<String> getAllFiledName(Object obj) {
Field[] declaredFields = obj.getClass().getDeclaredFields();
List<String> list=new ArrayList<>(declaredFields.length);
for (Field field : declaredFields) {
list.add(field.getName());
}
return list;
}
- this method can compare two object which have the same name field ,if they don't have same field,return all field Name
© 2022 - 2024 — McMap. All rights reserved.