JPA (Hibernate) Field Name Issues

I am using Hibernate 3.6, JPA 2.0, and Spring 3.0.6. Hibernate configuration files are being used instead of annotations on the model classes because we like the separation of the configuration files so that the models don’t carry Hibernate library dependencies. These classes utilize standard POJO/Bean definitions with field names and getters/setters for each field, utilizing Hibernate’s Field Access. Here is a sample of the class and orm file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * This is a generated class. Do not modify.
 */

@XmlRootElement(namespace = "http://foo.com/model", name = "ElectronicAddress")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace = "http://foo.com/model", name = "ElectronicAddress", propOrder = { "eAddress", "id" })
public class ElectronicAddress extends ContactPoint {

    /**
     * The serialize value.
     */

    private static final long serialVersionUID = -1L;
    /**
     * The eAddress field.
     */

    @XmlElement(name = "EAddress", namespace = "##default")
    private String eAddress;
    /**
     * The id field.
     */

    @XmlElement(name = "Id", namespace = "##default")
    private Long id; //NOPMD

    /**
     * Gets the value of the eAddress property.
     * This field is Required.
     *
     * @return eAddress object is {@link String }
     */

    public String getEAddress() {
        return eAddress;
    }

    /**
     * Sets the value of the eAddress property.
     * This field is Required
     *
     * @param eAddress object is {@link String }
     */

    public void setEAddress(String eAddress) {
        this.eAddress = eAddress;
    }

    /**
     * Gets the value of the id property.
     * This field is Optional.
     *
     * @return id object is {@link Long }
     */

    public Long getId() {
        return id;
    }

    /**
     * Sets the value of the id property.
     * This field is Optional
     * genericInheritGetterSetter
     * @param id object is {@link Long }
     */

    public void setId(Long id) {
        this.id = (Long) id;
    }

}

The following is the orm file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
  http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"

    version="2.0">
    <description>com.foo.model.ElectronicAddress Entity Mapping</description>
    <package>com.foo.model</package>
    <schema>COMMON</schema>
    <access>FIELD</access>
    <entity class="com.foo.model.ElectronicAddress"
        access="FIELD" metadata-complete="true">
        <table name="ELECTRONIC_ADDRESSES" />
        <attributes>
            <id name="id">
                <column name="ELECTRONIC_ADDRESS_ID" />
                <generated-value strategy="SEQUENCE" generator="ADDR_SEQ" />
                <sequence-generator name="ADDR_SEQ"
                    sequence-name="COMMON.ADDR_SEQ" allocation-size="1" />
            </id>
            <basic name="eAddress">
                <column name="ELECTRONIC_ADDRESS" />
            </basic>
        </attributes>
    </entity>
</entity-mappings>

When the application is deployed to the WebLogic Server, there are quite a few WARN statements that appear related to some of the properties that are defined within the orm files. The interesting thing is that the WARN messages only appear with relation to field names that have a single lowercase character, and these WARN messages don’t appear to change/effect any of the running application.

1
2
3
4
5
2011-02-22 15:38:10,785 [[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN org.hibernate.cfg.annotations.reflection.JPAOverridenAnnotationReader - Property com.foo.model.contactpoint.ElectronicAddress.eAddress not found in class but described in  (possible typo error)
2011-02-22 15:38:10,801 [[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN org.hibernate.cfg.annotations.reflection.JPAOverridenAnnotationReader - Property com.foo.model.person.PersonContact.eAddressCpId not found in class but described in  (possible typo error)
2011-02-22 15:38:10,801 [[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN org.hibernate.cfg.annotations.reflection.JPAOverridenAnnotationReader - Property com.foo.model.person.PersonContact.eAddress not found in class but described in  (possible typo error)
2011-02-22 15:38:10,817 [[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN org.hibernate.cfg.annotations.reflection.JPAOverridenAnnotationReader - Property com.foo.model.person.PartyContact.eAddressCpId not found in class but described in  (possible typo error)
2011-02-22 15:38:10,817 [[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN org.hibernate.cfg.annotations.reflection.JPAOverridenAnnotationReader - Property com.foo.model.person.PartyContact.eAddress not found in class but described in  (possible typo error)

After posting this to Stackoverflow.com under (In Hibernate/JPA can field names no begin with a single letter camel case?), I was pointed to the JPAOverridenAnnotationReader class.

When we take a look at the class JPAOverridenAnnotationReader, we find a method called checkForOrphanProperties

1
2
3
4
5
6
7
8
9
for (Method method : clazz.getMethods()) {
    String name = method.getName();
    if ( name.startsWith( "get" ) ) {
        properties.add( Introspector.decapitalize( name.substring( "get".length() ) ) );
    }
    else if ( name.startsWith( "is" ) ) {
        properties.add( Introspector.decapitalize( name.substring( "is".length() ) ) );
    }
}

The problem is that the method looks for all public fields and then starts adding field names based on “get” and “is” methods it finds. The Introspector.decapitalize method utilizes specific rules to determine what to decapitalize.

From the Introspector class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Utility method to take a string and convert it to normal Java variable
 * name capitalization.  This normally means converting the first
 * character from upper case to lower case, but in the (unusual) special
 * case when there is more than one character and both the first and
 * second characters are upper case, we leave it alone.
 * <p>
 * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
 * as "URL".
 *
 * @param  name The string to be decapitalized.
 * @return  The decapitalized version of the string.
 */

public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
    return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
        Character.isUpperCase(name.charAt(0))){
    return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}

The result is that the decapitalize method doesn’t work the way we expect based on how our field name/setter/getter methods are cased.

1
private String eAddress;

And our getter is:

1
2
3
public String getEAddress() {
        return eAddress;
}

So based on the Introspector.decapitalize functionality, the result of the decapitalize would be “EAddress”, not “eAddress”. Because it sees two capital letters in the “EAddress” when the code substrings off the “get”…it won’t decapitalize those. Therefore it complains that the eAddress field in the orm.xml doesn’t exist. The persistence of the field works totally fine, these warnings just show up when the war starts and the files are bootstrapped.

Share and Enjoy