How to correctly use Apache Common BeanUtil's BeanComparator to benefit of introspection?

3.2k views Asked by At

I searched around on SOF but don't find a so basic question related to BeanUtil use.

I have a POJO class let's say for example UserPojo whose the class code is :

public class UserPojo{

    private String name;
    private int    gender;
    private int    size;

    //Setters
    public void setName(String name) {this.name  =name;}
    public void setGender(int gender){this.gender=gender;}
    public void setSize(int size)    {this.size  =size;}

    //getters
    public String getName()  {return this.name;}
    public int    getGender(){return this.gender;}
    public int    getSize()  {return this.size;}
}

My question is, how to use BeanUtil to automatically compare two instance of this bean ?

I tried this :

final BeanComparator<UserPojo> comparator = new BeanComparator<UserPojo>();
final int comparison = comparator.compare(expectedPojo, receivedPojo);

But it end on the following error:

java.lang.ClassCastException : UserPojo cannot be cast to java.lang.Comparable

I understand that my Pojo should implement the standard Comparable interface, but this way the comparison do not rely on introspection and the import of BeanUtil seems very useless...

So, how to correctly use it ?

2

There are 2 answers

2
mystarrocks On

You've gotta look at the various constructors on there:

BeanComparator(String property)

  • Constructs a property-based comparator for beans.

BeanComparator(String property, Comparator comparator)

  • Constructs a property-based comparator for beans.

Here's the former's javadoc (2nd para is your answer):

Constructs a property-based comparator for beans. 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, that is java.lang.Comparable.

As you'd expect, the constructor you're invoking does just this:

this(null);

For comparing multiple properties, you could make use of the 2nd variant

Collections.sort(collection, 
  new BeanComparator("property1", 
    new BeanComparator("property2", 
      new BeanComparator("property3")))); 

I personally felt that the Apache Commons CompareToBuilder and Google Guava’s ComparisonChain as better choices.

0
Arthur Vaïsse On

I finally gave up and code this. It do not answer the question but this is my way to tackle this problem :

import org.apache.commons.beanutils.BeanUtils;

public static void assertBeansEqual(final Object expected, final Object given) {
    try {
        final Map<String, String> expectedDescription = BeanUtils.describe(expected);
        final Map<String, String> givenDescription = BeanUtils.describe(given);

        // if the two bean don't share the same attributes.
        if (!(expectedDescription.keySet().containsAll(givenDescription.keySet()))) {
            final Set<String> keySet = givenDescription.keySet();
            keySet.removeAll(expectedDescription.keySet());
            fail("The expected bean has not the same attributes than the given bean, the followings fields are not present in the expected bean :" + keySet);
        }
        if (!(givenDescription.keySet().containsAll(expectedDescription.keySet()))) {
            final Set<String> keySet = expectedDescription.keySet();
            keySet.removeAll(givenDescription.keySet());
            fail("The expected bean has not the same attributes than the given bean, the followings fields are not present in the given bean :" + keySet);
        }

        final List<String> differences = new LinkedList<String>();
        for (final String key : expectedDescription.keySet()) {
            if (isNull(expectedDescription.get(key))) {
                // if the bean1 value is null and not the other -> not equal. This test is
                // required to avoid NPE attributes values are null. (null object dot not have
                // equals method).
                if (!isNull(givenDescription.get(key))) {
                    differences.add(key);
                }
            }
            else {
                // if two attributes don't share an attributes value.
                if (!expectedDescription.get(key).equals(givenDescription.get(key))) {
                    differences.add(key);
                }
            }
        }
        if (!differences.isEmpty()) {
            String attributes = "";
            for (final String attr : differences) {
                attributes = attributes + "|" + attr;
            }
            fail("Assertion fail, the expected bean and the given bean attributes values differ for the followings attributes : " + attributes + "|");
        }
    }
    catch (final Exception e) {
        e.printStackTrace();
        fail(e.getMessage());
    }
}