persistence@glassfish.java.net

RE: row updates outside of JPA not reflected in JPA

From: Gordon Yorke <gordon.yorke_at_oracle.com>
Date: Thu, 17 Jan 2008 11:21:21 -0500

Hello Girish,
    toplink.cache.shared.<Enity> = false would be a better choice than toplink.cache.type.<Entity> = NONE if that is what you have chosen. cache.type=NONE has some behaviour implications beyond caching.
  In general choosing a combination of cache type, locking and refreshing strategies will produce the best performing application. In concurrent systems there is always stale data and removing caching will not aleviate that problem it just reduces it but eliminating caching will reduce read performance as well.

--Gordon
  -----Original Message-----
  From: Girish.Balachandran_at_Sun.COM [mailto:Girish.Balachandran_at_Sun.COM]
  Sent: Wednesday, January 16, 2008 3:29 PM
  To: persistence_at_glassfish.dev.java.net
  Cc: Christopher Webster
  Subject: Re: row updates outside of JPA not reflected in JPA


  Thanks Marina for the reply! I used toplink.cache.type.<ENTITY> and it seems to work well for me.

  For the benefit of the others, here is the list of all properties supported by Toplink.

  http://www.oracle.com/technology/products/ias/toplink/JPA/essentials/toplink-jpa-extensions.html

  -Girish

  Marina Vatkina wrote:
    Hi Girish,

    Your results will depend on the caching strategy of the persistence provider. TopLink Essentials (the default provider in GlassFish) has caching enabled by default, i.e. it relies on the internal cache for the values of instances that had been created or fetched by the EMs of the same EMF.

    To see changes that had been done to the database outside JPA you need to either disable the caching or use em.refresh() API.

    Regards,
    -marina

    Girish Kumar Balachandran wrote:

      (Not sure if this is the right alias to ask this question. If not please forward to the right alias.)

      Modifications to a row that are directly made on the DB externally via SQL does not seem to reflect in the JPA returned objects. New row additions are recognized though. Is this something that JPA supports or is it a bug?

      I have attached a simple program that demonstrates the scenario. I created this program in netbeans. Run the following table creation SQL statement first to the DB connection:

      CREATE TABLE VIR.GIRISH(
         ID BIGINT generated always as identity NOT NULL,
         STRING VARCHAR(20),
         VERSION INT DEFAULT 0,
         CONSTRAINT PRINCIPAL_PK PRIMARY KEY (ID)
      );

      Then compile and run the program. While it is running execute:

      UPDATE VIR.GIRISH SET STRING = 'modified', VERSION=2 WHERE ID = 1;

      and observe that the output of the program does not get the changes. Restarting the program will get the changes though.

      Is there a special thing I need to do to cover this usecase? I wonder how clustered environments use JPA if this is not supported inherently.


      Thanks,
      -Girish



      ------------------------------------------------------------------------

      import java.io.UnsupportedEncodingException;
      import java.sql.DriverManager;
      import java.sql.SQLException;
      import java.util.HashMap;
      import java.util.List;
      import javax.persistence.EntityManager;
      import javax.persistence.EntityManagerFactory;
      import javax.persistence.Persistence;
      import javax.persistence.Query;

      /**
       *
       * @author girix
       */
      public class JPATester {
           
          private static HashMap properties;
           
          private static String jdbcURL;
           
          private static String dbDriver;
           
          private static EntityManagerFactory emf;
           
          private static EntityManager em;
           
          /** Creates a new instance of Main */
          public JPATester() {
          }
           
          /**
           * @param args the command line arguments
           */
          public static void main(String[] args) throws SQLException, UnsupportedEncodingException, InterruptedException {
              DriverManager.registerDriver(new org.apache.derby.jdbc.ClientDriver());
              
              properties = new HashMap<String,String>();
              jdbcURL = "jdbc:derby://localhost:1527/vir";
              dbDriver = "org.apache.derby.jdbc.ClientDriver";
              
              properties.put("toplink.jdbc.user","app");
              properties.put("toplink.jdbc.password","app");
              properties.put("toplink.jdbc.url",jdbcURL);
              properties.put("toplink.jdbc.driver",dbDriver);
              properties.put("toplink.logging.level","FINE");
              
              emf = Persistence.createEntityManagerFactory("ScratchpadProjectPU", properties);
              
              
              em = emf.createEntityManager();
              
              DbGirish girish = new DbGirish();
              girish.setString("kumar");
              
              em.getTransaction().begin();
              em.persist(girish);
              em.getTransaction().commit();
              
              
              int j = 0;
              while(true){
                  //re-creating a new EMF also does not solve the problem.
                  //emf = Persistence.createEntityManagerFactory("ScratchpadProjectPU", properties);
                  //creating a new EM also does not help
                  //em = emf.createEntityManager();
                  Query q = em.createQuery("SELECT girish FROM DbGirish girish");
                  Thread.sleep(3000);
                  System.out.println("\n\nIteration number:"+j++);
                  List<DbGirish> lis = q.getResultList();
                  int i = 0;
                  for(DbGirish gir : lis){
                      System.out.println(i+". Entry ID:"+gir.getId()+" String:"+
                              gir.getString()+" Version:"+gir.getVersion());
                      i++;
                  }
                  em.close();
              }
           
          }
      }



      ------------------------------------------------------------------------



      import java.io.Serializable;
      import javax.persistence.Column;
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.GenerationType;
      import javax.persistence.Id;
      import javax.persistence.NamedQueries;
      import javax.persistence.NamedQuery;
      import javax.persistence.Table;
      import javax.persistence.Version;

      /**
       *
       * @author girix
       */
      @Entity
      @Table(name = "VIR.GIRISH")
      @NamedQueries({_at_NamedQuery(name = "DbGirish.findById", query = "SELECT d FROM DbGirish d WHERE d.id = :id"), @NamedQuery(name = "DbGirish.findByString", query = "SELECT d FROM DbGirish d WHERE d.string = :string"), @NamedQuery(name = "DbGirish.findByVersion", query = "SELECT d FROM DbGirish d WHERE d.version = :version")})
      public class DbGirish implements Serializable {
          private static final long serialVersionUID = 1L;
          @Id
          @GeneratedValue(strategy=GenerationType.IDENTITY)
          @Column(name = "ID", nullable = false)
          private Long id;
          @Column(name = "STRING")
          private String string;
          @Version
          @Column(name = "VERSION")
          private Integer version;

          public DbGirish() {
          }

          public DbGirish(Long id) {
              this.id = id;
          }

          public Long getId() {
              return id;
          }

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

          public String getString() {
              return string;
          }

          public void setString(String string) {
              this.string = string;
          }

          public Integer getVersion() {
              return version;
          }

          public void setVersion(Integer version) {
              this.version = version;
          }

          @Override
          public int hashCode() {
              int hash = 0;
              hash += (id != null ? id.hashCode() : 0);
              return hash;
          }

          @Override
          public boolean equals(Object object) {
              // TODO: Warning - this method won't work in the case the id fields are not set
              if (!(object instanceof DbGirish)) {
                  return false;
              }
              DbGirish other = (DbGirish) object;
              if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
                  return false;
              }
              return true;
          }

          @Override
          public String toString() {
              return "grt.DbGirish[id=" + id + "]";
          }

      }



      ------------------------------------------------------------------------

      <?xml version="1.0" encoding="UTF-8"?>
      <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
        <persistence-unit name="ScratchpadProjectPU" transaction-type="RESOURCE_LOCAL">
          <provider>oracle.toplink.essentials.PersistenceProvider</provider>
          <!-- <class>scratchpadproject.Table2</class>
          <class>scratchpadproject.Table1</class>
          <class>scratchpadproject.DbRefrencer</class> -->
          <class>DbGirish</class>
        </persistence-unit>
      </persistence>