Package se.unlogic.standardutils.dao

Source Code of se.unlogic.standardutils.dao.AnnotatedDAO

/*******************************************************************************
* Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-3.0-standalone.html
******************************************************************************/
package se.unlogic.standardutils.dao;

import se.unlogic.standardutils.annotations.UnsupportedFieldTypeException;
import se.unlogic.standardutils.bool.BooleanSignal;
import se.unlogic.standardutils.dao.annotations.DAOManaged;
import se.unlogic.standardutils.dao.annotations.Key;
import se.unlogic.standardutils.dao.annotations.ManyToMany;
import se.unlogic.standardutils.dao.annotations.ManyToOne;
import se.unlogic.standardutils.dao.annotations.OneToMany;
import se.unlogic.standardutils.dao.annotations.OneToOne;
import se.unlogic.standardutils.dao.annotations.OrderBy;
import se.unlogic.standardutils.dao.annotations.SimplifiedRelation;
import se.unlogic.standardutils.dao.annotations.Table;
import se.unlogic.standardutils.dao.enums.Order;
import se.unlogic.standardutils.dao.querys.ArrayListQuery;
import se.unlogic.standardutils.dao.querys.BooleanQuery;
import se.unlogic.standardutils.dao.querys.ObjectQuery;
import se.unlogic.standardutils.dao.querys.PreparedStatementQuery;
import se.unlogic.standardutils.dao.querys.UpdateQuery;
import se.unlogic.standardutils.db.DBUtils;
import se.unlogic.standardutils.numbers.IntegerCounter;
import se.unlogic.standardutils.populators.BeanStringPopulator;
import se.unlogic.standardutils.populators.EnumPopulator;
import se.unlogic.standardutils.populators.IntegerPopulator;
import se.unlogic.standardutils.populators.QueryParameterPopulator;
import se.unlogic.standardutils.populators.QueryParameterPopulatorRegistery;
import se.unlogic.standardutils.populators.annotated.AnnotatedResultSetPopulator;
import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.string.StringUtils;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

public class AnnotatedDAO<T> {

  private static final OrderByComparator ORDER_BY_COMPARATOR = new OrderByComparator();

  protected final AnnotatedResultSetPopulator<T> populator;
  protected final DataSource dataSource;
  protected final Class<T> beanClass;

  protected final List<QueryParameterPopulator<?>> queryParameterPopulators;
  protected final List<BeanStringPopulator<?>> beanStringPopulators;

  protected final ArrayList<Column<T, ?>> simpleKeys = new ArrayList<Column<T, ?>>();
  protected final ArrayList<Column<T, ?>> simpleColumns = new ArrayList<Column<T, ?>>();
  protected final HashMap<Field, Column<T, ?>> columnMap = new HashMap<Field, Column<T, ?>>();

  protected final ArrayList<ColumnKeyCollector<T>> columnKeyCollectors = new ArrayList<ColumnKeyCollector<T>>();

  protected final HashMap<Field, ManyToOneRelation<T, ?, ?>> manyToOneRelations = new HashMap<Field, ManyToOneRelation<T, ?, ?>>();
  protected final HashMap<Field, ManyToOneRelation<T, ?, ?>> manyToOneRelationKeys = new HashMap<Field, ManyToOneRelation<T, ?, ?>>();
  protected final HashMap<Field, OneToManyRelation<T, ?>> oneToManyRelations = new HashMap<Field, OneToManyRelation<T, ?>>();
  protected final HashMap<Field, ManyToManyRelation<T, ?>> manyToManyRelations = new HashMap<Field, ManyToManyRelation<T, ?>>();

  protected final TreeMap<OrderBy, Column<T, ?>> columnOrderMap = new TreeMap<OrderBy, Column<T, ?>>(ORDER_BY_COMPARATOR);

  protected final ArrayList<Field> autoAddRelations = new ArrayList<Field>();
  protected final ArrayList<Field> autoGetRelations = new ArrayList<Field>();
  protected final ArrayList<Field> autoUpdateRelations = new ArrayList<Field>();

  protected String tableName;

  protected String insertSQL;
  protected String updateSQL;
  protected String deleteSQL;
  protected String checkIfExistsSQL;
  protected String deleteByFieldSQL;
  protected String getSQL;
  protected String defaultSortingCriteria;

  public AnnotatedDAO(DataSource dataSource, Class<T> beanClass, AnnotatedDAOFactory daoFactory) {

    this(dataSource, beanClass, daoFactory, new AnnotatedResultSetPopulator<T>(beanClass), null, null);
  }

  public AnnotatedDAO(DataSource dataSource, Class<T> beanClass, AnnotatedDAOFactory daoFactory, AnnotatedResultSetPopulator<T> populator, QueryParameterPopulator<?>... queryParameterPopulators) {

    this(dataSource, beanClass, daoFactory, populator, Arrays.asList(queryParameterPopulators), null);
  }

  public AnnotatedDAO(DataSource dataSource, Class<T> beanClass, AnnotatedDAOFactory daoFactory, List<? extends QueryParameterPopulator<?>> queryParameterPopulators, List<? extends BeanStringPopulator<?>> typePopulators) {

    this(dataSource, beanClass, daoFactory, new AnnotatedResultSetPopulator<T>(beanClass, typePopulators), queryParameterPopulators, typePopulators);
  }

