Package com.j256.ormlite.dao

Source Code of com.j256.ormlite.dao.BaseDaoImpl

package com.j256.ormlite.dao;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.misc.BaseDaoEnabled;
import com.j256.ormlite.misc.SqlExceptionUtil;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.GenericRowMapper;
import com.j256.ormlite.stmt.PreparedDelete;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.PreparedUpdate;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.SelectArg;
import com.j256.ormlite.stmt.SelectIterator;
import com.j256.ormlite.stmt.StatementBuilder.StatementType;
import com.j256.ormlite.stmt.StatementExecutor;
import com.j256.ormlite.stmt.UpdateBuilder;
import com.j256.ormlite.stmt.Where;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.support.DatabaseResults;
import com.j256.ormlite.table.DatabaseTableConfig;
import com.j256.ormlite.table.ObjectFactory;
import com.j256.ormlite.table.TableInfo;

/**
* Base class for the Database Access Objects that handle the reading and writing a class from the database.
*
* <p>
* This class is also {@link Iterable} which means you can do a {@code for (T obj : dao)} type of loop code to iterate
* through the table of persisted objects. See {@link #iterator()}.
* </p>
*
* <p>
* <b> NOTE: </b> If you are using the Spring type wiring, {@link #initialize} should be called after all of the set
* methods. In Spring XML, init-method="initialize" should be used.
* </p>
*
* @param <T>
*            The class that the code will be operating on.
* @param <ID>
*            The class of the ID column associated with the class. The T class does not require an ID field. The class
*            needs an ID parameter however so you can use Void or Object to satisfy the compiler.
* @author graywatson
*/
public abstract class BaseDaoImpl<T, ID> implements Dao<T, ID> {

  private static final ThreadLocal<List<BaseDaoImpl<?, ?>>> daoConfigLevelLocal =
      new ThreadLocal<List<BaseDaoImpl<?, ?>>>() {
        @Override
        protected List<BaseDaoImpl<?, ?>> initialValue() {
          return new ArrayList<BaseDaoImpl<?, ?>>(10);
        }
      };
  private static ReferenceObjectCache defaultObjectCache;
  private static final Object constantObject = new Object();

  protected StatementExecutor<T, ID> statementExecutor;
  protected DatabaseType databaseType;
  protected final Class<T> dataClass;
  protected DatabaseTableConfig<T> tableConfig;
  protected TableInfo<T, ID> tableInfo;
  protected ConnectionSource connectionSource;
  protected CloseableIterator<T> lastIterator;
  protected ObjectFactory<T> objectFactory;

  private boolean initialized;
  private ObjectCache objectCache;
  private Map<DaoObserver, Object> daoObserverMap;

  /**
   * Construct our base DAO using Spring type wiring. The {@link ConnectionSource} must be set with the
   * {@link #setConnectionSource} method afterwards and then the {@link #initialize()} method must be called. The
   * dataClass provided must have its fields marked with {@link DatabaseField} annotations or the
   * {@link #setTableConfig} method must be called before the {@link #initialize()} method is called.
   *
   * <p>
   * If you are using Spring then your should use: init-method="initialize"
   * </p>
   *
   * @param dataClass
   *            Class associated with this Dao. This must match the T class parameter.
   */
  protected BaseDaoImpl(Class<T> dataClass) throws SQLException {
    this(null, dataClass, null);
  }

  /**
   * Construct our base DAO class. The dataClass provided must have its fields marked with {@link DatabaseField} or
   * javax.persistance annotations.
   *
   * @param connectionSource
   *            Source of our database connections.
   * @param dataClass
   *            Class associated with this Dao. This must match the T class parameter.
   */
  protected BaseDaoImpl(ConnectionSource connectionSource, Class<T> dataClass) throws SQLException {
    this(connectionSource, dataClass, null);
  }

