Try EqualsBuilder.reflectionEquals() of commons-lang. EqualsBuilder has a set of methods to include all fields, all non-transient fields and all but certain fields.

If all else fails, the code could serve as a good example how to implement this.

Answer from Aaron Digulla on Stack Overflow
🌐
TutorialsPoint
tutorialspoint.com › java_beanutils › java_beanutils_collections_comparing_beans.htm
Java BeanUtils - Comparing Beans
We will be creating two objects and set the first object to "BMW" and the other object to "AUDI". Then, we will compare the objects by using the BeanComparator by calling its compare() method. Note: For BeanComparator, commons-collection and commons-logging jar files need to be included. package ...
🌐
Apache Commons
commons.apache.org › proper › commons-beanutils › javadocs › v1.8.0 › apidocs › org › apache › commons › beanutils › BeanComparator.html
BeanComparator (Commons BeanUtils 1.8.0 API)
This compares two beans by the property specified in the property parameter. This constructor creates a BeanComparator that uses a ComparableComparator to compare the property values. Passing "null" to this constructor will cause the BeanComparator to compare objects based on natural order, ...
Top answer
1 of 4
23

Try EqualsBuilder.reflectionEquals() of commons-lang. EqualsBuilder has a set of methods to include all fields, all non-transient fields and all but certain fields.

If all else fails, the code could serve as a good example how to implement this.

2 of 4
8

To answer your question directly, you could use reflection to do equality checking of beans. There are a few snags you need to be aware of.

There are rules regarding the behaviour of equals() and hashcode(). These rules talk about symmetry, consitency and reflexiveness which may be hard to do when your equals method behaves dynamically based on the other object you're passing in.

Interesting read: http://www.geocities.com/technofundo/tech/java/equalhash.html

Generally speaking, I think you are better off creating your own hashcode and equals methods. There are a fwe good plugins which can automatically generate that code for you based on the class properties.

Having said all this, here are some (old style) methods for getting getters, setters and properties I wrote a long time ago:

private Map getPrivateFields(Class clazz, Map getters, Map setters) {
    Field[] fields = clazz.getDeclaredFields();
    Map m = new HashMap();
    for (int i = 0; i < fields.length; i++) {
        int modifiers = fields[i].getModifiers();
        if (Modifier.isPrivate(modifiers) && !Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
            String propName = fields[i].getName();
            if (getters.get(propName) != null && setters.get(propName) != null) {
                m.put(fields[i].getName(), fields[i]);
            }
        }
    }
    return m;
}

The Getters:

private Map getGetters(Class clazz) {
    Method[] methods = clazz.getMethods();
    Map m = new HashMap();
    for (int i = 0; i < methods.length; i++) {
        if (methods[i].getName().startsWith("get")) {
            int modifiers = methods[i].getModifiers();
            if (validAccessMethod(modifiers)) {
                m.put(getPropertyName(methods[i].getName()), methods[i]);
            }
        }
    }
    return m;
}

And the Setters:

private Map getSetters(Class clazz, Map getters) {
    Method[] methods = clazz.getMethods();
    Map m = new HashMap();
    for (int i = 0; i < methods.length; i++) {
        if (methods[i].getName().startsWith("set")) {
            int modifiers = methods[i].getModifiers();
            String propName = getPropertyName(methods[i].getName());
            Method getter = (Method) getters.get(propName);

            if (validAccessMethod(modifiers) && getter != null) {
                Class returnType = getter.getReturnType();
                Class setType = methods[i].getParameterTypes()[0];
                int numTypes = methods[i].getParameterTypes().length;

                if (returnType.equals(setType) && numTypes == 1) {
                    m.put(propName, methods[i]);
                }
            }
        }
    }
    return m;
}

Maybe you can use this to roll your own.

Edit: Ofcourse the reflectionbuilder in Aaron Digulla's answer is much better than my handywork.

