Skip to Content

In this tutorial, java development team experts will share the way to make comparison between two business entities using JPA/ Reflection. You can read the story here and discover how they do it.


Use case:

Let’s say you want to compare two objects of course same type as you can’t compare an Object type with an object type Integer because an Integer can never be String. If the object contains only few attributes we can compare them one by one but if the object contains 20 attributes, 30 attributes, 40 attributes, hundred attributes? Of course in a real time project enterprise application we can have an entity with hundreds of attributes. And out of all these attributes many of them are transiting attributes as some of them are persistent attributes but we have to compare only transient attributes.


How to do it?

Can you write hundred if else conditions and compare each of them? Then this is not at all good programming, if you come up with this solution your team lead will never accept this solution.


Then there comes the solution Java Reflection.

Let’s say you have an Employee entities like below:


Code snippet to create table:


EmployeeBE:

import java.io.Serializable;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.FetchType;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import javax.persistence.JoinColumn;

import javax.persistence.ManyToOne;

import javax.persistence.Table;

import javax.persistence.Transient;

//// Named queries

@Table(schema = “EMP”, name = “T_EMPLOYEE”)

@javax.persistence.SequenceGenerator(name = “EmployeeBE”, allocationSize = 1, initialValue = 1, sequenceName = “SEQ_EMPLOYEE”)

@Entity

publicclass EmployeeBE implements Serializable {

/**

* the serialVersionUID

*/

privatestaticfinallongserialVersionUID = 6058067959150204025L;

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “EmployeeBE”)

private Integer id;

@Column(name = “FIRS_NAME”)

private String firstName;

@Column(name = “LAST_NAME”)

private String lastName;

@Column(name = “ADDRESS”)

private String address;

@Column(name = “EMAIL”)

private String email;

@Column(name = “MOBILE_NUMBER”)

private String mobileNumber;

@Column(name = “MANAGER”)

privatebooleanisManager;

@ManyToOne(fetch = FetchType.LAZY)

@JoinColumn(name = “MANAGER_ID”)

privateMangerBEmanagerBE;

@Column(name = “ROLE”)

private String role;

@Column(name = “SALARY”)

private Double salary;

@Column(name = “MARRIED_STATUS”)

privatebooleanmarriedStatus;

@Transient

privatebooleanemployeeType;

/// many other fields

publicvoid setFirstName(String firstName) {

  1. this.firstName = firstName;

} publicvoid setLastName(String lastName) {

  1. this.lastName = lastName;

} publicvoid setId(Integer id) {

  1. this.id = id;

} publicvoid setAddress(String address) {

  1. this.address = address;

} publicvoid setManager(booleanpisManager) {

  1. this.isManager = pisManager;

} publicvoid setRole(String role) {

  1. this.role = role;

} publicvoid setSalary(Double salary) {

  1. this.salary = salary;

} publicvoid setEmployeeType(booleanemployeeType) {

  1. this.employeeType = employeeType;

}

}



If you observe the above Entity I added many attributes, many of them are in persistent state and some of them are transient state.


Now there is a need to check whether all the persistent attributes are equal or not.


I wrote one helper class, which will have the methods to compare the attributes by reflection.


import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Collection;

import javax.persistence.Column;

import javax.persistence.Id;

import javax.persistence.Version;

