/******************************************************************************* * 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 (typePopulators != 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; } }