  /**
   * Construct our base DAO class.
   *
   * @param connectionSource
   *            Source of our database connections.
   * @param tableConfig
   *            Hand or Spring wired table configuration information.
   */
  protected BaseDaoImpl(ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig) throws SQLException {
    this(connectionSource, tableConfig.getDataClass(), tableConfig);
  }

  private BaseDaoImpl(ConnectionSource connectionSource, Class<T> dataClass, DatabaseTableConfig<T> tableConfig)
      throws SQLException {
    this.dataClass = dataClass;
    this.tableConfig = tableConfig;
    if (connectionSource != null) {
      this.connectionSource = connectionSource;
      initialize();
    }
  }

  /**
   * Initialize the various DAO configurations after the various setters have been called.
   */
  public void initialize() throws SQLException {
    if (initialized) {
      // just skip it if already initialized
      return;
    }

    if (connectionSource == null) {
      throw new IllegalStateException("connectionSource was never set on " + getClass().getSimpleName());
    }

    databaseType = connectionSource.getDatabaseType();
    if (databaseType == null) {
      throw new IllegalStateException("connectionSource is getting a null DatabaseType in "
          + getClass().getSimpleName());
    }
    if (tableConfig == null) {
      tableInfo = new TableInfo<T, ID>(connectionSource, this, dataClass);
    } else {
      tableConfig.extractFieldTypes(connectionSource);
      tableInfo = new TableInfo<T, ID>(databaseType, this, tableConfig);
    }
    statementExecutor = new StatementExecutor<T, ID>(databaseType, tableInfo, this);

    /*
     * This is a bit complex. Initially, when we were configuring the field types, external DAO information would be
     * configured for auto-refresh, foreign BaseDaoEnabled classes, and foreign-collections. This would cause the
     * system to go recursive and for class loops, a stack overflow.
     *
     * Then we fixed this by putting a level counter in the FieldType constructor that would stop the configurations
     * when we reach some recursion level. But this created some bad problems because we were using the DaoManager
     * to cache the created DAOs that had been constructed already limited by the level.
     *
     * What we do now is have a 2 phase initialization. The constructor initializes most of the fields but then we
     * go back and call FieldType.configDaoInformation() after we are done. So for every DAO that is initialized
     * here, we have to see if it is the top DAO. If not we save it for dao configuration later.
     */
    List<BaseDaoImpl<?, ?>> daoConfigList = daoConfigLevelLocal.get();
    daoConfigList.add(this);
    if (daoConfigList.size() > 1) {
      // if we have recursed then just save the dao for later configuration
      return;
    }

    try {
      /*
       * WARNING: We do _not_ use an iterator here because we may be adding to the list as we process it and we'll
       * get exceptions otherwise. This is an ArrayList so the get(i) should be efficient.
       *
       * Also, do _not_ get a copy of daoConfigLevel.doArray because that array may be replaced by another, larger
       * one during the recursion.
       */
      for (int i = 0; i < daoConfigList.size(); i++) {
        BaseDaoImpl<?, ?> dao = daoConfigList.get(i);

        /*
         * Here's another complex bit. The first DAO that is being constructed as part of a DAO chain is not yet
         * in the DaoManager cache. If we continue onward we might come back around and try to configure this
         * DAO again. Forcing it into the cache here makes sure it won't be configured twice. This will cause it
         * to be stuck in twice when it returns out to the DaoManager but that's a small price to pay. This also
         * applies to self-referencing classes.
         */
        DaoManager.registerDao(connectionSource, dao);

        try {
          // config our fields which may go recursive
          for (FieldType fieldType : dao.getTableInfo().getFieldTypes()) {
            fieldType.configDaoInformation(connectionSource, dao.getDataClass());
          }
        } catch (SQLException e) {
          // unregister the DAO we just pre-registered
          DaoManager.unregisterDao(connectionSource, dao);
          throw e;
        }

        // it's now been fully initialized
        dao.initialized = true;
      }
    } finally {
      // if we throw we want to clear our class hierarchy here
      daoConfigList.clear();
      daoConfigLevelLocal.remove();
    }
  }