publicclass AttributeCompareHelper {

private AttributeCompareHelper() {

}

publicstaticboolean areObjectsEqual(EmployeeBE first, EmployeeBE second, Collection<String>toBeExcludeAttributes) {

booleanretVal = first == second;

if (first != null&&second != null&&first.getClass().equals(second.getClass())) {

final Field[] fields = first.getSupportedFields();

for (final Field field : fields) {

String fieldName = field.getName();

if (field.isAnnotationPresent(Column.class) && !field.isAnnotationPresent(Id.class)

&& !toBeExcludeAttributes.contains(fieldName)) {

final Object value1 = getValue(first, field);

final Object value2 = getValue(second, field);

if (!compareValues(value1, value2)) {

returnfalse;

          }

}

}

retVal = true;

}

returnretVal;

}

publicstatic Object getValue(Object object, Field field) {

final String fieldName = field.getName();

returnprocessGetMethod(object, fieldName);

}

privatestatic Object processGetMethod(Object object, String fieldName) {

final Class<?>objClass = object.getClass();

try {

final Method method = getGetterMethod(objClass, fieldName);

if (method == null) {

thrownew NoSuchMethodException();

}

  1. method.setAccessible(Boolean.TRUE);
  2. returnmethod.invoke(object);

} catch (final Exception e) { }returnobjClass; } publicstatic Method getGetterMethod(Class<?>objClass, String fieldName) throws NoSuchMethodException { String methodName = getGetterName(fieldName); Method isMethodFound = findGetterMethod(objClass, methodName);if (isMethodFound == null) { methodName = getBooleanGetterMethod(fieldName);isMethodFound = findGetterMethod(objClass, methodName);if (isMethodFound == null || !(isMethodFound.getReturnType() == Boolean.class || isMethodFound.getReturnType() == boolean.class)) { thrownew NoSuchMethodException(“No such method in the class “ + objClass.getClass()); } }returnisMethodFound; } publicstatic String getGetterName(String fieldName) {return“get” + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); } privatestatic Method findGetterMethod(Class<?>objClass, String methodName) throws SecurityException {if (objClass == null) {returnnull; }for (final Method method : objClass.getDeclaredMethods()) {if (isMethodMatched(method, methodName)) {returnmethod; } }returnfindGetterMethod(objClass.getSuperclass(), methodName); } privatestaticboolean isMethodMatched(Method md, String methodName, Class<?>… paramTypes) {if (paramTypes == null) {paramTypes = new Class[0]; }final Class<?>[] mdParamTypes = md.getParameterTypes();if (mdParamTypes.length != paramTypes.length) {returnfalse; }for (inti = 0; i<mdParamTypes.length; i++) {if (!mdParamTypes[i].equals(paramTypes[i])) {returnfalse; } }

  1. returnmd.getName().equalsIgnoreCase(methodName);

} publicstatic String getBooleanGetterMethod(String fieldName) {return“is” + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); } publicstaticboolean compareValues(Object first, Object second) { if (first == null) {if (second == null) {returntrue; }returnfalse; }booleanresult;if (second == null) {result = false; } else {result = first.equals(second); }returnresult; } public Field[] getSupportedFields() {returngetSupportedFields(this.getClass()); } staticpublic Field[] getSupportedFields(Class<?>cl) {final Collection<Field>fieldCol = new ArrayList<Field>();for (; cl != null; cl = cl.getSuperclass()) {

  1. fieldCol.addAll(Arrays.asList(cl.getDeclaredFields()));

}final Field[] fields = new Field[fieldCol.size()];

  1. returnfieldCol.toArray(fields);

}

}


The above class works like this.


  1. First it will check whether the @Column annotation is present or not on the attribute
  2. @Id column should not be compared that does not make sense to compare, if they are equal then those are same entities, so skip it
  3. Skips whether the attributes name is present in the excluded columns.
  4. Then gets the corresponding value by calling get Value() method.
  5. getValue() method in turn calls to process the get method for that attributer, but to execute it we need to have getter method for that attribute.
  6. So again it calls to find the getter method based on the type, because as per the java naming convention the getter name for the Boolean names start with is.
  7. If the method name is not present in the current class then it checks for the super class.
  8. If it finds the getter method then it process the method and gets the values for that attribute, the same thing will be repeated for that other object for each attribute.
  9. Compare value method checks for the equality of the two getter method values.
  10. If any of them are not equals then it returns true otherwise returns false.

Now let’s check this. For this I wrote on demo class as below.

import java.util.ArrayList;

publicclass AttributeCompareDemo {

publicstaticvoid main(String args[]) {

EmployeeBE e1 = new EmployeeBE();

e1.setFirstName(“Super”);

e1.setLastName(“Star”);

EmployeeBE e2 = new EmployeeBE();

e2.setFirstName(“Super”);

e2.setLastName(“Super”);

booleanareEqual = AttributeCompareHelper.areObjectsEqual(e1, e2, new ArrayList<String>());

    System.out.println(“Objects are equal: “ + areEqual);

}

}


If you run the above program output will be like this:

Info 1.png

If you observe the output it’s false, because the last name is false;


Info 2.png

If you observe the screen shot the output is true, because I changed the last name.


In this way you can set many attributes and check.


Experts of Java development team have just shared their views about comparison between two business entities using JPA/ Reflection. If you want to ask anything or any point is left unexplained, please write to the experts and wait for their response.

Conclusion:

By using the java reflection mechanism we can compare many attributes without writing many compare statements. Not only JPA entities as I said above we can compare any other objects.


But we need to set the customizable things so that we can differentiate them.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply