0

I'm using JPA's entitymanager.find(T, pk) to find a record. This is in an existing app where entitymanager.find() is used successfully, but this is the first instance of using a composite primary key. Here is the entity class:

@Entity
@Table(name = "host_property", schema = "app")
@IdClass(HostPropertyPK.class)
public class HostProperty implements Serializable {
    @Id
    @Column(name = "host")
    private String host_;

    @Id
    @Column(name = "name")
    private String name_;

    @Column(name = "value")
    private String value_;

    private static final long serialVersionUID = 1L;

    /**
     * Java Persistence requires a no-argument constructor. Private so that only
     * JPA may use it.
     */
    private HostProperty() {
    }

    /**
     * Gets the associated host.
     * 
     * @return String containing the host
     */
    public String getHost() {
        return host_;
    }

    /**
     * Gets the property name.
     * 
     * @return String containing the property name
     */
    public String getName() {
        return name_;
    }

    /**
     * Gets the property value.
     * 
     * @return String containing the property value
     */
    public String getValue() {
        return value_;
    }

    /**
     * Gets the value as an int.
     * 
     * @return int containing the value parsed as an int
     */
    public int getIntValue() {
        return Integer.parseInt(value_);
    }

}

and the primary key class:

public class HostPropertyPK implements Serializable {
    private static final long serialVersionUID = 1L;
    @Column(name = "host")
    private String host_;

    @Column(name = "name")
    private String name_;

    /**
     * Java Persistence requires a no-argument constructor. Private so that
     * only JPA may use it.
     */
    @SuppressWarnings("unused")
    private HostPropertyPK() {
    }

    /**
     * Creates a host property primary key.
     * 
     * @param host String containing the host
     * @param propertyName String containing the property name
     */
    public HostPropertyPK(String host, String propertyName) {
        this.host_ = host;
        this.name_ = propertyName;
    }

    /**
     * Gets the host of this primary key.
     * 
     * @return String containing the host
     */
    public String getHost() {
        return host_;
    }

    /**
     * Gets the property name of this primary key.
     * 
     * @return String containing the property name
     */
    public String getPropertyName() {
        return name_;
    }

    @Override
    public int hashCode() {
        int code = 1;
        code = 31 * code + (host_ == null ? 0 : host_.hashCode());
        code = 31 * code +
            (name_ == null ? 0 : name_.hashCode());
        return code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (!(o instanceof HostPropertyPK)) {
            return false;
        }
        HostPropertyPK other = (HostPropertyPK) o;
        if (host_ == null) {
            if (other.getHost() != null) {
                return false;
            }
        } else if (!host_.equals(other.getHost())) {
            return false;
        }
        if (name_ == null) {
            if (other.getPropertyName() != null) {
                return false;
            }
        } else if (!name_.equals(other.getPropertyName())) {
            return false;
        }
        return true;
    }
}

and the bean where I look up properties:

@Stateless
@Local(PropertyService.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class PropertyServiceBean implements PropertyService {
    @PersistenceContext(unitName="AppPersistence")
    private EntityManager entityManager_;

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public HostProperty getHostProperty(String host, String propertyName) {        
        HostPropertyPK hostPropertyPK =
            new HostPropertyPK(host, propertyName);
        HostProperty prop = entityManager_.find(HostProperty.class, hostPropertyPK);
        System.out.println("FOUND HOST PROPERTY : " + prop);
        return prop;
    }

}

The value of prop at the point of System.out.println is null. I can confirm that the entity manager is not null by the fact execution reaches this point and also because I can capture the underlying query using XRebel:

select
    hostpr0_.host as host623_0_,
    hostpr0_.name as name623_0_,
    hostpr0_.value as value623_0_
from
    app.host_property hostpr0_
where
    hostpr0_.host = 'testhost'
    and hostpr0_.name = 'testprop'

I've confirmed that this query indeed returns a single row. However, Hibernate doesn't ultimately return my object, but rather a null! What could cause Hibernate to be able to generate the correct query, execute it, but not map an object from it?

Neil Stockton
  • 11,383
  • 3
  • 34
  • 29
Richard
  • 9
  • 2
  • I'm curious if maybe this is a bug with `@IdClass`. Can you change the code to use an `@EmbeddedId` in conjunction with an `@Embeddable` and see if it works? – Naros Feb 01 '17 at 13:25
  • Is this really on something as old as JBoss 4.2? If I recall correctly composite primary keys were only added in JPA 2.0. EDIT: no, I'm wrong. But I still wonder if this is running on JBoss 4.2 or that the tag is misplaced – Gimby Feb 01 '17 at 13:41
  • @Gimby Yes, we are using JBoss 4.2.3, which was the first version that supported EJB3. Can't change that - I'm pushing for migration to Wildfly though. – Richard Feb 01 '17 at 14:05
  • @Naros I've tried EmbeddedId with Embeddable, and the results were the same. I changed to classes that have direct fields with IDClass to map to rather than an EmbeddedId primary key that JPA would have to do some work to figure out where the fields went. I figure having direct object fields would be more straightforward for JPA. – Richard Feb 01 '17 at 14:11
  • @richard right, so you're also running on a very outdated version of Hibernate then and won't really be able to upgrade - assuming it is a bug in Hibernate, because I don't see anything really wrong here. I have successfully used embedded IDs on an unaltered JBoss 4.2.3 environment however, so I am a little confused that it didn't work – Gimby Feb 01 '17 at 14:50
  • well... one thing. Why is the getter for the name property in the composite key `getPropertyName()` ? – Gimby Feb 01 '17 at 14:54
  • @Gimby The original field name was propertyName. I changed getPropertyName() to getName(), and the issue remains. Good observation, though. – Richard Feb 01 '17 at 15:00
  • 1
    I wouldn't be surprised if the underscore suffix of entity fields missing from getters and setters was causing problems with JPA. Not sure if this could be the culprit here, though. Also, the no-arg constructor of the PK is required to be public. – crizzis Feb 01 '17 at 20:05
  • @crizzis removing the underscores, making the PK noarg constructor public, and adding setters have made no difference. As an aside, we do use underscores in entities elsewhere in the application with no issue - the Column annotation seems to be paid attention to in those cases. – Richard Feb 02 '17 at 17:26
  • Is there a specific package I can increase logging level on in order to see the attempt to map the resultset back to the object? That seems to be where the failure is occurring. – Richard Feb 02 '17 at 17:29
  • Well, I upped the logging to ALL and even tried using getNativeQuery, passing the straight SQL with parameters as a string to eliminate the composite PK issue. It turns out that there is no row, according to hibernate at least. Here's the thing - I know it's executing against this database, because it complained when the table did not exist. – Richard Feb 02 '17 at 18:46
  • After creating the table, it stopped. I also took hibernates log statement "select hostpr0_.host as host3932_0_, hostpr0_.name as name3932_0_, hostpr0_.value as value3932_0_ from app.host_property hostpr0_ where hostpr0_.host='testhost' and hostpr0_.name='testprop'" and executed it myself, and yes, there is a row returned. But according to hibernate, 0 rows, so it throws a NoResultException on getSingleResult(). I am completely stumped. – Richard Feb 02 '17 at 18:46

0 Answers0