|
@@ -0,0 +1,280 @@
|
|
|
+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));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|