7

Is it possible to generate equals and compareTo methods for classes generated with jaxb, I use jaxb to generate classes from a schema. These classes actually have guids that allow them to be uniquely identified but how can I implement an equals/compare method so that Collection classes such as Set would recognise duplicate instances of the same entity ?

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351
  • 1
    Possible duplicate of http://stackoverflow.com/questions/6523497/how-to-generate-equals-and-hashcode-methods-using-wsimport-in-jaxws – Wundwin Born Jan 13 '15 at 11:02
  • No different, because want to create methods based on a particular element of the class, not by looking at all elements. For now the solution I'm using is to always use a TreeSet with custom comparator based on said id. – Paul Taylor Jan 16 '15 at 08:01

2 Answers2

4

Ok, here's another approach.

You could use the -XcodeInjector plugin to add hashCode and equals methods.

See this question:

Inserting code with XJC+xsd+jxb using the options " -Xinject-code -extension "

Something like:

<jxb:bindings schemaLocation="schema.xsd">
    <jxb:bindings node="/xs:schema/xs:complexType[@name='MyItemType']">
        <ci:code>
            @Override
            public int hashCode() { return guid == null? 0 : guid.hashCode();}
        </ci:code>
    </jxb:bindings>
</jxb:bindings>

If this is not good enough, consider filing an issue in JAXB2-Basics ("Allow selecting properties for hashCode/equals") or implementing your own plugin.

Community
  • 1
  • 1
lexicore
  • 42,748
  • 17
  • 132
  • 221
2

Disclaimer: I am the author of jaxb2-basics which provides JAXB2 plugins capable of generating hashCode and equals methods.

Here's usage example for Maven:

        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <extension>true</extension>
                <args>
                    <arg>-Xequals</arg>
                    <arg>-XhashCode</arg>
                </args>
                <plugins>
                    <plugin>
                        <groupId>org.jvnet.jaxb2_commons</groupId>
                        <artifactId>jaxb2-basics</artifactId>
                        <version>...</version>
                    </plugin>
                </plugins>
            </configuration>
        </plugin>

(See the documentation for Ant.)

You can use -XsimpleHashCode and -XsimpleEquals which generate runtime-less hashCode and equals methods (hash code or equals calculation is inlined) or -XhashCode/-Xequals which generate "strategic" hashCode and equals methods (hash code/equals calculations are delegated to the passed strategy methods).

Here's what -XsimpleHashCode generates:

public class Customer {

    ...

    public int hashCode() {
        int currentHashCode = 1;
        {
            currentHashCode = (currentHashCode* 31);
            String theAddress;
            theAddress = this.getAddress();
            if (theAddress!= null) {
                currentHashCode += theAddress.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            Boolean theBlueEyes;
            theBlueEyes = this.isBlueEyes();
            if (theBlueEyes!= null) {
                currentHashCode += theBlueEyes.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            String theFamilyName;
            theFamilyName = this.getFamilyName();
            if (theFamilyName!= null) {
                currentHashCode += theFamilyName.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            String theGivenName;
            theGivenName = this.getGivenName();
            if (theGivenName!= null) {
                currentHashCode += theGivenName.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            List<String> theMiddleInitials;
            theMiddleInitials = (this.isSetMiddleInitials()?this.getMiddleInitials():null);
            if (theMiddleInitials!= null) {
                currentHashCode += theMiddleInitials.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            String thePostCode;
            thePostCode = this.getPostCode();
            if (thePostCode!= null) {
                currentHashCode += thePostCode.hashCode();
            }
        }
        {
            currentHashCode = (currentHashCode* 31);
            boolean theSingle;
            theSingle = this.isSingle();
            currentHashCode += (theSingle? 1231 : 1237);
        }
        return currentHashCode;
    }

}

Here's what -XhashCode generates:

public class Customer implements HashCode
{

    ...

    public int hashCode(ObjectLocator locator, HashCodeStrategy strategy) {
        int currentHashCode = 1;
        {
            String theAddress;
            theAddress = this.getAddress();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "address", theAddress), currentHashCode, theAddress);
        }
        {
            Boolean theBlueEyes;
            theBlueEyes = this.isBlueEyes();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "blueEyes", theBlueEyes), currentHashCode, theBlueEyes);
        }
        {
            String theFamilyName;
            theFamilyName = this.getFamilyName();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "familyName", theFamilyName), currentHashCode, theFamilyName);
        }
        {
            String theGivenName;
            theGivenName = this.getGivenName();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "givenName", theGivenName), currentHashCode, theGivenName);
        }
        {
            List<String> theMiddleInitials;
            theMiddleInitials = (this.isSetMiddleInitials()?this.getMiddleInitials():null);
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "middleInitials", theMiddleInitials), currentHashCode, theMiddleInitials);
        }
        {
            String thePostCode;
            thePostCode = this.getPostCode();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "postCode", thePostCode), currentHashCode, thePostCode);
        }
        {
            boolean theSingle;
            theSingle = this.isSingle();
            currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "single", theSingle), currentHashCode, theSingle);
        }
        return currentHashCode;
    }

    public int hashCode() {
        final HashCodeStrategy strategy = JAXBHashCodeStrategy.INSTANCE;
        return this.hashCode(null, strategy);
    }

}

From my PoV, "strategic" versions are more powerful. Classes implement HashCode or Equals interfaces which accept locators and hash code/equals strategies. This allows you to control the hash code calculation or comparison from the outside. I often use this in unit tests not just to check if two objects equals or not, but also to find out where do they differ, exactly.

Both plugins generate reflection-free methods (this is critical for the performance). They also consider JAXB special cases like JAXBElements, primitive arrays and so on.

lexicore
  • 42,748
  • 17
  • 132
  • 221
  • Thanks, but bit confused do you use jaxbbasics instead of jaxb or as well as ? – Paul Taylor Jan 14 '15 at 20:54
  • JAXB2 Basics is a set of plugins for JAXB/XJC, you can't use them *instead* you have to use them *with* JAXB/XJC. – lexicore Jan 14 '15 at 21:37
  • Okay, my classes already have a uniqueId field so is there a way to just use that field to generate equals and hashcode. – Paul Taylor Jan 15 '15 at 08:53
  • @PaulTaylor This is a new question. – lexicore Jan 15 '15 at 08:57
  • lexicore if you look at my question it says 'These classes actually have guids that allow them to be uniquely identified but how can I implement an equals/compare method' so by that I meant an equals based on the unique id. – Paul Taylor Jan 15 '15 at 15:46
  • @PaulTaylor You're right, I've missed that. No, this use case is not supported OOTB. You could [ignore](http://confluence.highsource.org/display/J2B/JAXB2+Basics+Plugins#JAXB2BasicsPlugins-Excludingproperties) other properties, but it will be too much effort. – lexicore Jan 15 '15 at 16:48