🌐
GitHub
github.com › apache › commons-beanutils › blob › master › src › main › java › org › apache › commons › beanutils2 › BeanComparator.java
commons-beanutils/src/main/java/org/apache/commons/beanutils2/BeanComparator.java at master · apache/commons-beanutils
package org.apache.commons.beanutils2; · import java.util.Comparator; · /** * <p> * This comparator compares two beans by the specified bean property. It is also possible to compare beans based on nested, indexed, combined, mapped bean ...
Author   apache
Find elsewhere
🌐
Apache Commons
commons.apache.org › proper › commons-beanutils › javadocs › v1.9.4 › apidocs › org › apache › commons › beanutils › BeanComparator.html
BeanComparator (Apache Commons BeanUtils 1.9.4 API)
String method name to call to compare. A null value indicates that the actual objects will be compared ... Gets the Comparator being used to compare beans. ... Compare two JavaBeans by their shared property.
🌐
Tabnine
tabnine.com › home page › code › java › org.apache.commons.beanutils.beancomparator
org.apache.commons.beanutils.BeanComparator java code examples | Tabnine
origin: commons-beanutils/commons-beanutils · /** * Compare two JavaBeans by their shared property. * If {@link #getProperty} is null then the actual objects will be compared.
🌐
Apache Commons
commons.apache.org › proper › commons-beanutils › javadocs › v1.8.1 › apidocs › org › apache › commons › beanutils › BeanComparator.html
BeanComparator (Commons BeanUtils 1.8.1 API)
This compares two beans by the property specified in the property parameter. This constructor creates a BeanComparator that uses a ComparableComparator to compare the property values. Passing "null" to this constructor will cause the BeanComparator to compare objects based on natural order, ...
🌐
Apache Commons
commons.apache.org › proper › commons-beanutils › javadocs › v1.8.3 › apidocs › org › apache › commons › beanutils › BeanComparator.html
BeanComparator (Commons BeanUtils 1.8.3 API)
This compares two beans by the property specified in the property parameter. This constructor creates a BeanComparator that uses a ComparableComparator to compare the property values. Passing "null" to this constructor will cause the BeanComparator to compare objects based on natural order, ...
🌐
Apache Commons
commons.apache.org › proper › commons-beanutils › javadocs › v1.8.2 › apidocs › org › apache › commons › beanutils › BeanComparator.html
BeanComparator (Commons BeanUtils 1.8.2 API)
This compares two beans by the property specified in the property parameter. This constructor creates a BeanComparator that uses a ComparableComparator to compare the property values. Passing "null" to this constructor will cause the BeanComparator to compare objects based on natural order, ...
🌐
LogicBig
logicbig.com › how-to › java-beans › comparing-objects-fields.html
How to check if two instances of a class have same field values?
compare-properties-example · src · main · java · com · logicbig · example · BeanUtil.java · Main.java · Person.java ·
🌐
Javadoc.io
javadoc.io › doc › commons-beanutils › commons-beanutils › 1.5 › org › apache › commons › beanutils › BeanComparator.html
BeanComparator - commons-beanutils 1.5 javadoc
Latest version of commons-beanutils:commons-beanutils · https://javadoc.io/doc/commons-beanutils/commons-beanutils · Current version 1.5 · https://javadoc.io/doc/commons-beanutils/commons-beanutils/1.5 · package-list path (used for javadoc generation -link option) https://javadoc.io/doc/commons-beanutils/commons-beanutils/1.5/package-list ·
Top answer
1 of 4
30

You could use Apache Commons Beanutils. Here's a simple example:

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 of 4
18

Hey look at Javers it's exactly what you need - objects auditing and diff framework . With Javers you can persist changes done on your domain objects with a single javers.commit() call after every update. When you persist some changes you can easily read them by javers.getChangeHistory, e.g.

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()));
}

and the output is:

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
🌐
O'Reilly
oreilly.com › library › view › jakarta-commons-cookbook › 059600706X › ch03s11.html
3.10. Comparing Beans - Jakarta Commons Cookbook [Book]
BeanComparator obtains the value of the same bean property from two objects and, by default, compares the properties with a ComparableComparator. The following example sorts a list of Country objects using a BeanComparator and the default ...