  public T queryForId(ID id) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.queryForId(connection, id, objectCache);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public T queryForFirst(PreparedQuery<T> preparedQuery) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.queryForFirst(connection, preparedQuery, objectCache);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public List<T> queryForAll() throws SQLException {
    checkForInitialized();
    return statementExecutor.queryForAll(connectionSource, objectCache);
  }

  public List<T> queryForEq(String fieldName, Object value) throws SQLException {
    return queryBuilder().where().eq(fieldName, value).query();
  }

  public QueryBuilder<T, ID> queryBuilder() {
    checkForInitialized();
    return new QueryBuilder<T, ID>(databaseType, tableInfo, this);
  }

  public UpdateBuilder<T, ID> updateBuilder() {
    checkForInitialized();
    return new UpdateBuilder<T, ID>(databaseType, tableInfo, this);
  }

  public DeleteBuilder<T, ID> deleteBuilder() {
    checkForInitialized();
    return new DeleteBuilder<T, ID>(databaseType, tableInfo, this);
  }

  public List<T> query(PreparedQuery<T> preparedQuery) throws SQLException {
    checkForInitialized();
    return statementExecutor.query(connectionSource, preparedQuery, objectCache);
  }

  public List<T> queryForMatching(T matchObj) throws SQLException {
    return queryForMatching(matchObj, false);
  }

  public List<T> queryForMatchingArgs(T matchObj) throws SQLException {
    return queryForMatching(matchObj, true);
  }

  public List<T> queryForFieldValues(Map<String, Object> fieldValues) throws SQLException {
    return queryForFieldValues(fieldValues, false);
  }

  public List<T> queryForFieldValuesArgs(Map<String, Object> fieldValues) throws SQLException {
    return queryForFieldValues(fieldValues, true);
  }

  public T queryForSameId(T data) throws SQLException {
    checkForInitialized();
    if (data == null) {
      return null;
    }
    ID id = extractId(data);
    if (id == null) {
      return null;
    } else {
      return queryForId(id);
    }
  }

  public int create(T data) throws SQLException {
    checkForInitialized();
    // ignore creating a null object
    if (data == null) {
      return 0;
    }
    if (data instanceof BaseDaoEnabled) {
      @SuppressWarnings("unchecked")
      BaseDaoEnabled<T, ID> daoEnabled = (BaseDaoEnabled<T, ID>) data;
      daoEnabled.setDao(this);
    }
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.create(connection, data, objectCache);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int create(Collection<T> datas) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    boolean saved = false;
    try {
      saved = connectionSource.saveSpecialConnection(connection);
      int modCount = 0;
      for (T data : datas) {
        if (data instanceof BaseDaoEnabled) {
          @SuppressWarnings("unchecked")
          BaseDaoEnabled<T, ID> daoEnabled = (BaseDaoEnabled<T, ID>) data;
          daoEnabled.setDao(this);
        }
        modCount += statementExecutor.create(connection, data, objectCache);
      }
      return modCount;
    } finally {
      if (!saved) {
        connectionSource.clearSpecialConnection(connection);
      }
      connectionSource.releaseConnection(connection);
    }
  }

  public T createIfNotExists(T data) throws SQLException {
    if (data == null) {
      return null;
    }
    T existing = queryForSameId(data);
    if (existing == null) {
      create(data);
      return data;
    } else {
      return existing;
    }
  }

  public CreateOrUpdateStatus createOrUpdate(T data) throws SQLException {
    if (data == null) {
      return new CreateOrUpdateStatus(false, false, 0);
    }
    ID id = extractId(data);
    // assume we need to create it if there is no id
    if (id == null || !idExists(id)) {
      int numRows = create(data);
      return new CreateOrUpdateStatus(true, false, numRows);
    } else {
      int numRows = update(data);
      return new CreateOrUpdateStatus(false, true, numRows);
    }
  }

