package org.hsweb.web.mybatis.builder; import org.apache.commons.beanutils.BeanUtilsBean; import org.apache.commons.beanutils.PropertyUtilsBean; import org.apache.ibatis.mapping.ResultMap; import org.apache.ibatis.mapping.ResultMapping; import org.hsweb.commons.StringUtils; import org.hsweb.ezorm.core.param.QueryParam; import org.hsweb.ezorm.core.param.Term; import org.hsweb.ezorm.rdb.meta.RDBColumnMetaData; import org.hsweb.ezorm.rdb.meta.RDBDatabaseMetaData; import org.hsweb.ezorm.rdb.meta.RDBTableMetaData; import org.hsweb.ezorm.rdb.render.SqlAppender; import org.hsweb.ezorm.rdb.render.SqlRender; import org.hsweb.ezorm.rdb.render.dialect.Dialect; import org.hsweb.ezorm.rdb.render.dialect.H2RDBDatabaseMetaData; import org.hsweb.ezorm.rdb.render.dialect.MysqlRDBDatabaseMetaData; import org.hsweb.ezorm.rdb.render.dialect.OracleRDBDatabaseMetaData; import org.hsweb.ezorm.rdb.render.support.simple.CommonSqlRender; import org.hsweb.ezorm.rdb.render.support.simple.SimpleWhereSqlBuilder; import org.hsweb.web.bean.common.InsertParam; import org.hsweb.web.bean.common.UpdateParam; import org.hsweb.web.core.datasource.DataSourceHolder; import org.hsweb.web.core.datasource.DatabaseType; import org.hsweb.web.core.exception.BusinessException; import org.hsweb.web.mybatis.plgins.pager.Pager; import org.hsweb.web.mybatis.utils.ResultMapsUtils; import java.sql.JDBCType; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author zhouhao * @TODO */ public class EasyOrmSqlBuilder { private static final EasyOrmSqlBuilder instance = new EasyOrmSqlBuilder(); protected static final Map<Class, String> simpleName = new HashMap<>(); protected PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils(); public static EasyOrmSqlBuilder getInstance() { return instance; } private EasyOrmSqlBuilder() { } static { simpleName.put(Integer.class, "int"); simpleName.put(Byte.class, "byte"); simpleName.put(Double.class, "double"); simpleName.put(Float.class, "float"); simpleName.put(Boolean.class, "boolean"); simpleName.put(Long.class, "long"); simpleName.put(Short.class, "short"); simpleName.put(Character.class, "char"); simpleName.put(String.class, "string"); simpleName.put(int.class, "int"); simpleName.put(double.class, "double"); simpleName.put(float.class, "float"); simpleName.put(boolean.class, "boolean"); simpleName.put(long.class, "long"); simpleName.put(short.class, "short"); simpleName.put(char.class, "char"); simpleName.put(byte.class, "byte"); } public static String getJavaType(Class type) { String javaType = simpleName.get(type); if (javaType == null) javaType = type.getName(); return javaType; } private final RDBDatabaseMetaData mysql = new MysqlMeta(); private final RDBDatabaseMetaData oracle = new OracleMeta(); private final RDBDatabaseMetaData h2 = new H2Meta(); private final ConcurrentMap<RDBDatabaseMetaData, Map<String, RDBTableMetaData>> metaCache = new ConcurrentHashMap<RDBDatabaseMetaData, Map<String, RDBTableMetaData>>() { @Override public Map<String, RDBTableMetaData> get(Object key) { Map<String, RDBTableMetaData> map = super.get(key); if (map == null) { map = new HashMap<>(); put((RDBDatabaseMetaData) key, map); } return map; } }; public RDBDatabaseMetaData getActiveDatabase() { DatabaseType type = DataSourceHolder.getActiveDatabaseType(); switch (type) { case h2: return h2; case mysql: return mysql; case oracle: return oracle; default: return h2; } } protected RDBTableMetaData createMeta(String tableName, String resultMapId) { RDBDatabaseMetaData active = getActiveDatabase(); String cacheKey = tableName.concat("-").concat(resultMapId); Map<String, RDBTableMetaData> cache = metaCache.get(active); RDBTableMetaData cached = cache.get(cacheKey); if (cached != null) { return cached; } RDBTableMetaData rdbTableMetaData = new RDBTableMetaData(); rdbTableMetaData.setName(tableName); rdbTableMetaData.setDatabaseMetaData(active); ResultMap resultMaps = ResultMapsUtils.getResultMap(resultMapId); List<ResultMapping> resultMappings = new ArrayList<>(resultMaps.getResultMappings()); resultMappings.addAll(resultMaps.getIdResultMappings()); resultMappings.forEach(resultMapping -> { if (resultMapping.getNestedQueryId() == null) { RDBColumnMetaData column = new RDBColumnMetaData(); column.setJdbcType(JDBCType.valueOf(resultMapping.getJdbcType().name())); column.setName(resultMapping.getColumn()); if (!StringUtils.isNullOrEmpty(resultMapping.getProperty())) column.setAlias(resultMapping.getProperty()); column.setJavaType(resultMapping.getJavaType()); column.setProperty("resultMapping", resultMapping); rdbTableMetaData.addColumn(column); } }); cache.put(cacheKey, rdbTableMetaData); return rdbTableMetaData; } public String buildUpdateFields(String resultMapId, String tableName, UpdateParam param) { Pager.reset(); param.excludes("id"); RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); RDBDatabaseMetaData databaseMetaDate = getActiveDatabase(); Dialect dialect = databaseMetaDate.getDialect(); CommonSqlRender render = (CommonSqlRender) databaseMetaDate.getRenderer(SqlRender.TYPE.SELECT); List<CommonSqlRender.OperationColumn> columns = render.parseOperationField(tableMetaData, param); SqlAppender appender = new SqlAppender(); columns.forEach(column -> { RDBColumnMetaData columnMetaData = column.getRDBColumnMetaData(); if (columnMetaData.getName().contains(".")) return; if (columnMetaData == null) return; try { Object tmp = propertyUtils.getProperty(param.getData(), columnMetaData.getAlias()); if (tmp == null) return; } catch (Exception e) { return; } appender.add(",", encodeColumn(dialect, columnMetaData.getName()) , "=", "#{data.", columnMetaData.getAlias(), ",javaType=", EasyOrmSqlBuilder.getJavaType(columnMetaData.getJavaType()), ",jdbcType=", columnMetaData.getJdbcType(), "}"); }); if (!appender.isEmpty()) appender.removeFirst(); return appender.toString(); } public String encodeColumn(Dialect dialect, String field) { if (field.contains(".")) { String[] tmp = field.split("[.]"); return tmp[0] + "." + dialect.getQuoteStart() + (dialect.columnToUpperCase() ? (tmp[1].toUpperCase()) : tmp[1]) + dialect.getQuoteEnd(); } else { return dialect.getQuoteStart() + (dialect.columnToUpperCase() ? (field.toUpperCase()) : field) + dialect.getQuoteEnd(); } } public String buildInsertSql(String resultMapId, String tableName, InsertParam param) { Pager.reset(); RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); SqlRender<InsertParam> render = tableMetaData.getDatabaseMetaData().getRenderer(SqlRender.TYPE.INSERT); return render.render(tableMetaData, param).getSql(); } public String buildUpdateSql(String resultMapId, String tableName, UpdateParam param) { Pager.reset(); RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); SqlRender<UpdateParam> render = tableMetaData.getDatabaseMetaData().getRenderer(SqlRender.TYPE.UPDATE); return render.render(tableMetaData, param).getSql(); } public String buildSelectFields(String resultMapId, String tableName, QueryParam param) { if (param.isPaging() && Pager.get() == null) { Pager.doPaging(param.getPageIndex(), param.getPageSize()); } RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); RDBDatabaseMetaData databaseMetaDate = getActiveDatabase(); Dialect dialect = databaseMetaDate.getDialect(); CommonSqlRender render = (CommonSqlRender) databaseMetaDate.getRenderer(SqlRender.TYPE.SELECT); List<CommonSqlRender.OperationColumn> columns = render.parseOperationField(tableMetaData, param); SqlAppender appender = new SqlAppender(); columns.forEach(column -> { RDBColumnMetaData columnMetaData = column.getRDBColumnMetaData(); if (columnMetaData == null) return; String cname = columnMetaData.getName(); if (!cname.contains(".")) cname = tableName.concat(".").concat(cname); appender.add(",", encodeColumn(dialect, cname) , " AS " , dialect.getQuoteStart() , columnMetaData.getName() , dialect.getQuoteEnd()); }); param.getIncludes().remove("*"); if (appender.isEmpty()) return "*"; appender.removeFirst(); return appender.toString(); } public String buildOrder(String resultMapId, String tableName, QueryParam param) { RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); SqlAppender appender = new SqlAppender(" order by "); param.getSorts().stream() .forEach(sort -> { RDBColumnMetaData column = tableMetaData.getColumn(sort.getName()); if (column == null) column = tableMetaData.findColumn(sort.getName()); if (column == null) return; String cname = column.getName(); if (!cname.contains(".")) cname = tableName.concat(".").concat(cname); appender.add(encodeColumn(tableMetaData.getDatabaseMetaData().getDialect(), cname), " ", sort.getOrder(), ","); }); if (appender.isEmpty()) return ""; appender.removeLast(); return appender.toString(); } public String buildWhereForUpdate(String resultMapId, String tableName, List<Term> terms) { String where = buildWhere(resultMapId, tableName, terms); if (where.trim().isEmpty()) { throw new BusinessException("禁止执行无条件的更新操作"); } return where; } public String buildWhere(String resultMapId, String tableName, List<Term> terms) { RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId); RDBDatabaseMetaData databaseMetaDate = getActiveDatabase(); SimpleWhereSqlBuilder builder = new SimpleWhereSqlBuilder() { @Override public Dialect getDialect() { return databaseMetaDate.getDialect(); } }; SqlAppender appender = new SqlAppender(); builder.buildWhere(tableMetaData, "", terms, appender, new HashSet<>()); return appender.toString(); } class MysqlMeta extends MysqlRDBDatabaseMetaData { public MysqlMeta() { super(); renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder()); renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.MYSQL)); } } class OracleMeta extends OracleRDBDatabaseMetaData { public OracleMeta() { super(); renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder()); renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.MYSQL)); } } class H2Meta extends H2RDBDatabaseMetaData { public H2Meta() { super(); renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder()); renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.MYSQL)); } } }