package message.jdbc.mapper; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import message.base.convert.ConvertGetter; import message.jdbc.base.DynamicBeanUtils; import message.datasource.helper.SqlHelper; import message.jdbc.type.PersistentField; import message.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.NotWritablePropertyException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.JdbcUtils; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.HashMap; import java.util.Map; /** * 动态创建rowMapper * * @author sunhao(sunhao.java@gmail.com) * @version V1.0, 2012-4-10 上午12:37:12 */ public class DynamicBeanRowMapper extends ColumnMapRowMapper { private static final Logger logger = LoggerFactory.getLogger(DynamicBeanRowMapper.class); private Constructor constructor; private Map<String, PersistentField> mappedFields; private String sql; private String mapperKey; private final static Map mappers = new HashMap(); public DynamicBeanRowMapper() { } public DynamicBeanRowMapper(Class clazz) { super.setClazz(clazz); } public static RowMapper getInstance(Class clazz, SqlHelper sqlHelper, String sql) { String key = DynamicBeanUtils.createMapperKey(clazz, sql); DynamicBeanRowMapper mapper = new DynamicBeanRowMapper(clazz); mapper.setClazz(clazz); mapper.setSqlHelper(sqlHelper); mapper.setSql(sql); mapper.setMapperKey(key); mapper.initialize(); return mapper; } protected void initialize() { try { this.constructor = super.getClazz().getConstructor((Class[]) null); } catch (Exception e) { throw new DataAccessResourceFailureException("there is no default constructor in class " + super.getClazz().getName()); } this.mappedFields = new HashMap<String, PersistentField>(); Class metaClass = super.getClazz(); if (metaClass != null) { PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(metaClass); for (int i = 0; i < pds.length; i++) { PropertyDescriptor pd = pds[i]; //获得应该用于写入属性值的方法(setter) Method writeMethod = pd.getWriteMethod(); //获得应该用于读取属性值的方法(getter) Method readMethod = pd.getReadMethod(); if (writeMethod == null || readMethod == null) //如果都为空,跳出本次循环 continue; //字段名 String fieldName = pd.getName(); String columnName = findColumnNameByFieldName(fieldName); PersistentField field = new PersistentField(); field.setFieldName(fieldName); field.setJavaType(readMethod.getReturnType()); field.setWriteName(writeMethod.getName()); field.setColumnName(columnName); this.mappedFields.put(fieldName.toLowerCase(), field); this.mappedFields.put(columnName, field); } } } public Object mapRow(ResultSet rs, int rowNum) throws SQLException { DynamicRowMapper dynamicRowMapper = (DynamicRowMapper) mappers.get(this.mapperKey); if (dynamicRowMapper != null) return dynamicRowMapper.mapRow(rs, rowNum); StringBuffer script = new StringBuffer(); if (this.getClazz() == null) throw new InvalidDataAccessResourceUsageException("not found!"); Object result = null; try { result = this.constructor.newInstance((Object[]) null); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String column = JdbcUtils.lookupColumnName(metaData, i).toLowerCase(); PersistentField field = this.mappedFields.get(column); if (field == null) continue; BeanWrapperImpl wrapper = new BeanWrapperImpl(getClazz()); wrapper.setWrappedInstance(result); int type = metaData.getColumnType(i); field.setSqlType(type); Object value; Class fieldType = field.getJavaType(); if (fieldType.equals(String.class)) { if (type == Types.LONGVARCHAR) { this.addFieldContent(script, field, "sqlHelper.getLongAsString($1,", i, ")"); value = this.getLongAsString(rs, i); } else if (type == Types.CLOB) { this.addFieldContent(script, field, "sqlHelper.getClobAsString($1,", i, ")"); value = this.getClobStringValue(rs, i); } else { this.addFieldContent(script, field, "$1.getString(", i, ")"); value = rs.getString(i); } } else if (fieldType.equals(byte.class)) { addFieldContent(script, field, "$1.getByte(", i, ")"); value = new Byte(rs.getByte(i)); } else if (fieldType.equals(Byte.class)) { addFieldContent(script, field, "new Byte($1.getByte(", i, "))"); value = new Byte(rs.getByte(i)); } else if (fieldType.equals(short.class)) { addFieldContent(script, field, "$1.getShort(", i, ")"); value = new Short(rs.getShort(i)); } else if (fieldType.equals(Short.class)) { addFieldContent(script, field, "new Short($1.getShort(", i, "))"); value = new Short(rs.getShort(i)); } else if (fieldType.equals(int.class)) { addFieldContent(script, field, "$1.getInt(", i, ")"); value = new Integer(rs.getInt(i)); } else if (fieldType.equals(Integer.class)) { addFieldContent(script, field, "new Integer($1.getInt(", i, "))"); value = new Integer(rs.getInt(i)); } else if (fieldType.equals(long.class)) { addFieldContent(script, field, "$1.getLong(", i, ")"); value = new Long(rs.getLong(i)); } else if (fieldType.equals(Long.class)) { addFieldContent(script, field, "new Long($1.getLong(", i, "))"); value = new Long(rs.getLong(i)); } else if (fieldType.equals(float.class)) { addFieldContent(script, field, "$1.getFloat(", i, ")"); value = new Float(rs.getFloat(i)); } else if (fieldType.equals(Float.class)) { addFieldContent(script, field, "new Float($1.getFloat(", i, "))"); value = new Float(rs.getFloat(i)); } else if (fieldType.equals(double.class)) { addFieldContent(script, field, "$1.getDouble(", i, ")"); value = new Double(rs.getDouble(i)); } else if (fieldType.equals(Double.class)) { addFieldContent(script, field, "new Double($1.getDouble(", i, "))"); value = new Double(rs.getDouble(i)); } else if (fieldType.equals(BigDecimal.class)) { addFieldContent(script, field, "$1.getBigDecimal(", i, ")"); value = rs.getBigDecimal(i); } else if (fieldType.equals(boolean.class)) { addFieldContent(script, field, "$1.getBoolean(", i, ")"); value = (rs.getBoolean(i)) ? Boolean.TRUE : Boolean.FALSE; } else if (fieldType.equals(Boolean.class)) { addFieldContent(script, field, "new Boolean($1.getBoolean(", i, "))"); value = (rs.getBoolean(i)) ? Boolean.TRUE : Boolean.FALSE; } else if (containInConvert(fieldType)) { String val = rs.getString(i); script.append("bean."); script.append(field.getWriteName()); script.append("("); String methodName = ((val == null) ? "getPoJoNullValue" : "getPoJoValue"); script.append("(").append(fieldType.getName()).append(")sqlHelper.getConvert(") .append(fieldType.getName()).append(".class).") .append(methodName).append("($1.getString("); script.append(i); script.append(")));\n"); if (StringUtils.isEmpty(val)) { value = this.getSqlHelper().getConvert(fieldType).getPoJoNullValue(val); } else { value = this.getSqlHelper().getConvert(fieldType).getPoJoValue(val); } } else { addFieldContent(script, field, "(" + field.getJavaType().getName() + ")org.springframework.jdbc.support.JdbcUtils.getResultSetValue($1,", i, ")"); value = JdbcUtils.getResultSetValue(rs, i); } if (value != null) { if (wrapper.isWritableProperty(field.getFieldName())) { try { wrapper.setPropertyValue(field.getFieldName(), value); } catch (NotWritablePropertyException ex) { throw new DataRetrievalFailureException("Unable to map column " + column + " to property " + field.getFieldName(), ex); } } else { if (rowNum == 0) { logger.warn("Unable to access the setter for " + field.getFieldName() + ". Check that " + "set" + StringUtils.capitalize(field.getFieldName()) + " is declared and has public access."); } } } } this.createDynamicMapper(script.toString()); return result; } private void createDynamicMapper(String string) { StringBuffer script = new StringBuffer(); script.append(super.getClazz().getName()); script.append(" bean = new "); script.append(this.getClazz().getName()); script.append("();\n"); script.append(string); String scriptContent = script.toString(); try { ClassPool cp = DynamicBeanUtils.classPool; //创建一个类 CtClass ctClass = cp.makeClass("message.jdbc.dynamic.DynamicBeanRowMapperImpl$" + System.currentTimeMillis()); //创建一个接口(DynamicRowMapper) CtClass ctInterface = cp.makeInterface("message.jdbc.mapper.DynamicRowMapper"); //上面创建的类实现上面的接口 ctClass.addInterface(ctInterface); //创建一个字段(SqlHelper) CtField sqlHelperField = CtField.make("private message.datasource.helper.SqlHelper sqlHelper;", ctClass); //加入这个字段 ctClass.addField(sqlHelperField); //创建一个setter方法 CtMethod setterMethod = CtMethod.make("" + "public void setSqlHelper(message.datasource.helper.SqlHelper sh){" + " this.sqlHelper = sh;" + "}", ctClass); //加入这个方法 ctClass.addMethod(setterMethod); //创建一个getter方法 CtMethod getterMethod = CtMethod.make("" + "public message.datasource.helper.SqlHelper getSqlHelper(){" + " return this.sqlHelper;" + "}", ctClass); //加入这个方法 ctClass.addMethod(getterMethod); //创建实现RowMapper接口的mapRow方法 CtMethod mapRowMethod = CtMethod.make("" + "public Object mapRow(java.sql.ResultSet rs, int i) throws java.sql.SQLException{" + " return null;" + "}", ctClass); //方法体 StringBuffer body = new StringBuffer(); body.append("{\n"); body.append(scriptContent); body.append("return bean;"); body.append("}\n"); mapRowMethod.setBody(body.toString()); //加入这个方法 ctClass.addMethod(mapRowMethod); DynamicRowMapper obj = (DynamicRowMapper) ctClass.toClass().newInstance(); obj.setSqlHelper(this.getSqlHelper()); this.mappers.put(this.mapperKey, obj); } catch (Exception e) { logger.error(e.getMessage(), e); } } private void addFieldContent(StringBuffer script, PersistentField fieldMeta, String methodName, int i, String endStr) { script.append("bean."); script.append(fieldMeta.getWriteName()); script.append("("); script.append(methodName); script.append(i); script.append(endStr); script.append(");\n"); } private boolean containInConvert(Class fieldType) { return ConvertGetter.class.isAssignableFrom(fieldType) && this.getSqlHelper().getConvert(fieldType) != null; } public Constructor getConstructor() { return constructor; } public void setConstructor(Constructor constructor) { this.constructor = constructor; } public Map getMappedFields() { return mappedFields; } public void setMappedFields(Map mappedFields) { this.mappedFields = mappedFields; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getMapperKey() { return mapperKey; } public void setMapperKey(String mapperKey) { this.mapperKey = mapperKey; } public Map getMappers() { return mappers; } }