/*******************************************************************************
* 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.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import se.unlogic.standardutils.annotations.UnsupportedFieldTypeException;
import se.unlogic.standardutils.dao.annotations.DAOManaged;
import se.unlogic.standardutils.dao.annotations.ManyToOne;
import se.unlogic.standardutils.populators.QueryParameterPopulator;
import se.unlogic.standardutils.populators.BeanStringPopulator;
import se.unlogic.standardutils.populators.BeanStringPopulatorRegistery;
import se.unlogic.standardutils.reflection.ReflectionUtils;
import se.unlogic.standardutils.string.StringUtils;
public class DefaultManyToOneRelation<LocalType,RemoteType, RemoteKeyType> implements ManyToOneRelation<LocalType, RemoteType, RemoteKeyType>{
private final String columnName;
private final Field field;
private QueryParameterPopulator<RemoteKeyType> queryParameterPopulator;
private Method queryMethod;
private final BeanResultSetPopulator<RemoteKeyType> remoteKeyPopulator;
private final Field remoteKeyField;
private final AnnotatedDAOFactory daoFactory;
private AnnotatedDAO<RemoteType> annotatedDAO;
private QueryParameterFactory<RemoteType,RemoteKeyType> queryParameterFactory;
private final Class<RemoteType> remoteClass;
private final Class<RemoteKeyType> remoteRemoteKeyClass;
private boolean initialized;
public DefaultManyToOneRelation(Class<LocalType> beanClass, Class<RemoteType> remoteClass, Class<RemoteKeyType> remoteKeyClass, Field field, Field remoteKeyField, DAOManaged daoManaged, AnnotatedDAOFactory daoFactory) {
this.remoteClass = remoteClass;
this.remoteRemoteKeyClass = remoteKeyClass;
this.field = field;
this.remoteKeyField = remoteKeyField;
this.daoFactory = daoFactory;
if(!StringUtils.isEmpty(daoManaged.columnName())){
this.columnName = daoManaged.columnName();
}else{
this.columnName = field.getName();
}
ReflectionUtils.fixFieldAccess(remoteKeyField);
//TODO use column instead! somehow...
Method resultSetMethod = ResultSetMethods.getColumnNameMethod(remoteKeyClass);
if(resultSetMethod != null){
remoteKeyPopulator = new MethodBasedResultSetPopulator<RemoteKeyType>(resultSetMethod, columnName);
}else{
BeanStringPopulator<RemoteKeyType> typePopulator = BeanStringPopulatorRegistery.getBeanStringPopulator(remoteKeyClass);
if(typePopulator != null){
remoteKeyPopulator = new TypeBasedResultSetPopulator<RemoteKeyType>(typePopulator, columnName);
}else{
throw new UnsupportedFieldTypeException("Unable to find resultset method or type populator for field " + remoteKeyField.getName() + " in " + remoteClass + " when creating many to one relation for field " + field.getName() + " in " + beanClass, field, ManyToOne.class, beanClass);
}
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getColumnName()
*/
public String getColumnName(){
return columnName;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getQueryParameterPopulator()
*/
public QueryParameterPopulator<RemoteKeyType> getQueryParameterPopulator(){
if(queryParameterPopulator == null && queryMethod == null){
if(!initialized){
this.init();
}
this.queryParameterPopulator = annotatedDAO.getQueryParameterPopulator(remoteRemoteKeyClass);
}
return queryParameterPopulator;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getQueryMethod()
*/
public Method getQueryMethod(){
if(this.queryMethod == null){
this.queryMethod = PreparedStatementQueryMethods.getObjectQueryMethod();
}
return queryMethod;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getBeanValue(LocalType)
*/
@SuppressWarnings("unchecked")
public RemoteKeyType getBeanValue(LocalType bean){
try {
RemoteType subBean = (RemoteType) field.get(bean);
if(subBean == null){
return null;
}
return (RemoteKeyType)remoteKeyField.get(subBean);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getParamValue(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public RemoteKeyType getParamValue(Object bean) {
try {
if(bean == null){
return null;
}
return (RemoteKeyType) remoteKeyField.get(bean);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#setValue(LocalType, java.sql.ResultSet, java.sql.Connection, java.lang.reflect.Field[])
*/
public void getRemoteValue(LocalType bean, ResultSet resultSet, Connection connection, RelationQuery relationQuery) throws SQLException{
try {
if(!initialized){
this.init();
}
RemoteKeyType keyValue = remoteKeyPopulator.populate(resultSet);
if(keyValue != null){
HighLevelQuery<RemoteType> query = new HighLevelQuery<RemoteType>();
query.addParameter(queryParameterFactory.getParameter(keyValue));
if(relationQuery != null){
query.disableAutoRelations(relationQuery.isDisableAutoRelations());
}
query.addRelations(relationQuery);
RemoteType remoteBeanInstance = annotatedDAO.get(query ,connection);
this.getField().set(bean, remoteBeanInstance);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#add(LocalType, java.sql.Connection, java.lang.reflect.Field)
*/
@SuppressWarnings("unchecked")
public void add(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException{
if(!initialized){
this.init();
}
try {
RemoteType remoteBean = (RemoteType) field.get(bean);
if(remoteBean != null){
this.annotatedDAO.add(remoteBean, connection, relationQuery);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#update(LocalType, java.sql.Connection, java.lang.reflect.Field)
*/
@SuppressWarnings("unchecked")
public void update(LocalType bean, Connection connection, RelationQuery relationQuery) throws SQLException{
if(!initialized){
this.init();
}
try {
RemoteType remoteBean = (RemoteType) field.get(bean);
if(remoteBean != null){
this.annotatedDAO.addOrUpdate(remoteBean, connection, relationQuery);
}
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void init() {
this.annotatedDAO = this.daoFactory.getDAO(remoteClass);
this.queryParameterFactory = annotatedDAO.getParamFactory(remoteKeyField, remoteRemoteKeyClass);
this.initialized = true;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getField()
*/
public Field getField() {
return field;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getBeanField()
*/
public Field getBeanField() {
return this.field;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#getParamType()
*/
public Class<RemoteType> getParamType() {
return remoteClass;
}
/* (non-Javadoc)
* @see se.unlogic.utils.dao.ManyToOneRelation#isAutoGenerated()
*/
public boolean isAutoGenerated() {
return false;
}
public static <LT,RT,RKT> DefaultManyToOneRelation<LT, RT, RKT> getGenericInstance(Class<LT> beanClass, Class<RT> remoteClass, Class<RKT> remoteKeyClass, Field field, Field remoteField, DAOManaged daoManaged, AnnotatedDAOFactory daoFactory){
return new DefaultManyToOneRelation<LT, RT, RKT>(beanClass, remoteClass, remoteKeyClass, field, remoteField, daoManaged, daoFactory);
}
}