package com.github.walker.easydb.dao;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import com.github.walker.easydb.assistant.LogFactory;
import com.github.walker.easydb.assistant.MappingUtil;
import com.github.walker.easydb.exception.FileAccessException;
import org.apache.log4j.Logger;
import com.github.walker.easydb.assistant.EasyConfig;
import com.github.walker.easydb.exception.DataAccessException;
import com.github.walker.easydb.exception.IllegalEntityException;
/**
* This class builds the BaseEntity object. It transforms query result into
* BaseEntity object, and put the BaseEntity instance into PageList object.
*
* @author HuQingmiao
*/
public abstract class ResultAssembler {
protected Logger log = LogFactory.getLogger(this.getClass());
// 当读取大字段列时, 需要设置的存放EBinFile或ETxtFile的目录
protected static String BASE_FILE_DIRC = EasyConfig.getProperty("baseFileDirc");
// the constructor of BaseEntity
@SuppressWarnings("rawtypes")
protected Constructor cons;
// the map(index, SettingMethodExp)
protected HashMap<Integer, SettingMethodExp> indexMethodExpMap;
protected int colCount;// the column count
// the Class object of the BaseEntity
protected Class<?> entityClass;
// query result
protected ResultSet rs;
protected ArrayList<BaseEntity> rsList;
/**
* 获取数据库类型
*
* @return 数据库类型, 如'mysql', 'oracle'
*/
public abstract String getDBType();
/**
* Constructor of ResultAssembler
*
* @param rs ResultSet object.
* @param entityClass The Class object of BaseEntity which used to load the query
* result.
* @throws IllegalEntityException
* @throws DataAccessException
*/
public ResultAssembler(ResultSet rs, Class<?> entityClass) {
this.rs = rs;
this.entityClass = entityClass;
}
/**
* Analyzes the query result and put it into PageList.
*
* @throws IllegalEntityException
* @throws DataAccessException
*/
public void buildEntityList() throws IllegalEntityException, DataAccessException, FileAccessException {
this.analyzeRs();
this.buildList();
}
/**
* Load data to the specified entity
*/
protected boolean loadEntity(BaseEntity entity) throws IllegalEntityException, DataAccessException,
FileAccessException {
this.analyzeRs();
return this.loadData(entity);
}
/**
* Analyzes the structure of Entity Class which instance used to loading
* query result.
*
* @throws IllegalEntityException
* @throws DataAccessException
*/
protected void analyzeRs() throws IllegalEntityException, DataAccessException {
this.indexMethodExpMap = new HashMap<Integer, SettingMethodExp>();
try {
// Get the constructor that without any parameter
Class<?>[] types = new Class[]{};
this.cons = entityClass.getConstructor(types);
// Get the entity instance by the constructor and empty parameter
Object[] args = new Object[]{};
Object entity = cons.newInstance(args);
Field[] fields = entity.getClass().getDeclaredFields();
// 将fields缓存到map(fieldName,field)
HashMap<String, Field> fieldMap = new HashMap<String, Field>();
for (int i = 0; i < fields.length; i++) {
fieldMap.put(fields[i].getName(), fields[i]);
}
ResultSetMetaData rsmd = rs.getMetaData();
this.colCount = rsmd.getColumnCount();
/**
* 列_PAGER_ROW 是分页查询的行号. 若给定的SQL需要查询所有列(如" SELECT * FROM ... "),
* 则经过分页构造后的SQL必定有行号(ROWNUM _PAGER_ROW)这一列, 而实体类中没有声明对应属性, 因此必须忽略此列。
*/
// 判断最后一列是否是分页查询的行号, 如果是则滤掉
String colName = rsmd.getColumnName(colCount);
if (colName.equals("PAGER_ROW")) {
this.colCount--;
}
// the method name of class BaseEntity
StringBuffer methodName = new StringBuffer();
Method method = null;
for (int col = 1; col <= colCount; col++) {
colName = rsmd.getColumnName(col);
String fieldName = MappingUtil.getFieldName(colName);
Field field = (Field) fieldMap.get(fieldName);
// no field matching the column name
if (field == null) {
// throw new IllegalEntityException(
// IllegalEntityException.ILLEGAL_ENTITY, entityClass
// .getName()
// + "#" + fieldName);
continue;
}
String fieldType = field.getType().getName();
// builds the method name, such as: "setXX"
String firstChar = fieldName.substring(0, 1).toUpperCase();
if (fieldName.length() > 1 && Character.isUpperCase(fieldName.charAt(1))) {
firstChar = firstChar.toLowerCase();
}
methodName.append("set").append(firstChar).append(fieldName.substring(1));
method = entity.getClass().getMethod(methodName.toString(), new Class[]{Class.forName(fieldType)});
methodName.delete(0, methodName.length());
// put the method object into MethodExp vector
indexMethodExpMap.put(new Integer(col), new SettingMethodExp(fieldType, method));
}
fieldMap.clear();
} catch (NoSuchMethodException e) {
log.error("", e);
throw new IllegalEntityException(e.getMessage());
} catch (InstantiationException e) {
log.error("", e);
throw new IllegalEntityException(e.getMessage());
} catch (IllegalAccessException e) {
log.error("", e);
throw new IllegalEntityException(e.getMessage());
} catch (InvocationTargetException e) {
log.error("", e);
throw new IllegalEntityException(e.getMessage());
} catch (SQLException e) {
log.error("", e);
throw new DataAccessException(e.getMessage());
} catch (ClassNotFoundException e) {
log.error("", e);
throw new IllegalEntityException(e.getMessage());
} catch (Exception e) {
log.error("", e);
}
}
/**
* Builds PageList object with query result.
*
* @return
* @throws IllegalEntityException
* @throws DataAccessException
*/
protected abstract void buildList() throws IllegalEntityException, DataAccessException, FileAccessException;
protected abstract boolean loadData(BaseEntity entity) throws IllegalEntityException, DataAccessException,
FileAccessException;
/**
* @return the ArrayList object that have already assembled.
*/
public abstract ArrayList<BaseEntity> getRsList();
}