package com.github.walker.easydb.dao; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import com.github.walker.easydb.assistant.LogFactory; import com.github.walker.easydb.assistant.MappingUtil; import com.github.walker.easydb.datatype.EBinFile; import com.github.walker.easydb.datatype.ETxtFile; import com.github.walker.easydb.datatype.UpdateIdentifier; import com.github.walker.easydb.exception.BaseException; import com.github.walker.easydb.exception.DataAccessException; import com.github.walker.easydb.exception.IllegalEntityException; import com.github.walker.easydb.exception.IllegalParamException; import org.apache.log4j.Logger; /** * This class parses the BaseEntity object to following parts: the class name of * the given BaseEntity object, the fileds and methods of the BaseEntity object. * * @author HuQingmiao */ public class EntityParser { private static Logger log = LogFactory.getLogger(EntityParser.class); private Connection conn; private String className;// the class name of the entity object private HashMap<String, FieldExp> fieldExpMap; // 等待更新的属性(fieldName,fieldExp) private HashMap<String, Method> fieldMethodMap; // 等待更新的属性及相应Get方法,map(fieldName,gettingMethod) private HashSet<String> pkSet; // 主键属性的名称 // 用于装载非置空的文件类型的属性名称 // loads the field name of non-empty big field private HashSet<String> bigFieldNameSet; private String dbType = null; /** * @param conn * @param entity * @throws com.github.walker.easydb.exception.IllegalEntityException * @throws com.github.walker.easydb.exception.DataAccessException */ public EntityParser(String dbType, Connection conn, BaseEntity entity) throws IllegalEntityException, DataAccessException,BaseException { this.dbType = dbType; this.conn = conn; this.className = entity.getClass().getName(); String[] pkArray = entity.pk(); // 必须在pk()方法中指定主键属性 // if (pkArray == null || pkArray.length == 0) { // throw new IllegalEntityException( // IllegalEntityException.NOT_SPECIFY_PK); // } this.pkSet = new HashSet<String>(Arrays.asList(pkArray)); this.bigFieldNameSet = new HashSet<String>(2); this.fieldExpMap = new HashMap<String, FieldExp>(10); this.fieldMethodMap = new HashMap<String, Method>(10); // the getting method name of class BaseEntity StringBuffer methodName = new StringBuffer(); try { // the columns set which belong to this table HashSet<String> columnSet = this.getColumnNames(); Field[] fields = entity.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { // the field name of BaseEntity String fieldName = fields[i].getName(); // builds the method name, such as: "getXX" String firstChar = fieldName.substring(0, 1).toUpperCase(); if (fieldName.length() > 1 && Character.isUpperCase(fieldName.charAt(1))) { firstChar = firstChar.toLowerCase(); } methodName.append("get").append(firstChar).append(fieldName.substring(1)); Method method = entity.getClass().getMethod(methodName.toString(), new Class[]{}); methodName.delete(0, methodName.length()); // Getting value of the field by the method. Object fieldValue = method.invoke(entity, new Object[]{}); if (fieldValue != null) { UpdateIdentifier idFieldValue = (UpdateIdentifier) fieldValue; // 只有当该属性需要更新, 或者该属性构成主键时, 才有必要进行分析 if (idFieldValue.needUpdate() || pkSet.contains(fieldName)) { // check the field wether or not having column // reflecting if (!columnSet.contains(MappingUtil.getColumnName(fieldName))) { // 该属性在相关表中没有找到对应列, 则略过 // throw new IllegalEntityException( // IllegalEntityException.NOTFOUND_REFLECT_COLUMN, // fieldName); continue; } // save the field to map temporarily String fieldType = fields[i].getType().getName(); FieldExp fieldExp = new FieldExp(fieldName, fieldType, fieldValue); fieldExpMap.put(fieldName, fieldExp); // save the getting method to map temporarily fieldMethodMap.put(fieldName, method); // 对于非置空的大字段属性, 还要将其属性名存入bigFieldNameSet, 以方便后面对大字段进行处理 if (fieldValue instanceof EBinFile || fieldValue instanceof ETxtFile) { if (!idFieldValue.isEmpty()) { bigFieldNameSet.add(fieldName); } } }// end if (idFieldValue.needUpdate( }// end if (fieldValue != null } } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(dbType, e.getErrorCode(), e.getMessage()); } catch (NoSuchMethodException e) { log.error("", e); throw new IllegalEntityException(e.getMessage()); } catch (IllegalArgumentException 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()); } } /** * 只有批量操作才会调用到此方法 * * @param entityParser 首次解析器 * @param entity 待取值的entity * @throws IllegalEntityException * @throws com.github.walker.easydb.exception.IllegalParamException */ public EntityParser(EntityParser entityParser, BaseEntity entity) throws IllegalEntityException, IllegalParamException { // 引用给定的entityParser中的属性 this.conn = entityParser.conn; this.className = entityParser.className; this.pkSet = entityParser.pkSet; // 由于批量操作不会涉及大字段列, 因此不用对这个属性赋值 // this.bigFieldNameSet = new HashSet(2); this.fieldExpMap = entityParser.fieldExpMap; this.fieldMethodMap = entityParser.fieldMethodMap; // 重置fieldExpMap中的field值, 并且记录非置空的大字段属性 this.retrieveFieldValue(entity); } // 设置fieldExpMap中的field值, 并且记录非置空的大字段属性 private void retrieveFieldValue(BaseEntity entity) throws IllegalEntityException, IllegalParamException { try { for (Iterator<String> it = this.fieldExpMap.keySet().iterator(); it.hasNext(); ) { String fieldName = (String) it.next(); FieldExp fieldExp = (FieldExp) fieldExpMap.get(fieldName); Method method = (Method) fieldMethodMap.get(fieldName); Object fieldValue = method.invoke(entity, new Object[]{}); // 如果entity中该属性的值为空, 就表明此entity中值的存放位置与首次解析器解析的entity不一致 // 即某列值在数组中要么都为NULL, 要么都不为NULL. if (fieldValue == null) { throw new IllegalParamException(IllegalParamException.ENTITY_ARRAY_NOT_IDENTICAL, fieldName); } UpdateIdentifier idFieldValue = (UpdateIdentifier) fieldValue; // 数组中的某列要么都需要更新,要么都不需要更新!. if (!idFieldValue.needUpdate()) { throw new IllegalParamException(IllegalParamException.ENTITY_ARRAY_NOT_IDENTICAL, fieldName); } // 批量操作时,不允许对大字段列进行批量写入!但可以批量置空 if ((fieldValue instanceof EBinFile || fieldValue instanceof ETxtFile) && !((UpdateIdentifier) fieldValue).isEmpty()) { throw new IllegalParamException(IllegalParamException.BIGCOLUMN_CANNOT_BATCH, fieldName); } // set the value to fieldExp fieldExp.setFieldValue(fieldValue); } } catch (IllegalArgumentException 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()); } } // Gets the column names of the table which reflected this entity class private HashSet<String> getColumnNames() throws SQLException { // SQL Constructor SqlConstructor sqlConstructor = SqlConstructor.getInstance(dbType); PreparedStatement stmt = null; ResultSet rs = null; try { String sql = sqlConstructor.buildGettingMetaSql(MappingUtil.getTableName(this.className)); log.info("SQL: " + sql); stmt = this.conn.prepareStatement(sql); rs = stmt.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); HashSet<String> set = new HashSet<String>(); for (int i = 1; i <= colCount; i++) { set.add(rsmd.getColumnName(i).toUpperCase()); } return set; } catch (SQLException e) { log.error("", e); throw e; } finally { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } } } public String getClassName() { return className; } public HashMap<String, FieldExp> getFieldExpMap() { return fieldExpMap; } public HashMap<String, Method> getFieldMethodMap() { return fieldMethodMap; } public HashSet<String> getBigFieldNameSet() { return bigFieldNameSet; } public HashSet<String> getPKSet() { return pkSet; } public void myFinalize() { try { if (pkSet != null) { pkSet.clear(); } if (bigFieldNameSet != null) { bigFieldNameSet.clear(); } if (fieldExpMap != null) { fieldExpMap.clear(); } if (fieldMethodMap != null) { fieldMethodMap.clear(); } pkSet = null; bigFieldNameSet = null; fieldExpMap = null; fieldMethodMap = null; super.finalize(); } catch (Throwable e) { log.error("", e); } } }