org.pojava.persistence.sql.DatabaseCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of persistence Show documentation
Show all versions of persistence Show documentation
POJava Persistence is a small library of tools used in the
persistence and retrieval of data to and from storage, such as to a
database or XML document. It contains an ORM (Object Relational
Mapping) tool that supports use of existing beans and POJO's rather
than requiring POJO's to be instrumented to support a tool.
The newest version!
package org.pojava.persistence.sql;
import org.pojava.persistence.util.SqlTool;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.security.InvalidParameterException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* This singleton caches object properties that facilitate interchange between Java and external
* data stores.
*
* @author John Pile
*/
public class DatabaseCache {
/**
* Holds DataSourceMetadata objects by DataSource name.
*/
private static Map dataSourceMetadataCache = new HashMap();
/**
* Holds DataSource objects by DataSource name.
*/
private static Map dataSourceCache = new HashMap();
/**
* Holds TableMap objects by Java class + table name.
*/
private static Map> tableMapCache = new HashMap>();
/**
* Holds the names of DataSource objects used for locks.
*/
private static Map dataSourceLocks = new HashMap();
/**
* Return the metadata for the named DataSource
*
* @param dsName
* Name of DataSource
* @return Metadata gathered from a connection from this dataSource
*/
public static DataSourceMetadata getDataSourceMetadata(String dsName) throws SQLException {
// Try the fastest option first
if (dataSourceMetadataCache.containsKey(dsName)) {
return (DataSourceMetadata) dataSourceMetadataCache.get(dsName);
}
DataSourceMetadata metadata;
Object lock;
// Acquire a lock unique to the dsName
synchronized (dataSourceLocks) {
lock = dataSourceLocks.get(dsName);
if (lock == null) {
lock = new Object();
dataSourceLocks.put(dsName, lock);
}
}
// The lock allows one thread per DataSource name
synchronized (lock) {
metadata = (DataSourceMetadata) dataSourceMetadataCache.get(dsName);
if (metadata == null) {
DataSource dataSource = getDataSource(dsName);
Connection conn = dataSource.getConnection();
metadata = new DataSourceMetadata(conn);
dataSourceMetadataCache.put(dsName, metadata);
}
}
return metadata;
}
/**
* Fetch a DataSource from the registry.
*
* @param dsName named datasource
* @return DataSource retrieved from cache or JNDI registry.
*/
public static DataSource getDataSource(String dsName) {
// Try the fastest option first
if (dataSourceCache.containsKey(dsName)) {
return dataSourceCache.get(dsName);
}
// Retrieve from JNDI Registry
Context ctx;
DataSource ds;
try {
ctx = new InitialContext();
} catch (NamingException ex) {
throw new IllegalStateException(
ex.getMessage()
+ " See org.pojava.persistence.jndi.JNDIRegistry if you need to create an InitialContext for unit testing.");
}
try {
ds = (DataSource) ctx.lookup("java:comp/env/jdbc/" + dsName.trim());
} catch (NamingException ex) {
throw new InvalidParameterException(dsName.trim() + " triggered NamingException "
+ ex.getMessage());
}
if (ds == null) {
throw new IllegalStateException("Could not find java:comp/env/jdbc/"
+ dsName.trim() + " in registry.");
} else {
dataSourceCache.put(dsName, ds);
}
return ds;
}
/**
* Build a key combining a java class with a table name.
*
* @param javaClass Class of java file
* @param tableName Table name
* @param dataSourceName Data Source name
* @return String combining name, table, dataSource for use as a key
*/
private static String tableMapKey(Class> javaClass, String tableName,
String dataSourceName) {
return javaClass.getName() + ":" + tableName + ":" + dataSourceName;
}
/**
* Register a tableMap
*
* @param tableMap Mapped fields
*/
public static void registerTableMap(TableMap> tableMap) {
tableMapCache.put(tableMapKey(tableMap.getClass(), tableMap.getTableName(), tableMap
.getDataSourceName()), tableMap);
}
/**
* Register a DataSource.
*
* @param dataSourceName datasource name
* @param dataSource JDBC data source
*/
public static void registerDataSource(String dataSourceName, DataSource dataSource) {
dataSourceCache.put(dataSourceName, dataSource);
}
/**
* Retrieve a tableMap
*
* @param javaClass Java Class
* @param tableName Table Name
* @param dataSourceName Data Source Name
* @return TableMap or null if not found.
*/
@SuppressWarnings("unchecked")
public static TableMap getTableMap(Class javaClass, String tableName,
String dataSourceName) {
String key = tableMapKey(javaClass, tableName, dataSourceName);
// Use cached version if it exists
if (tableMapCache.containsKey(key)) {
return (TableMap) tableMapCache.get(key);
}
Object lock;
// Acquire a lock unique to the key
synchronized (dataSourceLocks) {
lock = dataSourceLocks.get(key);
if (lock == null) {
lock = new Object();
dataSourceLocks.put(key, lock);
}
}
// The lock allows one thread per unique key
TableMap tableMap;
synchronized (lock) {
tableMap = (TableMap) tableMapCache.get(key);
if (tableMap == null) {
tableMap = (TableMap) SqlTool.autoGenerateTableMap(javaClass, tableName,
dataSourceName);
tableMapCache.put(key, tableMap);
}
}
return tableMap;
}
}