0

A multi-tiered application that I am developing accesses its database through a Payara application server. Originally I was using webservices (jax-ws) to provide the access. That decision was prompted by the ease of creating the services by using the @WebService and @Stateless annotations on my entity facade classes.

Due to some limitations of webservices (things like equals and hashCode methods not being created in the webservice interface), I decided to try to use EJB's to accomplish the same functionality. Using the webservices I was able to successfully perform all CRUD functionality on all of the database entities. All of my entity classes extend an AbstractEntity class, which is annotated @MappedSuperClass.

@MappedSuperclass
public abstract class AbstractEntity {

    @Column(name = "UUID")
    @Basic
    private String uuid;

    @Version
    @Access(AccessType.FIELD)
    @Column(name = "Revision")
    @Basic
    private long revision;

    public AbstractEntity() {
        uuid = UUID.randomUUID().toString();
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    /**
     * @return entity's database primary key if persisted
     */
    public abstract Long getId();

    public long getRevision() {
        return revision;
    }

    public void setRevision(long revision) {
        this.revision = revision;
    }

    /**
     * @return true if this entity is persisted to database, false 
otherwise.
     */
    public boolean isPersisted() {
        return getId() != null;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj instanceof AbstractEntity) {
            return this.uuid.equals(((AbstractEntity) obj).uuid);
        }

        return false;
     }

    @Override
    public int hashCode() {
        return uuid.hashCode();
    }
}

The client application correctly looks up the remote interfaces through JNDI, and I'm able to run my methods to query the data and return result lists exactly the same as I can using webservices. The problem, however, is in the version number and uuid that are returned with each entity instance. In all cases, the revision number that is returned is 0, and the revision and uuid don't match the revision and uuid stored in the database. I have verified that the result of my query on the server contains entities that have the correct version numbers, but when the entities get to the client, all of them are set to 0. Of course, if I make any changes to the entity on the client and then try to update the entity, I get an optimistic locking exception on the update method.

Does this have something to do with the entities being detached from the database? The update method is:

    @Override
    public ShiplistItem updateShipList(ShiplistItem shipitem) {
        LOGGER.log(Level.INFO, "Entering updateShipList.");
        LOGGER.log(Level.INFO, "shipitem: {0}", shipitem.toString());
        if (shipitem.isPersisted()) {
            return em.merge(shipitem);
        } else {
            em.persist(shipitem);
            return shipitem;
        }
    }

I don't understand why the webservice would return the entities correctly, but the ejb seems to neglect the stored values for revision and uuid.

All help is appreciated!!

EDIT - Entity class snippet

public class ShiplistItem extends AbstractEntity implements 
Serializable, Comparable<ShiplistItem> {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    private Long id;
    ....

EDIT #2

    @Override
    public List<ShiplistItem> listShipListByDate(String date) {
        Query query = em.createQuery("SELECT s from ShiplistItem s 
where s.shipdate = :shipdate", ShiplistItem.class)
                .setParameter("shipdate", date);
        LOGGER.log(Level.INFO, "SQL: {0}.", query.toString());
        List<ShiplistItem> result = new ArrayList<>();
        try {
            result = (List<ShiplistItem>) query.getResultList();
        } catch (Exception ex) {
            LOGGER.log(Level.INFO, "No shipping list for date {0}", date);
        }
        for (ShiplistItem shiplistItem : result) {
            LOGGER.log(Level.INFO, "revision: {0}",shiplistItem.getRevision());
        }
        LOGGER.log(Level.INFO, "shiplist size: {0}", result.size());
        return result;
    }
kpenrose
  • 186
  • 2
  • 14
  • JPA allows 2 levels of caching; one at the EntityManager level and the other at the EntityManagerFactory level. If your entity is created or read in and then the version changed outside of JPA's cache, you may need to refresh or reload the entity. What is your ID on these objects, as the uuid is not marked with the ID annotation, and you are setting a new random uuid in the empty constructor. – Chris May 09 '17 at 15:32
  • I have added a snippet of the entity class to my original comment. I don't change version numbers myself, I leave that to the JPA. – kpenrose May 09 '17 at 15:37
  • How does your isPersisted method work, is the code using merge or persist? How and where are you verifying that entities are read correctly - where exactly are the 0 values coming from in your process of sending entities out and then merging them back in - have you verified the data you are calling merge with? – Chris May 09 '17 at 17:17
  • The isPersisted() method checks for the existence of a value in the ID field. If true, merge, else, persist. I log the results of the query on the payara server, and verify that the version and uuid values match what's actually in the database. When I examine the results on the client, it appears that the inherited fields are ignored. As a matter of fact, I found a similar question posted almost a year ago with the same problem I'm experiencing: http://stackoverflow.com/questions/38084425/entity-inherited-fields-not-loaded-in-java-remote-session-bean - There were no solutions offered. – kpenrose May 09 '17 at 17:40
  • You need to narrow down the problem further. It sounds like JPA is reading in the inherited values from the DB and populating them in the entity (?) but they are getting lost when sent to your remote client, but it isn't clear. Show the part that reads them in and sends them to the client, but it seems like you are returning an incomplete, detached object instead of the one with the properties going into the DB. – Chris May 09 '17 at 18:30
  • Added the query to the original post. After the query is completed, there is a loop which logs the version numbers, and that output shows the correct values. In the client, version numbers are all 0. – kpenrose May 09 '17 at 19:49
  • If the fields are set when read from the DB then the issue has nothing to do with JPA. How are you sending these objects back to your client? – Chris May 10 '17 at 18:52
  • Yes, I kinda figured it had more to do with the transport. I don't know how to tell how it's returned, the method just does return results; Is there a way to look into how that is done? – kpenrose May 11 '17 at 12:36
  • 1
    Check with your server support. Looking up Glassfish though, it might be related to http://stackoverflow.com/q/10889268/496099 and https://java.net/jira/browse/GLASSFISH-16164 - try adding to see (if it works it was JPA related and there are other solutions available). – Chris May 11 '17 at 14:20

0 Answers0