boot-start-ssm模块提供可选的分表支持
This commit is contained in:
@@ -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缓存
|
||||
- 多数据源支持
|
||||
- 多数据库,数据库分表支持
|
||||
- 完善的项目使用文档
|
||||
- 用心的开发者
|
||||
## 整体技术选型
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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传递参数.
|
||||
|
||||
@@ -55,6 +55,16 @@
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
</dependency>
|
||||
<!--分表插件-->
|
||||
<dependency>
|
||||
<groupId>org.shardbatis</groupId>
|
||||
<artifactId>shardbatis</artifactId>
|
||||
<version>2.0.0B</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
</dependency>
|
||||
<!--数据库连接-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
|
||||
+15
-2
@@ -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();
|
||||
//开启下划线转驼峰命名
|
||||
|
||||
+13
@@ -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";
|
||||
}
|
||||
+158
@@ -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<String,Object> map = (HashMap<String,Object>)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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+32
@@ -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;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* shardbatis的java config 包
|
||||
*/
|
||||
package com.micro.fast.common.shardbatis.config;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 利用shardbatis实现分表
|
||||
*/
|
||||
package com.micro.fast.common.shardbatis;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* shardbatis插件配置参数
|
||||
*/
|
||||
package com.micro.fast.common.shardbatis.properties;
|
||||
+66
@@ -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;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
*提供默认的分表策略
|
||||
*/
|
||||
package com.micro.fast.common.shardbatis.shardstrategy;
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE shardingConfig PUBLIC
|
||||
"-//shardbatis.googlecode.com//DTD Shardbatis 2.0//EN"
|
||||
"http://shardbatis.googlecode.com/dtd/shardbatis-config.dtd">
|
||||
<!-- shardbatis的示例配置-->
|
||||
<shardingConfig>
|
||||
<ignoreList>
|
||||
<value></value>
|
||||
</ignoreList>
|
||||
<!-- parseList可选配置 如果配置了parseList,只有在parseList范围内并且不再ignoreList内的sql才会被解析和修改 -->
|
||||
<parseList>
|
||||
<value>xxx.dao.UserMapper.insertSelective</value>
|
||||
<value>xxx.dao.UserMapper.selectByPrimaryKey</value>
|
||||
<value>xxx.UserMapper.updateByPrimaryKeySelective</value>
|
||||
</parseList>
|
||||
<!-- 配置分表策略 tableName指的是实际的表名,strategyClass对应的分表策略实现类 -->
|
||||
<strategy tableName="user" strategyClass="xxx.UserShardStrategyImpl"/>
|
||||
</shardingConfig>
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
<spring-boot-devtools.optional>true</spring-boot-devtools.optional>
|
||||
<spring-boot-admin.version>1.5.7</spring-boot-admin.version>
|
||||
<quartz.version>2.2.1</quartz.version>
|
||||
<shardbatis.version>2.0.0B</shardbatis.version>
|
||||
<net.sf.jsqlparser.jsqlparser.version>0.7.0</net.sf.jsqlparser.jsqlparser.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -318,6 +320,17 @@
|
||||
<artifactId>gatling-charts-highcharts</artifactId>
|
||||
<version>${gatling.version}</version>
|
||||
</dependency>
|
||||
<!--分表依赖-->
|
||||
<dependency>
|
||||
<groupId>org.shardbatis</groupId>
|
||||
<artifactId>shardbatis</artifactId>
|
||||
<version>${shardbatis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>${net.sf.jsqlparser.jsqlparser.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -405,11 +418,11 @@
|
||||
<registryUrl>registry.cn-hangzhou.aliyuncs.com</registryUrl>
|
||||
<!--构建镜像所有的资源,这些资源将被复制到dockerFile的目录-->
|
||||
<!--<resources>-->
|
||||
<!--<resource>-->
|
||||
<!--<targetPath>/</targetPath>-->
|
||||
<!--<directory>${project.build.directory}</directory>-->
|
||||
<!--<include>${project.build.finalName}.jar</include>-->
|
||||
<!--</resource>-->
|
||||
<!--<resource>-->
|
||||
<!--<targetPath>/</targetPath>-->
|
||||
<!--<directory>${project.build.directory}</directory>-->
|
||||
<!--<include>${project.build.finalName}.jar</include>-->
|
||||
<!--</resource>-->
|
||||
<!--</resources>-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
Reference in New Issue
Block a user