/*******************************************************************************
* 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 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;
import javax.sql.DataSource;
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;
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;
}
}