  public AnnotatedDAO(DataSource dataSource, Class<T> beanClass, AnnotatedDAOFactory daoFactory, AnnotatedResultSetPopulator<T> populator,
      List<? extends QueryParameterPopulator<?>> queryParameterPopulators, List<? extends BeanStringPopulator<?>> typePopulators) {

    super();
    this.populator = populator;
    this.dataSource = dataSource;
    this.beanClass = beanClass;

    if (queryParameterPopulators != null) {

      this.queryParameterPopulators = new ArrayList<QueryParameterPopulator<?>>(queryParameterPopulators);

    } else {

      this.queryParameterPopulators = null;
    }

    if (this.beanStringPopulators != null) {

      this.beanStringPopulators = new ArrayList<BeanStringPopulator<?>>(typePopulators);

    } else {

      this.beanStringPopulators = null;
    }

    Table table = beanClass.getAnnotation(Table.class);

    if (table == null) {

      throw new RuntimeException("No @Table annotation found in  " + beanClass);
    } else {
      tableName = table.name();
    }

    this.tableName = table.name();

    List<Field> fields = ReflectionUtils.getFields(beanClass);

    int generatedKeyColumnIndex = 1;

    for (Field field : fields) {

      DAOManaged daoManaged = field.getAnnotation(DAOManaged.class);
      OrderBy orderBy = field.getAnnotation(OrderBy.class);

      if (daoManaged != null) {

        ReflectionUtils.fixFieldAccess(field);

        if (field.isAnnotationPresent(OneToOne.class)) {

          this.checkAutoGeneration(daoManaged);

          // TODO Relation use this class pk, no extra field
          // TODO check auto fields
          throw new RuntimeException("OneToOne relations are not implemented yet!");

        } else if (field.isAnnotationPresent(OneToMany.class)) {

          OneToMany oneToMany = field.getAnnotation(OneToMany.class);

          this.checkOrderByAnnotation(field, orderBy);

          if (field.getType() != List.class) {

            throw new UnsupportedFieldTypeException("The annotated field " + field.getName() + " in  " + beanClass + " is of unsupported type "
                + field.getType() + ". Fields annotated as @OneToMany have to be a genericly typed " + List.class, field, OneToMany.class,
                beanClass);
          }

          if (ReflectionUtils.getGenericlyTypeCount(field) != 1) {

            throw new UnsupportedFieldTypeException("The annotated field " + field.getName() + " in  " + beanClass
                + " is genericly typed. Fields annotated as @OneToMany have to be a genericly typed " + List.class, field, OneToMany.class,
                beanClass);
          }

          // This is a bit ugly but still necessary until someone else
          // comes up with something smarter...
          Class<?> remoteClass = (Class<?>) ReflectionUtils.getGenericType(field);

          SimplifiedRelation simplifiedRelation = field.getAnnotation(SimplifiedRelation.class);

          if (simplifiedRelation != null) {

            this.oneToManyRelations.put(field, SimplifiedOneToManyRelation.getGenericInstance(beanClass, remoteClass, field, this, typePopulators, queryParameterPopulators));

          } else {

            // Use this class pks, no extra field
            this.oneToManyRelations.put(field, DefaultOneToManyRelation.getGenericInstance(beanClass, remoteClass, field, daoFactory, daoManaged));
          }

          if (oneToMany.autoAdd()) {
            this.autoAddRelations.add(field);
          }

          if (oneToMany.autoUpdate()) {
            this.autoUpdateRelations.add(field);
          }

          if (oneToMany.autoGet()) {
            this.autoGetRelations.add(field);
          }

        } else if (field.isAnnotationPresent(ManyToOne.class)) {

          this.checkAutoGeneration(daoManaged);

          ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);

          List<Field> remoteClassFields = ReflectionUtils.getFields(field.getType());

          Field matchingRemoteField = null;

          if (remoteClassFields != null) {

            for (Field remoteField : remoteClassFields) {

              if (remoteField.isAnnotationPresent(DAOManaged.class) && remoteField.isAnnotationPresent(OneToMany.class)
                  && remoteField.getType() == List.class && ReflectionUtils.isGenericlyTyped(remoteField)
                  && ((Class<?>) ReflectionUtils.getGenericType(remoteField) == this.beanClass)) {

                matchingRemoteField = remoteField;

                break;
              }
            }
          }

          if (matchingRemoteField == null) {

            throw new RuntimeException("No corresponding @OneToMany annotated field found in  " + field.getType()
                + " matching @ManyToOne relation of field " + field.getName() + " in  " + beanClass + "!");
          }

          Field remoteKeyField = null;

          if (!StringUtils.isEmpty(manyToOne.remoteKeyField())) {

            remoteKeyField = ReflectionUtils.getField(field.getType(), manyToOne.remoteKeyField());

            //TODO Check if the remote key field is @DAOPopluate annotated
            if (remoteKeyField == null) {

              throw new RuntimeException("Unable to find @Key annotated field " + manyToOne.remoteKeyField() + " in " + field.getType() + " specified for @ManyToOne annotated field "
                  + field.getName() + " in " + beanClass);
            }

          } else {

            for (Field remoteField : remoteClassFields) {

              if (remoteField.isAnnotationPresent(DAOManaged.class) && remoteField.isAnnotationPresent(Key.class)) {

                if (remoteKeyField != null) {

                  throw new RuntimeException("Found multiple @Key annotated fields in " + field.getType() + ", therefore the remoteKeyField property needs to be specified for the @ManyToOne annotated field "
                      + field.getName() + " in " + beanClass);
                }

                remoteKeyField = remoteField;
              }
            }

            if (remoteKeyField == null) {

              throw new RuntimeException("Unable to find @Key annotated field in " + field.getType() + " while parsing @ManyToOne annotated field "
                  + field.getName() + " in " + beanClass);
            }
          }

          DefaultManyToOneRelation<T, ?, ?> relation = null;

          if (field.isAnnotationPresent(Key.class)) {

            relation = DefaultManyToOneRelation.getGenericInstance(beanClass, field.getType(), remoteKeyField.getType(), field, remoteKeyField, daoManaged, daoFactory);

            manyToOneRelationKeys.put(field, relation);

          } else {

            relation = DefaultManyToOneRelation.getGenericInstance(beanClass, field.getType(), remoteKeyField.getType(), field, remoteKeyField, daoManaged, daoFactory);

            this.manyToOneRelations.put(field, relation);
          }

          this.columnMap.put(field, relation);

          if (orderBy != null) {
            this.columnOrderMap.put(orderBy, relation);
          }

          if (manyToOne.autoAdd()) {
            this.autoAddRelations.add(field);
          }

          if (manyToOne.autoUpdate()) {
            this.autoUpdateRelations.add(field);
          }

          if (manyToOne.autoGet()) {
            this.autoGetRelations.add(field);
          }

        } else if (field.isAnnotationPresent(ManyToMany.class)) {

          this.checkAutoGeneration(daoManaged);

          this.checkOrderByAnnotation(field, orderBy);

          if (field.getType() != List.class) {

            throw new UnsupportedFieldTypeException("The annotated field " + field.getName() + " in  " + beanClass + " is of unsupported type "
                + field.getType() + ". Fields annotated as @ManyToMany have to be a genericly typed " + List.class, field, ManyToMany.class,
                beanClass);
          }

          if (ReflectionUtils.getGenericlyTypeCount(field) != 1) {

            throw new UnsupportedFieldTypeException("The annotated field " + field.getName() + " in  " + beanClass
                + " is genericly typed. Fields annotated as @ManyToMany have to be a genericly typed " + List.class, field, ManyToMany.class,
                beanClass);
          }

          // This is a bit ugly but still necessary until someone else
          // comes up with something smarter...
          Class<?> remoteClass = (Class<?>) ReflectionUtils.getGenericType(field);

          this.manyToManyRelations.put(field, DefaultManyToManyRelation.getGenericInstance(beanClass, remoteClass, field, daoFactory, daoManaged));

          ManyToMany manyToMany = field.getAnnotation(ManyToMany.class);

          if (manyToMany.autoAdd()) {
            this.autoAddRelations.add(field);
          }

          if (manyToMany.autoUpdate()) {
            this.autoUpdateRelations.add(field);
          }

          if (manyToMany.autoGet()) {
            this.autoGetRelations.add(field);
          }

        } else {

          QueryParameterPopulator<?> queryPopulator = this.getQueryParameterPopulator(field.getType());

          Method method = null;

          if (queryPopulator == null) {

            method = PreparedStatementQueryMethods.getQueryMethod(field.getType());

            if (method == null && field.getType().isEnum()) {

              queryPopulator = EnumPopulator.getInstanceFromField(field);
            }

            if (method == null && queryPopulator == null) {

              throw new RuntimeException("No query method or query parameter populator found for @DAOManaged annotate field " + field.getName() + " in  " + beanClass);
            }
          }

          String columnName = daoManaged.columnName();

          if (StringUtils.isEmpty(columnName)) {

            columnName = field.getName();
          }

          SimpleColumn<T, ?> simpleColumn = null;

          Key primaryKey = field.getAnnotation(Key.class);

          if (primaryKey != null) {

            simpleColumn = SimpleColumn.getGenericInstance(beanClass, field.getType(), field, method, queryPopulator, columnName, daoManaged
                .autoGenerated());

            this.simpleKeys.add(simpleColumn);

          } else {

            simpleColumn = SimpleColumn.getGenericInstance(beanClass, field.getType(), field, method, queryPopulator, columnName, daoManaged
                .autoGenerated());

            this.simpleColumns.add(simpleColumn);
          }

          this.columnMap.put(field, simpleColumn);

          if (daoManaged.autoGenerated()) {

            if (daoManaged.autGenerationColumnIndex() != 0) {

              this.columnKeyCollectors.add(new ColumnKeyCollector<T>(field, populator, daoManaged.autGenerationColumnIndex()));

            } else {

              this.columnKeyCollectors.add(new ColumnKeyCollector<T>(field, populator, generatedKeyColumnIndex));

              generatedKeyColumnIndex++;
            }
          }

          if (orderBy != null) {
            this.columnOrderMap.put(orderBy, simpleColumn);
          }
        }
      }
    }

