All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.j256.ormlite.dao.DaoManager Maven / Gradle / Ivy

Go to download

Lightweight Object Relational Model (ORM) for persisting objects to SQL databases.

There is a newer version: 6.1
Show newest version
package com.j256.ormlite.dao;

import java.lang.reflect.Constructor;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.logger.Logger;
import com.j256.ormlite.logger.LoggerFactory;
import com.j256.ormlite.misc.SqlExceptionUtil;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.DatabaseTable;
import com.j256.ormlite.table.DatabaseTableConfig;

/**
 * Class which caches created DAOs. Sometimes internal DAOs are used to support such features as auto-refreshing of
 * foreign fields or collections of sub-objects. Since instantiation of the DAO is a bit expensive, this class is used
 * in an attempt to only create a DAO once for each class.
 * 
 * 

* NOTE: To use this cache, you should make sure you've added a {@link DatabaseTable#daoClass()} value to the * annotation to the top of your class. *

* * @author graywatson */ public class DaoManager { private static Map, DatabaseTableConfig> configMap = null; private static Map> classMap = null; private static Map> tableConfigMap = null; private static Logger logger = LoggerFactory.getLogger(DaoManager.class); /** * Helper method to create a DAO object without having to define a class. This checks to see if the DAO has already * been created. If not then it is a call through to {@link BaseDaoImpl#createDao(ConnectionSource, Class)}. */ public synchronized static , T> D createDao(ConnectionSource connectionSource, Class clazz) throws SQLException { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } ClassConnectionSource key = new ClassConnectionSource(connectionSource, clazz); Dao dao = lookupDao(key); if (dao != null) { @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } // see if we can build it from source dao = createDaoFromConfig(connectionSource, clazz); if (dao != null) { @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } DatabaseTable databaseTable = clazz.getAnnotation(DatabaseTable.class); if (databaseTable == null || databaseTable.daoClass() == Void.class || databaseTable.daoClass() == BaseDaoImpl.class) { // see if the database type has some special table config extract method (Android) DatabaseType databaseType = connectionSource.getDatabaseType(); DatabaseTableConfig config = databaseType.extractDatabaseTableConfig(connectionSource, clazz); Dao daoTmp; if (config == null) { daoTmp = BaseDaoImpl.createDao(connectionSource, clazz); } else { daoTmp = BaseDaoImpl.createDao(connectionSource, config); } dao = daoTmp; logger.debug("created dao for class {} with reflection", clazz); } else { Class daoClass = databaseTable.daoClass(); Object[] arguments = new Object[] { connectionSource, clazz }; // look first for the constructor with a class parameter in case it is a generic dao Constructor daoConstructor = findConstructor(daoClass, arguments); if (daoConstructor == null) { // then look for the constructor with just the ConnectionSource arguments = new Object[] { connectionSource }; daoConstructor = findConstructor(daoClass, arguments); if (daoConstructor == null) { throw new SQLException( "Could not find public constructor with ConnectionSource and optional Class parameters " + daoClass + ". Missing static on class?"); } } try { dao = (Dao) daoConstructor.newInstance(arguments); logger.debug("created dao for class {} from constructor", clazz); } catch (Exception e) { throw SqlExceptionUtil.create("Could not call the constructor in class " + daoClass, e); } } registerDao(connectionSource, dao); @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } /** * Helper method to lookup a DAO if it has already been associated with the class. Otherwise this returns null. */ public synchronized static , T> D lookupDao(ConnectionSource connectionSource, Class clazz) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } ClassConnectionSource key = new ClassConnectionSource(connectionSource, clazz); Dao dao = lookupDao(key); @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } /** * Helper method to create a DAO object without having to define a class. This checks to see if the DAO has already * been created. If not then it is a call through to * {@link BaseDaoImpl#createDao(ConnectionSource, DatabaseTableConfig)}. */ public synchronized static , T> D createDao(ConnectionSource connectionSource, DatabaseTableConfig tableConfig) throws SQLException { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } return doCreateDao(connectionSource, tableConfig); } /** * Helper method to lookup a DAO if it has already been associated with the table-config. Otherwise this returns * null. */ public synchronized static , T> D lookupDao(ConnectionSource connectionSource, DatabaseTableConfig tableConfig) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } TableConfigConnectionSource key = new TableConfigConnectionSource(connectionSource, tableConfig); Dao dao = lookupDao(key); if (dao == null) { return null; } else { @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } } /** * Register the DAO with the cache. This will allow folks to build a DAO externally and then register so it can be * used internally as necessary. * *

* NOTE: By default this registers the DAO to be associated with the class that it uses. If you need to * register multiple dao's that use different {@link DatabaseTableConfig}s then you should use * {@link #registerDaoWithTableConfig(ConnectionSource, Dao)}. *

* *

* NOTE: You should maybe use the {@link DatabaseTable#daoClass()} and have the DaoManager construct the DAO * if possible. *

*/ public static synchronized void registerDao(ConnectionSource connectionSource, Dao dao) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } addDaoToClassMap(new ClassConnectionSource(connectionSource, dao.getDataClass()), dao); } /** * Remove a DAO from the cache. This is necessary if we've registered it already but it throws an exception during * configuration. */ public static synchronized void unregisterDao(ConnectionSource connectionSource, Dao dao) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } removeDaoToClassMap(new ClassConnectionSource(connectionSource, dao.getDataClass())); } /** * Remove all DAOs from the cache for the connection source. */ public static synchronized void unregisterDaos(ConnectionSource connectionSource) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } removeDaosFromConnectionClassMap(connectionSource); } /** * Same as {@link #registerDao(ConnectionSource, Dao)} but this allows you to register it just with its * {@link DatabaseTableConfig}. This allows multiple versions of the DAO to be configured if necessary. */ public static synchronized void registerDaoWithTableConfig(ConnectionSource connectionSource, Dao dao) { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } if (dao instanceof BaseDaoImpl) { DatabaseTableConfig tableConfig = ((BaseDaoImpl) dao).getTableConfig(); if (tableConfig != null) { addDaoToTableMap(new TableConfigConnectionSource(connectionSource, tableConfig), dao); return; } } addDaoToClassMap(new ClassConnectionSource(connectionSource, dao.getDataClass()), dao); } /** * Clear out all of internal caches. */ public static synchronized void clearCache() { if (configMap != null) { configMap.clear(); configMap = null; } clearDaoCache(); } /** * Clear out our DAO caches. */ public static synchronized void clearDaoCache() { if (classMap != null) { classMap.clear(); classMap = null; } if (tableConfigMap != null) { tableConfigMap.clear(); tableConfigMap = null; } } /** * This adds database table configurations to the internal cache which can be used to speed up DAO construction. * This is especially true of Android and other mobile platforms. */ public static synchronized void addCachedDatabaseConfigs(Collection> configs) { Map, DatabaseTableConfig> newMap; if (configMap == null) { newMap = new HashMap, DatabaseTableConfig>(); } else { newMap = new HashMap, DatabaseTableConfig>(configMap); } for (DatabaseTableConfig config : configs) { newMap.put(config.getDataClass(), config); logger.info("Loaded configuration for {}", config.getDataClass()); } configMap = newMap; } private static void addDaoToClassMap(ClassConnectionSource key, Dao dao) { if (classMap == null) { classMap = new HashMap>(); } classMap.put(key, dao); } private static void removeDaoToClassMap(ClassConnectionSource key) { if (classMap != null) { classMap.remove(key); } } private static void removeDaosFromConnectionClassMap(ConnectionSource connectionSource) { if (classMap != null) { Iterator classIterator = classMap.keySet().iterator(); while (classIterator.hasNext()) { if(classIterator.next().connectionSource == connectionSource){ classIterator.remove(); } } } } private static void addDaoToTableMap(TableConfigConnectionSource key, Dao dao) { if (tableConfigMap == null) { tableConfigMap = new HashMap>(); } tableConfigMap.put(key, dao); } private static Dao lookupDao(ClassConnectionSource key) { if (classMap == null) { classMap = new HashMap>(); } Dao dao = classMap.get(key); if (dao == null) { return null; } else { return dao; } } private static Dao lookupDao(TableConfigConnectionSource key) { if (tableConfigMap == null) { tableConfigMap = new HashMap>(); } Dao dao = tableConfigMap.get(key); if (dao == null) { return null; } else { return dao; } } private static Constructor findConstructor(Class daoClass, Object[] params) { for (Constructor constructor : daoClass.getConstructors()) { Class[] paramsTypes = constructor.getParameterTypes(); if (paramsTypes.length == params.length) { boolean match = true; for (int i = 0; i < paramsTypes.length; i++) { if (!paramsTypes[i].isAssignableFrom(params[i].getClass())) { match = false; break; } } if (match) { return constructor; } } } return null; } /** * Creates the DAO if we have config information cached and caches the DAO. */ private static D createDaoFromConfig(ConnectionSource connectionSource, Class clazz) throws SQLException { // no loaded configs if (configMap == null) { return null; } @SuppressWarnings("unchecked") DatabaseTableConfig config = (DatabaseTableConfig) configMap.get(clazz); // if we don't config information cached return null if (config == null) { return null; } // else create a DAO using configuration Dao configedDao = doCreateDao(connectionSource, config); @SuppressWarnings("unchecked") D castDao = (D) configedDao; return castDao; } private static , T> D doCreateDao(ConnectionSource connectionSource, DatabaseTableConfig tableConfig) throws SQLException { TableConfigConnectionSource tableKey = new TableConfigConnectionSource(connectionSource, tableConfig); // look up in the table map Dao dao = lookupDao(tableKey); if (dao != null) { @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } // now look it up in the class map Class dataClass = tableConfig.getDataClass(); ClassConnectionSource classKey = new ClassConnectionSource(connectionSource, dataClass); dao = lookupDao(classKey); if (dao != null) { // if it is not in the table map but is in the class map, add it addDaoToTableMap(tableKey, dao); @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } // build the DAO using the table information DatabaseTable databaseTable = tableConfig.getDataClass().getAnnotation(DatabaseTable.class); if (databaseTable == null || databaseTable.daoClass() == Void.class || databaseTable.daoClass() == BaseDaoImpl.class) { Dao daoTmp = BaseDaoImpl.createDao(connectionSource, tableConfig); dao = daoTmp; } else { Class daoClass = databaseTable.daoClass(); Object[] arguments = new Object[] { connectionSource, tableConfig }; Constructor constructor = findConstructor(daoClass, arguments); if (constructor == null) { throw new SQLException( "Could not find public constructor with ConnectionSource, DatabaseTableConfig parameters in class " + daoClass); } try { dao = (Dao) constructor.newInstance(arguments); } catch (Exception e) { throw SqlExceptionUtil.create("Could not call the constructor in class " + daoClass, e); } } addDaoToTableMap(tableKey, dao); logger.debug("created dao for class {} from table config", dataClass); // if it is not in the class config either then add it if (lookupDao(classKey) == null) { addDaoToClassMap(classKey, dao); } @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } /** * Key for our class DAO map. */ private static class ClassConnectionSource { ConnectionSource connectionSource; Class clazz; public ClassConnectionSource(ConnectionSource connectionSource, Class clazz) { this.connectionSource = connectionSource; this.clazz = clazz; } @Override public int hashCode() { final int prime = 31; int result = prime + clazz.hashCode(); result = prime * result + connectionSource.hashCode(); return result; } @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } ClassConnectionSource other = (ClassConnectionSource) obj; if (!clazz.equals(other.clazz)) { return false; } else if (!connectionSource.equals(other.connectionSource)) { return false; } else { return true; } } } /** * Key for our table-config DAO map. */ private static class TableConfigConnectionSource { ConnectionSource connectionSource; DatabaseTableConfig tableConfig; public TableConfigConnectionSource(ConnectionSource connectionSource, DatabaseTableConfig tableConfig) { this.connectionSource = connectionSource; this.tableConfig = tableConfig; } @Override public int hashCode() { final int prime = 31; int result = prime + tableConfig.hashCode(); result = prime * result + connectionSource.hashCode(); return result; } @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } TableConfigConnectionSource other = (TableConfigConnectionSource) obj; if (!tableConfig.equals(other.tableConfig)) { return false; } else if (!connectionSource.equals(other.connectionSource)) { return false; } else { return true; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy