diff --git a/README.md b/README.md index 455a0db..e9c0d2d 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ ### 本地部署文档详见[ms开发文档](./all/ms/deploy.md) ## 项目概览 - `mybatis-generator-extend-maven-plugin`模块是便于快速根据条件生成mybatis-generator-plugin配置文件的maven扩展插件,基本增 -删改查、按条件分页查询`dao` `servcie` `controller`层利用了词向量、卷积神经网络、使用人工智能技术进行代码语意分析(好吧,我吹不 -下去了)自动生成,自动生成70%代码,降低开发中重复性耗时工作,此插件可在其他项目中单独使用。 +删改查、按条件分页查询`dao` `servcie` `controller`层自动生成,自动生成70%代码,降低开发中重复性耗时工作,此插件可在其他项目中单独使用。 - 合理的拆分,拥有众多的可复用模块,开箱即用 - 高可用注册中心 - 高可用配置配置管理中心,通过`rabbitmq`支持配置刷新 @@ -25,7 +24,7 @@ - 支持docker容器部署 - 使用flume收集系统日志,便于日志分析 - 热点数据使用redis缓存 -- 多数据源支持 +- 多数据库,数据库分表支持 - 完善的项目使用文档 - 用心的开发者 ## 整体技术选型 diff --git a/all/docker/maven/shardbatis-2.0.0B.jar b/all/docker/maven/shardbatis-2.0.0B.jar new file mode 100644 index 0000000..9b5f2cb Binary files /dev/null and b/all/docker/maven/shardbatis-2.0.0B.jar differ diff --git a/all/docker/rabbitmq/rabbitmq-runsh.sh b/all/docker/rabbitmq/rabbitmq-runsh.sh index cafd704..c58a1bb 100644 --- a/all/docker/rabbitmq/rabbitmq-runsh.sh +++ b/all/docker/rabbitmq/rabbitmq-runsh.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 25672:25672 -p 4369:4369 rabbitmq \ No newline at end of file +docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 25672:25672 -p 4369:4369 -p 15672:15672 -p 15671:15671 rabbitmq:3-management \ No newline at end of file diff --git a/all/quickStartDev/startKafka-kafka.sh b/all/quickStartDev/startKafka-kafka.sh new file mode 100644 index 0000000..1d42b22 --- /dev/null +++ b/all/quickStartDev/startKafka-kafka.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +cd /opt/tools/kafka_2.11-1.1.0 +bin/kafka-server-start.sh config/server.properties \ No newline at end of file diff --git a/all/quickStartDev/startKafka-zookeeper.sh b/all/quickStartDev/startKafka-zookeeper.sh new file mode 100644 index 0000000..fbb1e5e --- /dev/null +++ b/all/quickStartDev/startKafka-zookeeper.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +cd /opt/tools/kafka_2.11-1.1.0 +bin/zookeeper-server-start.sh config/zookeeper.properties + + diff --git a/boot-starter/boot-starter-ssm/README.md b/boot-starter/boot-starter-ssm/README.md index 3e537b4..20d860f 100644 --- a/boot-starter/boot-starter-ssm/README.md +++ b/boot-starter/boot-starter-ssm/README.md @@ -27,4 +27,8 @@ spring-boot提供了默认的启动时执行数据库脚本功能,指定`sprin 更多的数据源,像增加多个druid数据源一样增加`com.micro.fast.common.init.properties.SqlInitProperties`类的配置bean,然后满足上述 两个数据源时同样要求即可。 - +### 启用基于shardbatis的分表功能 +- 通过设置shardbatis.configPath(shardbatis的xml配置文件的classpath路径)属性来激活分表功能.使用`@IdShard`来在mapper接口参数中指定分表策略字段 +或者`@IdShard('filedName')`来指定参数对象中哪个字段是分表策略所需的字段. +- shardbatis.tableCount 指定分表的数量 +- 注意,使用默认的分表策略的时候mapper接口中不允许使用只有一个map传递参数. diff --git a/boot-starter/boot-starter-ssm/pom.xml b/boot-starter/boot-starter-ssm/pom.xml index 0263333..92caa09 100644 --- a/boot-starter/boot-starter-ssm/pom.xml +++ b/boot-starter/boot-starter-ssm/pom.xml @@ -55,6 +55,16 @@ com.github.jsqlparser jsqlparser + + + org.shardbatis + shardbatis + 2.0.0B + + + net.sf.jsqlparser + jsqlparser + mysql diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/config/SqlSessionFactoryConfig.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/config/SqlSessionFactoryConfig.java index b94495a..703d261 100644 --- a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/config/SqlSessionFactoryConfig.java +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/config/SqlSessionFactoryConfig.java @@ -1,6 +1,7 @@ package com.micro.fast.common.config; import com.github.pagehelper.PageHelper; +import com.google.code.shardbatis.plugin.ShardPlugin; import com.micro.fast.common.config.param.MybatisParamConfig; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; @@ -8,12 +9,12 @@ import org.mybatis.spring.SqlSessionFactoryBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -49,6 +50,13 @@ public class SqlSessionFactoryConfig implements TransactionManagementConfigurer{ @Autowired private MybatisParamConfig mybatisParamConfig; + /** + * 分表插件的配置 + */ + @Autowired(required = false) + @Qualifier("shardPlugin") + private ShardPlugin shardPlugin; + /** * mybatis连接工厂bean * @return sqlSessionFactory @@ -97,7 +105,12 @@ public class SqlSessionFactoryConfig implements TransactionManagementConfigurer{ } } pageHelper.setProperties(properties); - sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper}); + // 选择性启用shardbatisf分表插件 + if (shardPlugin == null){ + sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper}); + } else { + sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper,shardPlugin}); + } //设置详细的配置 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); //开启下划线转驼峰命名 diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShard.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShard.java new file mode 100644 index 0000000..19b9f92 --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShard.java @@ -0,0 +1,13 @@ +package com.micro.fast.common.shardbatis; + +import java.lang.annotation.*; + +/** + * 指定分表依据的参数的注解,分表的参数上必须有这个注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface IdShard{ + String value() default "id"; +} diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShardUtils.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShardUtils.java new file mode 100644 index 0000000..027a9de --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/IdShardUtils.java @@ -0,0 +1,158 @@ +package com.micro.fast.common.shardbatis; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +/** + * 根据idshard注解判断哪个参数是分表的依据,以及根据反射获取分表的依据参数的值 + * + * @author lsy + */ +@Slf4j +public class IdShardUtils { + + public static final String PARAM = "param"; + + /** + * 工具类私有构造防止被实例化 + */ + private IdShardUtils() { + + } + + /** + * 根据statementId反射出方法,查找方法中带有IdShard朱姐标记的参数,获取其值用于分表策略 + * + * @param params + * @param statementId + * @return + */ + public static Object getShardIdValue(Object params, String statementId) throws Exception { + int lastIndexOfD = statementId.lastIndexOf("."); + String methodName = statementId.substring(lastIndexOfD + 1); + String className = statementId.substring(0, lastIndexOfD); + Class aClass = null; + try { + aClass = Class.forName(className); + Method[] methods = aClass.getMethods(); + for (Method method : methods) { + String name = method.getName(); + int parameterIndex = 0; + // 方法名相同就找到了语句 + if (name.equals(methodName)) { + Parameter[] parameters = method.getParameters(); + for (Parameter parameter : parameters) { + parameterIndex++; + // 参数是否包含IdShard.class注解 + Annotation[] annotations = parameter.getAnnotations(); + for (Annotation annotation : annotations) { + // 找到带有分表标记符号的参数 + if (annotation.annotationType() == IdShard.class) { + Class type = parameter.getType(); + // statement是不是只有一个参数,有一个的参数的话参数类型应该相同.但是这一个参数不能是map类型 + if (params.getClass() == type) { + return getValueByFieldName((IdShard) annotation,params); + } else { + // 如果有多个参数的话,mybatis会把参数封装成map类型,key是param+参数的顺序 + String key = PARAM + (parameterIndex + 1); + Map map = (HashMap)params; + Object o = map.get(key); + return getValueByFieldName((IdShard) annotation,o); + } + } + } + + } + } + } + } catch (ClassNotFoundException e) { + log.info("sql语句对应的类不存在:{}", e); + } + // 找不到就返回null + return null; + } + + /** + * 根据注解和mybatis的参数 + * @param idShard + * @param params + * @return + * @throws Exception + */ + private static Object getValueByFieldName(IdShard idShard, Object params) throws Exception { + // 如果是基础类型(表明是主键)就直接返回 + if (isBasicType(params)) { + return params; + } else { + // 如果不是基础类型,就使用IdShard的value属性指定对象的哪个属性是分表策略需要的值 + String shardFieldName = idShard.value(); + if (StringUtils.isBlank(shardFieldName)) { + throw new Exception("the shardFieldName was not annotated"); + } + Field field = null; + try { + // 根据字段名反射获取字段的类类型 + field = params.getClass().getDeclaredField(shardFieldName); + } catch (NoSuchFieldException e) { + field = params.getClass().getSuperclass().getDeclaredField(shardFieldName); + } + // 设置属性的访问权限 + field.setAccessible(true); + // 获取对象中属性的值 + return field.get(params); + } + } + + /** + *  检查参数是不是基础类型 + * + * @param param + * @return + */ + private static boolean isBasicType(Object param) { + if (param == null) { + return false; + } + if (param instanceof String) { + return true; + } + if (param instanceof BigDecimal) { + return true; + } + if (param instanceof Integer) { + return true; + } + if (param instanceof Long) { + return true; + } + if (param instanceof Double) { + return true; + } + if (param instanceof Float) { + return true; + } + if (param instanceof Character) { + return true; + } + if (param instanceof Byte) { + return true; + } + if (param instanceof Short) { + return true; + } + if (param instanceof Boolean) { + return true; + } + return false; + } + + +} diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/ShardbatisConfig.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/ShardbatisConfig.java new file mode 100644 index 0000000..7b8d01c --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/ShardbatisConfig.java @@ -0,0 +1,32 @@ +package com.micro.fast.common.shardbatis.config; + +import com.google.code.shardbatis.plugin.ShardPlugin; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +/** + * shardbatis的配置 + * @author lsy + */ +@Configuration +public class ShardbatisConfig { + + /** + * shardPlugin的配置类,当这个配置属性不存在的时候分表插件不生效 + * @param xmlClassPath shard + * @return + */ + @Bean(name = "shardPlugin") + @ConditionalOnProperty(prefix = "shardbatis",name = "configPath",matchIfMissing = false) + public ShardPlugin shardPlugin(@Value("${shardbatis.configPath}") String xmlClassPath){ + ShardPlugin shardPlugin = new ShardPlugin(); + Properties properties = new Properties(); + properties.setProperty(ShardPlugin.SHARDING_CONFIG,xmlClassPath); + shardPlugin.setProperties(properties); + return shardPlugin; + } +} diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/package-info.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/package-info.java new file mode 100644 index 0000000..850d1e6 --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/config/package-info.java @@ -0,0 +1,4 @@ +/** + * shardbatis的java config 包 + */ +package com.micro.fast.common.shardbatis.config; \ No newline at end of file diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/package-info.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/package-info.java new file mode 100644 index 0000000..8cffa3d --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/package-info.java @@ -0,0 +1,4 @@ +/** + * 利用shardbatis实现分表 + */ +package com.micro.fast.common.shardbatis; \ No newline at end of file diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/properties/package-info.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/properties/package-info.java new file mode 100644 index 0000000..5fa6208 --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/properties/package-info.java @@ -0,0 +1,4 @@ +/** + * shardbatis插件配置参数 + */ +package com.micro.fast.common.shardbatis.properties; \ No newline at end of file diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/SsmDefaultShardStrategy.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/SsmDefaultShardStrategy.java new file mode 100644 index 0000000..966c8e4 --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/SsmDefaultShardStrategy.java @@ -0,0 +1,66 @@ +package com.micro.fast.common.shardbatis.shardstrategy; + +import com.google.code.shardbatis.strategy.ShardStrategy; +import com.micro.fast.common.config.SqlSessionFactoryConfig; +import com.micro.fast.common.shardbatis.IdShardUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.context.annotation.Configuration; + +/** + * 提供默认的分表策略 + * @author lsy + */ +@Slf4j +@Configuration +@AutoConfigureBefore(SqlSessionFactoryConfig.class) +public class SsmDefaultShardStrategy implements ShardStrategy{ + + + private static Integer tableCount; + + /** + * 分表策略的实现 + * @param baseTableName 逻辑表名 + * @param params mybatis语句的参数 + * @param statementId 语句的id,一般是类名.方法名 + * @return  + */ + @Override + public String getTargetTableName(String baseTableName, Object params, String statementId) { + StringBuilder shardTableName = new StringBuilder(baseTableName); + shardTableName.append("_"); + try { + Object shardIdValue = IdShardUtils.getShardIdValue(params, statementId); + if (shardIdValue == null){ + shardTableName.append("000"); + }else { + String s = shardIdValue.toString(); + Integer integer = Integer.valueOf(s); + Integer index = integer % tableCount; + if (index<10){ + shardTableName.append("00"+index); + } else if (index<100){ + shardTableName.append("0"+index); + } else { + shardTableName.append(index); + } + } + } catch (Exception e) { + log.info("分表失败:{}",e); + throw new RuntimeException("分表失败"); + } + + return shardTableName.toString(); + } + + /** + * springBoot不允许直接给静态变量赋值,但是可以通过非静态方法给静态变量赋值 + * @param tableCount + */ + @Value("${shardbatis.tableCount}") + public void setTableCount(Integer tableCount){ + SsmDefaultShardStrategy.tableCount = tableCount; + } +} diff --git a/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/package-info.java b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/package-info.java new file mode 100644 index 0000000..fc2f9a2 --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/java/com/micro/fast/common/shardbatis/shardstrategy/package-info.java @@ -0,0 +1,4 @@ +/** + *提供默认的分表策略 + */ +package com.micro.fast.common.shardbatis.shardstrategy; \ No newline at end of file diff --git a/boot-starter/boot-starter-ssm/src/main/resources/shard_config.xml b/boot-starter/boot-starter-ssm/src/main/resources/shard_config.xml new file mode 100644 index 0000000..33e185e --- /dev/null +++ b/boot-starter/boot-starter-ssm/src/main/resources/shard_config.xml @@ -0,0 +1,19 @@ + + + + + + + + + xxx.dao.UserMapper.insertSelective + xxx.dao.UserMapper.selectByPrimaryKey + xxx.UserMapper.updateByPrimaryKeySelective + + + + + + diff --git a/pom.xml b/pom.xml index 329f148..1837c47 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,8 @@ true 1.5.7 2.2.1 + 2.0.0B + 0.7.0 @@ -318,6 +320,17 @@ gatling-charts-highcharts ${gatling.version} + + + org.shardbatis + shardbatis + ${shardbatis.version} + + + net.sf.jsqlparser + jsqlparser + ${net.sf.jsqlparser.jsqlparser.version} + @@ -405,11 +418,11 @@ registry.cn-hangzhou.aliyuncs.com - - - - - + + + + +