    if (this.simpleKeys.isEmpty() && this.manyToOneRelationKeys.isEmpty()) {

      throw new RuntimeException("No @Key annotated field found in  " + beanClass + "!");
    }

    // Genearate SQL statements
    this.generateInsertSQL();
    this.generateUpdateSQL();
    this.generateDeleteSQL();
    this.generateCheckIfExistsSQL();
    this.generateDeleteByFieldSQL();
    this.generateGetSQL();
    this.generateDefaultSortingCriteria();
  }

  public DataSource getDataSource() {
    return dataSource;
  }
 
  private void checkAutoGeneration(DAOManaged daoManaged) {

    if (daoManaged.autoGenerated()) {

      throw new RuntimeException("Fields with relations cannot be auto generated!");
    }
  }

  @SuppressWarnings("unchecked")
  public <QPT> QueryParameterPopulator<QPT> getQueryParameterPopulator(Class<QPT> type) {

    if (queryParameterPopulators != null) {

      for (QueryParameterPopulator<?> queryParameterPopulator : queryParameterPopulators) {

        if (type.equals(queryParameterPopulator.getType())) {

          return (QueryParameterPopulator<QPT>) queryParameterPopulator;
        }
      }
    }

    for (QueryParameterPopulator<?> queryParameterPopulator : QueryParameterPopulatorRegistery.getQueryParameterPopulators()) {

      if (type.equals(queryParameterPopulator.getType())) {

        return (QueryParameterPopulator<QPT>) queryParameterPopulator;
      }
    }

    return null;
  }

  private void checkOrderByAnnotation(Field field, OrderBy orderBy) {

    if (orderBy != null) {

      throw new RuntimeException("Invalid @OrderBy annotation on field " + field.getName() + " in " + this.beanClass + ", the @OrderBy annotation is not allowed on @OneToMany and @ManyToMany annotated fields.");
    }
  }

  private void generateDefaultSortingCriteria() {

    if (!this.columnOrderMap.isEmpty()) {

      StringBuilder stringBuilder = new StringBuilder(" ORDER BY ");

      boolean first = true;

      for (Entry<OrderBy, Column<T, ?>> entry : this.columnOrderMap.entrySet()) {

        if (first) {

          first = false;

        } else {

          stringBuilder.append(", ");
        }

        stringBuilder.append(entry.getValue().getColumnName() + " " + entry.getKey().order().toString());
      }

      this.defaultSortingCriteria = stringBuilder.toString();

    } else {

      this.defaultSortingCriteria = "";
    }
  }

  protected void generateInsertSQL() {

    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.append("INSERT INTO " + this.tableName + "(");

    boolean first = true;

    for (Column<T, ?> column : this.columnMap.values()) {

      if (first) {

        first = false;

      } else {

        stringBuilder.append(", ");
      }

      stringBuilder.append(column.getColumnName());
    }

    stringBuilder.append(") VALUES (");

    first = true;

    for (@SuppressWarnings("unused")
        Column<T, ?> column : this.columnMap.values()) {

      if (first) {

        first = false;

      } else {

        stringBuilder.append(", ");
      }

      stringBuilder.append("?");
    }

    stringBuilder.append(")");

    this.insertSQL = stringBuilder.toString();
  }

  protected void generateUpdateSQL() {

    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.append("UPDATE " + this.tableName + " SET ");

    boolean first = true;

    for (Column<T, ?> column : this.columnMap.values()) {

      if (first) {

        first = false;

      } else {

        stringBuilder.append(", ");
      }

      stringBuilder.append(column.getColumnName() + " = ?");
    }

    stringBuilder.append(" WHERE ");

    this.appendPrimaryKeyWhereStatement(stringBuilder);

    this.updateSQL = stringBuilder.toString();
  }

  protected void generateCheckIfExistsSQL() {

    StringBuilder stringBuilder = new StringBuilder("SELECT 1 FROM " + tableName + " WHERE ");

    this.appendPrimaryKeyWhereStatement(stringBuilder);

    this.checkIfExistsSQL = stringBuilder.toString();
  }

  private void appendPrimaryKeyWhereStatement(StringBuilder stringBuilder) {

    boolean first = true;

    for (Column<T, ?> column : this.simpleKeys) {

      if (first) {

        first = false;

      } else {

        stringBuilder.append(" AND ");
      }

      stringBuilder.append(column.getColumnName() + " = ?");
    }

    for (Column<T, ?> column : this.manyToOneRelationKeys.values()) {

      if (first) {

        first = false;

      } else {

        stringBuilder.append(" AND ");
      }

      stringBuilder.append(column.getColumnName() + " = ?");
    }
  }

  protected void generateDeleteSQL() {

    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.append("DELETE FROM ");
    stringBuilder.append(tableName);
    stringBuilder.append(" WHERE ");

    this.appendPrimaryKeyWhereStatement(stringBuilder);

    this.deleteSQL = stringBuilder.toString();
  }

  protected void generateDeleteByFieldSQL() {

    this.deleteByFieldSQL = "DELETE FROM " + tableName;
  }

  protected void generateGetSQL() {

    this.getSQL = "SELECT * FROM " + tableName;
  }

  public TransactionHandler createTransaction() throws SQLException {

    return new TransactionHandler(dataSource);
  }

  public void add(T bean) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.add(bean, transactionHandler.getConnection(), null);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void add(T bean, RelationQuery relationQuery) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.add(bean, transactionHandler.getConnection(), relationQuery);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void addAll(Collection<T> beans, RelationQuery relationQuery) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.addAll(beans, transactionHandler.getConnection(), relationQuery);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void add(T bean, TransactionHandler transactionHandler, RelationQuery relationQuery) throws SQLException {

    this.add(bean, transactionHandler.getConnection(), relationQuery);
  }

  public void addAll(List<T> beans, TransactionHandler transactionHandler, RelationQuery relationQuery) throws SQLException {

    this.addAll(beans, transactionHandler.getConnection(), relationQuery);
  }

  public void addAll(Collection<T> beans, Connection connection, RelationQuery relationQuery) throws SQLException {

    for (T bean : beans) {

      this.add(bean, connection, relationQuery);
    }
  }

  public void add(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    this.preAddRelations(bean, connection, relationQuery);

    UpdateQuery query = null;

    try {

      query = new UpdateQuery(connection, false, this.insertSQL);

      IntegerCounter integerCounter = new IntegerCounter();

      setQueryValues(bean, query, integerCounter, this.columnMap.values());

      this.executeUpdateQuery(query, bean);

      this.addRelations(bean, connection, relationQuery);

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  private void executeUpdateQuery(UpdateQuery query, T bean) throws SQLException {

    if (!this.columnKeyCollectors.isEmpty()) {

      query.executeUpdate(new ColumnKeyCollectorWrapper<T>(this.columnKeyCollectors, bean));

    } else {

      query.executeUpdate();
    }
  }

  private void preAddRelations(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        preAddRelation(bean, connection, relationQuery, relation);
      }
    }

    if (relationQuery == null || !relationQuery.isDisableAutoRelations()) {

      for (Field relation : autoAddRelations) {

        if (relationQuery == null || (!relationQuery.containsRelation(relation) && !relationQuery.containsExcludedRelation(relation))) {

          preAddRelation(bean, connection, relationQuery, relation);
        }
      }
    }
  }

  private void preAddRelation(T bean, Connection connection, RelationQuery relationQuery, Field relation) throws SQLException {

    ManyToOneRelation<T, ?, ?> manyToOneRelation = this.manyToOneRelations.get(relation);

    if (manyToOneRelation != null) {

      manyToOneRelation.add(bean, connection, relationQuery);
      return;
    }

    manyToOneRelation = this.manyToOneRelationKeys.get(relation);

    if (manyToOneRelation != null) {

      manyToOneRelation.add(bean, connection, relationQuery);
    }
  }

  private void addRelations(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        addRelation(bean, connection, relationQuery, relation);
      }
    }

    if (relationQuery == null || !relationQuery.isDisableAutoRelations()) {
      for (Field relation : autoAddRelations) {

        if (relationQuery == null || (!relationQuery.containsRelation(relation) && !relationQuery.containsExcludedRelation(relation))) {

          addRelation(bean, connection, relationQuery, relation);
        }
      }
    }

  }

  private void addRelation(T bean, Connection connection, RelationQuery relationQuery, Field relation) throws SQLException {

    OneToManyRelation<T, ?> oneToManyRelation = this.oneToManyRelations.get(relation);

    if (oneToManyRelation != null) {

      oneToManyRelation.add(bean, connection, relationQuery);
      return;
    }

    ManyToManyRelation<T, ?> manyToManyRelation = this.manyToManyRelations.get(relation);

    if (manyToManyRelation != null) {

      manyToManyRelation.add(bean, connection, relationQuery);
    }
  }

  private void preUpdateRelations(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        preUpdateRelation(bean, connection, relationQuery, relation);
      }
    }

    if (relationQuery == null || !relationQuery.isDisableAutoRelations()) {

      for (Field relation : autoUpdateRelations) {

        if (relationQuery == null || (relationQuery == null || (!relationQuery.containsRelation(relation) && !relationQuery.containsExcludedRelation(relation)))) {

          preUpdateRelation(bean, connection, relationQuery, relation);
        }
      }
    }
  }

  private void preUpdateRelation(T bean, Connection connection, RelationQuery relationQuery, Field relation) throws SQLException {

    ManyToOneRelation<T, ?, ?> manyToOneRelation = this.manyToOneRelations.get(relation);

    if (manyToOneRelation != null) {

      manyToOneRelation.update(bean, connection, relationQuery);
      return;
    }

    manyToOneRelation = this.manyToOneRelationKeys.get(relation);

    if (manyToOneRelation != null) {

      manyToOneRelation.update(bean, connection, relationQuery);
    }
  }

  private void updateRelations(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        updateRelation(bean, connection, relationQuery, relation);
      }
    }

    if (relationQuery == null || !relationQuery.isDisableAutoRelations()) {

      for (Field relation : autoUpdateRelations) {

        if (relationQuery == null || (!relationQuery.containsRelation(relation) && !relationQuery.containsExcludedRelation(relation))) {

          updateRelation(bean, connection, relationQuery, relation);
        }
      }
    }
  }

  private void updateRelation(T bean, Connection connection, RelationQuery relationQuery, Field relation) throws SQLException {

    OneToManyRelation<T, ?> oneToManyRelation = this.oneToManyRelations.get(relation);

    if (oneToManyRelation != null) {

      oneToManyRelation.update(bean, connection, relationQuery);
      return;
    }

    ManyToManyRelation<T, ?> manyToManyRelation = this.manyToManyRelations.get(relation);

    if (manyToManyRelation != null) {

      manyToManyRelation.update(bean, connection, relationQuery);
    }
  }

  private void setQueryValues(T bean, PreparedStatementQuery query, IntegerCounter integerCounter, Collection<? extends Column<T, ?>> columns) throws SQLException {

    for (Column<T, ?> column : columns) {

      if (column.getQueryParameterPopulator() != null) {

        column.getQueryParameterPopulator().populate(query, integerCounter.increment(), column.getBeanValue(bean));

      } else {

        try {
          column.getQueryMethod().invoke(query, integerCounter.increment(), column.getBeanValue(bean));

        } catch (IllegalArgumentException e) {

          throw new RuntimeException(e);

        } catch (IllegalAccessException e) {

          throw new RuntimeException(e);

        } catch (InvocationTargetException e) {

          throw new RuntimeException(e);
        }
      }
    }
  }

  private void setQueryValues(List<T> beans, PreparedStatementQuery query, IntegerCounter integerCounter, Collection<? extends Column<T, ?>> columns, Field excludedField) throws SQLException {

    for (Column<T, ?> column : columns) {

      if(column.getBeanField().equals(excludedField)) {
        continue;
      }

      for (T bean : beans) {

        if (column.getQueryParameterPopulator() != null) {

          column.getQueryParameterPopulator().populate(query, integerCounter.increment(), column.getBeanValue(bean));

        } else {

          try {
            column.getQueryMethod().invoke(query, integerCounter.increment(), column.getBeanValue(bean));

          } catch (IllegalArgumentException e) {

            throw new RuntimeException(e);

          } catch (IllegalAccessException e) {

            throw new RuntimeException(e);

          } catch (InvocationTargetException e) {

            throw new RuntimeException(e);
          }
        }
      }
    }
  }

  public void addOrUpdateAll(Collection<T> beans, TransactionHandler transactionHandler, RelationQuery relationQuery) throws SQLException {
    this.addOrUpdateAll(beans, transactionHandler.getConnection(), relationQuery);
  }
 
  public void addOrUpdateAll(Collection<T> beans, Connection connection, RelationQuery relationQuery) throws SQLException {

    for (T bean : beans) {
      this.addOrUpdate(bean, connection, relationQuery);
    }
  }

  public void addOrUpdate(T bean, RelationQuery relationQuery) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.addOrUpdate(bean, transactionHandler.getConnection(), relationQuery);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void addOrUpdate(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (!isNewBean(bean, connection)) {

      this.update(bean, connection, relationQuery);

    } else {

      this.add(bean, connection, relationQuery);
    }
  }

  public boolean isNewBean(T bean, Connection connection) throws SQLException {

    if(!hasKeysSet(bean)){

      return true;
    }

    //Unable to determine if beans is new or not so far, do db query to check
    return !beanExists(bean, connection);
  }

  public boolean hasKeysSet(T bean) throws SQLException {

    for (Column<T, ?> column : this.simpleKeys) {

      if (column.getBeanValue(bean) == null) {

        //Key not set, presume new bean
        return false;
      }
    }

    for (Column<T, ?> column : this.manyToOneRelationKeys.values()) {

      if (column.getBeanValue(bean) == null) {

        //Key not set, presume new bean
        return false;
      }
    }

    return true;
  }

  public boolean beanExists(T bean) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return beanExists(bean, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public boolean beanExists(T bean, TransactionHandler transactionHandler) throws SQLException {

    return beanExists(bean, transactionHandler.getConnection());
  }

  public boolean beanExists(T bean, Connection connection) throws SQLException {

    BooleanQuery query = null;

    try {

      query = new BooleanQuery(connection, false, this.checkIfExistsSQL);

      IntegerCounter integerCounter = new IntegerCounter();

      // Keys from SimpleColumns for where statement
      this.setQueryValues(bean, query, integerCounter, this.simpleKeys);

      // Keys from many to one relations for where statement
      this.setQueryValues(bean, query, integerCounter, this.manyToOneRelationKeys.values());

      return query.executeQuery();

    } finally {

      BooleanQuery.autoCloseQuery(query);
    }
  }

  public void update(T bean) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.update(bean, transactionHandler.getConnection(), null);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void update(T bean, RelationQuery relationQuery) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.update(bean, transactionHandler.getConnection(), relationQuery);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void update(T bean, TransactionHandler transactionHandler, RelationQuery relationQuery) throws SQLException {

    this.update(bean, transactionHandler.getConnection(), relationQuery);
  }

  public Integer update(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    UpdateQuery query = null;

    try {

      this.preUpdateRelations(bean, connection, relationQuery);

      query = new UpdateQuery(connection, false, this.updateSQL);

      IntegerCounter integerCounter = new IntegerCounter();

      // All fields
      this.setQueryValues(bean, query, integerCounter, this.columnMap.values());

      // Keys from SimpleColumns for where statement
      this.setQueryValues(bean, query, integerCounter, this.simpleKeys);

      // Keys from many to one relations for where statement
      this.setQueryValues(bean, query, integerCounter, this.manyToOneRelationKeys.values());

      query.executeUpdate();

    } finally {

      UpdateQuery.autoCloseQuery(query);
    }

    this.updateRelations(bean, connection, relationQuery);

    return query.getAffectedRows();
  }

  public void update(List<T> beans, RelationQuery relationQuery) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.update(beans, transactionHandler.getConnection(), relationQuery);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void update(List<T> beans, TransactionHandler transactionHandler, RelationQuery relationQuery) throws SQLException {

    this.update(beans, transactionHandler.getConnection(), relationQuery);
 
 
  public void update(List<T> beans, Connection connection, RelationQuery relationQuery) throws SQLException {
   
    for(T bean : beans){
     
      update(bean, connection, relationQuery);
    }
  }
 
  public void update(LowLevelQuery<T> lowLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      this.update(lowLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public void update(LowLevelQuery<T> lowLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    this.update(lowLevelQuery, transactionHandler.getConnection());
  }

  public void update(LowLevelQuery<T> lowLevelQuery, Connection connection) throws SQLException {

    UpdateQuery query = null;

    try {

      query = new UpdateQuery(connection, false, lowLevelQuery.getSql());

      this.setCustomQueryParameters(query, lowLevelQuery.getParameters());

      if (lowLevelQuery.getGeneratedKeyCollectors() != null) {

        query.executeUpdate(lowLevelQuery.getGeneratedKeyCollectors());

      } else {

        query.executeUpdate();
      }

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public T get(LowLevelQuery<T> lowLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.get(lowLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public T get(LowLevelQuery<T> lowLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.get(lowLevelQuery, transactionHandler.getConnection());
  }

  public T get(LowLevelQuery<T> lowLevelQuery, Connection connection) throws SQLException {

    BeanResultSetPopulator<T> populator = this.getPopulator(connection, lowLevelQuery);

    ObjectQuery<T> query = null;

    try {

      query = new ObjectQuery<T>(connection, false, lowLevelQuery.getSql(), populator);

      this.setCustomQueryParameters(query, lowLevelQuery.getParameters());

      T bean = query.executeQuery();

      if (bean != null && (RelationQuery.hasRelations(lowLevelQuery) || !autoGetRelations.isEmpty())) {

        this.populateRelations(bean, connection, lowLevelQuery);
      }

      return bean;

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }









  public boolean getBoolean(LowLevelQuery<T> lowLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.getBoolean(lowLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public boolean getBoolean(LowLevelQuery<T> lowLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.getBoolean(lowLevelQuery, transactionHandler.getConnection());
  }

  public boolean getBoolean(LowLevelQuery<T> lowLevelQuery, Connection connection) throws SQLException {

    BooleanQuery query = null;

    try {

      query = new BooleanQuery(connection, false, lowLevelQuery.getSql());

      this.setCustomQueryParameters(query, lowLevelQuery.getParameters());

      return query.executeQuery();

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }




  public T get(HighLevelQuery<T> highLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.get(highLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public T get(HighLevelQuery<T> highLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.get(highLevelQuery, transactionHandler.getConnection());
  }

  public T get(HighLevelQuery<T> highLevelQuery, Connection connection) throws SQLException {

    BeanResultSetPopulator<T> populator = this.getPopulator(connection, highLevelQuery);

    ObjectQuery<T> query = null;

    try {
      query = new ObjectQuery<T>(connection, false, this.getSQL + this.getCriterias(highLevelQuery, true), populator);

      if (highLevelQuery.getParameters() != null) {

        setQueryParameters(query, highLevelQuery, 1);
      }

      T bean = query.executeQuery();

      if (bean != null && (RelationQuery.hasRelations(highLevelQuery) || !autoGetRelations.isEmpty())) {

        this.populateRelations(bean, connection, highLevelQuery);
      }

      return bean;

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public boolean getBoolean(HighLevelQuery<T> highLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.getBoolean(highLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public boolean getBoolean(HighLevelQuery<T> highLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.getBoolean(highLevelQuery, transactionHandler.getConnection());
  }

  public boolean getBoolean(HighLevelQuery<T> highLevelQuery, Connection connection) throws SQLException {

    BooleanQuery query = null;

    try {
      query = new BooleanQuery(connection, false, this.getSQL + this.getCriterias(highLevelQuery, true));

      if (highLevelQuery.getParameters() != null) {

        setQueryParameters(query, highLevelQuery, 1);
      }

      return query.executeQuery();

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  private String getCriterias(HighLevelQuery<T> highLevelQuery, boolean orderBy) {

    if (highLevelQuery == null) {

      if (orderBy) {

        return this.defaultSortingCriteria;

      } else {

        return "";
      }
    }

    StringBuilder stringBuilder = new StringBuilder();

    if (highLevelQuery.getParameters() != null) {

      boolean first = true;

      for (QueryParameter<T, ?> queryParameter : highLevelQuery.getParameters()) {

        if (first) {

          stringBuilder.append(" WHERE ");

          first = false;

        } else {

          stringBuilder.append(" AND ");
        }

        stringBuilder.append(queryParameter.getColumn().getColumnName() + " " + queryParameter.getOperator());

        if (queryParameter.hasValues()) {

          if (queryParameter.hasMultipleValues()) {

            //stringBuilder.append(queryParameter.getOperator() + " " + queryParameter.getColumn().getColumnName());

            stringBuilder.append(" (?");

            if (queryParameter.getValues().size() > 1) {

              StringUtils.repeatString(",?", queryParameter.getValues().size() - 1, stringBuilder);
            }

            stringBuilder.append(")");

          } else {

            stringBuilder.append(" ?");
          }
        }
      }
    }

    if (orderBy && highLevelQuery.getOrderByCriterias() != null) {

      boolean first = true;

      for (OrderByCriteria<T> criteria : highLevelQuery.getOrderByCriterias()) {

        if (first) {

          stringBuilder.append(" ORDER BY " + criteria.getColumn().getColumnName() + " " + criteria.getOrder().toString());

          first = false;

        } else {

          stringBuilder.append(", " + criteria.getColumn().getColumnName() + " " + criteria.getOrder().toString());
        }
      }

    } else {

      stringBuilder.append(this.defaultSortingCriteria);
    }

    return stringBuilder.toString();
  }

  @SuppressWarnings("unchecked")
  private <ColumnType> Column<T, ? super ColumnType> getColumn(Field field, Class<ColumnType> paramClass) {

    Column<T, ?> column = this.columnMap.get(field);

    if (column == null) {

      throw new RuntimeException("Field " + field + " not found in  " + this.beanClass + "!");

    } else if (!column.getParamType().isAssignableFrom(paramClass)) {

      throw new RuntimeException(" " + paramClass + " is not compatible with type " + column.getParamType() + " of field " + field + " in  "
          + this.beanClass + "!");
    }

    return (Column<T, ColumnType>) column;
  }

  protected BeanResultSetPopulator<T> getPopulator(Connection connection, LowLevelQuery<T> query) {

    BeanResultSetPopulator<T> populator = getPopulator(connection, (RelationQuery)query);

    if(query.hasChainedBeanResultSetPopulators()){

      return new BeanChainPopulator<T>(query.getChainedResultSetPopulators(), populator);
    }

    return populator;
  }

  protected BeanResultSetPopulator<T> getPopulator(Connection connection, RelationQuery relationQuery) {

    ArrayList<ManyToOneRelation<T, ?, ?>> manyToOneRelations = null;

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        ManyToOneRelation<T, ?, ?> manyToOneRelation = this.manyToOneRelations.get(relation);

        if (manyToOneRelation == null) {

          manyToOneRelation = this.manyToOneRelationKeys.get(relation);
        }

        if (manyToOneRelation != null) {

          if (manyToOneRelations == null) {

            manyToOneRelations = new ArrayList<ManyToOneRelation<T, ?, ?>>();
          }

          manyToOneRelations.add(manyToOneRelation);
        }
      }
    }

    if((!autoGetRelations.isEmpty() && (relationQuery == null || !relationQuery.isDisableAutoRelations()))){

      for (Field relation : autoGetRelations) {

        ManyToOneRelation<T, ?, ?> manyToOneRelation = this.manyToOneRelations.get(relation);

        if (manyToOneRelation == null) {

          manyToOneRelation = this.manyToOneRelationKeys.get(relation);
        }

        if (manyToOneRelation != null) {

          if (manyToOneRelations == null) {

            manyToOneRelations = new ArrayList<ManyToOneRelation<T, ?, ?>>();
          }

          manyToOneRelations.add(manyToOneRelation);
        }
      }
    }

    if (manyToOneRelations != null) {

      return new BeanRelationPopulator<T>(this.populator, manyToOneRelations, connection, relationQuery);
    }

    return this.populator;
  }

  protected void populateRelations(T bean, Connection connection, RelationQuery relationQuery) throws SQLException {

    if (RelationQuery.hasRelations(relationQuery)) {

      for (Field relation : relationQuery.getRelations()) {

        populateRelation(bean, connection, relationQuery, relation);
      }
    }

    if (relationQuery == null || !relationQuery.isDisableAutoRelations()) {

      for (Field relation : autoGetRelations) {

        if (relationQuery == null || (!relationQuery.containsRelation(relation) && !relationQuery.containsExcludedRelation(relation))) {

          populateRelation(bean, connection, relationQuery, relation);
        }
      }
    }
  }

  protected void populateRelation(T bean, Connection connection, RelationQuery relationQuery, Field relation) throws SQLException {

    OneToManyRelation<T, ?> oneToManyRelation = this.oneToManyRelations.get(relation);

    if (oneToManyRelation != null) {

      oneToManyRelation.getRemoteValue(bean, connection, relationQuery);
      return;
    }

    ManyToManyRelation<T, ?> manyToManyRelation = this.manyToManyRelations.get(relation);

    if (manyToManyRelation != null) {

      manyToManyRelation.getRemoteValue(bean, connection, relationQuery);
    }
  }

  public List<T> getAll(LowLevelQuery<T> lowLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.getAll(lowLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public List<T> getAll(LowLevelQuery<T> lowLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.getAll(lowLevelQuery, transactionHandler.getConnection());
  }

  public List<T> getAll(LowLevelQuery<T> lowLevelQuery, Connection connection) throws SQLException {

    BeanResultSetPopulator<T> populator = this.getPopulator(connection, lowLevelQuery);

    ArrayListQuery<T> query = null;

    try {

      query = new ArrayListQuery<T>(connection, false, lowLevelQuery.getSql(), populator);

      setCustomQueryParameters(query, lowLevelQuery.getParameters());

      ArrayList<T> beans = query.executeQuery();

      if (beans != null && (RelationQuery.hasRelations(lowLevelQuery) || !autoGetRelations.isEmpty())) {

        for (T bean : beans) {

          this.populateRelations(bean, connection, lowLevelQuery);
        }
      }

      return beans;

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  private void setCustomQueryParameters(PreparedStatementQuery query, List<?> parameters) {

    if (parameters != null) {

      int i = 1;

      for (Object object : parameters) {

        Method queryMethod;

        if (object == null) {

          queryMethod = PreparedStatementQueryMethods.getObjectQueryMethod();

        } else if(object.getClass().isEnum()){

          object = object.toString();

          queryMethod = PreparedStatementQueryMethods.getQueryMethod(String.class);

        }else {

          queryMethod = PreparedStatementQueryMethods.getQueryMethod(object.getClass());
        }

        if (queryMethod == null) {
          throw new RuntimeException("Unable to find suitable prepared statement query method for parameter " + object.getClass());
        }

        try {
          queryMethod.invoke(query, i++, object);

        } catch (IllegalArgumentException e) {

          throw new RuntimeException(e);

        } catch (IllegalAccessException e) {

          throw new RuntimeException(e);

        } catch (InvocationTargetException e) {

          throw new RuntimeException(e);
        }
      }
    }
  }

  protected List<T> getAll(String sql, CustomQueryParameter<?> queryParameter, Connection connection,
      RelationQuery relationQuery) throws SQLException {

    BeanResultSetPopulator<T> populator = this.getPopulator(connection, relationQuery);

    ArrayListQuery<T> query = null;

    try {

      query = new ArrayListQuery<T>(connection, false, sql, populator);

      if (queryParameter.getQueryParameterPopulator() != null) {

        queryParameter.getQueryParameterPopulator().populate(query, 1, queryParameter.getParamValue());

      } else {

        try {
          queryParameter.getQueryMethod().invoke(query, 1, queryParameter.getParamValue());

        } catch (IllegalArgumentException e) {

          throw new RuntimeException(e);

        } catch (IllegalAccessException e) {

          throw new RuntimeException(e);

        } catch (InvocationTargetException e) {

          throw new RuntimeException(e);
        }
      }

      ArrayList<T> beans = query.executeQuery();

      if (beans != null && (RelationQuery.hasRelations(relationQuery) || !autoGetRelations.isEmpty())) {

        for (T bean : beans) {

          this.populateRelations(bean, connection, relationQuery);
        }
      }

      return beans;

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public List<T> getAll(HighLevelQuery<T> highLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.getAll(highLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public List<T> getAll(HighLevelQuery<T> highLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.getAll(highLevelQuery, transactionHandler.getConnection());
  }

  public List<T> getAll(HighLevelQuery<T> highLevelQuery, Connection connection)
  throws SQLException {

    BeanResultSetPopulator<T> populator = this.getPopulator(connection, highLevelQuery);

    ArrayListQuery<T> query = null;

    try {
      query = new ArrayListQuery<T>(connection, false, this.getSQL + this.getCriterias(highLevelQuery, true), populator);

      setQueryParameters(query, highLevelQuery, 1);

      ArrayList<T> beans = query.executeQuery();

      if (beans != null && (RelationQuery.hasRelations(highLevelQuery) || !autoGetRelations.isEmpty())) {

        for (T bean : beans) {

          this.populateRelations(bean, connection, highLevelQuery);
        }
      }

      return beans;

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public List<T> getAll() throws SQLException {

    return this.getAll((HighLevelQuery<T>) null);
  }

  public void delete(T bean) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.delete(bean, transactionHandler);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void delete(T bean, TransactionHandler transactionHandler) throws SQLException {

    this.delete(bean, transactionHandler.getConnection());
  }

  public void delete(T bean, Connection connection) throws SQLException {

    UpdateQuery query = null;

    try {

      query = new UpdateQuery(connection, false, this.deleteSQL);

      IntegerCounter integerCounter = new IntegerCounter();

      // Keys from SimpleColumns for where statement
      this.setQueryValues(bean, query, integerCounter, this.simpleKeys);

      // Keys from many to one relations for where statement
      this.setQueryValues(bean, query, integerCounter, this.manyToOneRelationKeys.values());

      query.executeUpdate();

    } finally {
      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public void delete(List<T> beans) throws SQLException {

    TransactionHandler transactionHandler = null;

    try {

      transactionHandler = new TransactionHandler(dataSource);

      this.delete(beans, transactionHandler);

      transactionHandler.commit();
    } finally {
      TransactionHandler.autoClose(transactionHandler);
    }
  }

  public void delete(List<T> beans, TransactionHandler transactionHandler) throws SQLException {

    this.delete(beans, transactionHandler.getConnection());
  }

  public void delete(List<T> beans, Connection connection) throws SQLException {
   
    for(T bean : beans){
     
      delete(bean, connection);
    }
  }
 
  public Integer delete(HighLevelQuery<T> highLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return this.delete(highLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public Integer delete(HighLevelQuery<T> highLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return this.delete(highLevelQuery, transactionHandler.getConnection());
  }

  public Integer delete(HighLevelQuery<T> highLevelQuery, Connection connection) throws SQLException {

    UpdateQuery query = null;

    try {
      query = new UpdateQuery(connection, false, this.deleteByFieldSQL + this.getCriterias(highLevelQuery, false));

      setQueryParameters(query, highLevelQuery, 1);

      query.executeUpdate();

      return query.getAffectedRows();

    } finally {

      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  public boolean deleteWhereNotIn(List<T> beans, TransactionHandler transactionHandler, Field excludedField, QueryParameter<T, ?>... queryParameters) throws SQLException {
    return this.deleteWhereNotIn(beans, transactionHandler.getConnection(), excludedField, queryParameters);
  }
   
  public boolean deleteWhereNotIn(List<T> beans, Connection connection, Field excludedField, QueryParameter<T, ?>... queryParameters)
  throws SQLException {

    // Generate SQL
    StringBuilder stringBuilder = new StringBuilder();

    stringBuilder.append("DELETE FROM ");
    stringBuilder.append(tableName);
    stringBuilder.append(" WHERE");

    //Filter away any beans that don't have their keys set
    ArrayList<T> filteredBeans = new ArrayList<T>(beans);

    Iterator<T> iterator = filteredBeans.iterator();

    while(iterator.hasNext()){

      if(!hasKeysSet(iterator.next())){

        iterator.remove();
      }
    }

    if(filteredBeans.isEmpty()){

      //No beans left, abort
      return false;
    }

    int beanCount = filteredBeans.size();

    BooleanSignal signal = new BooleanSignal();

    this.generateColumWhereNotInSQL(this.simpleKeys, excludedField, stringBuilder, beanCount, signal);
    this.generateColumWhereNotInSQL(this.manyToOneRelationKeys.values(), excludedField, stringBuilder, beanCount, signal);

    if (queryParameters != null) {

      for (QueryParameter<T, ?> queryParameter : queryParameters) {

        stringBuilder.append(" AND " + queryParameter.getColumn().getColumnName() + " " + queryParameter.getOperator() + " ?");
      }
    }

    UpdateQuery query = null;

    try {

      query = new UpdateQuery(connection, false, stringBuilder.toString());

      IntegerCounter integerCounter = new IntegerCounter();

      // Keys values from SimpleColumns for where not in statement
      this.setQueryValues(filteredBeans, query, integerCounter, this.simpleKeys, excludedField);

      // Keys values from many to one relations for where not in statement
      this.setQueryValues(filteredBeans, query, integerCounter, this.manyToOneRelationKeys.values(), excludedField);

      if (queryParameters != null) {

        // Set query param values
        this.setQueryParameters(query, new HighLevelQuery<T>(queryParameters), integerCounter.increment());
      }

      query.executeUpdate();

    } finally {
      PreparedStatementQuery.autoCloseQuery(query);
    }

    return true;
  }

  public Integer getCount(HighLevelQuery<T> highLevelQuery) throws SQLException {

    Connection connection = null;

    try {

      connection = this.dataSource.getConnection();

      return getCount(highLevelQuery, connection);

    } finally {
      DBUtils.closeConnection(connection);
    }
  }

  public Integer getCount(HighLevelQuery<T> highLevelQuery, TransactionHandler transactionHandler) throws SQLException {

    return getCount(highLevelQuery, transactionHandler.getConnection());
  }

  public Integer getCount(HighLevelQuery<T> highLevelQuery, Connection connection) throws SQLException {

    ObjectQuery<Integer> query = null;

    try{
      query = new ObjectQuery<Integer>(connection, false, "SELECT COUNT(*) FROM " + tableName + this.getCriterias(highLevelQuery, false), IntegerPopulator.getPopulator());

      if (highLevelQuery != null && highLevelQuery.getParameters() != null) {

        setQueryParameters(query, highLevelQuery, 1);
      }

      return query.executeQuery();

    }finally{
      PreparedStatementQuery.autoCloseQuery(query);
    }
  }

  private void generateColumWhereNotInSQL(Collection<? extends Column<T, ?>> keyColumns, Field excludedField, StringBuilder stringBuilder, int beanCount, BooleanSignal signal) {

    for (Column<T, ?> column : keyColumns) {

      if(column.getBeanField().equals(excludedField)) {
        continue;
      }

      if (signal.isSignal()) {

        stringBuilder.append(" AND");

      } else {

        signal.setSignal(true);
      }

      stringBuilder.append(" ");
      stringBuilder.append(column.getColumnName());
      stringBuilder.append(" NOT IN (");

      this.addQuestionMarks(beanCount, stringBuilder);

      stringBuilder.append(")");
    }

  }

  private void addQuestionMarks(int size, StringBuilder stringBuilder) {

    stringBuilder.append("?");

    if (size > 1) {

      for (int i = 2; i <= size; i++) {

        stringBuilder.append(",?");
      }
    }
  }

  public <ParamType> QueryParameterFactory<T, ParamType> getParamFactory(Field field, Class<ParamType> paramClass) {

    return new QueryParameterFactory<T, ParamType>(this.getColumn(field, paramClass));
  }

  public <ParamType> QueryParameterFactory<T, ParamType> getParamFactory(String fieldName, Class<ParamType> paramClass) {

    Field field = ReflectionUtils.getField(beanClass, fieldName);

    if (field == null) {
      throw new RuntimeException("Field " + fieldName + " not found in  " + this.beanClass + "!");
    }

    return new QueryParameterFactory<T, ParamType>(this.getColumn(field, paramClass));
  }

  public OrderByCriteria<T> getOrderByCriteria(Field field, Order order) {

    Column<T, ?> column = this.columnMap.get(field);

    if (column == null) {
      throw new RuntimeException("No @DAOManaged annotated field with name " + field.getName() + " not found in  " + this.beanClass + "!");
    }

    return new OrderByCriteria<T>(order, column);
  }

  public OrderByCriteria<T> getOrderByCriteria(String fieldName, Order order) {

    Field field = ReflectionUtils.getField(beanClass, fieldName);

    if (field == null) {
      throw new RuntimeException("Field " + fieldName + " not found in  " + this.beanClass + "!");
    }

    return this.getOrderByCriteria(field, order);
  }

  public String getTableName() {

    return tableName;
  }

  Column<T, ?> getColumn(Field field) {

    return this.columnMap.get(field);
  }

  public void setQueryParameters(PreparedStatementQuery query, HighLevelQuery<T> highLevelQuery, final int startIndex) throws SQLException {

    if (highLevelQuery != null && highLevelQuery.getParameters() != null) {

      int index = startIndex;

      for (QueryParameter<?, ?> queryParameter : highLevelQuery.getParameters()) {

        if (queryParameter.hasValues()) {

          if (queryParameter.hasMultipleValues()) {

            for (Object value : queryParameter.getValues()) {

              setQueryParameter(queryParameter, value, query, index++);
            }

          } else {

            setQueryParameter(queryParameter, queryParameter.getValue(), query, index++);
          }
        }
      }
    }
  }

  private void setQueryParameter(QueryParameter<?, ?> queryParameter, Object value, PreparedStatementQuery query, int index) throws SQLException {

    Column<?, ?> column = queryParameter.getColumn();

    if (column.getQueryParameterPopulator() != null) {

      column.getQueryParameterPopulator().populate(query, index, column.getParamValue(value));

    } else {

      try {
        column.getQueryMethod().invoke(query, index, column.getParamValue(value));

      } catch (IllegalArgumentException e) {

        throw new RuntimeException(e);

      } catch (IllegalAccessException e) {

        throw new RuntimeException(e);

      } catch (InvocationTargetException e) {

        throw new RuntimeException(e);
      }
    }
  }

 
  public Class<T> getBeanClass() {
 
    return beanClass;
  }
}
TOP

Related Classes of se.unlogic.standardutils.dao.AnnotatedDAO

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.