  public int update(T data) throws SQLException {
    checkForInitialized();
    // ignore updating a null object
    if (data == null) {
      return 0;
    }
    if (data instanceof BaseDaoEnabled) {
      @SuppressWarnings("unchecked")
      BaseDaoEnabled<T, ID> daoEnabled = (BaseDaoEnabled<T, ID>) data;
      daoEnabled.setDao(this);
    }
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.update(connection, data, objectCache);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int updateId(T data, ID newId) throws SQLException {
    checkForInitialized();
    // ignore updating a null object
    if (data == null) {
      return 0;
    } else {
      DatabaseConnection connection = connectionSource.getReadWriteConnection();
      try {
        return statementExecutor.updateId(connection, data, newId, objectCache);
      } finally {
        connectionSource.releaseConnection(connection);
      }
    }
  }

  public int update(PreparedUpdate<T> preparedUpdate) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.update(connection, preparedUpdate);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int refresh(T data) throws SQLException {
    checkForInitialized();
    // ignore refreshing a null object
    if (data == null) {
      return 0;
    }
    if (data instanceof BaseDaoEnabled) {
      @SuppressWarnings("unchecked")
      BaseDaoEnabled<T, ID> daoEnabled = (BaseDaoEnabled<T, ID>) data;
      daoEnabled.setDao(this);
    }
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.refresh(connection, data, objectCache);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int delete(T data) throws SQLException {
    checkForInitialized();
    // ignore deleting a null object
    if (data == null) {
      return 0;
    } else {
      DatabaseConnection connection = connectionSource.getReadWriteConnection();
      try {
        return statementExecutor.delete(connection, data, objectCache);
      } finally {
        connectionSource.releaseConnection(connection);
      }
    }
  }

  public int deleteById(ID id) throws SQLException {
    checkForInitialized();
    // ignore deleting a null id
    if (id == null) {
      return 0;
    } else {
      DatabaseConnection connection = connectionSource.getReadWriteConnection();
      try {
        return statementExecutor.deleteById(connection, id, objectCache);
      } finally {
        connectionSource.releaseConnection(connection);
      }
    }
  }

  public int delete(Collection<T> datas) throws SQLException {
    checkForInitialized();
    // ignore deleting a null object
    if (datas == null || datas.isEmpty()) {
      return 0;
    } else {
      DatabaseConnection connection = connectionSource.getReadWriteConnection();
      try {
        return statementExecutor.deleteObjects(connection, datas, objectCache);
      } finally {
        connectionSource.releaseConnection(connection);
      }
    }
  }

  public int deleteIds(Collection<ID> ids) throws SQLException {
    checkForInitialized();
    // ignore deleting a null object
    if (ids == null || ids.isEmpty()) {
      return 0;
    } else {
      DatabaseConnection connection = connectionSource.getReadWriteConnection();
      try {
        return statementExecutor.deleteIds(connection, ids, objectCache);
      } finally {
        connectionSource.releaseConnection(connection);
      }
    }
  }

  public int delete(PreparedDelete<T> preparedDelete) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.delete(connection, preparedDelete);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public CloseableIterator<T> iterator() {
    return iterator(DatabaseConnection.DEFAULT_RESULT_FLAGS);
  }

  public CloseableIterator<T> closeableIterator() {
    return iterator(DatabaseConnection.DEFAULT_RESULT_FLAGS);
  }

  public CloseableIterator<T> iterator(int resultFlags) {
    checkForInitialized();
    lastIterator = createIterator(resultFlags);
    return lastIterator;
  }

  public CloseableWrappedIterable<T> getWrappedIterable() {
    checkForInitialized();
    return new CloseableWrappedIterableImpl<T>(new CloseableIterable<T>() {
      public Iterator<T> iterator() {
        return closeableIterator();
      }
      public CloseableIterator<T> closeableIterator() {
        try {
          return BaseDaoImpl.this.createIterator(DatabaseConnection.DEFAULT_RESULT_FLAGS);
        } catch (Exception e) {
          throw new IllegalStateException("Could not build iterator for " + dataClass, e);
        }
      }
    });
  }

  public CloseableWrappedIterable<T> getWrappedIterable(final PreparedQuery<T> preparedQuery) {
    checkForInitialized();
    return new CloseableWrappedIterableImpl<T>(new CloseableIterable<T>() {
      public Iterator<T> iterator() {
        return closeableIterator();
      }
      public CloseableIterator<T> closeableIterator() {
        try {
          return BaseDaoImpl.this.createIterator(preparedQuery, DatabaseConnection.DEFAULT_RESULT_FLAGS);
        } catch (Exception e) {
          throw new IllegalStateException("Could not build prepared-query iterator for " + dataClass, e);
        }
      }
    });
  }

  public void closeLastIterator() throws IOException {
    if (lastIterator != null) {
      lastIterator.close();
      lastIterator = null;
    }
  }

  public CloseableIterator<T> iterator(PreparedQuery<T> preparedQuery) throws SQLException {
    return iterator(preparedQuery, DatabaseConnection.DEFAULT_RESULT_FLAGS);
  }

  public CloseableIterator<T> iterator(PreparedQuery<T> preparedQuery, int resultFlags) throws SQLException {
    checkForInitialized();
    lastIterator = createIterator(preparedQuery, resultFlags);
    return lastIterator;
  }

  public GenericRawResults<String[]> queryRaw(String query, String... arguments) throws SQLException {
    checkForInitialized();
    try {
      return statementExecutor.queryRaw(connectionSource, query, arguments, objectCache);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not perform raw query for " + query, e);
    }
  }

  public <GR> GenericRawResults<GR> queryRaw(String query, RawRowMapper<GR> mapper, String... arguments)
      throws SQLException {
    checkForInitialized();
    try {
      return statementExecutor.queryRaw(connectionSource, query, mapper, arguments, objectCache);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not perform raw query for " + query, e);
    }
  }

  public <UO> GenericRawResults<UO> queryRaw(String query, DataType[] columnTypes, RawRowObjectMapper<UO> mapper,
      String... arguments) throws SQLException {
    checkForInitialized();
    try {
      return statementExecutor.queryRaw(connectionSource, query, columnTypes, mapper, arguments, objectCache);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not perform raw query for " + query, e);
    }
  }

  public GenericRawResults<Object[]> queryRaw(String query, DataType[] columnTypes, String... arguments)
      throws SQLException {
    checkForInitialized();
    try {
      return statementExecutor.queryRaw(connectionSource, query, columnTypes, arguments, objectCache);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not perform raw query for " + query, e);
    }
  }

  public long queryRawValue(String query, String... arguments) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.queryForLong(connection, query, arguments);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not perform raw value query for " + query, e);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int executeRaw(String statement, String... arguments) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.executeRaw(connection, statement, arguments);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not run raw execute statement " + statement, e);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int executeRawNoArgs(String statement) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.executeRawNoArgs(connection, statement);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not run raw execute statement " + statement, e);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public int updateRaw(String statement, String... arguments) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return statementExecutor.updateRaw(connection, statement, arguments);
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not run raw update statement " + statement, e);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public <CT> CT callBatchTasks(Callable<CT> callable) throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    boolean saved = false;
    try {
      /*
       * We need to save the connection because we are going to be disabling auto-commit on it and we don't want
       * pooled connection factories to give us another connection where auto-commit might still be enabled.
       */
      saved = connectionSource.saveSpecialConnection(connection);
      return statementExecutor.callBatchTasks(connection, saved, callable);
    } finally {
      if (!saved) {
        connectionSource.clearSpecialConnection(connection);
      }
      connectionSource.releaseConnection(connection);
    }
  }

