Persistences Caching - JPA Grouped


The basics

If you want to store your value
Value
in a
Database
,
but you want multiple caching instances to share the same table, because Key/Value is the same types.

But each caching instance have its own "groupname" (of type String) in the table, but the "groupname,key" constitutues the unique primary key. This is still really simple.
The only thing you need is to :
  • MyCachedGroupJPAID
    - Implement a Class of the interface "CachedGroupJPAID<K>" - see example below, where Key/Value is both Strings. (Embedded ID class should include method definitions for equals() and hashcode()).

  • MyCachedGroupJPAEntity
    Implement a Class of the interface "CachedGroupJPAEntity<K,V>" - see example below, where Key/Value is both Strings.

  • Getting an instance of "EntityManagerFactory".

The cache is constructed like
DefaultLRUCachingMapConfiguration configuration = new DefaultLRUCachingMapConfiguration();
			
DefaultCachedGroupJPAEntityRepository<String,String> repositoryA = new DefaultCachedGroupJPAEntityRepository<String,String>("group1",MyCachedGroupJPAEntity.class,entityManagerFactory);				
DefaultLRUCachingMap<String, String> cacheA = new DefaultLRUCachingMap<String, String>(configuration, new DefaultGroupJPAEntityValueManager<String,String>(repositoryA,PersistencesRestoreMethod.DELETE_ALL));		
		
DefaultCachedGroupJPAEntityRepository<String,String> repositoryB = new DefaultCachedGroupJPAEntityRepository<String,String>("group2",MyCachedGroupJPAEntity.class,entityManagerFactory);		
DefaultLRUCachingMap<String, String> cacheB = new DefaultLRUCachingMap<String, String>(configuration, new DefaultGroupJPAEntityValueManager<String,String>(repositoryB,PersistencesRestoreMethod.DELETE_ALL));

This is equivalent to

DefaultLRUCachingMapConfiguration configuration = new DefaultLRUCachingMapConfiguration();

DefaultLRUCachingMap<String, String> cacheA = new DefaultLRUCachingMap<String, String>(configuration,new DefaultGroupJPAEntityValueManager<String,String>("group1",MyCachedGroupJPAEntity.class,entityManagerFactory,PersistencesRestoreMethod.DELETE_ALL));

DefaultLRUCachingMap<String, String> cacheB = new DefaultLRUCachingMap<String, String>(configuration,new DefaultGroupJPAEntityValueManager<String,String>("group2",MyCachedGroupJPAEntity.class,entityManagerFactory,PersistencesRestoreMethod.DELETE_ALL));

LRUCachingMapFactory

There is also a factory class to all this -
LRUCachingMapFactory
.

So the code above can in reality be done like.
DefaultCachedGroupJPALRUCachingMap<String,String> cacheA = LRUCachingMapFactory.createJPAGroupedCache(new DefaultLRUCachingMapConfiguration(),"group1",MyCachedGroupJPAEntity.class,entityManagerFactory,PersistencesRestoreMethod.DELETE_ALL);
DefaultCachedGroupJPALRUCachingMap<String,String> cacheB = LRUCachingMapFactory.createJPAGroupedCache(new DefaultLRUCachingMapConfiguration(),"group2",MyCachedGroupJPAEntity.class,entityManagerFactory,PersistencesRestoreMethod.DELETE_ALL);

SQL

    //(HSQLDB sql)
    create table MyGroupCacheTable (
        mygroup varchar(100) not null,  //Primary key
        mykey varchar(1000) not null,   //Primary key
        myexpiretime timestamp not null,
        mystoretime timestamp not null,
        myvalue varchar(4000) not null,
        primary key (mygroup, mykey)
    )



Example - MyCachedGroupJPAID

import javax.persistence.Column;
import javax.persistence.Embeddable;

import dk.heick.caching.persistences.jpa.grouping.CachedGroupJPAID;

@Embeddable
public class MyCachedGroupJPAID implements CachedGroupJPAID<String> {

	private static final long serialVersionUID = 4826620945757639307L;
		
	@Column(name="mykey",length=1000,nullable=false)	
	private String key;
	
	//Remember name must NOT be "group" it clashes with SQL reserved names.
	@Column(name="mygroup",length=100,nullable=false)	
	private String group;
	

	public MyCachedGroupJPAID() {
		super();
	}

	@Override
	public String getKey() {
		return key;
	}
	@Override
	public void setKey(String key) {
		this.key=key;
	}
	@Override
	public String getGroup() {
		return group;
	}
	@Override
	public void setGroup(String group) {
		this.group=group;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((group == null) ? 0 : group.hashCode());
		result = prime * result + ((key == null) ? 0 : key.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		MyCachedGroupJPAID other = (MyCachedGroupJPAID) obj;
		if (group == null) {
			if (other.group != null)
				return false;
		} else if (!group.equals(other.group))
			return false;
		if (key == null) {
			if (other.key != null)
				return false;
		} else if (!key.equals(other.key))
			return false;
		return true;
	}	
}



Example - MyCachedGroupJPAEntity

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

import dk.heick.caching.persistences.jpa.grouping.CachedGroupJPAEntity;
import dk.heick.caching.persistences.jpa.grouping.CachedGroupJPAID;

@Entity(name="MyGroupCacheTable")
public class MyCachedGroupJPAEntity implements CachedGroupJPAEntity<String,String> {

	private static final long serialVersionUID = -4629332102745914743L;
	
	@EmbeddedId
	private MyCachedGroupJPAID id;

	@Column(name="myvalue",length=4000,nullable=false)
	private String value;
	
	@Column(name="mystoretime",nullable=false)
	private Date storeDateTime;
	
	@Column(name="myexpiretime",nullable=false)
	private Date expireDateTime;
		
	public MyCachedGroupJPAEntity() {
		super();
		//Really important, otherwise it wont work.
		this.id = new MyCachedGroupJPAID();
	}
	
	@Override
	public Date getExpireDateTime() {		
		return expireDateTime;
	}
	@Override
	public CachedGroupJPAID<String> getId() {
		return id;
	}
	@Override
	public Date getStoredDateTime() {
		return storeDateTime;
	}
	@Override
	public String getValue() {
		return value;
	}
	@Override
	public void setExpireDateTime(Date expireDateTime) {
		this.expireDateTime=expireDateTime;		
	}
	@Override
	public void setStoredDateTime(Date storeDateTime) { 
		this.storeDateTime=storeDateTime;
	}
	@Override
	public void setValue(String value) {
		this.value=value;		
	}	
	@Override
	public void setId(CachedGroupJPAID<String> id) {
		this.id=id;		
	}
}

Example - persistences.xml (src/test/resources/META-INF/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_2_0.xsd" version="2.0">
    <persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>dk.heick.caching.persistences.jpa.MyCachedJPAEntity</class>
        <class>dk.heick.caching.persistences.jpa.group.MyCachedGroupJPAID</class>
        <class>dk.heick.caching.persistences.jpa.group.MyCachedGroupJPAEntity</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.use_sql_comments" value="true" />
            <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
        </properties>
    </persistence-unit>
</persistence>