Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Collection cache : How to use?

I have two entities Book and Author. 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

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;



@Entity
@Cacheable
@Table(name = "BOOK")
@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")}) 

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) {

    }


}

Here is the code for Author Entity

  package bookentity.ejb;

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;


@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);
  }
}

}

Here is the persistence.xml file

<?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" />   
<property name="hibernate.cache.region_prefix" value="infinispan" />
<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>

Here is the 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>
    <jmxStatistics enabled="false"/>
    <!--transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.JBossTransactionManagerLookup"/-->
</default>
<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>
like image 778
Meena Rajani Avatar asked Dec 29 '12 10:12

Meena Rajani


People also ask

When should I use Hibernate cache?

Hibernate caching acts as a layer between the actual database and your application. It reduces the time taken to obtain the required data — as it fetches from memory instead of directly hitting the database. It is very useful when you need to fetch the same kind of data multiple times.

Why do we use cache in Hibernate?

Caching is one of the strengths of the Hibernate framework, and it is available at multiple levels. The first-level cache is the first place that Hibernate checks for cached data. It is built in and active by default to reduce the number of SQL queries directly to the database.

What is L1 and L2 cache in Hibernate?

By default, Hibernate only uses per-session (L1) cache, so, objects, cached in one session, are not seen in another. However, an L2 cache may be used, in which the cached objects are seen for all sessions that use the same L2 cache configuration.

How do you check if Hibernate cache is working?

You can enable Hibernate statistics generation be setting hibernate. generate_statistics property to true . Then you can monitor cache hit/miss count via SessionFactory. getStatistics() .


1 Answers

The collection itself must be cached, using the @Cache annotation. See the documentation:

Hibernate also let's you cache the content of a collection or the identifiers if the collection contains other entities. Use the @Cache annotation on the collection property.

Example 21.6. Caching collections using annotations

@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public SortedSet<Ticket> getTickets() {
    return tickets;
}
like image 120
JB Nizet Avatar answered Sep 21 '22 04:09

JB Nizet