/*******************************************************************************
* 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.List;
import se.unlogic.standardutils.collections.CollectionUtils;
import se.unlogic.standardutils.dao.annotations.DAOManaged;
import se.unlogic.standardutils.dao.annotations.Key;
import se.unlogic.standardutils.dao.annotations.SimplifiedRelation;
import se.unlogic.standardutils.dao.enums.Order;
import se.unlogic.standardutils.dao.querys.ArrayListQuery;
import se.unlogic.standardutils.dao.querys.PreparedStatementQuery;
import se.unlogic.standardutils.dao.querys.UpdateQuery;
import se.unlogic.standardutils.populators.QueryParameterPopulator;
import se.unlogic.standardutils.populators.BeanStringPopulator;
import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.string.StringUtils;
public class SimplifiedOneToManyRelation<LocalType, RemoteType> implements OneToManyRelation<LocalType, RemoteType> {
private final AnnotatedDAO<LocalType> localDAO;
private final Field field;
private String selectSQL;
private String insertSQL;
private String deleteSQL;
private boolean preserveListOrder;
private String indexColumnName;
private QueryParameterPopulator<RemoteType> queryParameterPopulator;
private Method preparedStatementMethod;
private BeanResultSetPopulator<RemoteType> beanResultSetPopulator;
private Field keyField;
private Column<LocalType, ?> keyColumn;
private final String remoteTableName;
private String remoteKeyColumnName;
private final String remoteValueColumnName;
private Order order;
private boolean initialized;
@SuppressWarnings("unchecked")
public SimplifiedOneToManyRelation(Class<LocalType> beanClass, Class<RemoteType> remoteClass, Field field, AnnotatedDAO<LocalType> localDAO, List<? extends BeanStringPopulator<?>> typePopulators, List<? extends QueryParameterPopulator<?>> queryParameterPopulators) {
super();
this.localDAO = localDAO;
this.field = field;
SimplifiedRelation simplifiedRelation = field.getAnnotation(SimplifiedRelation.class);
remoteKeyColumnName = simplifiedRelation.remoteKeyColumnName();
remoteValueColumnName = simplifiedRelation.remoteValueColumnName();
order = simplifiedRelation.order();
if(simplifiedRelation.addTablePrefix()){
if(simplifiedRelation.deplurifyTablePrefix() && localDAO.getTableName().endsWith("s")){
remoteTableName = localDAO.getTableName().substring(0, localDAO.getTableName().length()-1) + simplifiedRelation.table();
}else{
remoteTableName = localDAO.getTableName() + simplifiedRelation.table();
}
}else{
remoteTableName = simplifiedRelation.table();
}
if (!StringUtils.isEmpty(simplifiedRelation.keyField())) {
try {
keyField = ReflectionUtils.getField(beanClass, simplifiedRelation.keyField());
if (keyField == null) {
throw new RuntimeException("Unable to find field " + simplifiedRelation.keyField() + " in " + beanClass.getClass());
}
} catch (SecurityException e) {
throw new RuntimeException(e);
}
} else {
List<Field> fields = ReflectionUtils.getFields(beanClass);
for (Field localBeanField : fields) {
if (localBeanField.isAnnotationPresent(DAOManaged.class) && localBeanField.isAnnotationPresent(Key.class)) {
if (this.keyField == null) {
keyField = localBeanField;
} else {
throw new RuntimeException("Multiple fields marked with @Key annotation found in class " + beanClass + " therefore keyField has to set on the @SimplifiedRelation annotation of field " + field.getName());
}
}
}
}
if (queryParameterPopulators != null) {
for (QueryParameterPopulator<?> queryParameterPopulator : queryParameterPopulators) {
if (queryParameterPopulator.getType().equals(remoteClass)) {
this.queryParameterPopulator = (QueryParameterPopulator<RemoteType>) queryParameterPopulator;
}
}
}
if (this.queryParameterPopulator == null) {
preparedStatementMethod = PreparedStatementQueryMethods.getQueryMethod(remoteClass);
if (preparedStatementMethod == null) {
throw new RuntimeException("Unable to to find a query parameter populator or prepared statement method matching " + remoteClass + " of @SimplfiedRelation and @OneToMany annotated field " + field.getName() + " in " + beanClass);
}
}
if (typePopulators != null) {
for (BeanStringPopulator<?> typePopulator : typePopulators) {
if (typePopulator.getType().equals(remoteClass)) {
beanResultSetPopulator = new TypeBasedResultSetPopulator<RemoteType>((BeanStringPopulator<RemoteType>) typePopulator, remoteValueColumnName);
}
}
}
if (beanResultSetPopulator == null) {
Method resultSetMethod = ResultSetMethods.getColumnNameMethod(remoteClass);
if (resultSetMethod != null) {
beanResultSetPopulator = new MethodBasedResultSetPopulator<RemoteType>(resultSetMethod, remoteValueColumnName);
} else {
throw new RuntimeException("Unable to to find a type populator or resultset method matching " + remoteClass + " of @SimplfiedRelation and @OneToMany annotated field " + field.getName() + " in " + beanClass);
}
}
if(simplifiedRelation.preserveListOrder()){
if(StringUtils.isEmpty(simplifiedRelation.indexColumn())){
throw new RuntimeException("Preserve list order enabled but no index column specified for @SimplifiedRelation annotated field " + field.getName() + " in " + beanClass);
}
preserveListOrder = true;
indexColumnName = simplifiedRelation.indexColumn();
}
}
private void init() {
this.keyColumn = localDAO.getColumn(keyField);
if (StringUtils.isEmpty(remoteKeyColumnName)) {
remoteKeyColumnName = keyColumn.getColumnName();
}
this.deleteSQL = "DELETE FROM " + remoteTableName + " WHERE " + remoteKeyColumnName + "=?";
if(preserveListOrder){
this.selectSQL = "SELECT * FROM " + remoteTableName + " WHERE " + remoteKeyColumnName + " = ? ORDER BY " + indexColumnName + " " + order;
this.insertSQL = "INSERT INTO " + remoteTableName + "(" + remoteKeyColumnName + "," + remoteValueColumnName + "," + indexColumnName + ") VALUES (?,?,?)";
}else{
this.selectSQL = "SELECT * FROM " + remoteTableName + " WHERE " + remoteKeyColumnName + " = ? ORDER BY " + remoteValueColumnName + " " + order;
this.insertSQL = "INSERT INTO " + remoteTableName + "(" + remoteKeyColumnName + "," + remoteValueColumnName + ") VALUES (?,?)";
}
this.initialized = true;
}
/*
* (non-Javadoc)
*
* @see se.unlogic.utils.dao.OneToManyRelation#setValue(LocalType, java.sql.Connection, java.lang.reflect.Field[])
*/
public void getRemoteValue(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException {
if (!initialized) {
init();
}
try {
ArrayListQuery<RemoteType> query = new ArrayListQuery<RemoteType>(connection, false, selectSQL, beanResultSetPopulator);
setKey(query, bean);
ArrayList<RemoteType> list = query.executeQuery();
if (list != null) {
CollectionUtils.removeNullValues(list);
}
field.set(bean, list);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void setKey(PreparedStatementQuery query, LocalType bean) throws SQLException {
if (keyColumn.getQueryParameterPopulator() != null) {
keyColumn.getQueryParameterPopulator().populate(query, 1, bean);
} else {
try {
keyColumn.getQueryMethod().invoke(query, 1, keyColumn.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 setValue(RemoteType value, UpdateQuery query) throws SQLException {
if (queryParameterPopulator != null) {
queryParameterPopulator.populate(query, 2, value);
} else {
try {
preparedStatementMethod.invoke(query, 2, value);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
/*
* (non-Javadoc)
*
* @see se.unlogic.utils.dao.OneToManyRelation#add(LocalType, java.sql.Connection, java.lang.reflect.Field[])
*/
@SuppressWarnings("unchecked")
public void add(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException {
if (!initialized) {
init();
}
try {
List<RemoteType> values = (List<RemoteType>) field.get(bean);
if (values != null) {
int listIndex = 0;
for (RemoteType value : values) {
UpdateQuery query = new UpdateQuery(connection, false, insertSQL);
setKey(query, bean);
setValue(value, query);
if(preserveListOrder){
query.setInt(3, listIndex);
listIndex++;
}
query.executeUpdate();
}
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
*
* @see se.unlogic.utils.dao.OneToManyRelation#update(LocalType, java.sql.Connection, java.lang.reflect.Field[])
*/
public void update(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException {
if (!initialized) {
init();
}
UpdateQuery query = new UpdateQuery(connection, false, deleteSQL);
setKey(query, bean);
query.executeUpdate();
this.add(bean, connection, relationQuery);
}
public static <LT, RT> OneToManyRelation<LT, RT> getGenericInstance(Class<LT> beanClass, Class<RT> remoteClass, Field field, AnnotatedDAO<LT> localDAO, List<? extends BeanStringPopulator<?>> typePopulators, List<? extends QueryParameterPopulator<?>> queryParameterPopulators) {
return new SimplifiedOneToManyRelation<LT, RT>(beanClass, remoteClass, field, localDAO, typePopulators, queryParameterPopulators);
}
}