/** * * Copyright 2014 The Darks ORM Project (Liu lihua) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package darks.orm.core.factory; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.NClob; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import net.sf.cglib.reflect.FastMethod; import darks.orm.annotation.Entity; import darks.orm.app.SqlSession; import darks.orm.core.data.EntityData; import darks.orm.core.data.FieldData; import darks.orm.core.data.FieldData.FieldFlag; import darks.orm.core.session.SessionContext; import darks.orm.exceptions.TransformException; import darks.orm.util.DataTypeHelper; import darks.orm.util.ReflectHelper; /** * * * <p> * <h1>TransformFactory.java</h1> * <p> * @author Liu LiHua * @version 1.0.0 v05/03/2012 * @since JDK1.5 */ public class TransformFactory { public static final String SQL_COUNT_ALIAS = "SQL_COUNT_ALIAS"; public static final int SQL_TRANSMAP_INIT_SIZE = 100; public static final int SQL_JDBCRET_INIT_SIZE = 50; public static final int SQL_STRCLASS_INIT_SIZE = 50; public static final int SQL_COLUMN_INIT_SIZE = 128; private static final String ENTITY_CASCADE_DIV = "_"; private transient static final ConcurrentMap<String, String> sqlTranMap = new ConcurrentHashMap<String, String>( SQL_TRANSMAP_INIT_SIZE); private transient static final ConcurrentMap<Class<?>, String> jdbcResultMethodMap = new ConcurrentHashMap<Class<?>, String>(SQL_JDBCRET_INIT_SIZE); private transient static final ConcurrentMap<String, Class<?>> stringClassMap = new ConcurrentHashMap<String, Class<?>>(SQL_STRCLASS_INIT_SIZE); private transient static final ConcurrentMap<String, Map<String, Integer>> colsMap = new ConcurrentHashMap<String, Map<String, Integer>>(SQL_COLUMN_INIT_SIZE); private static volatile TransformFactory instance = null; private static final Lock lock = new ReentrantLock(); private TransformFactory() { registerJdbcResultMethodMap(); registerStringClassMap(); } /** * ע��ResultSet�����ͻ�ȡӳ�� */ public void registerJdbcResultMethodMap() { registerJdbcResultMethod(Integer.class, "getInt"); registerJdbcResultMethod(int.class, "getInt"); registerJdbcResultMethod(String.class, "getString"); registerJdbcResultMethod(Short.class, "getShort"); registerJdbcResultMethod(short.class, "getShort"); registerJdbcResultMethod(Long.class, "getLong"); registerJdbcResultMethod(long.class, "getLong"); registerJdbcResultMethod(Double.class, "getDouble"); registerJdbcResultMethod(double.class, "getDouble"); registerJdbcResultMethod(Date.class, "getDate"); registerJdbcResultMethod(Time.class, "getTime"); registerJdbcResultMethod(Timestamp.class, "getTimestamp"); registerJdbcResultMethod(Blob.class, "getBlob"); registerJdbcResultMethod(Clob.class, "getClob"); registerJdbcResultMethod(NClob.class, "getNClob"); registerJdbcResultMethod(Array.class, "getArray"); registerJdbcResultMethod(BigDecimal.class, "getBigDecimal"); registerJdbcResultMethod(Boolean.class, "getBoolean"); registerJdbcResultMethod(boolean.class, "getBoolean"); registerJdbcResultMethod(Byte.class, "getByte"); registerJdbcResultMethod(byte.class, "getByte"); registerJdbcResultMethod(byte[].class, "getBytes"); registerJdbcResultMethod(float.class, "getFloat"); registerJdbcResultMethod(Float.class, "getFloat"); registerJdbcResultMethod(URL.class, "getURL"); registerJdbcResultMethod(SQLXML.class, "getSQLXML"); registerJdbcResultMethod(Ref.class, "getRef"); registerJdbcResultMethod(RowId.class, "getRowId"); } public void registerStringClassMap() { registerStringClass(Integer.class, "Integer"); registerStringClass(int.class, "int"); registerStringClass(String.class, "String"); registerStringClass(Short.class, "Short"); registerStringClass(short.class, "short"); registerStringClass(Long.class, "Long"); registerStringClass(long.class, "long"); registerStringClass(Double.class, "Double"); registerStringClass(double.class, "double"); registerStringClass(Boolean.class, "Boolean"); registerStringClass(boolean.class, "boolean"); registerStringClass(Byte.class, "Byte"); registerStringClass(byte.class, "byte"); registerStringClass(byte[].class, "bytes"); registerStringClass(float.class, "float"); registerStringClass(Float.class, "Float"); } /** * ͨ���ַ������ʵ���������� * * @param strClass ���ַ��� * @return �� * @throws ClassNotFoundException * @throws Exception */ public Class<?> stringToEntityClass(String strClass) throws ClassNotFoundException { Class<?> ret = stringClassMap.get(strClass); if (ret == null) { ret = SessionContext.getConfigure().getEntityConfig().getEntity(strClass); if (ret != null) return ret; return SessionContext.getConfigure().getEntityConfig().addEntityConfig(null, strClass); } return ret; } /** * ͨ�����ͻ�ȡ��ȡ�������� * * @param key ��/��ֵ * @return */ public String getJdbcResultMethod(Class<?> key) { String val = jdbcResultMethodMap.get(key); if (val != null) return val; return "getObject"; } /** * ע�᷽������ӳ�� * * @param key ��/��ֵ * @param value �������� */ private void registerJdbcResultMethod(Class<?> key, String value) { jdbcResultMethodMap.put(key, value); } /** * ע������/��ӳ�� * * @param cls �� * @param className ������ */ private void registerStringClass(Class<?> cls, String className) { stringClassMap.put(className, cls); } /** * ��̨ģʽ * * @return TransformFactoryʵ�� */ public static TransformFactory getInstance() { if (instance == null) { lock.lock(); try { if (instance == null) instance = new TransformFactory(); } finally { lock.unlock(); } } return instance; } /** * �������ת����ʵ�岢���浽�б����� * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @return ����ʵ���༯�� * @throws Exception */ public <T> List<T> ResultToList(Class<T> c, String sql, ResultSet rs) throws Exception { List<T> list = new ArrayList<T>(); while (rs.next()) { T n = ResultToBean(c, sql, rs, false); list.add(n); } return list; } /** * �������ת����ʵ���࣬�����з�ҳ������ҳ��Ľ������ * * @param <T> ʵ���෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @param page ��ǰҳ�� * @param pageSize ��ҳ��С * @return ʵ�����б� * @throws Exception */ public <T> List<T> ResultToPageScroll(Class<T> c, String sql, ResultSet rs, int page, int pageSize) throws Exception { List<T> list = new ArrayList<T>(); int cur = (page - 1) * pageSize + 1; rs.absolute(cur); for (int i = 0; i < pageSize; i++) { T n = ResultToBean(c, sql, rs, false); list.add(n); if (!rs.next()) break; } return list; } /** * �������ת����ʵ���࣬�����з�ҳ������ҳ��Ľ������ * * @param <T> ʵ���෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @param page ��ǰҳ�� * @param pageSize ��ҳ��С * @return ʵ�����б� * @throws Exception */ public <T> List<T> ResultToPageForward(Class<T> c, String sql, ResultSet rs, int page, int pageSize) throws Exception { List<T> list = new ArrayList<T>(); if (!rs.next()) return list; int cur = (page - 1) * pageSize; for (int i = 0; i < cur; i++) { if (!rs.next()) return list; } for (int i = 0; i < pageSize; i++) { T n = ResultToBean(c, sql, rs, false); list.add(n); if (!rs.next()) break; } return list; } /** * �����ת����ʵ�壬������¼ * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @return ��ʵ�� * @throws Exception */ public <T> T ResultToBean(Class<T> c, String sql, ResultSet rs, boolean cursorNext) throws Exception { if (DataTypeHelper.checkClassIsBasicDataType(c)) { return ResultSetToBasicDataType(c, rs, cursorNext); } return ResultToEntityDataType(c, sql, rs, cursorNext, true, null); } /** * �����ת����ʵ�壬������¼ * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @param recursion �Ƿ�ݹ� * @return ��ʵ�� * @throws Exception */ @SuppressWarnings("unchecked") public <T> T ResultToEntityDataType(Class<T> c, String sql, ResultSet rs, boolean cursorNext, boolean recursion, String alias) throws Exception { sql = sql.toUpperCase(); boolean flag = c.isAnnotationPresent(Entity.class); if (!flag) return null; Entity classType = (Entity)c.getAnnotation(Entity.class); if (classType == null) return null; if (cursorNext) { if (!rs.next()) return null; } if (!rs.isAfterLast() && !rs.isBeforeFirst()) { EntityData entityData = ClassFactory.parseClass(c); if (entityData == null) return null; c = (Class<T>)entityData.getClassProxy(); Object entity = entityData.newInstance(); if (entity == null) return null; // ��������������� Map<String, Integer> colsIndexMap = cacheSqlColumnIndex(rs, sql); // ����ʵ������������ for (Entry<String, FieldData> entry : entityData.getMapFields().entrySet()) { String key = entry.getKey(); if (alias != null) { key = alias + "_" + key; } FieldData fdata = entry.getValue(); if (!fdata.isQueryable() || fdata.getFieldFlag() == FieldFlag.Collection) continue; if (!colsIndexMap.containsKey(key)) { continue; } // �������ʵ�崦�� if (fdata.getFieldFlag() == FieldFlag.FkEntity) { // ��ȡ���ʵ������ EntityData fkData = fdata.getFkData(); if (fkData == null) continue; FieldData fpkdata = fkData.getPkField(); if (fpkdata == null) continue; String tname = fkData.getTableName(); tname = tname.toUpperCase(); if (sql.indexOf(" " + tname + " ") >= 0 && !tname.equals(entityData.getTableName()) && recursion) { // �жϸ�SQL����Ƿ��ѯ�˸ñ� Class<?> fc = fdata.getFkClass(); // �ݹ�ת�����ʵ����� Object obj = this.ResultToEntityDataType(fc, sql, rs, false, false, alias); fdata.setValue(entity, obj); } else { // �����ֵ���������ֶ� Object obj = ReflectHelper.getResultSetValue(rs, fdata.getFieldClass(), key); if (obj == null && DataTypeHelper.checkClassIsNumberBasicDataType(fpkdata.getFieldClass())) { obj = 0; } String fname = fdata.getFkSetMethod(); FastMethod fm = ReflectHelper.parseFastMethod(c, fname, fpkdata.getFieldClass()); fm.invoke(entity, new Object[] {obj}); } continue; } // ���������ֶ� Object obj = ReflectHelper.getResultSetValue(rs, fdata.getFieldClass(), key); if (obj == null && DataTypeHelper.checkClassIsNumberBasicDataType(fdata.getFieldClass())) { obj = 0; } fdata.setValue(entity, obj); } return (T)entity; } return null; } /** * �����ת���������������� * * @param c ���������� * @param rs ����� * @param corsorNext �Ƿ����ƹ�� * @return */ @SuppressWarnings("unchecked") public <T> T ResultSetToBasicDataType(Class<T> c, ResultSet rs, boolean cursorNext) throws TransformException { if (cursorNext) { try { if (!rs.next()) return null; } catch (SQLException e) { throw new TransformException("TransformFactory::ResultSetToBasicDataType " + e.toString(), e); } } return (T)ReflectHelper.getResultSetValue(rs, c, 1); } /** * ������ѯת��SQL��� * * @param sql ԭSQL��� * @param c ������ * @param map ������ * @param pkey ��ֵ * @return * @throws Exception */ public <T> String transformSQLToCascade(String sql, Class<T> c, Map<String, String> map, StringBuffer pkey) throws Exception { sql = sql.toUpperCase(); pkey.append("[SQL]"); pkey.append(sql); pkey.append("[C]"); pkey.append(c.getName()); String key = pkey.toString(); if (sqlTranMap.containsKey(key)) { return sqlTranMap.get(key); } if (sql.indexOf("SELECT") >= 0) { int from = sql.indexOf("FROM"); String fieldSql = sql.substring(0, from); if (fieldSql.indexOf("*") >= 0) { sql = sql.substring(from); // ���ʵ�������� EntityData entityData = ClassFactory.parseClass(c); ConcurrentMap<String, FieldData> mapNameFields = entityData.getMapNameFields(); StringBuffer buf = new StringBuffer(256); buf.append("SELECT "); // ��ȡ���ʵ�����ֶ��ַ��� if (!map.containsKey(SqlSession.SELF)) { buf.append(entityData.getFieldString()); buf.append(","); } else { String alias = map.get(SqlSession.SELF); buf.append(entityData.getAliasString(alias)); buf.append(","); } // ��ȡ����ʵ�����ֶ��ַ��� FieldData fieldData = null; for (Entry<String, String> entry : map.entrySet()) { String entityName = entry.getKey(); String alias = entry.getValue(); if (SqlSession.SELF.equals(entityName)) continue; if (entityName.indexOf(ENTITY_CASCADE_DIV) > 0) { String[] attrs = entityName.split(ENTITY_CASCADE_DIV); EntityData data = entityData; for (String attr : attrs) { ConcurrentMap<String, FieldData> dataFields = data.getMapNameFields(); fieldData = dataFields.get(attr); data = fieldData.getFkData(); } } else { fieldData = mapNameFields.get(entityName); } if (fieldData != null) { if (fieldData.getFieldFlag() == FieldFlag.FkEntity && fieldData.getFkData() != null) { EntityData fkData = fieldData.getFkData(); buf.append(fkData.getAliasString(alias, entityName)); buf.append(","); } else { throw new Exception("the '" + entityName + "' of Entity is not a foreign key field"); } } else { throw new Exception("Entity '" + entityData.getClassName() + "' does not include '" + entityName + "' field"); } } if (buf.length() > 0) buf.deleteCharAt(buf.length() - 1); buf.append(" "); buf.append(sql); sql = buf.toString(); sqlTranMap.put(key, sql); return sql; } else { return sql; } } else { return null; } } /** * ת��������ѯSQL * * @param sql ԭSQL * @return ת����SQL */ public String transformSqlToCount(String sql) throws Exception { sql = sql.toUpperCase(); int selectIndex = sql.indexOf("SELECT"); int fromIndex = sql.indexOf("FROM"); if (selectIndex < 0 || fromIndex < 0) { throw new Exception("sql is error"); } String tmp = sql.substring(fromIndex); StringBuffer buf = new StringBuffer(tmp.length() + 17); buf.append("SELECT COUNT(*) "); buf.append(tmp); // buf.append(") "); // buf.append(SQL_COUNT_ALIAS); return buf.toString(); } /** * �����������ת����ʵ�岢���浽�б����� * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @return ����ʵ���༯�� * @throws Exception */ public <T> List<T> cascadeResultToList(Class<T> c, String sql, ResultSet rs, Map<String, String> map) throws Exception { if (rs == null) return null; sql = sql.toUpperCase(); boolean flag = c.isAnnotationPresent(Entity.class); if (!flag) return null; List<T> list = new ArrayList<T>(); Entity classType = (Entity)c.getAnnotation(Entity.class); if (classType == null) return null; ClassFactory.parseClass(c); // c=ClassFactory.getClass(c.getName()); while (rs.next()) { T n = cascadeResultToBean(c, sql, rs, map, false); list.add(n); } return list; } /** * �����������ת����ʵ���࣬�����з�ҳ������ҳ��Ľ������ * * @param <T> ʵ���෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @param page ��ǰҳ�� * @param pageSize ��ҳ��С * @return ʵ�����б� * @throws Exception */ public <T> List<T> cascadeResultToPage(Class<T> c, String sql, ResultSet rs, int page, int pageSize, Map<String, String> map) throws Exception { sql = sql.toUpperCase(); boolean flag = c.isAnnotationPresent(Entity.class); if (!flag) return null; List<T> list = new ArrayList<T>(); Entity classType = (Entity)c.getAnnotation(Entity.class); if (classType == null) return null; ClassFactory.parseClass(c); int cur = (page - 1) * pageSize + 1; rs.absolute(cur); for (int i = 0; i < pageSize; i++) { T n = cascadeResultToBean(c, sql, rs, map, false); list.add(n); if (!rs.next()) break; } return list; } /** * ���������ת����ʵ�壬������¼ * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @return ��ʵ�� * @throws Exception */ public <T> T cascadeResultToBean(Class<T> c, String sql, ResultSet rs, Map<String, String> map, boolean corsorNext) throws Exception { return this.cascadeResultToBean(c, sql, rs, map, corsorNext, null); } /** * ���������ת����ʵ�壬������¼ * * @param <T> �෶�� * @param c ʵ���� * @param sql SQL��ѯ��� * @param rs ����� * @return ��ʵ�� * @throws Exception */ @SuppressWarnings("unchecked") public <T> T cascadeResultToBean(Class<T> c, String sql, ResultSet rs, Map<String, String> map, boolean corsorNext, String alias) throws Exception { sql = sql.toUpperCase(); boolean flag = c.isAnnotationPresent(Entity.class); if (!flag) return null; Entity classType = (Entity)c.getAnnotation(Entity.class); if (classType == null) return null; EntityData entityData = ClassFactory.parseClass(c); if (corsorNext) { if (!rs.next()) return null; } if (!rs.isAfterLast() && !rs.isBeforeFirst()) { c = (Class<T>)ClassFactory.getClass(c.getName()); Object entity = entityData.newInstance(); // ------- Map<String, Integer> colsIndexMap = cacheSqlColumnIndex(rs, sql); // -------- for (Entry<String, FieldData> entry : entityData.getMapFields().entrySet()) { String key = entry.getKey(); if (alias != null) { key = alias.toUpperCase() + "_" + key; } FieldData fdata = entry.getValue(); if (!fdata.isQueryable() || fdata.getFieldFlag() == FieldFlag.Collection) { continue; } if (!fdata.isQueryable()) continue; if (sql.indexOf(key.toUpperCase()) < 0 && sql.indexOf('*') < 0) { continue; } if (!colsIndexMap.containsKey(key)) { continue; } if (fdata.getFieldFlag() == FieldFlag.FkEntity) { EntityData fkData = fdata.getFkData(); if (fkData == null) continue; FieldData fpkdata = fkData.getPkField(); if (fpkdata == null) continue; Object obj = ReflectHelper.getResultSetValue(rs, fdata.getFieldClass(), key); if (obj == null) { continue; } String fname = fdata.getFkSetMethod(); FastMethod fm = ReflectHelper.parseFastMethod(c, fname, fpkdata.getFieldClass()); fm.invoke(entity, new Object[] {obj}); // ------------------- String check = fdata.getFieldName(alias); if (map.containsKey(check)) { Class<?> fc = fdata.getFkClass(); obj = cascadeResultToBean(fc, sql, rs, map, false, fdata.getFieldName(alias)); fdata.setValue(entity, obj); } continue; } Object obj = ReflectHelper.getResultSetValue(rs, fdata.getFieldClass(), key); fdata.setValue(entity, obj); } return (T)entity; } return null; } /** * �������������� * * @param rs ����� * @param sql SQL��� ��ֵ * @return * @throws SQLException */ public Map<String, Integer> cacheSqlColumnIndex(ResultSet rs, String sql) throws SQLException { Map<String, Integer> colsIndexMap = colsMap.get(sql); if (colsIndexMap == null) { ResultSetMetaData rsmd = rs.getMetaData(); int colsCount = rsmd.getColumnCount(); colsIndexMap = new HashMap<String, Integer>(colsCount); for (int i = 1; i <= rsmd.getColumnCount(); i++) { String key = rsmd.getColumnLabel(i).toUpperCase(); colsIndexMap.put(key, i); } colsMap.put(sql, colsIndexMap); } return colsIndexMap; } }