  public String objectToString(T data) {
    checkForInitialized();
    return tableInfo.objectToString(data);
  }

  public boolean objectsEqual(T data1, T data2) throws SQLException {
    checkForInitialized();
    for (FieldType fieldType : tableInfo.getFieldTypes()) {
      Object fieldObj1 = fieldType.extractJavaFieldValue(data1);
      Object fieldObj2 = fieldType.extractJavaFieldValue(data2);
      // we can't just do fieldObj1.equals(fieldObj2) because of byte[].equals()
      if (!fieldType.getDataPersister().dataIsEqual(fieldObj1, fieldObj2)) {
        return false;
      }
    }
    return true;
  }

  public ID extractId(T data) throws SQLException {
    checkForInitialized();
    FieldType idField = tableInfo.getIdField();
    if (idField == null) {
      throw new SQLException("Class " + dataClass + " does not have an id field");
    }
    @SuppressWarnings("unchecked")
    ID id = (ID) idField.extractJavaFieldValue(data);
    return id;
  }

  public Class<T> getDataClass() {
    return dataClass;
  }

  public FieldType findForeignFieldType(Class<?> clazz) {
    checkForInitialized();
    for (FieldType fieldType : tableInfo.getFieldTypes()) {
      if (fieldType.getType() == clazz) {
        return fieldType;
      }
    }
    return null;
  }

