From f0611848f6313a6020b3d6b02ef2929585c1d6fa Mon Sep 17 00:00:00 2001 From: 王彬 Date: Tue, 23 Mar 2021 17:45:57 +0800 Subject: [PATCH] extend to sharding sphere --- build.gradle | 5 +++-- gradle.properties | 2 +- src/main/java/com/taover/repository/CustomJdbcTemplate.java | 2 ++ src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenant.java | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------- src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenantInterface.java | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/JdbcRepositoryWrapperTenant.java | 152 -------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/com/taover/repository/UtilsSql.java | 192 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ src/main/java/com/taover/repository/UtilsString.java | 237 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/com/taover/repository/autoconfigure/ShardingSphereKeyGeneratorConfiguration.java | 31 +++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/autoconfigure/TaoverRepositoryAutoConfiguration.java | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/bean/TenantIdentity.java | 6 ++++++ src/main/java/com/taover/repository/exception/MultiRowException.java | 1 + src/main/java/com/taover/repository/exception/NotFoundException.java | 8 +++++++- src/main/java/com/taover/repository/exception/SqlParsePagerException.java | 10 ++++++++++ src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenant.java | 966 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenantImpl.java | 662 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingAlgorithmHint.java | 33 +++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingInfoEntity.java | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingInfoRepository.java | 11 +++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorExt.java | 21 +++++++++++++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorImpl.java | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/shardingsphere/ShardingSphereService.java | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/util/UtilsSql.java | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/taover/repository/util/UtilsString.java | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator | 1 + src/main/resources/META-INF/spring.factories | 1 + src/test/java/com/taover/repository/test/TestAutoconfigure.java | 20 ++++++++++++++++++++ src/test/resources/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator | 1 + src/test/resources/META-INF/spring.factories | 1 + src/test/resources/application.properties | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 30 files changed, 2924 insertions(+), 718 deletions(-) create mode 100644 src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenantInterface.java delete mode 100644 src/main/java/com/taover/repository/JdbcRepositoryWrapperTenant.java delete mode 100644 src/main/java/com/taover/repository/UtilsSql.java delete mode 100644 src/main/java/com/taover/repository/UtilsString.java create mode 100644 src/main/java/com/taover/repository/autoconfigure/ShardingSphereKeyGeneratorConfiguration.java create mode 100644 src/main/java/com/taover/repository/autoconfigure/TaoverRepositoryAutoConfiguration.java create mode 100644 src/main/java/com/taover/repository/bean/TenantIdentity.java create mode 100644 src/main/java/com/taover/repository/exception/SqlParsePagerException.java create mode 100644 src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenant.java create mode 100644 src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenantImpl.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingAlgorithmHint.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingInfoEntity.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingInfoRepository.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorExt.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorImpl.java create mode 100644 src/main/java/com/taover/repository/shardingsphere/ShardingSphereService.java create mode 100644 src/main/java/com/taover/repository/util/UtilsSql.java create mode 100644 src/main/java/com/taover/repository/util/UtilsString.java create mode 100644 src/main/resources/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator create mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 src/test/java/com/taover/repository/test/TestAutoconfigure.java create mode 100644 src/test/resources/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator create mode 100644 src/test/resources/META-INF/spring.factories create mode 100644 src/test/resources/application.properties diff --git a/build.gradle b/build.gradle index 5a2c7f1..99bd486 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,6 @@ * For more details take a look at the Java Libraries chapter in the Gradle * user guide available at https://docs.gradle.org/4.5.1/userguide/java_library_plugin.html */ - plugins { // Apply the java-library plugin to add support for Java Library id 'java' @@ -19,10 +18,12 @@ group = 'com.taover.repository' mainClassName = 'com.taover.repository.UtilsString' dependencies { + compile("org.springframework.boot:spring-boot-starter:2.0.5.RELEASE") compile("org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final") compile('org.springframework:spring-jdbc:5.1.9.RELEASE') compile('mysql:mysql-connector-java:5.1.47') compile('com.alibaba:druid:1.2.4') + compile("org.apache.shardingsphere:sharding-jdbc-spring-boot-starter:4.1.1") } repositories { @@ -56,7 +57,7 @@ uploadArchives { authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD) } pom.project { - version '2.1.38' + version '2.1.44' artifactId ARTIFACT_Id groupId GROUP_ID packaging TYPE diff --git a/gradle.properties b/gradle.properties index 77f7a9b..2fd93ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ NEXUS_PASSWORD=Nexus@dev # groupid GROUP_ID=com.taover -ARTIFACT_Id=com-taover-repository +ARTIFACT_Id=com-taover-repository-starter # type TYPE=jar diff --git a/src/main/java/com/taover/repository/CustomJdbcTemplate.java b/src/main/java/com/taover/repository/CustomJdbcTemplate.java index 0e99777..d16c4ed 100644 --- a/src/main/java/com/taover/repository/CustomJdbcTemplate.java +++ b/src/main/java/com/taover/repository/CustomJdbcTemplate.java @@ -18,6 +18,8 @@ import javax.persistence.Table; import org.springframework.jdbc.core.JdbcTemplate; +import com.taover.repository.util.UtilsSql; + /** * * @author root diff --git a/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenant.java b/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenant.java index 4a844ac..6cbc676 100644 --- a/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenant.java +++ b/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenant.java @@ -16,12 +16,13 @@ import javax.persistence.Id; import javax.persistence.Table; import org.springframework.dao.DataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; import com.taover.repository.exception.MultiRowException; import com.taover.repository.exception.NoContainTenantException; import com.taover.repository.exception.NotFoundException; import com.taover.repository.exception.ObjectReflectException; +import com.taover.repository.jdbctemplate.JdbcTemplateWrapperTenant; +import com.taover.repository.util.UtilsSql; /** * @@ -30,52 +31,47 @@ import com.taover.repository.exception.ObjectReflectException; * @param * @param */ -public class CustomJdbcTemplateWrapperTenant implements JdbcRepositoryWrapperTenant{ +public class CustomJdbcTemplateWrapperTenant implements CustomJdbcTemplateWrapperTenantInterface{ @Resource - private JdbcTemplate jdbcTemplateWrite; + private JdbcTemplateWrapperTenant _jdbcTemplateWrapperTenant; - private Map beanToTableField; - private Map tableToBeanField; - private String tableFieldNameListGapWithComma; - private String idTableFieldName; - private String idBeanFieldName; - private String dbName; - private String tableName; - private Class tClassInfo; - private CustomJdbcTemplateRowMapper customJdbcTemplateRowMapper; + private Map _beanToTableField; + private Map _tableToBeanField; + private String _tableFieldNameListGapWithComma; + private String _idTableFieldName; + private String _dbName; + private String _tableName; + private Class _tClassInfo; + private CustomJdbcTemplateRowMapper _customJdbcTemplateRowMapper; - public CustomJdbcTemplateRowMapper getCustomJdbcTemplateRowMapper(){ - return this.customJdbcTemplateRowMapper; - } - - public CustomJdbcTemplateWrapperTenant(JdbcTemplate jdbcTemplateWrite) throws Exception{ - this(); - this.jdbcTemplateWrite = jdbcTemplateWrite; - } + public CustomJdbcTemplateRowMapper getCustomJdbcTemplateRowMapper(){ + return this._customJdbcTemplateRowMapper; + } + @SuppressWarnings("unchecked") public CustomJdbcTemplateWrapperTenant() throws Exception{ //获取泛型类Class - this.tClassInfo = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + this._tClassInfo = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]; //检查实体声明 - Table annoTable = (Table) tClassInfo.getAnnotation(Table.class); + Table annoTable = (Table) _tClassInfo.getAnnotation(Table.class); if(annoTable == null){ - throw new Exception("DAO层初始化失败,失败原因:"+tClassInfo.getName()+"实体类,没有@Table注解指定表名"); + throw new Exception("DAO层初始化失败,失败原因:"+_tClassInfo.getName()+"实体类,没有@Table注解指定表名"); } - this.tableName = annoTable.name(); + this._tableName = annoTable.name(); String schema = annoTable.schema(); String catalog = annoTable.catalog(); if(schema != null && !"".equals(schema)){ - this.dbName = schema; + this._dbName = schema; }else if(catalog != null && !"".equals(catalog)){ - this.dbName = catalog; + this._dbName = catalog; } //初始化数据 - beanToTableField = new HashMap(); - tableToBeanField = new HashMap(); - tableFieldNameListGapWithComma = ""; - Field[] declaredFields = tClassInfo.getDeclaredFields(); + _beanToTableField = new HashMap(); + _tableToBeanField = new HashMap(); + _tableFieldNameListGapWithComma = ""; + Field[] declaredFields = _tClassInfo.getDeclaredFields(); for(int i=0; i impleme Column annoColumn = (Column)currField.getAnnotation(Column.class); if(annoId != null){ if(annoColumn == null){ - idTableFieldName = this.camelToUnderline(fieldName); + _idTableFieldName = this.camelToUnderline(fieldName); }else{ - idTableFieldName = annoColumn.name(); + _idTableFieldName = annoColumn.name(); } - idBeanFieldName = fieldName; - tableFieldNameListGapWithComma += idTableFieldName+","; - beanToTableField.put(fieldName, idTableFieldName); - tableToBeanField.put(idTableFieldName, fieldName); + _tableFieldNameListGapWithComma += _idTableFieldName+","; + _beanToTableField.put(fieldName, _idTableFieldName); + _tableToBeanField.put(_idTableFieldName, fieldName); }else{ if(annoColumn != null){ String tableFieldName = annoColumn.name(); - tableFieldNameListGapWithComma += tableFieldName+","; - beanToTableField.put(fieldName, tableFieldName); - tableToBeanField.put(tableFieldName, fieldName); + _tableFieldNameListGapWithComma += tableFieldName+","; + _beanToTableField.put(fieldName, tableFieldName); + _tableToBeanField.put(tableFieldName, fieldName); } } } //检验是否有属性 - if(beanToTableField.isEmpty()){ - throw new Exception("DAO层初始化失败,失败原因:"+this.tClassInfo.getName()+"实体类,没有在实体中找到属性信息"); + if(_beanToTableField.isEmpty()){ + throw new Exception("DAO层初始化失败,失败原因:"+this._tClassInfo.getName()+"实体类,没有在实体中找到属性信息"); } //去除逗号 - tableFieldNameListGapWithComma = tableFieldNameListGapWithComma.substring(0, tableFieldNameListGapWithComma.length()-1); + _tableFieldNameListGapWithComma = _tableFieldNameListGapWithComma.substring(0, _tableFieldNameListGapWithComma.length()-1); //创建rowmapper - this.customJdbcTemplateRowMapper = new CustomJdbcTemplateRowMapper(this.tClassInfo, this.tableToBeanField); + this._customJdbcTemplateRowMapper = new CustomJdbcTemplateRowMapper(this._tClassInfo, this._tableToBeanField); } /** @@ -137,42 +132,6 @@ public class CustomJdbcTemplateWrapperTenant impleme } return result.toString(); } - - /** - * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
- * 例如:HELLO_WORLD->HelloWorld - * @param name 转换前的下划线大写方式命名的字符串 - * @return 转换后的驼峰式命名的字符串 - */ - private String underlineToCamel(String name) { - StringBuilder result = new StringBuilder(); - // 快速检查 - if (name == null || name.isEmpty()) { - // 没必要转换 - return ""; - } else if (!name.contains("_")) { - // 不含下划线,仅将首字母小写 - return name.substring(0, 1).toLowerCase() + name.substring(1); - } - // 用下划线将原始字符串分割 - String camels[] = name.split("_"); - for (String camel : camels) { - // 跳过原始字符串中开头、结尾的下换线或双重下划线 - if (camel.isEmpty()) { - continue; - } - // 处理真正的驼峰片段 - if (result.length() == 0) { - // 第一个驼峰片段,全部字母都小写 - result.append(camel.toLowerCase()); - } else { - // 其他的驼峰片段,首字母大写 - result.append(camel.substring(0, 1).toUpperCase()); - result.append(camel.substring(1).toLowerCase()); - } - } - return result.toString(); - } private void appendWhereCondition(StringBuffer sql, StringBuffer pql, List list, List condition) { if (condition == null || condition.size() == 0) return; @@ -236,20 +195,20 @@ public class CustomJdbcTemplateWrapperTenant impleme } private String getTableSql(){ - return (this.dbName == null || "".equals(this.dbName.trim()))? - ("`"+this.tableName+"`"): - ("`"+this.dbName+"`.`"+this.tableName+"`"); + return (this._dbName == null || "".equals(this._dbName.trim()))? + ("`"+this._tableName+"`"): + ("`"+this._dbName+"`.`"+this._tableName+"`"); } private String constructUpdateSql(T entity, List beanFieldList) { StringBuffer sqlInsertPart = new StringBuffer("INSERT INTO "+this.getTableSql()+"("); - Iterator beanFieldIter = this.beanToTableField.keySet().iterator(); + Iterator beanFieldIter = this._beanToTableField.keySet().iterator(); while(beanFieldIter.hasNext()){ String beanFieldName = beanFieldIter.next(); - String tableFieldName = this.beanToTableField.get(beanFieldName); + String tableFieldName = this._beanToTableField.get(beanFieldName); Field beanField = null; try { - beanField = this.tClassInfo.getDeclaredField(beanFieldName); + beanField = this._tClassInfo.getDeclaredField(beanFieldName); beanField.setAccessible(true); if(beanField.get(entity) == null) { continue; @@ -280,7 +239,7 @@ public class CustomJdbcTemplateWrapperTenant impleme return 0; } } - + @Override public T findEntityByID(ID id, Long tenantId) throws NotFoundException { return findEntityByID(id, tenantId, false); @@ -288,16 +247,16 @@ public class CustomJdbcTemplateWrapperTenant impleme @Override public T findEntityByID(ID id, Long tenantId, boolean isLock) throws NotFoundException { - StringBuffer sql = new StringBuffer("SELECT "+this.tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); - sql.append(" WHERE "+idTableFieldName+" = ? and tenant_id="+tenantId); + StringBuffer sql = new StringBuffer("SELECT "+this._tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); + sql.append(" WHERE "+_idTableFieldName+" = ? and tenant_id="+tenantId); if (isLock) { sql.append(" FOR UPDATE"); } try { - return (T) jdbcTemplateWrite.queryForObject(sql.toString(), this.customJdbcTemplateRowMapper, id); - }catch (DataAccessException e) { - throw new NotFoundException(); - } + return (T) _jdbcTemplateWrapperTenant.queryForObject(sql.toString(), this._customJdbcTemplateRowMapper, tenantId, id); + }catch (DataAccessException e) { + throw new NotFoundException(e); + } } @Override @@ -333,20 +292,20 @@ public class CustomJdbcTemplateWrapperTenant impleme @Override public List findListByCondition(List condition, String sortCondition, Long tenantId){ - StringBuffer sql = new StringBuffer("SELECT "+this.tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); + StringBuffer sql = new StringBuffer("SELECT "+this._tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); List list = new ArrayList(); condition.add(new Object[] {"tenant_id", "=", tenantId}); this.appendWhereCondition(sql, new StringBuffer(), list, condition); if(sortCondition != null && !sortCondition.equals("")){ sql.append(" " + sortCondition + " "); } - return (List)jdbcTemplateWrite.query(sql.toString(), this.customJdbcTemplateRowMapper, list.toArray()); + return (List)_jdbcTemplateWrapperTenant.query(sql.toString(), this._customJdbcTemplateRowMapper, tenantId, list.toArray()); } @Override public List findListBySql(String sqlCondition, Long tenantId){ - StringBuffer sql = new StringBuffer("SELECT "+this.tableFieldNameListGapWithComma+" FROM "+this.getTableSql()+" WHERE tenant_id="+tenantId + " and " + sqlCondition); - return (List)jdbcTemplateWrite.query(sql.toString(), this.customJdbcTemplateRowMapper); + StringBuffer sql = new StringBuffer("SELECT "+this._tableFieldNameListGapWithComma+" FROM "+this.getTableSql()+" WHERE tenant_id="+tenantId + " and " + sqlCondition); + return (List)_jdbcTemplateWrapperTenant.query(sql.toString(), this._customJdbcTemplateRowMapper, tenantId); } @Override @@ -356,7 +315,7 @@ public class CustomJdbcTemplateWrapperTenant impleme @Override public Map findPageByCondition(List condition, String sortCondition, int page, int pageSize, Long tenantId){ - StringBuffer sql = new StringBuffer("SELECT "+this.tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); + StringBuffer sql = new StringBuffer("SELECT "+this._tableFieldNameListGapWithComma+" FROM "+this.getTableSql()); StringBuffer pql = new StringBuffer(sql.toString()); StringBuffer sqlCount = new StringBuffer("SELECT COUNT(1) rowCount FROM "+this.getTableSql()); condition.add(new Object[] {"tenant_id", "=", tenantId}); @@ -376,40 +335,39 @@ public class CustomJdbcTemplateWrapperTenant impleme } String pageSql = sql.toString() + " limit ?, ?"; - - Map totalRowsMap = jdbcTemplateWrite.queryForMap(sqlCount.toString(), count_list.toArray()) ; - List resultList = jdbcTemplateWrite.query(pageSql.toString(), this.customJdbcTemplateRowMapper, page_list.toArray()); + Map totalRowsMap = _jdbcTemplateWrapperTenant.queryForMap(sqlCount.toString(), tenantId, count_list.toArray()) ; + List resultList = _jdbcTemplateWrapperTenant.query(pageSql.toString(), this._customJdbcTemplateRowMapper, tenantId, page_list.toArray()); return UtilsSql.createPage(page, pageSize, Integer.valueOf(totalRowsMap.get("rowCount").toString()), resultList); } @Override public Map findPageBySql(String sqlCondition, int page, int pageSize, Long tenantId){ - StringBuffer sql = new StringBuffer("SELECT "+this.tableFieldNameListGapWithComma+" FROM "+this.getTableSql()+" WHERE tenant_id=" + tenantId + " and " + sqlCondition ); + StringBuffer sql = new StringBuffer("SELECT "+this._tableFieldNameListGapWithComma+" FROM "+this.getTableSql()+" WHERE tenant_id=" + tenantId + " and " + sqlCondition ); StringBuffer sqlCount = new StringBuffer("SELECT count(1) rowCount FROM "+this.getTableSql()+" WHERE tenant_id=" + tenantId + " and " + sqlCondition); sql.append(" limit ?, ?"); List page_list = new ArrayList(); page_list.add((page - 1) * pageSize); page_list.add(page * pageSize); - Map totalRowsMap = jdbcTemplateWrite.queryForMap(sqlCount.toString()); - List resultList = jdbcTemplateWrite.query(sql.toString(), this.customJdbcTemplateRowMapper, page_list.toArray()); + Map totalRowsMap = _jdbcTemplateWrapperTenant.queryForMap(sqlCount.toString(), tenantId); + List resultList = _jdbcTemplateWrapperTenant.query(sql.toString(), this._customJdbcTemplateRowMapper, tenantId, page_list.toArray()); return UtilsSql.createPage(page, pageSize, Integer.valueOf(totalRowsMap.get("rowCount").toString()), resultList); } @Override - public void addEntity(T entity) { + public void addEntity(T entity, Long tenantId) { StringBuffer sqlInsertPart = new StringBuffer("INSERT INTO "+this.getTableSql()+"("); StringBuffer sqlColumnPart = new StringBuffer(") VALUES ("); List paramList = new ArrayList(); - Iterator beanFieldIter = this.beanToTableField.keySet().iterator(); + Iterator beanFieldIter = this._beanToTableField.keySet().iterator(); while(beanFieldIter.hasNext()){ String beanFieldName = beanFieldIter.next(); - String tableFieldName = this.beanToTableField.get(beanFieldName); + String tableFieldName = this._beanToTableField.get(beanFieldName); Field beanField; Object beanFieldValue = null; try { - beanField = this.tClassInfo.getDeclaredField(beanFieldName); + beanField = this._tClassInfo.getDeclaredField(beanFieldName); beanField.setAccessible(true); beanFieldValue = beanField.get(entity); } catch (Exception e) { @@ -423,19 +381,19 @@ public class CustomJdbcTemplateWrapperTenant impleme sqlColumnPart.append(" ?,"); paramList.add(beanFieldValue); } - + //执行SQL String exeSql = sqlInsertPart.substring(0, sqlInsertPart.length()-1)+sqlColumnPart.substring(0, sqlColumnPart.length()-1)+")"; - jdbcTemplateWrite.update(exeSql, paramList.toArray()); + _jdbcTemplateWrapperTenant.update(exeSql, tenantId, paramList.toArray()); } @Override - public void addEntityList(List entityList) { + public void addEntityList(List entityList, Long tenantId) { if(entityList == null || entityList.isEmpty()) { return; } //构造SQL语句及Entity Field列表 - List beanFieldList = new ArrayList(this.beanToTableField.size()); + List beanFieldList = new ArrayList(this._beanToTableField.size()); StringBuffer exeSql = new StringBuffer(this.constructUpdateSql(entityList.get(0), beanFieldList)); //构造参数信息 @@ -464,15 +422,15 @@ public class CustomJdbcTemplateWrapperTenant impleme } exeSql.setCharAt(exeSql.length()-1, ';'); - //执行SQL - this.jdbcTemplateWrite.update(exeSql.toString(), args.toArray()); + //执行SQL + this._jdbcTemplateWrapperTenant.update(exeSql.toString(), tenantId, args.toArray()); } @Override public int deleteEntityByID(ID id, Long tenantId) { StringBuffer sql = new StringBuffer("DELETE FROM "+this.getTableSql()+" WHERE"); - sql.append(" "+this.idTableFieldName+" = ? and tenant_id="+tenantId); - return jdbcTemplateWrite.update(sql.toString(), id); + sql.append(" "+this._idTableFieldName+" = ? and tenant_id="+tenantId); + return _jdbcTemplateWrapperTenant.update(sql.toString(), tenantId, id); } @Override @@ -486,7 +444,7 @@ public class CustomJdbcTemplateWrapperTenant impleme StringBuffer pql = new StringBuffer(sql.toString()); condition.add(new Object[] {"tenant_id", "=", tenantId}); this.appendWhereCondition(sql, pql, list, condition); - return jdbcTemplateWrite.update( sql.toString(), list.toArray()); + return _jdbcTemplateWrapperTenant.update( sql.toString(), tenantId, list.toArray()); } @Override @@ -494,7 +452,7 @@ public class CustomJdbcTemplateWrapperTenant impleme if("".equals(sqlCondition.trim())) { throw new RuntimeException("params[sqlCondition] is empty"); } - return jdbcTemplateWrite.update( "DELETE FROM "+this.getTableSql()+" WHERE tenant_id="+tenantId + " and " + sqlCondition); + return _jdbcTemplateWrapperTenant.update( "DELETE FROM "+this.getTableSql()+" WHERE tenant_id="+tenantId + " and " + sqlCondition, tenantId); } @Override @@ -503,7 +461,7 @@ public class CustomJdbcTemplateWrapperTenant impleme for(ID id: idList) { idSb.append(id); } - return this.deleteEntityBySql(this.idTableFieldName + " in ("+idSb.toString().substring(0, idSb.length()-1)+") and tenant_id="+tenantId, tenantId); + return this.deleteEntityBySql(this._idTableFieldName + " in ("+idSb.toString().substring(0, idSb.length()-1)+") and tenant_id="+tenantId, tenantId); } @Override @@ -520,10 +478,10 @@ public class CustomJdbcTemplateWrapperTenant impleme List list = new ArrayList(); this.appendSetSql(sql, pql, list, changeList); - String where = " WHERE "+this.idTableFieldName+"=? and tenant_id="+tenantId; + String where = " WHERE "+this._idTableFieldName+"=? and tenant_id="+tenantId; String updateSql = sql.substring(0, sql.length()-1)+where; - list.add(id); - return jdbcTemplateWrite.update(updateSql, list.toArray()); + list.add(id); + return _jdbcTemplateWrapperTenant.update(updateSql, tenantId, list.toArray()); } @Override @@ -546,7 +504,7 @@ public class CustomJdbcTemplateWrapperTenant impleme this.appendWhereCondition(where, pwhere, list, condition); String updateSql = sql.substring(0, sql.length()-1)+where.toString(); - return jdbcTemplateWrite.update(updateSql, list.toArray()); + return _jdbcTemplateWrapperTenant.update(updateSql, tenantId, list.toArray()); } @Override @@ -564,17 +522,13 @@ public class CustomJdbcTemplateWrapperTenant impleme this.appendSetSql(sql, pql, list, updateObj); String updateSql = sql.toString().substring(0, sql.length()-1) + " WHERE tenant_id="+tenantId+" and "+sqlCondition; - return jdbcTemplateWrite.update(updateSql, list.toArray()); + return _jdbcTemplateWrapperTenant.update(updateSql, tenantId, list.toArray()); } @Override public Map getPageData(String coreSql, String orderByPartSql, Integer page, Integer pageSize, Long tenantId){ - try { - String[] splitedSql = UtilsSql.splitCoreSql(coreSql); - return this.getPageData(splitedSql[0], splitedSql[1], orderByPartSql, page, pageSize, tenantId); - }catch (Exception e) { - return UtilsSql.createPage(page, pageSize, 0, new ArrayList()); - } + String[] splitedSql = UtilsSql.splitCoreSql(coreSql); + return this.getPageData(splitedSql[0], splitedSql[1], orderByPartSql, page, pageSize, tenantId); } @Override @@ -589,10 +543,9 @@ public class CustomJdbcTemplateWrapperTenant impleme //执行查询 List> queryData = new ArrayList>(); - Map countData = new HashMap(); - queryData = this.jdbcTemplateWrite.queryForList(querySql); - countData = this.jdbcTemplateWrite.queryForMap(countSql); + Map countData = new HashMap(); + queryData = this._jdbcTemplateWrapperTenant.queryForList(querySql, tenantId); + countData = this._jdbcTemplateWrapperTenant.queryForMap(countSql, tenantId); return UtilsSql.createPage(page, pageSize, Integer.valueOf(countData.get("rowsCount").toString()), queryData); } - } diff --git a/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenantInterface.java b/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenantInterface.java new file mode 100644 index 0000000..62b747b --- /dev/null +++ b/src/main/java/com/taover/repository/CustomJdbcTemplateWrapperTenantInterface.java @@ -0,0 +1,153 @@ +package com.taover.repository; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import com.taover.repository.exception.MultiRowException; +import com.taover.repository.exception.NoContainTenantException; +import com.taover.repository.exception.NotFoundException; + +public interface CustomJdbcTemplateWrapperTenantInterface { + + /** + * 按主键查询 + */ + public T findEntityByID(ID id, Long tenantId) throws NotFoundException; + + /** + * 按主键查询 + * isLock 是否锁定, 默认不锁 + * fromWriteDB 是否从写库读写,默认从读库查询 + */ + public T findEntityByID(ID id, Long tenantId, boolean isLock) throws NotFoundException; + + /** + * 根据条件List查询 + * Object[]数组长度是3 + * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public T findEntityByCondition(List condition, Long tenantId) throws NotFoundException,MultiRowException,NoContainTenantException; + + /** + * 根据条件sql查询 + * sqlCondition 为where 后面的条件。 + */ + public T findEntityBySql(String sqlCondition, Long tenantId) throws NotFoundException,MultiRowException,NoContainTenantException; + + /** + * 根据条件List查询 + * Object[]数组长度是3 + * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public List findListByCondition(List condition, Long tenantId); + + /** + * 根据条件List查询 + * Object[]数组长度是3 + * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public List findListByCondition(List condition, String sortCondition, Long tenantId); + + /** + * 根据条件sql查询 + * sqlCondition 为where 后面的条件。 + */ + public List findListBySql(String sqlCondition, Long tenantId); + + /** + * 按条件分页查询 + * Object[]数组长度是3 + * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public Map findPageByCondition(List condition, int page, int pageSize, Long tenantId); + + /** + * 按条件分页查询 + * Object[]数组长度是3 + * Object[]第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + * boolean isUseCache, 是否用缓存,默认用。 + * boolean isAddCache, 是否添加缓存,默认添加。 + */ + public Map findPageByCondition(List condition, String sortCondition, int page, int pageSize, Long tenantId); + + /** + * 按sql分页查询, sqlCondition为where 后条件sql + */ + public Map findPageBySql(String sqlCondition, int page, int pageSize, Long tenantId); + + /** + * 添加 + */ + public void addEntity(T entity, Long tenantId); + + /** + * 批量添加 + * @throws Exception + */ + public void addEntityList(List entityList, Long tenantId); + + /** + * 按ID删除 + */ + public int deleteEntityByID(ID id, Long tenantId); + + /** + * 删除按List条件 + * Object[]数组长度是3 + * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public int deleteEntityByCondition(List condition, Long tenantId); + + /** + * 删除按condition条件 + * 建议使用deleteTByCondition(List condition), 如果removeTByCondition(List condition)满足不了where条件可以使用此方法。 + * condition为where后面的条件,condition不能为空。 + */ + public int deleteEntityBySql(String sqlCondition, Long tenantId); + + /** + * 根据list对象逐个删除。 + * @throws NoContainTenantException + */ + public int deleteEntityList(List idList, Long tenantId); + + /** + * 根据ID修改指定的值 + */ + public int updateEntityById(List changeList, ID id, Long tenantId); + + /** + * List updateObj 要修改成的值,数组长度为2,第一个值为列名,第二个值是要改成的值。 + * List condition 修改的条件, 数组长度是3, 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 + */ + public int updateEntityByCondition(List updateObj, List condition, Long tenantId); + + /** + * List updateObj 要修改成的值,数组长度为2,第一个值为列名,第二个值是要改成的值。 + * String sqlCondition 修改的条件。 + */ + public int updateEntityBySql(List updateObj, String sqlCondition, Long tenantId); + + /** + * 获取分页数据 + * @param coreSql + * @param orderByPartSql + * @param page + * @param pageSize + * @return + */ + public Map getPageData(String coreSql, String orderByPartSql, Integer page, Integer pageSize, Long tenantId); + + /** + * 获取分页数据 + * @param selectSql + * @param fromAndWhereSql + * @param orderByPartSql + * @param page + * @param pageSize + * @return + */ + public Map getPageData(String selectSql, String fromAndWhereSql, String orderByPartSql, Integer page, Integer pageSize, Long tenantId); + +} diff --git a/src/main/java/com/taover/repository/JdbcRepositoryWrapperTenant.java b/src/main/java/com/taover/repository/JdbcRepositoryWrapperTenant.java deleted file mode 100644 index f5e0125..0000000 --- a/src/main/java/com/taover/repository/JdbcRepositoryWrapperTenant.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.taover.repository; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -import com.taover.repository.exception.MultiRowException; -import com.taover.repository.exception.NoContainTenantException; -import com.taover.repository.exception.NotFoundException; - -public interface JdbcRepositoryWrapperTenant { - - /** - * 按主键查询 - */ - public T findEntityByID(ID id, Long tenantId) throws NotFoundException; - - /** - * 按主键查询 - * isLock 是否锁定, 默认不锁 - * fromWriteDB 是否从写库读写,默认从读库查询 - */ - public T findEntityByID(ID id, Long tenantId, boolean isLock) throws NotFoundException; - - /** - * 根据条件List查询 - * Object[]数组长度是3 - * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public T findEntityByCondition(List condition, Long tenantId) throws NotFoundException,MultiRowException,NoContainTenantException; - - /** - * 根据条件sql查询 - * sqlCondition 为where 后面的条件。 - */ - public T findEntityBySql(String sqlCondition, Long tenantId) throws NotFoundException,MultiRowException,NoContainTenantException; - - /** - * 根据条件List查询 - * Object[]数组长度是3 - * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public List findListByCondition(List condition, Long tenantId); - - /** - * 根据条件List查询 - * Object[]数组长度是3 - * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public List findListByCondition(List condition, String sortCondition, Long tenantId); - - /** - * 根据条件sql查询 - * sqlCondition 为where 后面的条件。 - */ - public List findListBySql(String sqlCondition, Long tenantId); - - /** - * 按条件分页查询 - * Object[]数组长度是3 - * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public Map findPageByCondition(List condition, int page, int pageSize, Long tenantId); - - /** - * 按条件分页查询 - * Object[]数组长度是3 - * Object[]第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - * boolean isUseCache, 是否用缓存,默认用。 - * boolean isAddCache, 是否添加缓存,默认添加。 - */ - public Map findPageByCondition(List condition, String sortCondition, int page, int pageSize, Long tenantId); - - /** - * 按sql分页查询, sqlCondition为where 后条件sql - */ - public Map findPageBySql(String sqlCondition, int page, int pageSize, Long tenantId); - - /** - * 添加 - */ - public void addEntity(T entity); - - /** - * 批量添加 - * @throws Exception - */ - public void addEntityList(List entityList); - - /** - * 按ID删除 - */ - public int deleteEntityByID(ID id, Long tenantId); - - /** - * 删除按List条件 - * Object[]数组长度是3 - * Object[], 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public int deleteEntityByCondition(List condition, Long tenantId); - - /** - * 删除按condition条件 - * 建议使用deleteTByCondition(List condition), 如果removeTByCondition(List condition)满足不了where条件可以使用此方法。 - * condition为where后面的条件,condition不能为空。 - */ - public int deleteEntityBySql(String sqlCondition, Long tenantId); - - /** - * 根据list对象逐个删除。 - * @throws NoContainTenantException - */ - public int deleteEntityList(List idList, Long tenantId); - - /** - * 根据ID修改指定的值 - */ - public int updateEntityById(List changeList, ID id, Long tenantId); - - /** - * List updateObj 要修改成的值,数组长度为2,第一个值为列名,第二个值是要改成的值。 - * List condition 修改的条件, 数组长度是3, 第一个参数是列名,第二个参数是操作符,第三个参数是查询条件的值。 - */ - public int updateEntityByCondition(List updateObj, List condition, Long tenantId); - - /** - * List updateObj 要修改成的值,数组长度为2,第一个值为列名,第二个值是要改成的值。 - * String sqlCondition 修改的条件。 - */ - public int updateEntityBySql(List updateObj, String sqlCondition, Long tenantId); - - /** - * 获取分页数据 - * @param coreSql - * @param orderByPartSql - * @param page - * @param pageSize - * @return - */ - public Map getPageData(String coreSql, String orderByPartSql, Integer page, Integer pageSize, Long tenantId); - - /** - * 获取分页数据 - * @param selectSql - * @param fromAndWhereSql - * @param orderByPartSql - * @param page - * @param pageSize - * @return - */ - public Map getPageData(String selectSql, String fromAndWhereSql, String orderByPartSql, Integer page, Integer pageSize, Long tenantId); -} diff --git a/src/main/java/com/taover/repository/UtilsSql.java b/src/main/java/com/taover/repository/UtilsSql.java deleted file mode 100644 index e11c95a..0000000 --- a/src/main/java/com/taover/repository/UtilsSql.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.taover.repository; - -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; - -import org.springframework.util.StringUtils; - -public class UtilsSql { - - public static String[] splitCoreSql(String coreSql) throws Exception { - //去除''内的信息 - int fromIndex = calcFromIndex(coreSql); - if(fromIndex > -1) { - return new String[] {coreSql.substring(0, fromIndex), coreSql.substring(fromIndex, coreSql.length())}; - }else { - throw new Exception("未找到FROM子句"); - } - } - - private static int calcFromIndex(String coreSql) { - Stack operStack = new Stack(); - String coreSqlUpperCase = coreSql.toUpperCase(); - int sqlLen = coreSqlUpperCase.length(); - int currIndex = 0; - while(currIndex < sqlLen) { - String originContainBlank = subFirstStrEndWithBlank(coreSqlUpperCase, currIndex); - String itemWithoutBlank = originContainBlank.trim(); - if("".equals(itemWithoutBlank)) { - currIndex += originContainBlank.length(); - continue; - } - if(operStack.isEmpty() && "FROM".equals(itemWithoutBlank)) { - return currIndex; - } - dealCoupleMark(itemWithoutBlank, operStack); - currIndex += originContainBlank.length(); - } - return -1; - } - private static void dealCoupleMark(String itemWithoutBlank, Stack operStack) { - char preChar = ' '; - for(int i=0; i createPage(int page, int size, int total, Object data){ - Map pageData = new HashMap(); - pageData.put("page", page); - pageData.put("rows", data); - pageData.put("size", size); - pageData.put("total", total); - return pageData; - } - - public static void main(String[] args) { - String[] testSql = new String[] { - "\\", - "select actionFrom as 'seelctsele\\'ctd',(select a as 'fromCActio' from t) from www where 223=1 ", - "SELECT wxorder_order.refund_way refundWay,wxorder_order.refund_delivery_sn refundDeliverySn, (SELECT sum(wxorder_compensate.`ware_refund_money`) from `wxorder_compensate` where wxorder_compensate.`order_id` = wxorder_order.id) as wareRefundMoney,(SELECT sum(wxorder_compensate.`refund_money`) from `wxorder_compensate` where wxorder_compensate.`order_id` = wxorder_order.id) as refundMoney, wxorder_order.operate_refund_time applyCompensateTime,wxorder_order.refund_instructions refundInstructions, wxorder_order.channel_id channelId, wxorder_channel.name channelName, wxorder_channel.platform_code platformCode, wxorder_order.pre_control_status as refundPreStatus, wxorder_order.ware_id wareId, wxorder_ware.name wareName, wxorder_order.id, wxorder_order.order_sn orderSn, wxorder_order.upload_sn uploadSn, wxorder_order.money_paid moneyPaid, wxorder_order.consignee, wxorder_order.mobile, wxorder_order.province_name provinceName, wxorder_order.city_name cityName, wxorder_order.district_name districtName, wxorder_order.address, wxorder_order.channel_remark channelRemark, wxorder_order.customer_remark customerRemark, wxorder_order.platform_customer_remark platformCustomerRemark, wxorder_order.sender_name senderName, wxorder_order.sender_mobile senderMobile, date_format(wxorder_order.create_time, '%Y-%m-%d %H:%i:%s') createTime, date_format(wxorder_order.real_delivery_time, '%Y-%m-%d %H:%i:%s') realDeliveryTime, wxorder_order.progress_distribute progressDistribute, wxorder_order.progress_delivery progressDelivery, wxorder_order.control_status controlStatus, wxorder_order.express_name expressName, wxorder_order.express_number expressNumber,wxorder_order.shipping_price shippingPrice, wxorder_order.customer_network_name ,(select count(*) from wxorder_compensate WHERE wxorder_compensate.order_id = wxorder_order.id) as compensateCountAll,(select count(*) from wxorder_compensate WHERE wxorder_compensate.order_id = wxorder_order.id and wxorder_compensate.progress_status = 1) as compensateCountDealed ,(select count(*) from wxorder_channel_refund_payment where wxorder_channel_refund_payment.order_id = wxorder_order.id) as channelRefundPaymentStatus,(select count(*) from wxorder_ware_refund_payment where wxorder_ware_refund_payment.order_id = wxorder_order.id) as wareRefundPaymentStatus FROM wxorder_order wxorder_order INNER JOIN wxorder_channel wxorder_channel ON wxorder_order.channel_id=wxorder_channel.id and wxorder_channel.tenant_id=54 INNER JOIN wxorder_ware wxorder_ware ON wxorder_order.ware_id=wxorder_ware.id and wxorder_ware.tenant_id=54 INNER JOIN wxorder_order_goods wxorder_order_goods ON wxorder_order.id=wxorder_order_goods.order_id and wxorder_order_goods.tenant_id=54 WHERE 1=1 AND wxorder_order.tenant_id='54' AND wxorder_order.control_status='4' GROUP BY wxorder_order.id", - "select wdd, fromType from www where 223", - "SELECT excel_data.already_deal_line alreadyDealLine, excel_data.channel_id channelId, excel_data.channel_name channelName, excel_data.contain_express_status containExpressStatus, excel_data.create_time createTime, excel_data.deal_control dealControl, excel_data.deal_status dealStatus, excel_data.excel_path excelPath, excel_data.excel_title_index excelTitleIndex, excel_data.exists_error existsError, excel_data.file_id fileId, excel_data.file_name fileName, excel_data.group_name groupName, excel_data.id, excel_data.key_mapping keyMapping, excel_data.message_ssid messageSsid, excel_data.order_line orderLine, excel_data.payload payload, excel_data.progress_step progressStep, excel_data.select_sheet_name selectSheetName, excel_data.step_deal_time stepDealTime, excel_data.tenant_id tenantId, excel_data.update_time updateTime, excel_data.xls_batch_no xlsBatchNo FROM wxorder_excel_data excel_data WHERE excel_data.tenant_id=16", - "SELECT excel_data.xls_batch_no as 'xlsBatchNo', (select now()) as 'temp' FROM wxorder_excel_data excel_data WHERE excel_data.tenant_id=16" - }; - - for(int i=0; i 0){ - createdAtStart = sdf.parse(createdAtArr[0]); - } - if(createdAtArr.length > 1){ - createdAtEnd = sdf.parse(createdAtArr[1]); - } - } - resultObj[0] = createdAtStart; - resultObj[1] = createdAtEnd; - return resultObj; - } - - /** - * 获取URL path部分 - * @param url - * @return - */ - public static String getUrlPath(String url){ - if(url == null){ - return null; - } - if(url.startsWith("http")){ - try{ - URL dataUrl = new URL(url); - return dataUrl.getPath(); - }catch(Exception e){ - e.printStackTrace(); - } - } - return url; - } - - /** - * 在compares字符数组查找pattern字符串,找到则返回字串在数组中的索引,未找到返回-1 - * @param pattern - * @param compares - * @return - */ - public static int getStringIndex(String pattern, String compares[]){ - //参数检验 - if(pattern==null || compares==null){ - return -1; - } - - //循环遍历compares - for(int i=0; iLong.MAX_VALUE || value0; --i){ - long baseDiv = (long) Math.pow(10, i-1); - long baseRand = (long) Math.pow(10, i); - long currentDigit = value % baseRand / baseDiv; - if(currentDigit == 0){ - result = result + preffixChar; - }else{ - result = result + currentDigit; - } - } - return result; - } - - /** - * 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
- * 例如:HelloWorld->HELLO_WORLD - * @param name 转换前的驼峰式命名的字符串 - * @return 转换后下划线大写方式命名的字符串 - */ - public static String underscoreName(String name) { - StringBuilder result = new StringBuilder(); - if (name != null && name.length() > 0) { - // 将第一个字符处理成大写 - result.append(name.substring(0, 1).toUpperCase()); - // 循环处理其余字符 - for (int i = 1; i < name.length(); i++) { - String s = name.substring(i, i + 1); - // 在大写字母前添加下划线 - if (Character.isUpperCase(s.charAt(0)) && Character.isLetter(s.charAt(0))) { - result.append("_"); - } - // 其他字符直接转成大写 - result.append(s.toUpperCase()); - } - } - return result.toString(); - } - - /** - * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
- * 例如:HELLO_WORLD->HelloWorld - * @param name 转换前的下划线大写方式命名的字符串 - * @return 转换后的驼峰式命名的字符串 - */ - public static String camelName(String name) { - StringBuilder result = new StringBuilder(); - // 快速检查 - if (name == null || name.isEmpty()) { - // 没必要转换 - return ""; - } else if (!name.contains("_")) { - // 不含下划线,仅将首字母小写 - return name.substring(0, 1).toLowerCase() + name.substring(1); - } - // 用下划线将原始字符串分割 - String camels[] = name.split("_"); - for (String camel : camels) { - // 跳过原始字符串中开头、结尾的下换线或双重下划线 - if (camel.isEmpty()) { - continue; - } - // 处理真正的驼峰片段 - if (result.length() == 0) { - // 第一个驼峰片段,全部字母都小写 - result.append(camel.toLowerCase()); - } else { - // 其他的驼峰片段,首字母大写 - result.append(camel.substring(0, 1).toUpperCase()); - result.append(camel.substring(1).toLowerCase()); - } - } - return result.toString(); - } - - public static String getRandomStr(int digitNum){ - int numStartZero = 48; - int charStartUpperA = 65; - int charStartLowerA = 97; - int numSelectCount = 10; - int charSelectCount = 26; - int numCount = numSelectCount+2*charSelectCount; - String randomResult = ""; - for(int i=0; iThe callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param action a callback object that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(ConnectionCallback action, Long tenantId) throws DataAccessException; + + + //------------------------------------------------------------------------- + // Methods dealing with static SQL (java.sql.Statement) + //------------------------------------------------------------------------- + + /** + * Execute a JDBC data access operation, implemented as callback action + * working on a JDBC Statement. This allows for implementing arbitrary data + * access operations on a single Statement, within Spring's managed JDBC + * environment: that is, participating in Spring-managed transactions and + * converting JDBC SQLExceptions into Spring's DataAccessException hierarchy. + *

The callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param action a callback that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(StatementCallback action, Long tenantId) throws DataAccessException; + + /** + * Issue a single SQL execute, typically a DDL statement. + * @param sql static SQL to execute + * @throws DataAccessException if there is any problem + */ + void execute(String sql, Long tenantId) throws DataAccessException; + + /** + * Execute a query given static SQL, reading the ResultSet with a + * ResultSetExtractor. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code query} method with {@code null} as argument array. + * @param sql the SQL query to execute + * @param rse a callback that will extract all rows of results + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if there is any problem executing the query + * @see #query(String, Object[], ResultSetExtractor) + */ + @Nullable + T query(String sql, ResultSetExtractor rse, Long tenantId) throws DataAccessException; + + /** + * Execute a query given static SQL, reading the ResultSet on a per-row + * basis with a RowCallbackHandler. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code query} method with {@code null} as argument array. + * @param sql the SQL query to execute + * @param rch a callback that will extract results, one row at a time + * @throws DataAccessException if there is any problem executing the query + * @see #query(String, Object[], RowCallbackHandler) + */ + void query(String sql, RowCallbackHandler rch, Long tenantId) throws DataAccessException; + + /** + * Execute a query given static SQL, mapping each row to a result object + * via a RowMapper. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code query} method with {@code null} as argument array. + * @param sql the SQL query to execute + * @param rowMapper a callback that will map one object per row + * @return the result List, containing mapped objects + * @throws DataAccessException if there is any problem executing the query + * @see #query(String, Object[], RowMapper) + */ + List query(String sql, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Execute a query given static SQL, mapping a single result row to a + * result object via a RowMapper. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@link #queryForObject(String, RowMapper, Object...)} method with + * {@code null} as argument array. + * @param sql the SQL query to execute + * @param rowMapper a callback that will map one object per row + * @return the single mapped object (may be {@code null} if the given + * {@link RowMapper} returned {@code} null) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if there is any problem executing the query + * @see #queryForObject(String, Object[], RowMapper) + */ + @Nullable + T queryForObject(String sql, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Execute a query for a result object, given static SQL. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@link #queryForObject(String, Class, Object...)} method with + * {@code null} as argument array. + *

This method is useful for running static SQL with a known outcome. + * The query is expected to be a single row/single column query; the returned + * result will be directly mapped to the corresponding object type. + * @param sql the SQL query to execute + * @param requiredType the type that the result object is expected to match + * @return the result object of the required type, or {@code null} in case of SQL NULL + * @throws IncorrectResultSizeDataAccessException if the query does not return + * exactly one row, or does not return exactly one column in that row + * @throws DataAccessException if there is any problem executing the query + * @see #queryForObject(String, Object[], Class) + */ + @Nullable + T queryForObject(String sql, Class requiredType, Long tenantId) throws DataAccessException; + + /** + * Execute a query for a result map, given static SQL. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@link #queryForMap(String, Object...)} method with {@code null} + * as argument array. + *

The query is expected to be a single row query; the result row will be + * mapped to a Map (one entry for each column, using the column name as the key). + * @param sql the SQL query to execute + * @return the result Map (one entry per column, with column name as key) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if there is any problem executing the query + * @see #queryForMap(String, Object[]) + * @see ColumnMapRowMapper + */ + Map queryForMap(String sql, Long tenantId) throws DataAccessException; + + /** + * Execute a query for a result list, given static SQL. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code queryForList} method with {@code null} as argument array. + *

The results will be mapped to a List (one entry for each row) of + * result objects, each of them matching the specified element type. + * @param sql the SQL query to execute + * @param elementType the required type of element in the result list + * (for example, {@code Integer.class}) + * @return a List of objects that match the specified element type + * @throws DataAccessException if there is any problem executing the query + * @see #queryForList(String, Object[], Class) + * @see SingleColumnRowMapper + */ + List queryForList(String sql, Class elementType, Long tenantId) throws DataAccessException; + + /** + * Execute a query for a result list, given static SQL. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code queryForList} method with {@code null} as argument array. + *

The results will be mapped to a List (one entry for each row) of + * Maps (one entry for each column using the column name as the key). + * Each element in the list will be of the form returned by this interface's + * {@code queryForMap} methods. + * @param sql the SQL query to execute + * @return an List that contains a Map per row + * @throws DataAccessException if there is any problem executing the query + * @see #queryForList(String, Object[]) + */ + List> queryForList(String sql, Long tenantId) throws DataAccessException; + + /** + * Execute a query for a SqlRowSet, given static SQL. + *

Uses a JDBC Statement, not a PreparedStatement. If you want to + * execute a static query with a PreparedStatement, use the overloaded + * {@code queryForRowSet} method with {@code null} as argument array. + *

The results will be mapped to an SqlRowSet which holds the data in a + * disconnected fashion. This wrapper will translate any SQLExceptions thrown. + *

Note that, for the default implementation, JDBC RowSet support needs to + * be available at runtime: by default, Sun's {@code com.sun.rowset.CachedRowSetImpl} + * class is used, which is part of JDK 1.5+ and also available separately as part of + * Sun's JDBC RowSet Implementations download (rowset.jar). + * @param sql the SQL query to execute + * @return a SqlRowSet representation (possibly a wrapper around a + * {@code javax.sql.rowset.CachedRowSet}) + * @throws DataAccessException if there is any problem executing the query + * @see #queryForRowSet(String, Object[]) + * @see SqlRowSetResultSetExtractor + * @see javax.sql.rowset.CachedRowSet + */ + SqlRowSet queryForRowSet(String sql, Long tenantId) throws DataAccessException; + + /** + * Issue a single SQL update operation (such as an insert, update or delete statement). + * @param sql static SQL to execute + * @return the number of rows affected + * @throws DataAccessException if there is any problem. + */ + int update(String sql, Long tenantId) throws DataAccessException; + + /** + * Issue multiple SQL updates on a single JDBC Statement using batching. + *

Will fall back to separate updates on a single Statement if the JDBC + * driver does not support batch updates. + * @param sql defining an array of SQL statements that will be executed. + * @return an array of the number of rows affected by each statement + * @throws DataAccessException if there is any problem executing the batch + */ + int[] batchUpdate(Long tenantId, String... sql) throws DataAccessException; + + + //------------------------------------------------------------------------- + // Methods dealing with prepared statements + //------------------------------------------------------------------------- + + /** + * Execute a JDBC data access operation, implemented as callback action + * working on a JDBC PreparedStatement. This allows for implementing arbitrary + * data access operations on a single Statement, within Spring's managed JDBC + * environment: that is, participating in Spring-managed transactions and + * converting JDBC SQLExceptions into Spring's DataAccessException hierarchy. + *

The callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param psc a callback that creates a PreparedStatement given a Connection + * @param action a callback that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(PreparedStatementCreator psc, PreparedStatementCallback action, Long tenantId) throws DataAccessException; + + /** + * Execute a JDBC data access operation, implemented as callback action + * working on a JDBC PreparedStatement. This allows for implementing arbitrary + * data access operations on a single Statement, within Spring's managed JDBC + * environment: that is, participating in Spring-managed transactions and + * converting JDBC SQLExceptions into Spring's DataAccessException hierarchy. + *

The callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param sql the SQL to execute + * @param action a callback that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(String sql, PreparedStatementCallback action, Long tenantId) throws DataAccessException; + + /** + * Query using a prepared statement, reading the ResultSet with a ResultSetExtractor. + *

A PreparedStatementCreator can either be implemented directly or + * configured through a PreparedStatementCreatorFactory. + * @param psc a callback that creates a PreparedStatement given a Connection + * @param rse a callback that will extract results + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if there is any problem + * @see PreparedStatementCreatorFactory + */ + @Nullable + T query(PreparedStatementCreator psc, ResultSetExtractor rse, Long tenantId) throws DataAccessException; + + /** + * Query using a prepared statement, reading the ResultSet with a ResultSetExtractor. + * @param sql the SQL query to execute + * @param pss a callback that knows how to set values on the prepared statement. + * If this is {@code null}, the SQL will be assumed to contain no bind parameters. + * Even if there are no bind parameters, this callback may be used to set the + * fetch size and other performance options. + * @param rse a callback that will extract results + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if there is any problem + */ + @Nullable + T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor rse, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of arguments + * to bind to the query, reading the ResultSet with a ResultSetExtractor. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param rse a callback that will extract results + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if the query fails + * @see java.sql.Types + */ + @Nullable + T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor rse, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of arguments + * to bind to the query, reading the ResultSet with a ResultSetExtractor. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param rse a callback that will extract results + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if the query fails + */ + @Nullable + T query(String sql, Object[] args, ResultSetExtractor rse, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of arguments + * to bind to the query, reading the ResultSet with a ResultSetExtractor. + * @param sql the SQL query to execute + * @param rse a callback that will extract results + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return an arbitrary result object, as returned by the ResultSetExtractor + * @throws DataAccessException if the query fails + * @since 3.0.1 + */ + @Nullable + T query(String sql, ResultSetExtractor rse, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query using a prepared statement, reading the ResultSet on a per-row basis + * with a RowCallbackHandler. + *

A PreparedStatementCreator can either be implemented directly or + * configured through a PreparedStatementCreatorFactory. + * @param psc a callback that creates a PreparedStatement given a Connection + * @param rch a callback that will extract results, one row at a time + * @throws DataAccessException if there is any problem + * @see PreparedStatementCreatorFactory + */ + void query(PreparedStatementCreator psc, RowCallbackHandler rch, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a + * PreparedStatementSetter implementation that knows how to bind values to the + * query, reading the ResultSet on a per-row basis with a RowCallbackHandler. + * @param sql the SQL query to execute + * @param pss a callback that knows how to set values on the prepared statement. + * If this is {@code null}, the SQL will be assumed to contain no bind parameters. + * Even if there are no bind parameters, this callback may be used to set the + * fetch size and other performance options. + * @param rch a callback that will extract results, one row at a time + * @throws DataAccessException if the query fails + */ + void query(String sql, @Nullable PreparedStatementSetter pss, RowCallbackHandler rch, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, reading the ResultSet on a per-row basis + * with a RowCallbackHandler. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param rch a callback that will extract results, one row at a time + * @throws DataAccessException if the query fails + * @see java.sql.Types + */ + void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, reading the ResultSet on a per-row basis + * with a RowCallbackHandler. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param rch a callback that will extract results, one row at a time + * @throws DataAccessException if the query fails + */ + void query(String sql, Object[] args, RowCallbackHandler rch, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, reading the ResultSet on a per-row basis + * with a RowCallbackHandler. + * @param sql the SQL query to execute + * @param rch a callback that will extract results, one row at a time + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @throws DataAccessException if the query fails + * @since 3.0.1 + */ + void query(String sql, RowCallbackHandler rch, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query using a prepared statement, mapping each row to a result object + * via a RowMapper. + *

A PreparedStatementCreator can either be implemented directly or + * configured through a PreparedStatementCreatorFactory. + * @param psc a callback that creates a PreparedStatement given a Connection + * @param rowMapper a callback that will map one object per row + * @return the result List, containing mapped objects + * @throws DataAccessException if there is any problem + * @see PreparedStatementCreatorFactory + */ + List query(PreparedStatementCreator psc, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a + * PreparedStatementSetter implementation that knows how to bind values + * to the query, mapping each row to a result object via a RowMapper. + * @param sql the SQL query to execute + * @param pss a callback that knows how to set values on the prepared statement. + * If this is {@code null}, the SQL will be assumed to contain no bind parameters. + * Even if there are no bind parameters, this callback may be used to set the + * fetch size and other performance options. + * @param rowMapper a callback that will map one object per row + * @return the result List, containing mapped objects + * @throws DataAccessException if the query fails + */ + List query(String sql, @Nullable PreparedStatementSetter pss, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, mapping each row to a result object + * via a RowMapper. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param rowMapper a callback that will map one object per row + * @return the result List, containing mapped objects + * @throws DataAccessException if the query fails + * @see java.sql.Types + */ + List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, mapping each row to a result object + * via a RowMapper. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param rowMapper a callback that will map one object per row + * @return the result List, containing mapped objects + * @throws DataAccessException if the query fails + */ + List query(String sql, Object[] args, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, mapping each row to a result object + * via a RowMapper. + * @param sql the SQL query to execute + * @param rowMapper a callback that will map one object per row + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return the result List, containing mapped objects + * @throws DataAccessException if the query fails + * @since 3.0.1 + */ + List query(String sql, RowMapper rowMapper, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list + * of arguments to bind to the query, mapping a single result row to a + * result object via a RowMapper. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type) + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param rowMapper a callback that will map one object per row + * @return the single mapped object (may be {@code null} if the given + * {@link RowMapper} returned {@code} null) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if the query fails + */ + @Nullable + T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper, Long tenantId) + throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list + * of arguments to bind to the query, mapping a single result row to a + * result object via a RowMapper. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param rowMapper a callback that will map one object per row + * @return the single mapped object (may be {@code null} if the given + * {@link RowMapper} returned {@code} null) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if the query fails + */ + @Nullable + T queryForObject(String sql, Object[] args, RowMapper rowMapper, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list + * of arguments to bind to the query, mapping a single result row to a + * result object via a RowMapper. + * @param sql the SQL query to execute + * @param rowMapper a callback that will map one object per row + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return the single mapped object (may be {@code null} if the given + * {@link RowMapper} returned {@code} null) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if the query fails + * @since 3.0.1 + */ + @Nullable + T queryForObject(String sql, RowMapper rowMapper, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result object. + *

The query is expected to be a single row/single column query; the returned + * result will be directly mapped to the corresponding object type. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param requiredType the type that the result object is expected to match + * @return the result object of the required type, or {@code null} in case of SQL NULL + * @throws IncorrectResultSizeDataAccessException if the query does not return + * exactly one row, or does not return exactly one column in that row + * @throws DataAccessException if the query fails + * @see #queryForObject(String, Class) + * @see java.sql.Types + */ + @Nullable + T queryForObject(String sql, Object[] args, int[] argTypes, Class requiredType, Long tenantId) + throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result object. + *

The query is expected to be a single row/single column query; the returned + * result will be directly mapped to the corresponding object type. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param requiredType the type that the result object is expected to match + * @return the result object of the required type, or {@code null} in case of SQL NULL + * @throws IncorrectResultSizeDataAccessException if the query does not return + * exactly one row, or does not return exactly one column in that row + * @throws DataAccessException if the query fails + * @see #queryForObject(String, Class) + */ + @Nullable + T queryForObject(String sql, Object[] args, Class requiredType, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result object. + *

The query is expected to be a single row/single column query; the returned + * result will be directly mapped to the corresponding object type. + * @param sql the SQL query to execute + * @param requiredType the type that the result object is expected to match + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return the result object of the required type, or {@code null} in case of SQL NULL + * @throws IncorrectResultSizeDataAccessException if the query does not return + * exactly one row, or does not return exactly one column in that row + * @throws DataAccessException if the query fails + * @since 3.0.1 + * @see #queryForObject(String, Class) + */ + @Nullable + T queryForObject(String sql, Class requiredType, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result map. + *

The query is expected to be a single row query; the result row will be + * mapped to a Map (one entry for each column, using the column name as the key). + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @return the result Map (one entry per column, with column name as key) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if the query fails + * @see #queryForMap(String) + * @see ColumnMapRowMapper + * @see java.sql.Types + */ + Map queryForMap(String sql, Object[] args, int[] argTypes, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result map. + *

The {@code queryForMap} methods defined by this interface are appropriate + * when you don't have a domain model. Otherwise, consider using one of the + * {@code queryForObject} methods. + *

The query is expected to be a single row query; the result row will be + * mapped to a Map (one entry for each column, using the column name as the key). + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return the result Map (one entry for each column, using the + * column name as the key) + * @throws IncorrectResultSizeDataAccessException if the query does not + * return exactly one row + * @throws DataAccessException if the query fails + * @see #queryForMap(String) + * @see ColumnMapRowMapper + */ + Map queryForMap(String sql, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * result objects, each of them matching the specified element type. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @param elementType the required type of element in the result list + * (for example, {@code Integer.class}) + * @return a List of objects that match the specified element type + * @throws DataAccessException if the query fails + * @see #queryForList(String, Class) + * @see SingleColumnRowMapper + */ + List queryForList(String sql, Object[] args, int[] argTypes, Class elementType, Long tenantId) + throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * result objects, each of them matching the specified element type. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @param elementType the required type of element in the result list + * (for example, {@code Integer.class}) + * @return a List of objects that match the specified element type + * @throws DataAccessException if the query fails + * @see #queryForList(String, Class) + * @see SingleColumnRowMapper + */ + List queryForList(String sql, Object[] args, Class elementType, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * result objects, each of them matching the specified element type. + * @param sql the SQL query to execute + * @param elementType the required type of element in the result list + * (for example, {@code Integer.class}) + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return a List of objects that match the specified element type + * @throws DataAccessException if the query fails + * @since 3.0.1 + * @see #queryForList(String, Class) + * @see SingleColumnRowMapper + */ + List queryForList(String sql, Class elementType, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * Maps (one entry for each column, using the column name as the key). + * Each element in the list will be of the form returned by this interface's + * {@code queryForMap} methods. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @return a List that contains a Map per row + * @throws DataAccessException if the query fails + * @see #queryForList(String) + * @see java.sql.Types + */ + List> queryForList(String sql, Object[] args, int[] argTypes, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * Maps (one entry for each column, using the column name as the key). + * Each element in the list will be of the form returned by this interface's + * {@code queryForMap} methods. + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return a List that contains a Map per row + * @throws DataAccessException if the query fails + * @see #queryForList(String) + */ + List> queryForList(String sql, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a SqlRowSet. + *

The results will be mapped to an SqlRowSet which holds the data in a + * disconnected fashion. This wrapper will translate any SQLExceptions thrown. + *

Note that, for the default implementation, JDBC RowSet support needs to + * be available at runtime: by default, Sun's {@code com.sun.rowset.CachedRowSetImpl} + * class is used, which is part of JDK 1.5+ and also available separately as part of + * Sun's JDBC RowSet Implementations download (rowset.jar). + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @return a SqlRowSet representation (possibly a wrapper around a + * {@code javax.sql.rowset.CachedRowSet}) + * @throws DataAccessException if there is any problem executing the query + * @see #queryForRowSet(String) + * @see SqlRowSetResultSetExtractor + * @see javax.sql.rowset.CachedRowSet + * @see java.sql.Types + */ + SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes, Long tenantId) throws DataAccessException; + + /** + * Query given SQL to create a prepared statement from SQL and a list of + * arguments to bind to the query, expecting a SqlRowSet. + *

The results will be mapped to an SqlRowSet which holds the data in a + * disconnected fashion. This wrapper will translate any SQLExceptions thrown. + *

Note that, for the default implementation, JDBC RowSet support needs to + * be available at runtime: by default, Sun's {@code com.sun.rowset.CachedRowSetImpl} + * class is used, which is part of JDK 1.5+ and also available separately as part of + * Sun's JDBC RowSet Implementations download (rowset.jar). + * @param sql the SQL query to execute + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return a SqlRowSet representation (possibly a wrapper around a + * {@code javax.sql.rowset.CachedRowSet}) + * @throws DataAccessException if there is any problem executing the query + * @see #queryForRowSet(String) + * @see SqlRowSetResultSetExtractor + * @see javax.sql.rowset.CachedRowSet + */ + SqlRowSet queryForRowSet(String sql, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Issue a single SQL update operation (such as an insert, update or delete + * statement) using a PreparedStatementCreator to provide SQL and any + * required parameters. + *

A PreparedStatementCreator can either be implemented directly or + * configured through a PreparedStatementCreatorFactory. + * @param psc a callback that provides SQL and any necessary parameters + * @return the number of rows affected + * @throws DataAccessException if there is any problem issuing the update + * @see PreparedStatementCreatorFactory + */ + int update(PreparedStatementCreator psc, Long tenantId) throws DataAccessException; + + /** + * Issue an update statement using a PreparedStatementCreator to provide SQL and + * any required parameters. Generated keys will be put into the given KeyHolder. + *

Note that the given PreparedStatementCreator has to create a statement + * with activated extraction of generated keys (a JDBC 3.0 feature). This can + * either be done directly or through using a PreparedStatementCreatorFactory. + * @param psc a callback that provides SQL and any necessary parameters + * @param generatedKeyHolder a KeyHolder that will hold the generated keys + * @return the number of rows affected + * @throws DataAccessException if there is any problem issuing the update + * @see PreparedStatementCreatorFactory + * @see org.springframework.jdbc.support.GeneratedKeyHolder + */ + int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder, Long tenantId) throws DataAccessException; + + /** + * Issue an update statement using a PreparedStatementSetter to set bind parameters, + * with given SQL. Simpler than using a PreparedStatementCreator as this method + * will create the PreparedStatement: The PreparedStatementSetter just needs to + * set parameters. + * @param sql the SQL containing bind parameters + * @param pss helper that sets bind parameters. If this is {@code null} + * we run an update with static SQL. + * @return the number of rows affected + * @throws DataAccessException if there is any problem issuing the update + */ + int update(String sql, @Nullable PreparedStatementSetter pss, Long tenantId) throws DataAccessException; + + /** + * Issue a single SQL update operation (such as an insert, update or delete statement) + * via a prepared statement, binding the given arguments. + * @param sql the SQL containing bind parameters + * @param args arguments to bind to the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @return the number of rows affected + * @throws DataAccessException if there is any problem issuing the update + * @see java.sql.Types + */ + int update(String sql, Object[] args, int[] argTypes, Long tenantId) throws DataAccessException; + + /** + * Issue a single SQL update operation (such as an insert, update or delete statement) + * via a prepared statement, binding the given arguments. + * @param sql the SQL containing bind parameters + * @param args arguments to bind to the query + * (leaving it to the PreparedStatement to guess the corresponding SQL type); + * may also contain {@link SqlParameterValue} objects which indicate not + * only the argument value but also the SQL type and optionally the scale + * @return the number of rows affected + * @throws DataAccessException if there is any problem issuing the update + */ + int update(String sql, Long tenantId, @Nullable Object... args) throws DataAccessException; + + /** + * Issue multiple update statements on a single PreparedStatement, + * using batch updates and a BatchPreparedStatementSetter to set values. + *

Will fall back to separate updates on a single PreparedStatement + * if the JDBC driver does not support batch updates. + * @param sql defining PreparedStatement that will be reused. + * All statements in the batch will use the same SQL. + * @param pss object to set parameters on the PreparedStatement + * created by this method + * @return an array of the number of rows affected by each statement + * @throws DataAccessException if there is any problem issuing the update + */ + int[] batchUpdate(String sql, BatchPreparedStatementSetter pss, Long tenantId) throws DataAccessException; + + /** + * Execute a batch using the supplied SQL statement with the batch of supplied arguments. + * @param sql the SQL statement to execute + * @param batchArgs the List of Object arrays containing the batch of arguments for the query + * @return an array containing the numbers of rows affected by each update in the batch + */ + int[] batchUpdate(String sql, List batchArgs, Long tenantId) throws DataAccessException; + + /** + * Execute a batch using the supplied SQL statement with the batch of supplied arguments. + * @param sql the SQL statement to execute. + * @param batchArgs the List of Object arrays containing the batch of arguments for the query + * @param argTypes the SQL types of the arguments + * (constants from {@code java.sql.Types}) + * @return an array containing the numbers of rows affected by each update in the batch + */ + int[] batchUpdate(String sql, List batchArgs, int[] argTypes, Long tenantId) throws DataAccessException; + + /** + * Execute multiple batches using the supplied SQL statement with the collect of supplied arguments. + * The arguments' values will be set using the ParameterizedPreparedStatementSetter. + * Each batch should be of size indicated in 'batchSize'. + * @param sql the SQL statement to execute. + * @param batchArgs the List of Object arrays containing the batch of arguments for the query + * @param batchSize batch size + * @param pss the ParameterizedPreparedStatementSetter to use + * @return an array containing for each batch another array containing the numbers of rows affected + * by each update in the batch + * @since 3.1 + */ + int[][] batchUpdate(String sql, Collection batchArgs, int batchSize, + ParameterizedPreparedStatementSetter pss, Long tenantId) throws DataAccessException; + + + //------------------------------------------------------------------------- + // Methods dealing with callable statements + //------------------------------------------------------------------------- + + /** + * Execute a JDBC data access operation, implemented as callback action + * working on a JDBC CallableStatement. This allows for implementing arbitrary + * data access operations on a single Statement, within Spring's managed JDBC + * environment: that is, participating in Spring-managed transactions and + * converting JDBC SQLExceptions into Spring's DataAccessException hierarchy. + *

The callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param csc a callback that creates a CallableStatement given a Connection + * @param action a callback that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(CallableStatementCreator csc, CallableStatementCallback action, Long tenantId) throws DataAccessException; + + /** + * Execute a JDBC data access operation, implemented as callback action + * working on a JDBC CallableStatement. This allows for implementing arbitrary + * data access operations on a single Statement, within Spring's managed JDBC + * environment: that is, participating in Spring-managed transactions and + * converting JDBC SQLExceptions into Spring's DataAccessException hierarchy. + *

The callback action can return a result object, for example a domain + * object or a collection of domain objects. + * @param callString the SQL call string to execute + * @param action a callback that specifies the action + * @return a result object returned by the action, or {@code null} if none + * @throws DataAccessException if there is any problem + */ + @Nullable + T execute(String callString, CallableStatementCallback action, Long tenantId) throws DataAccessException; + + /** + * Execute a SQL call using a CallableStatementCreator to provide SQL and + * any required parameters. + * @param csc a callback that provides SQL and any necessary parameters + * @param declaredParameters list of declared SqlParameter objects + * @return a Map of extracted out parameters + * @throws DataAccessException if there is any problem issuing the update + */ + Map call(CallableStatementCreator csc, List declaredParameters, Long tenantId) throws DataAccessException; +} diff --git a/src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenantImpl.java b/src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenantImpl.java new file mode 100644 index 0000000..4cbeda8 --- /dev/null +++ b/src/main/java/com/taover/repository/jdbctemplate/JdbcTemplateWrapperTenantImpl.java @@ -0,0 +1,662 @@ +package com.taover.repository.jdbctemplate; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Resource; + +import org.apache.shardingsphere.api.hint.HintManager; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.CallableStatementCallback; +import org.springframework.jdbc.core.CallableStatementCreator; +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; +import org.springframework.jdbc.core.PreparedStatementCallback; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.PreparedStatementSetter; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowCallbackHandler; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.jdbc.core.StatementCallback; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.jdbc.support.rowset.SqlRowSet; + +import com.taover.repository.shardingsphere.ShardingSphereService; + +public class JdbcTemplateWrapperTenantImpl implements JdbcTemplateWrapperTenant { + @Resource + private JdbcTemplate jdbcTemplate; + @Resource + private ShardingSphereService shardingSphereService; + + private void loadShardingInfo(Long tenantId) { + Map shardingInfo = this.shardingSphereService.getShardingInfoMapByTenantId(tenantId); + if(shardingInfo == null || shardingInfo.isEmpty()) { + return; + } + HintManager.clear(); + for(Entry item: shardingInfo.entrySet()) { + HintManager.getInstance().addTableShardingValue(item.getKey(), item.getValue()); + } + } + + private void clearShardingInfo() { + HintManager.clear(); + } + + @Override + public E execute(ConnectionCallback action, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E execute(StatementCallback action, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void execute(String sql, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.execute(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(String sql, ResultSetExtractor rse, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, rse); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(String sql, RowCallbackHandler rch, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(sql, rch); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(String sql, RowMapper rowMapper, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, RowMapper rowMapper, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Class requiredType, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, requiredType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public Map queryForMap(String sql, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForMap(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List queryForList(String sql, Class elementType, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, elementType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List> queryForList(String sql, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public SqlRowSet queryForRowSet(String sql, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForRowSet(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(String sql, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int[] batchUpdate(Long tenantId, String... sql) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.batchUpdate(sql); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E execute(PreparedStatementCreator psc, PreparedStatementCallback action, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(psc, action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E execute(String sql, PreparedStatementCallback action, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(sql, action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(PreparedStatementCreator psc, ResultSetExtractor rse, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(psc, rse); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, pss, rse); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(String sql, Object[] args, int[] argTypes, ResultSetExtractor rse, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, args, argTypes, rse); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(String sql, Object[] args, ResultSetExtractor rse, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, args, rse); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E query(String sql, ResultSetExtractor rse, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, rse, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(PreparedStatementCreator psc, RowCallbackHandler rch, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(psc, rch); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(sql, pss, rch); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(sql, args, argTypes, rch); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(String sql, Object[] args, RowCallbackHandler rch, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(sql, args, rch); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public void query(String sql, RowCallbackHandler rch, Long tenantId, Object... args) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + this.jdbcTemplate.query(sql, rch, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(PreparedStatementCreator psc, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(psc, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, pss, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, args, argTypes, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(String sql, Object[] args, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, args, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List query(String sql, RowMapper rowMapper, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.query(sql, rowMapper, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, args, argTypes, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Object[] args, RowMapper rowMapper, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, args, rowMapper); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, RowMapper rowMapper, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, rowMapper, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Object[] args, int[] argTypes, Class requiredType, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, args, argTypes, requiredType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Object[] args, Class requiredType, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, args, requiredType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E queryForObject(String sql, Class requiredType, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForObject(sql, requiredType, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public Map queryForMap(String sql, Object[] args, int[] argTypes, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForMap(sql, args, argTypes); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public Map queryForMap(String sql, Long tenantId, Object... args) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForMap(sql, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List queryForList(String sql, Object[] args, int[] argTypes, Class elementType, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, args, argTypes, elementType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List queryForList(String sql, Object[] args, Class elementType, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, args, elementType); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List queryForList(String sql, Class elementType, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, elementType, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List> queryForList(String sql, Object[] args, int[] argTypes, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, args, argTypes); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public List> queryForList(String sql, Long tenantId, Object... args) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForList(sql, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForRowSet(sql, args, argTypes); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public SqlRowSet queryForRowSet(String sql, Long tenantId, Object... args) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.queryForRowSet(sql, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(PreparedStatementCreator psc, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(psc); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(psc, generatedKeyHolder); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(String sql, PreparedStatementSetter pss, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(sql, pss); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(String sql, Object[] args, int[] argTypes, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(sql, args, argTypes); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int update(String sql, Long tenantId, Object... args) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.update(sql, args); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int[] batchUpdate(String sql, BatchPreparedStatementSetter pss, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.batchUpdate(sql, pss); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int[] batchUpdate(String sql, List batchArgs, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.batchUpdate(sql, batchArgs); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int[] batchUpdate(String sql, List batchArgs, int[] argTypes, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.batchUpdate(sql, batchArgs); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public int[][] batchUpdate(String sql, Collection batchArgs, int batchSize, + ParameterizedPreparedStatementSetter pss, Long tenantId) throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.batchUpdate(sql, batchArgs, batchSize, pss); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E execute(CallableStatementCreator csc, CallableStatementCallback action, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(csc, action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public E execute(String callString, CallableStatementCallback action, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.execute(callString, action); + }finally { + this.clearShardingInfo(); + } + } + + @Override + public Map call(CallableStatementCreator csc, List declaredParameters, Long tenantId) + throws DataAccessException { + try { + this.loadShardingInfo(tenantId); + return this.jdbcTemplate.call(csc, declaredParameters); + }finally { + this.clearShardingInfo(); + } + } +} diff --git a/src/main/java/com/taover/repository/shardingsphere/ShardingAlgorithmHint.java b/src/main/java/com/taover/repository/shardingsphere/ShardingAlgorithmHint.java new file mode 100644 index 0000000..8135cd5 --- /dev/null +++ b/src/main/java/com/taover/repository/shardingsphere/ShardingAlgorithmHint.java @@ -0,0 +1,33 @@ +package com.taover.repository.shardingsphere; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.hint.HintShardingValue; + +public class ShardingAlgorithmHint implements HintShardingAlgorithm { + Log log = LogFactory.getLog(ShardingAlgorithmHint.class); + + public ShardingAlgorithmHint() { + System.out.println("ShardingAlgorithmHint()"); + } + + @Override + public Collection doSharding(Collection availableTargetNames, HintShardingValue shardingValue) { + Collection values = shardingValue.getValues(); + if(values == null || values.isEmpty()) { + String message = shardingValue.getLogicTableName()+"已启用分库分表,SQL语句请指定tenant_id 或 使用封装好的DAO层方法"; + log.error(message); + throw new RuntimeException(message); + } + Set result = new HashSet(availableTargetNames); + for(String item: values) { + result.add(shardingValue.getLogicTableName()+item); + } + return result; + } +} diff --git a/src/main/java/com/taover/repository/shardingsphere/ShardingInfoEntity.java b/src/main/java/com/taover/repository/shardingsphere/ShardingInfoEntity.java new file mode 100644 index 0000000..c1aab15 --- /dev/null +++ b/src/main/java/com/taover/repository/shardingsphere/ShardingInfoEntity.java @@ -0,0 +1,106 @@ +package com.taover.repository.shardingsphere; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Id; +import javax.persistence.Column; + +/** + * @version 1.0.0 + */ +@Entity +@Table(name="sharding_info", catalog="") +public class ShardingInfoEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + /** + * + */ + @Id + @Column(name="id") + private java.lang.Long id; + + public java.lang.Long getId(){ + return id; + } + public void setId(java.lang.Long id){ + this.id = id; + } + + /** + * 表名 + */ + @Column(name="table_name") + private java.lang.String tableName; + + public java.lang.String getTableName(){ + return tableName; + } + public void setTableName(java.lang.String tableName){ + this.tableName = tableName; + } + + /** + * 租户ID + */ + @Column(name="tenant_id") + private java.lang.Long tenantId; + + public java.lang.Long getTenantId(){ + return tenantId; + } + public void setTenantId(java.lang.Long tenantId){ + this.tenantId = tenantId; + } + + /** + * 后缀 + */ + @Column(name="sharding_suffix") + private java.lang.String shardingSuffix; + + public java.lang.String getShardingSuffix(){ + return shardingSuffix; + } + public void setShardingSuffix(java.lang.String shardingSuffix){ + this.shardingSuffix = shardingSuffix; + } + + /** + * + */ + @Column(name="create_time") + private java.sql.Timestamp createTime; + + public java.sql.Timestamp getCreateTime(){ + return createTime; + } + public void setCreateTime(java.sql.Timestamp createTime){ + this.createTime = createTime; + } + + /** + * + */ + @Column(name="update_time") + private java.sql.Timestamp updateTime; + + public java.sql.Timestamp getUpdateTime(){ + return updateTime; + } + public void setUpdateTime(java.sql.Timestamp updateTime){ + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "ShardingInfoEntity: [id="+id+",tableName="+tableName+",tenantId="+tenantId+",shardingSuffix="+shardingSuffix+",createTime="+createTime+",updateTime="+updateTime+"]"; + } + } diff --git a/src/main/java/com/taover/repository/shardingsphere/ShardingInfoRepository.java b/src/main/java/com/taover/repository/shardingsphere/ShardingInfoRepository.java new file mode 100644 index 0000000..36ec381 --- /dev/null +++ b/src/main/java/com/taover/repository/shardingsphere/ShardingInfoRepository.java @@ -0,0 +1,11 @@ +package com.taover.repository.shardingsphere; + +import com.taover.repository.CustomJdbcTemplate; + +public class ShardingInfoRepository extends CustomJdbcTemplate{ + + public ShardingInfoRepository() throws Exception { + super(); + } + +} diff --git a/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorExt.java b/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorExt.java new file mode 100644 index 0000000..a1a9778 --- /dev/null +++ b/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorExt.java @@ -0,0 +1,21 @@ +package com.taover.repository.shardingsphere; + +import java.util.List; + +import com.taover.repository.autoconfigure.ShardingSphereKeyGeneratorConfiguration; + +public interface ShardingKeyGeneratorExt { + /** + * 生成ID List + * @param shardingOffset 分片索引 + * @param genNum 生成数量 + * @return + */ + List generateKeyList(int genNum); + + /** + * 设置属性值 + * @param config + */ + void loalConfig(ShardingSphereKeyGeneratorConfiguration config); +} diff --git a/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorImpl.java b/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorImpl.java new file mode 100644 index 0000000..0c9be75 --- /dev/null +++ b/src/main/java/com/taover/repository/shardingsphere/ShardingKeyGeneratorImpl.java @@ -0,0 +1,212 @@ +package com.taover.repository.shardingsphere; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Properties; + +import org.apache.shardingsphere.core.strategy.keygen.TimeService; +import org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator; + +import com.google.common.base.Preconditions; +import com.taover.repository.autoconfigure.ShardingSphereKeyGeneratorConfiguration; + +/** + * Snowflake distributed primary key generator. + * + *

+ * Use snowflake algorithm. Length is 64 bit. + *

+ * + *
+ * 1bit sign bit.
+ * 41bits timestamp offset from 2016.11.01(ShardingSphere distributed primary key published data) to now.
+ * 10bits worker process id.
+ * 12bits auto increment offset in one mills
+ * 
+ * + *

+ * Call @{@code SnowflakeShardingKeyGenerator.setWorkerId} to set worker id, default value is 0. + *

+ * + *

+ * Call @{@code SnowflakeShardingKeyGenerator.setMaxTolerateTimeDifferenceMilliseconds} to set max tolerate time difference milliseconds, default value is 0. + *

+ */ +public final class ShardingKeyGeneratorImpl implements ShardingKeyGenerator, ShardingKeyGeneratorExt { + + public static final long EPOCH; + + private static final long SEQUENCE_BITS = 11L; + + private static final long BATCH_FLAG_BITS = 1L; + + private static final long WORKER_ID_BITS = 10L; + + private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1; + + private static final long BATCH_FLAG_LEFT_SHIFT_BITS = SEQUENCE_BITS; + + private static final long WORKER_ID_LEFT_SHIFT_BITS = BATCH_FLAG_LEFT_SHIFT_BITS + BATCH_FLAG_BITS; + + private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS; + + private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS; + + private static final long WORKER_ID = 0; + + private static final int DEFAULT_VIBRATION_VALUE = 1; + + private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS = 10; + + private static TimeService timeService = new TimeService(); + + private Properties properties = new Properties(); + + private int sequenceOffset = -1; + + private long sequence; + + private long lastMilliseconds; + + static { + Calendar calendar = Calendar.getInstance(); + calendar.set(2016, Calendar.NOVEMBER, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + EPOCH = calendar.getTimeInMillis(); + } + + @Override + public String getType() { + return "SNOWFLAKE-SELF"; + } + + @Override + public synchronized Comparable generateKey() { + long currentMilliseconds = timeService.getCurrentMillis(); + if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) { + currentMilliseconds = timeService.getCurrentMillis(); + } + if (lastMilliseconds == currentMilliseconds) { + if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) { + currentMilliseconds = waitUntilNextTime(currentMilliseconds); + } + } else { + vibrateSequenceOffset(); + sequence = sequenceOffset; + } + lastMilliseconds = currentMilliseconds; + return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | (0 << BATCH_FLAG_LEFT_SHIFT_BITS) | sequence; + } + + private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) { + if (lastMilliseconds <= currentMilliseconds) { + return false; + } + long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds; + Preconditions.checkState(timeDifferenceMilliseconds < getMaxTolerateTimeDifferenceMilliseconds(), + "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds); + try { + Thread.sleep(timeDifferenceMilliseconds); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } + + private long getWorkerId() { + long result = Long.valueOf(properties.getProperty("worker.id", String.valueOf(WORKER_ID))); + Preconditions.checkArgument(result >= 0L && result < WORKER_ID_MAX_VALUE); + return result; + } + + private int getMaxVibrationOffset() { + int result = Integer.parseInt(properties.getProperty("max.vibration.offset", String.valueOf(DEFAULT_VIBRATION_VALUE))); + Preconditions.checkArgument(result >= 0 && result <= SEQUENCE_MASK, "Illegal max vibration offset"); + return result; + } + + private int getMaxTolerateTimeDifferenceMilliseconds() { + return Integer.valueOf(properties.getProperty("max.tolerate.time.difference.milliseconds", String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS))); + } + + private long waitUntilNextTime(final long lastTime) { + long result = timeService.getCurrentMillis(); + while (result <= lastTime) { + result = timeService.getCurrentMillis(); + } + return result; + } + + private void vibrateSequenceOffset() { + sequenceOffset = sequenceOffset >= getMaxVibrationOffset() ? 0 : sequenceOffset + 1; + } + + @Override + public List generateKeyList(int genNum) { + long currentMilliseconds = timeService.getCurrentMillis(); + if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) { + currentMilliseconds = timeService.getCurrentMillis(); + } + List data = new ArrayList(genNum); + for(int i=0; i> CACHED_TABLE_SUFFIX_BY_TENANT = null; + private Map GENERATOR_HOLDER = new HashMap(); + + @Resource + private ShardingInfoRepository shardingInfoRepository; + private ShardingSphereKeyGeneratorConfiguration config; + + public ShardingSphereService(ShardingSphereKeyGeneratorConfiguration config) { + this.config = config; + } + + public List generateKeyList(String tableName, int number){ + if(!GENERATOR_HOLDER.containsKey(tableName)) { + loadShardingKeyGenerator(tableName); + } + return GENERATOR_HOLDER.get(tableName).generateKeyList(number); + } + + public Map getShardingInfoMapByTenantId(Long tenantId) { + if(CACHED_TABLE_SUFFIX_BY_TENANT == null) { + loadCacheTableShardingInfoByTenantId(); + } + return CACHED_TABLE_SUFFIX_BY_TENANT.get(tenantId); + } + + private synchronized void loadCacheTableShardingInfoByTenantId() { + if(CACHED_TABLE_SUFFIX_BY_TENANT != null) { + return; + } + List dataList = this.shardingInfoRepository.findListBySql("1=1"); + Map> tempData = new HashMap>(); + for(ShardingInfoEntity item: dataList) { + Map tempItem = tempData.getOrDefault(item.getTenantId(), new HashMap()); + tempItem.put(item.getTableName(), item.getShardingSuffix()); + tempData.put(item.getTenantId(), tempItem); + } + CACHED_TABLE_SUFFIX_BY_TENANT = tempData; + } + + private synchronized void loadShardingKeyGenerator(String tableName) { + if(GENERATOR_HOLDER.containsKey(tableName)) { + return; + } + ShardingKeyGeneratorExt generator = new ShardingKeyGeneratorImpl(); + generator.loalConfig(config); + GENERATOR_HOLDER.put(tableName, generator); + } +} diff --git a/src/main/java/com/taover/repository/util/UtilsSql.java b/src/main/java/com/taover/repository/util/UtilsSql.java new file mode 100644 index 0000000..545fa5e --- /dev/null +++ b/src/main/java/com/taover/repository/util/UtilsSql.java @@ -0,0 +1,194 @@ +package com.taover.repository.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import org.springframework.util.StringUtils; + +import com.taover.repository.exception.SqlParsePagerException; + +public class UtilsSql { + + public static String[] splitCoreSql(String coreSql) throws SqlParsePagerException { + //去除''内的信息 + int fromIndex = calcFromIndex(coreSql); + if(fromIndex > -1) { + return new String[] {coreSql.substring(0, fromIndex), coreSql.substring(fromIndex, coreSql.length())}; + }else { + throw new SqlParsePagerException("未找到FROM子句"); + } + } + + private static int calcFromIndex(String coreSql) { + Stack operStack = new Stack(); + String coreSqlUpperCase = coreSql.toUpperCase(); + int sqlLen = coreSqlUpperCase.length(); + int currIndex = 0; + while(currIndex < sqlLen) { + String originContainBlank = subFirstStrEndWithBlank(coreSqlUpperCase, currIndex); + String itemWithoutBlank = originContainBlank.trim(); + if("".equals(itemWithoutBlank)) { + currIndex += originContainBlank.length(); + continue; + } + if(operStack.isEmpty() && "FROM".equals(itemWithoutBlank)) { + return currIndex; + } + dealCoupleMark(itemWithoutBlank, operStack); + currIndex += originContainBlank.length(); + } + return -1; + } + private static void dealCoupleMark(String itemWithoutBlank, Stack operStack) { + char preChar = ' '; + for(int i=0; i createPage(int page, int size, int total, Object data){ + Map pageData = new HashMap(); + pageData.put("page", page); + pageData.put("rows", data); + pageData.put("size", size); + pageData.put("total", total); + return pageData; + } + + public static void main(String[] args) { + String[] testSql = new String[] { + "\\", + "select actionFrom as 'seelctsele\\'ctd',(select a as 'fromCActio' from t) from www where 223=1 ", + "SELECT wxorder_order.refund_way refundWay,wxorder_order.refund_delivery_sn refundDeliverySn, (SELECT sum(wxorder_compensate.`ware_refund_money`) from `wxorder_compensate` where wxorder_compensate.`order_id` = wxorder_order.id) as wareRefundMoney,(SELECT sum(wxorder_compensate.`refund_money`) from `wxorder_compensate` where wxorder_compensate.`order_id` = wxorder_order.id) as refundMoney, wxorder_order.operate_refund_time applyCompensateTime,wxorder_order.refund_instructions refundInstructions, wxorder_order.channel_id channelId, wxorder_channel.name channelName, wxorder_channel.platform_code platformCode, wxorder_order.pre_control_status as refundPreStatus, wxorder_order.ware_id wareId, wxorder_ware.name wareName, wxorder_order.id, wxorder_order.order_sn orderSn, wxorder_order.upload_sn uploadSn, wxorder_order.money_paid moneyPaid, wxorder_order.consignee, wxorder_order.mobile, wxorder_order.province_name provinceName, wxorder_order.city_name cityName, wxorder_order.district_name districtName, wxorder_order.address, wxorder_order.channel_remark channelRemark, wxorder_order.customer_remark customerRemark, wxorder_order.platform_customer_remark platformCustomerRemark, wxorder_order.sender_name senderName, wxorder_order.sender_mobile senderMobile, date_format(wxorder_order.create_time, '%Y-%m-%d %H:%i:%s') createTime, date_format(wxorder_order.real_delivery_time, '%Y-%m-%d %H:%i:%s') realDeliveryTime, wxorder_order.progress_distribute progressDistribute, wxorder_order.progress_delivery progressDelivery, wxorder_order.control_status controlStatus, wxorder_order.express_name expressName, wxorder_order.express_number expressNumber,wxorder_order.shipping_price shippingPrice, wxorder_order.customer_network_name ,(select count(*) from wxorder_compensate WHERE wxorder_compensate.order_id = wxorder_order.id) as compensateCountAll,(select count(*) from wxorder_compensate WHERE wxorder_compensate.order_id = wxorder_order.id and wxorder_compensate.progress_status = 1) as compensateCountDealed ,(select count(*) from wxorder_channel_refund_payment where wxorder_channel_refund_payment.order_id = wxorder_order.id) as channelRefundPaymentStatus,(select count(*) from wxorder_ware_refund_payment where wxorder_ware_refund_payment.order_id = wxorder_order.id) as wareRefundPaymentStatus FROM wxorder_order wxorder_order INNER JOIN wxorder_channel wxorder_channel ON wxorder_order.channel_id=wxorder_channel.id and wxorder_channel.tenant_id=54 INNER JOIN wxorder_ware wxorder_ware ON wxorder_order.ware_id=wxorder_ware.id and wxorder_ware.tenant_id=54 INNER JOIN wxorder_order_goods wxorder_order_goods ON wxorder_order.id=wxorder_order_goods.order_id and wxorder_order_goods.tenant_id=54 WHERE 1=1 AND wxorder_order.tenant_id='54' AND wxorder_order.control_status='4' GROUP BY wxorder_order.id", + "select wdd, fromType from www where 223", + "SELECT excel_data.already_deal_line alreadyDealLine, excel_data.channel_id channelId, excel_data.channel_name channelName, excel_data.contain_express_status containExpressStatus, excel_data.create_time createTime, excel_data.deal_control dealControl, excel_data.deal_status dealStatus, excel_data.excel_path excelPath, excel_data.excel_title_index excelTitleIndex, excel_data.exists_error existsError, excel_data.file_id fileId, excel_data.file_name fileName, excel_data.group_name groupName, excel_data.id, excel_data.key_mapping keyMapping, excel_data.message_ssid messageSsid, excel_data.order_line orderLine, excel_data.payload payload, excel_data.progress_step progressStep, excel_data.select_sheet_name selectSheetName, excel_data.step_deal_time stepDealTime, excel_data.tenant_id tenantId, excel_data.update_time updateTime, excel_data.xls_batch_no xlsBatchNo FROM wxorder_excel_data excel_data WHERE excel_data.tenant_id=16", + "SELECT excel_data.xls_batch_no as 'xlsBatchNo', (select now()) as 'temp' FROM wxorder_excel_data excel_data WHERE excel_data.tenant_id=16" + }; + + for(int i=0; i 0){ + createdAtStart = sdf.parse(createdAtArr[0]); + } + if(createdAtArr.length > 1){ + createdAtEnd = sdf.parse(createdAtArr[1]); + } + } + resultObj[0] = createdAtStart; + resultObj[1] = createdAtEnd; + return resultObj; + } + + /** + * 获取URL path部分 + * @param url + * @return + */ + public static String getUrlPath(String url){ + if(url == null){ + return null; + } + if(url.startsWith("http")){ + try{ + URL dataUrl = new URL(url); + return dataUrl.getPath(); + }catch(Exception e){ + e.printStackTrace(); + } + } + return url; + } + + /** + * 在compares字符数组查找pattern字符串,找到则返回字串在数组中的索引,未找到返回-1 + * @param pattern + * @param compares + * @return + */ + public static int getStringIndex(String pattern, String compares[]){ + //参数检验 + if(pattern==null || compares==null){ + return -1; + } + + //循环遍历compares + for(int i=0; iLong.MAX_VALUE || value0; --i){ + long baseDiv = (long) Math.pow(10, i-1); + long baseRand = (long) Math.pow(10, i); + long currentDigit = value % baseRand / baseDiv; + if(currentDigit == 0){ + result = result + preffixChar; + }else{ + result = result + currentDigit; + } + } + return result; + } + + /** + * 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
+ * 例如:HelloWorld->HELLO_WORLD + * @param name 转换前的驼峰式命名的字符串 + * @return 转换后下划线大写方式命名的字符串 + */ + public static String underscoreName(String name) { + StringBuilder result = new StringBuilder(); + if (name != null && name.length() > 0) { + // 将第一个字符处理成大写 + result.append(name.substring(0, 1).toUpperCase()); + // 循环处理其余字符 + for (int i = 1; i < name.length(); i++) { + String s = name.substring(i, i + 1); + // 在大写字母前添加下划线 + if (Character.isUpperCase(s.charAt(0)) && Character.isLetter(s.charAt(0))) { + result.append("_"); + } + // 其他字符直接转成大写 + result.append(s.toUpperCase()); + } + } + return result.toString(); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:HELLO_WORLD->HelloWorld + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelName(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母小写 + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String camels[] = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 处理真正的驼峰片段 + if (result.length() == 0) { + // 第一个驼峰片段,全部字母都小写 + result.append(camel.toLowerCase()); + } else { + // 其他的驼峰片段,首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + } + return result.toString(); + } + + public static String getRandomStr(int digitNum){ + int numStartZero = 48; + int charStartUpperA = 65; + int charStartLowerA = 97; + int numSelectCount = 10; + int charSelectCount = 26; + int numCount = numSelectCount+2*charSelectCount; + String randomResult = ""; + for(int i=0; i