1 Reply Latest reply on Jan 1, 2013 12:08 AM by usydrajani

    Does not use second level cache when retrieving a collection.

    usydrajani

      I have two entities Book and Author. A book has a collection of authors. I am using second level cache to keep the book entity with its authors. When debugging I can see there  is putForExternalRead is  happening for Book instance and each author in collection. But  when I am  calling find(Book.class, ISBN) method again it is using cache just for book while the collection of authors is retrieved each time from database. Each time collection of authors are put in second  level cache. Please let me know if there is some where I need to change cache access strategy for collection.  I am using Jboss 6.0 Infinispan 5. and postgres 9 DBMS.

       

       

      Here is my code

       

      Book Entity

       

      package bookentity.ejb;
      /*
       * To change this template, choose Tools | Templates
       * and open the template in the editor.
       */
      
      
      
      
      
      
      import java.io.Serializable;
      import java.util.ArrayList;
      
      
      import javax.persistence.Cacheable;
      import javax.persistence.Entity;
      import javax.persistence.Id;
      import javax.persistence.NamedQueries;
      import javax.persistence.NamedQuery;
      import javax.persistence.Table;
      import javax.persistence.ManyToMany;
      import javax.persistence.JoinTable;
      //import javax.persistence.JoinColumns;
      import javax.persistence.JoinColumn;
      import java.util.Collection;
      import java.util.List;
      import javax.persistence.FetchType;
      import javax.persistence.GeneratedValue;
      import javax.persistence.GenerationType;
      //import javax.persistence.inv
      import javax.persistence.OneToMany;
      
      
      /**
       *
       * @author Meena
       * Book is in many-to-many relationship with Author and one-to-many with Review
       */
      @Entity
      @Cacheable
      @Table(name = "BOOK")
      // just commented following on 27th dec 2012 to see where is problem in HSQL
      //@NamedQueries({@NamedQuery(name="findBookByAuthorName",query="SELECT b FROM Book b, Author a  WHERE  a.authorName=:authorName AND  a.books=b"),
      //@NamedQuery(name="findBookByTitle",query="SELECT b FROM Book b WHERE b.title=:bTitle")})
      @NamedQueries({@NamedQuery(name="findBookByAuthorName",query="SELECT b FROM Book b, Author a  WHERE  a.authorName=:authorName AND b = SOME(SELECT x FROM a.books x)"),
      @NamedQuery(name="findBookByTitle",query="SELECT b FROM Book b WHERE b.title=:bTitle")})
      //,query="SELECT b.title FROM Book b, Author a WHERE  a.authorName=:authorName OR b.ISBN='8'")
      //query="SELECT b.title FROM Book b, Author a WHERE  a.authorName=:authorName OR b.ISBN='8'")
      //,query="SELECT b.title FROM Book b, Author a WHERE  a.authorName=:authorName OR b.ISBN='8'")
      //This is normal sql query which did not work with ORM infact in code ORM does not refer to Relationship Type Table Book_Author. query="SELECT b.title FROM BOOK b, BOOK_AUTHOR ba, Author a where a.authorName,=:authorName AND a.author_id=ba.AUTHOR_ID AND ba.BOOK_ID=b.ISBN")
      //query="SELECT b.title FROM Book b where b.ISBN=:ISBN")
      public class Book implements Serializable {
          private static final long serialVersionUID = 1L;
          @Id
          private int ISBN;
          private String title;
          private String description;
          private Author author;
       // @ManyToMany(fetch=FetchType.LAZY)
          @ManyToMany(fetch=FetchType.EAGER)
          @JoinTable(name="BOOK_AUTHOR", joinColumns=@JoinColumn(name="BOOK_ID"),
          inverseJoinColumns=@JoinColumn(name="AUTHOR_ID"))
          private Collection<Author> authors;
          
          
         
      //    @OneToMany(fetch=FetchType.EAGER, mappedBy="bookEntity")
            @OneToMany(fetch=FetchType.LAZY, mappedBy="bookEntity")
           public Collection<Review> reviews; 
         
             public Book() {
              authors = new ArrayList<Author>();
              reviews = new ArrayList<Review>();
          }
      
      
          public int getISBN() {
              return ISBN;
          }
      
      
          public void setISBN(int ISBN) {
              this.ISBN = ISBN;
          }
          public String getTitle(){
              return title;
          }
          
           public void  setTitle(String title){
              this.title =  title;
          }
          
            public String getDescription(){
              return description;
          }
          
           public void  setDescription(String description){
              this.description =  description;
          }
           public void addReview(Review review){
               if(!getReviews().contains(review)){
                   getReviews().add(review);
               
               if(review.getBookEntity()!=null){
                   review.getBookEntity().getReviews().remove(this);
               }
                   review.setBookEntity(this);
               }
           }
           public void addAuthor(Author author){
               if  (!getAuthors().contains(author)){
                       getAuthors().add(author);
                 }
               if(!author.getBooks().contains(this)){
                   author.getBooks().add(this);
               }
           }
           
           public Collection<Review> getReviews(){
               return reviews;
           }
      
      
           public Collection<Author> getAuthors(){
               
               return authors;
           }
         
           void setAuhorId(int authorID) {
               
           }
           
                  
      }
      
      

       

      Author Entity

       

       

      package bookentity.ejb;
      /*
      
      
       * To change this template, choose Tools | Templates
       * and open the template in the editor.
       */
      
      
      
      
      
      
      import java.io.Serializable;
      import java.util.ArrayList;
      
      
      import javax.persistence.Cacheable;
      import javax.persistence.Entity;
      import javax.persistence.Id;
      import javax.persistence.ManyToMany;
      import java.util.Collection;
      import java.util.Hashtable;
      import java.util.List;
      import javax.naming.Context;
      import javax.naming.InitialContext;
      
      
      /**
       *
       * @author Meena
       * Author is in many to many realtionship with Book
       */
      @Entity
      @Cacheable
      public class Author implements Serializable {
          private static final long serialVersionUID = 1L;
          @Id 
          private int author_id;
          String authorName;
           String authAddress;
          @ManyToMany(mappedBy = "authors")
          private Collection<Book> books;
          
          public Author() { 
              books = new ArrayList<Book>();
          }
        
          
          
          public void setAuthor_id(int author_id) {
              this.author_id = author_id;
              
          }
      
      
      
          public int getAuthorId() {
              return this.author_id;
          }
          
           public void setAuthorName(String  authorName) {
              this.authorName = authorName;
          }
      
      
          public String getAuthorName() {
              return authorName;
          }
          public String getAuthorAddress(){
              return this.authAddress;
          }
           public void setAuthorAddress(String authAddress){
              this.authAddress = authAddress;
          }
      
      
      public Collection<Book> getBooks() {
          return books;
           
      }
      
      
      
      
          
      public void addBook(Book book){
          if(!getBooks().contains(book)) {
              getBooks().add(book);
              //book.getAuthors().add(this);
          }
          if (!book.getAuthors().contains(this)){
              book.getAuthors().add(this);
          }
      }
        
      }
      
      

       

      BookSessionBean

       

       

      @Stateless
      @Remote(BookInterface.class)
      public class BookSessionBean  implements BookInterface {
          
      @PersistenceContext (unitName="BookAuthorApp3-ejbPU")
          protected EntityManager em;
          protected Statistics stats;
      
      
      
      public Book  findBook(int isbn) {
                     this.initCacheConfig();
              Book bookIns = em.find(Book.class, isbn);
              this.initCacheConfig();
             return booIns;
       }
      
      
      
      

       

      Persistence.xml

       

      <?xml version="1.0" encoding="UTF-8"?>
      <persistence 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"
         version="1.0">
         <persistence-unit name="BookAuthorApp3-ejbPU" transaction-type="JTA">
         <provider>org.hibernate.ejb.HibernatePersistence</provider> 
            <jta-data-source>java:/PostgresDS</jta-data-source>
                  <properties>
                <property name="hibernate.hbm2ddl.auto" value="update"/>
                          <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>   
                          <property name="hibernate.session_factory_name" value="SessionFactories/infinispan1" />
                           <property name="javax.persistence.sharedCache.mode" value="ALL" /> 
                                     <property name="hibernate.cache.use_second_level_cache" value="true" />
                          <property name="hibernate.cache.use_query_cache" value="true" />  
                                    <property name="hibernate.cacheable" value="true" /> 
                                       <property name="hibernate.cache.use_structured_entries" value="true" />
                                       <property name="hibernate.cache.infinispan.collection.cfg" value="entity" />
                                       <property name="hibernate.cache.infinispan.bookentity.ej.Book.cfg" value="Books"/>
                        <property name="hibernate.cache.infinispan.bookentity.ej.Book.authors.cfg"value="Authors"/>
                                    <property name="hibernate.cache.infinispan.statistics" value="true"/>
                                    <property name="hibernate.generate_statistics" value="true" />
                                    <!-- changed from name="hibernate.cache.region_prefix" value="infinispan1 to following   -->
                                    <property name="hibernate.cache.region_prefix" value="infinispan" />
                                    <!--  without this following  line  property cache was starting in invalidation_sync mode 
                                    <property name="hibernate.cache.infinispan.entity.cfg" value="replicated-entity" />  -->
                                    <property name="hibernate.cache.infinispan.entity.cfg" value="entity" />
        
                                    <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.infinispan.JndiInfinispanRegionFactory" />
                                    <property name="hibernate.cache.infinispan.cachemanager" value="java:CacheManager/entity" />
        
            </properties>
            
            
         </persistence-unit>
      </persistence>
      
      

       

      My Infinispan-configs.xml file

       

       <infinispan-config name="hibernate" jndi-name="java:CacheManager/entity">
          <infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
           xsi:schemaLocation="urn:infinispan:config:5.0 http://www.infinispan.org/schemas/infinispan-config-5.0.xsd"  
            xmlns="urn:infinispan:config:5.0">  
            <global>
              <transport clusterName="${jboss.partition.name:DefaultPartition}-Hibernate" distributedSyncTimeout="17500">
                <properties>
                  <property name="stack" value="${jboss.default.jgroups.stack:udp}"/>
                </properties>
              </transport>
              <globalJmxStatistics enabled="true"/>
              <shutdown hookBehavior="DONT_REGISTER"/>
            </global>
            <default>
              <!-- Used to register JMX statistics in any available MBean server -->
              <jmxStatistics enabled="false"/>
              <!--transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.JBossTransactionManagerLookup"/-->
            </default>
            
            <!-- Default configuration is appropriate for entity/collection caching. -->
            <namedCache name="entity">
      
             <clustering mode="invalidation">
                <stateRetrieval fetchInMemoryState="false" timeout="20000"/>
                <sync replTimeout="20000"/>
              </clustering>
      
              <locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000"
                       lockAcquisitionTimeout="15000" useLockStriping="false" />
      
               <eviction wakeUpInterval="5000" maxEntries="10000" strategy="LRU"/>
              <expiration lifespan = "-1" maxIdle="-1"/>   
              <lazyDeserialization enabled="true"/>
      
       </namedCache>
      
      
      
      
        • 1. Re: Does not use second level cache when retrieving a collection.
          usydrajani

          I figured it out. Though I could not find any annotation in JPA for that. But i used  this from hibernate

          org.hibernate.annotations.CacheConcurrencyStrategy. 

          By  putting this   @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) on top of  @ManyToMany(fetch=FetchType.EAGER) in my code , it is using secondlevel cache not only for book instance but all the authors of that book(collection)

           

          Thanks