  public boolean isUpdatable() {
    return tableInfo.isUpdatable();
  }

  public boolean isTableExists() throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return connection.isTableExists(tableInfo.getTableName());
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public long countOf() throws SQLException {
    checkForInitialized();
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.queryForCountStar(connection);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public long countOf(PreparedQuery<T> preparedQuery) throws SQLException {
    checkForInitialized();
    if (preparedQuery.getType() != StatementType.SELECT_LONG) {
      throw new IllegalArgumentException("Prepared query is not of type " + StatementType.SELECT_LONG
          + ", you need to call QueryBuilder.setCountOf(true)");
    }
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.queryForLong(connection, preparedQuery);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public void assignEmptyForeignCollection(T parent, String fieldName) throws SQLException {
    makeEmptyForeignCollection(parent, fieldName);
  }

  public <FT> ForeignCollection<FT> getEmptyForeignCollection(String fieldName) throws SQLException {
    return makeEmptyForeignCollection(null, fieldName);
  }

  public void setObjectCache(boolean enabled) throws SQLException {
    if (enabled) {
      if (objectCache == null) {
        if (tableInfo.getIdField() == null) {
          throw new SQLException("Class " + dataClass + " must have an id field to enable the object cache");
        }
        synchronized (BaseDaoImpl.class) {
          if (defaultObjectCache == null) {
            // only make one
            defaultObjectCache = ReferenceObjectCache.makeWeakCache();
          }
          objectCache = defaultObjectCache;
        }
        objectCache.registerClass(dataClass);
      }
    } else {
      if (objectCache != null) {
        objectCache.clear(dataClass);
        objectCache = null;
      }
    }
  }

  public void setObjectCache(ObjectCache objectCache) throws SQLException {
    if (objectCache == null) {
      if (this.objectCache != null) {
        // help with GC-ing
        this.objectCache.clear(dataClass);
        this.objectCache = null;
      }
    } else {
      if (this.objectCache != null && this.objectCache != objectCache) {
        // help with GC-ing
        this.objectCache.clear(dataClass);
      }
      if (tableInfo.getIdField() == null) {
        throw new SQLException("Class " + dataClass + " must have an id field to enable the object cache");
      }
      this.objectCache = objectCache;
      this.objectCache.registerClass(dataClass);
    }
  }

  public ObjectCache getObjectCache() {
    return objectCache;
  }

  public void clearObjectCache() {
    if (objectCache != null) {
      objectCache.clear(dataClass);
    }
  }

  /**
   * Special call mostly used in testing to clear the internal object caches so we can reset state.
   */
  public synchronized static void clearAllInternalObjectCaches() {
    if (defaultObjectCache != null) {
      defaultObjectCache.clearAll();
      defaultObjectCache = null;
    }
  }

  public T mapSelectStarRow(DatabaseResults results) throws SQLException {
    return statementExecutor.getSelectStarRowMapper().mapRow(results);
  }

  public void notifyChanges() {
    if (daoObserverMap != null) {
      for (DaoObserver daoObserver : daoObserverMap.keySet()) {
        daoObserver.onChange();
      }
    }
  }

  public void registerObserver(DaoObserver observer) {
    if (daoObserverMap == null) {
      /*
       * We are doing this so no other systems need to pay the penalty for the new observer code. This seems like
       * it would be a double-check locking bug but I don't think so. We are putting into a synchronized
       * collection that locks so we should not have partial constructor issues.
       */
      synchronized (this) {
        if (daoObserverMap == null) {
          daoObserverMap = new ConcurrentHashMap<Dao.DaoObserver, Object>();
        }
      }
    }
    // no values in the map
    daoObserverMap.put(observer, constantObject);
  }

  public void unregisterObserver(DaoObserver observer) {
    if (daoObserverMap != null) {
      synchronized (daoObserverMap) {
        daoObserverMap.remove(observer);
      }
    }
  }

  public GenericRowMapper<T> getSelectStarRowMapper() throws SQLException {
    return statementExecutor.getSelectStarRowMapper();
  }

  public RawRowMapper<T> getRawRowMapper() {
    return statementExecutor.getRawRowMapper();
  }

  public boolean idExists(ID id) throws SQLException {
    DatabaseConnection connection = connectionSource.getReadOnlyConnection();
    try {
      return statementExecutor.ifExists(connection, id);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public DatabaseConnection startThreadConnection() throws SQLException {
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    connectionSource.saveSpecialConnection(connection);
    return connection;
  }

  public void endThreadConnection(DatabaseConnection connection) throws SQLException {
    connectionSource.clearSpecialConnection(connection);
    connectionSource.releaseConnection(connection);
  }

  public void setAutoCommit(boolean autoCommit) throws SQLException {
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      setAutoCommit(connection, autoCommit);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public void setAutoCommit(DatabaseConnection connection, boolean autoCommit) throws SQLException {
    connection.setAutoCommit(autoCommit);
  }

  public boolean isAutoCommit() throws SQLException {
    DatabaseConnection connection = connectionSource.getReadWriteConnection();
    try {
      return isAutoCommit(connection);
    } finally {
      connectionSource.releaseConnection(connection);
    }
  }

  public boolean isAutoCommit(DatabaseConnection connection) throws SQLException {
    return connection.isAutoCommit();
  }

  public void commit(DatabaseConnection connection) throws SQLException {
    connection.commit(null);
  }

  public void rollBack(DatabaseConnection connection) throws SQLException {
    connection.rollback(null);
  }

  public ObjectFactory<T> getObjectFactory() {
    return objectFactory;
  }

  public void setObjectFactory(ObjectFactory<T> objectFactory) {
    checkForInitialized();
    this.objectFactory = objectFactory;
  }

  /**
   * Returns the table configuration information associated with the Dao's class or null if none.
   */
  public DatabaseTableConfig<T> getTableConfig() {
    return tableConfig;
  }

  /**
   * Used by internal classes to get the table information structure for the Dao's class.
   */
  public TableInfo<T, ID> getTableInfo() {
    return tableInfo;
  }

  public ConnectionSource getConnectionSource() {
    return connectionSource;
  }

  public void setConnectionSource(ConnectionSource connectionSource) {
    this.connectionSource = connectionSource;
  }

  /**
   * Used if you want to configure the class for the Dao by hand or with spring instead of using the
   * {@link DatabaseField} annotation in the class. This must be called <i>before</i> {@link #initialize}.
   */
  public void setTableConfig(DatabaseTableConfig<T> tableConfig) {
    this.tableConfig = tableConfig;
  }

  /**
   * Helper method to create a Dao object without having to define a class. Dao classes are supposed to be convenient
   * but if you have a lot of classes, they can seem to be a pain.
   *
   * <p>
   * <b>NOTE:</b> You should use {@link DaoManager#createDao(ConnectionSource, DatabaseTableConfig)} instead of this
   * method so you won't have to create the DAO multiple times.
   * </p>
   */
  static <T, ID> Dao<T, ID> createDao(ConnectionSource connectionSource, Class<T> clazz) throws SQLException {
    return new BaseDaoImpl<T, ID>(connectionSource, clazz) {
    };
  }

  /**
   * Helper method to create a Dao object used by some internal methods that already have the {@link TableInfo}.
   *
   * <p>
   * <b>NOTE:</b> You should use {@link DaoManager#createDao(ConnectionSource, DatabaseTableConfig)} instead of this
   * method so you won't have to create the DAO multiple times.
   * </p>
   */
  static <T, ID> Dao<T, ID> createDao(ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig)
      throws SQLException {
    return new BaseDaoImpl<T, ID>(connectionSource, tableConfig) {
    };
  }

  protected void checkForInitialized() {
    if (!initialized) {
      throw new IllegalStateException("you must call initialize() before you can use the dao");
    }
  }

  private <FT> ForeignCollection<FT> makeEmptyForeignCollection(T parent, String fieldName) throws SQLException {
    checkForInitialized();
    ID id;
    if (parent == null) {
      id = null;
    } else {
      id = extractId(parent);
    }
    for (FieldType fieldType : tableInfo.getFieldTypes()) {
      if (fieldType.getColumnName().equals(fieldName)) {
        @SuppressWarnings("unchecked")
        ForeignCollection<FT> collection = (ForeignCollection<FT>) fieldType.buildForeignCollection(parent, id);
        if (parent != null) {
          fieldType.assignField(parent, collection, true, null);
        }
        return collection;
      }
    }
    throw new IllegalArgumentException("Could not find a field named " + fieldName);
  }

  private CloseableIterator<T> createIterator(int resultFlags) {
    try {
      SelectIterator<T, ID> iterator =
          statementExecutor.buildIterator(this, connectionSource, resultFlags, objectCache);
      return iterator;
    } catch (Exception e) {
      throw new IllegalStateException("Could not build iterator for " + dataClass, e);
    }
  }

  private CloseableIterator<T> createIterator(PreparedQuery<T> preparedQuery, int resultFlags) throws SQLException {
    try {
      SelectIterator<T, ID> iterator =
          statementExecutor.buildIterator(this, connectionSource, preparedQuery, objectCache, resultFlags);
      return iterator;
    } catch (SQLException e) {
      throw SqlExceptionUtil.create("Could not build prepared-query iterator for " + dataClass, e);
    }
  }

  private List<T> queryForMatching(T matchObj, boolean useArgs) throws SQLException {
    checkForInitialized();
    QueryBuilder<T, ID> qb = queryBuilder();
    Where<T, ID> where = qb.where();
    int fieldC = 0;
    for (FieldType fieldType : tableInfo.getFieldTypes()) {
      Object fieldValue = fieldType.getFieldValueIfNotDefault(matchObj);
      if (fieldValue != null) {
        if (useArgs) {
          fieldValue = new SelectArg(fieldValue);
        }
        where.eq(fieldType.getColumnName(), fieldValue);
        fieldC++;
      }
    }
    if (fieldC == 0) {
      return Collections.emptyList();
    } else {
      where.and(fieldC);
      return qb.query();
    }
  }

  private List<T> queryForFieldValues(Map<String, Object> fieldValues, boolean useArgs) throws SQLException {
    checkForInitialized();
    QueryBuilder<T, ID> qb = queryBuilder();
    Where<T, ID> where = qb.where();
    for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
      Object fieldValue = entry.getValue();
      if (useArgs) {
        fieldValue = new SelectArg(fieldValue);
      }
      where.eq(entry.getKey(), fieldValue);
    }
    if (fieldValues.size() == 0) {
      return Collections.emptyList();
    } else {
      where.and(fieldValues.size());
      return qb.query();
    }
  }
}
TOP

Related Classes of com.j256.ormlite.dao.BaseDaoImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.