一、项目重点有:
(1)、SpringBoot+Mybatis+Mysql+Durid整合
(2)、错误后跳转到指定页面
(3)、多数据源动态切换
(4)、mybatis分页
(5)、durid监控
(6)、集成log4j2日志
(7)、通过mybatis拦截器,在控制台打印完整的sql
二、项目截图:
三、SpringBoot+Mybatis+Mysql+Durid整合
(1)、application.yml:
spring:
dynamic-datasource:
druid:
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
# 配置DruidStatFilter
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
# 配置DruidStatViewServlet
stat-view-servlet:
url-pattern: "/druid/*"
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 127.0.0.1
# IP黑名单 (存在共同时,deny优先于allow)
deny: 192.168.1.73
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
druid-datasources:
jwpd:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: 137972zc
lkj:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: 137972zc
mvc:
view:
prefix: /WEB-INF/page/
suffix: .jsp
server:
port: 9090
logging:
config: classpath:log4j2-spring-dev.xml
mybatis:
type-aliases-package: com.base.springboot.entity
说明:配置druid数据连接池,配置jdbc连接(两个数据源)
(2)、配置数据源(DataSourceProperties.class)
@Configuration
public class DataSourceProperties {
@ConfigurationProperties(prefix = "spring.dynamic-datasource.druid-datasources.jwpd")
@Bean(name = "JWPDDataSource")
public DataSource JWPDDataSource(StandardEnvironment env){
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return common(env,druidDataSource);
}
@ConfigurationProperties(prefix = "spring.dynamic-datasource.druid-datasources.lkj")
@Bean(name = "LKJDataSource")
public DataSource LKJDataSource(StandardEnvironment env){
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return common(env,druidDataSource);
}
public DataSource common(StandardEnvironment env, DruidDataSource druidDataSource){
Properties properties = new Properties();
PropertySource> appProperties = env.getPropertySources().get("applicationConfig: [classpath:/application.yml]");
Map source = (Map) appProperties.getSource();
properties.putAll(source);
druidDataSource.configFromPropety(properties);
return druidDataSource;
}
}
说明:配置数据源,common(env,druidDataSource)方法,是为了继续设置为null的属性(durid配置属性,最大最小连接数、监控地址等等)
(3)、Spring和Mybatis的整合配置文件(MybatisConfig.class)
@Configuration
public class MybatisConfig {
//注入数据源JWPDDataSource
@Autowired
@Qualifier("JWPDDataSource")
public DataSource JWPDDataSource;
//注入数据源LKJDataSource
@Autowired
@Qualifier("LKJDataSource")
public DataSource LKJDataSource;
//声明动态数据源,默认值为JWPDDataSource
@Bean("dynamicDataSource")
@Primary
public DynamicDataSource dynamicDataSource(){
//动态数据源集合
Map
说明:
(4)、创建动态数据源对象(DynamicDataSource.class)
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 有参构造方法,声明对象的时候执行,调用父类AbstractRoutingDataSource的方法
* @param targetDataSources 数据源Map集合
* @param defaultTargetDataSource 默认数据源
*/
public DynamicDataSource(Map
说明:继承AbstractRoutingDataSource抽象类,实现setTargetDataSources(设置目标数据源集合)、setDefaultTargetDataSource(设置默认数据源)、determineCurrentLookupKey(返回当前数据源)等方法。
(5)、动态数据源操作上下文类(DynamicDataSourceContextHolder.class)
public class DynamicDataSourceContextHolder {
/**
* 静态ThreadLocal常量contextHolder,用来装当前线程的数据源key
*/
public static final ThreadLocal contextHolder=new ThreadLocal<>();
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List
说明:声明set、get当前数据源key、判断是否包含当前数据源、添加数据源key的方法
(6)、设置当前数据源注解(TargetDs.class)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDs {
/**
* 数据源key值
* @return
*/
String value();
}
(7)、设置动态数据源切换类(DynamicDataSourceAspect.class)
@Aspect
@Order(-1) // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {
/**
* 前置通知,进入切点之前,先切换数据源
* @param point
* @param targetDs
*/
@Before("@annotation(targetDs)")
public void switchDataSource(JoinPoint point, TargetDs targetDs) {
//判断,如果没有此数据源
if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDs.value())){
System.out.println("没有找到key为[{}]的数据源,所以当前还是使用默认数据源!"+targetDs.value());
}else {
DynamicDataSourceContextHolder.setDataSourceKey(targetDs.value());
System.out.println("方法"+point.getSignature().getName()+"上发现@TargetDs注解,"+"当前数据源已经切换为[{}]!"+targetDs.value());
}
}
/**
* 后置通知,切合方法执行完成之后,重置数据源
* @param point
* @param targetDs
*/
@After("@annotation(targetDs)")
public void restoreDataSource(JoinPoint point, TargetDs targetDs) {
System.out.println("重置数据源 [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]");
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}
说明:设置切面类,切点是带有@targetDs注解的方法,当遇到这种方法,执行前将数据源切换到对应的key对应的数据源,执行完成后还原到默认数据源。
(8)、事务配置(TransactionAdviceConfig.class)
@Aspect
@Configuration
public class TransactionAdviceConfig {
//声明切面
private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.base.springboot.service.*.impl.*.*(..))";
//事务管理器
@Autowired
@Qualifier("transactionManager")
private PlatformTransactionManager transactionManager;
//声明通知
@Bean(name = "txInterceptor")
public TransactionInterceptor txInterceptor(){
Properties attributes = new Properties();
attributes.setProperty("insert*", "PROPAGATION_REQUIRED");
attributes.setProperty("add*", "PROPAGATION_REQUIRED");
attributes.setProperty("update*", "PROPAGATION_REQUIRED");
attributes.setProperty("delete*", "PROPAGATION_REQUIRED");
attributes.setProperty("deploy*", "PROPAGATION_REQUIRED");
attributes.setProperty("select*", "PROPAGATION_REQUIRED,readOnly");
attributes.setProperty("get*", "PROPAGATION_REQUIRED,readOnly");
attributes.setProperty("query*", "PROPAGATION_REQUIRED,readOnly");
return new TransactionInterceptor(transactionManager, attributes);
}
@Bean
public AspectJExpressionPointcutAdvisor pointcutAdvisor(@Qualifier("txInterceptor") TransactionInterceptor txInterceptor){
AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
pointcutAdvisor.setAdvice(txInterceptor);
pointcutAdvisor.setExpression(AOP_POINTCUT_EXPRESSION);
return pointcutAdvisor;
}
}
说明:注入事务管理器,声明切面(控制的范围),声明事务拦截器(设置不同的方法对应的事务策略),声明AspectJExpressionPointcutAdvisor,传入切面和事务拦截通知,完成aop切入。
以上8个步骤,就完成了SpringBoot+MyBatis+Mysql+Durid的配置,多数据源通过自定义注解,动态切换,没有数据源都被事务管控,实现多数据源动态切换核心点就是AbstractRoutingDataSource。
四、集成log4j2日志
(1)、log4j2-spring-dev.xml
D://log4j2Logs
%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l - %m%n
%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %C.%M - %m%n
(2)、application.yml:
logging:
config: classpath:log4j2-spring-dev.xml
说明:配置log4j2日志,记录不同的级别的日志到不同的文件。
五、不同的错误跳转到错误页面
1、创建错误配置类(ErrorPageConfig.class),继承HandlerInterceptorAdapter。
@Component
public class ErrorPageConfig extends HandlerInterceptorAdapter {
private List errorList= Arrays.asList(404, 405, 500);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (errorList.contains(response.getStatus())){
response.sendRedirect("/error/"+response.getStatus());
return false;
}
return super.preHandle(request, response, handler);
}
}
2、在Controller里面写错误页面跳转方法(BaseController.class)
@Controller
public class BaseController {
@RequestMapping("/error/{code}")
public String errorController(@PathVariable("code") String code, Model model){
String errorStr="";
switch (code){
case "404":
errorStr="找不到页面!";
break;
case "405":
errorStr="405错误!";
break;
case "500":
errorStr="服务器错误!";
break;
}
model.addAttribute("errorStr",errorStr);
return "errorPage";
}
}
说明:继承拦截器,重写preHandle方法(执行之前拦截),当response.getStatus()状态码为错误码时,重定向到"/error/"+response.getStatus()方法,这个方法里面做不同的处理。
六、mybatis分页
(1)、声明列表视图类ListVo.class
public class ListVo {
//数据量
private int totalSize = 0;
//数据列表
private List list = new ArrayList();
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
}
说明:属性有数量和list列表
(2)、MybatisConfig.class配置类里面声明pageHelper对象,并且在sql会话里面插入插件。
@Bean(name="pageHelper")
public PageHelper pageHelper() {
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
p.setProperty("dialect", "mysql");
pageHelper.setProperties(p);
return pageHelper;
}
/**
* 声明sql会话
* @return
*/
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("pageHelper")PageHelper pageHelper,@Qualifier("fullSqlInterceptor")FullSqlInterceptor fullSqlInterceptor) throws Exception{
//声明sql会话工厂
SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
//设置数据源
factoryBean.setDataSource(dynamicDataSource());
//设置扫描mybatisXml的路径
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:**/dao/*/*.xml"));
factoryBean.setPlugins(new Interceptor[] {pageHelper(), fullSqlInterceptor});//添加分页插件
//返回sql会话
return factoryBean.getObject();
}
(3)、Dao层的base操作类里面(BaseDaoImpl.class)
@Repository("baseDao")
public class BaseDaoImpl implements IBaseDao {
@Autowired
public SqlSessionTemplate sqlSessionTemplate;
@Override
public List
说明:
ListVo listVo = new ListVo();
PageHelper.startPage(Integer.parseInt(start),Integer.parseInt(limit),true);
List list=sqlSessionTemplate.selectList(statement,paramMap);
listVo.setList(list);
Page page = (Page)list;
listVo.setTotalSize((int)page.getTotal());
return listVo;
PageHelper.startPage:进行物理分页
listVo.setList(list);:将list设置进listVo类的list属性
Page page = (Page)list;
listVo.setTotalSize((int)page.getTotal());:设置进listVo类的totalSize属性。
七、通过mybatis拦截器,在控制台打印完整的sql(FullSqlInterceptor.class)
@Intercepts({
@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }) })
public class FullSqlInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
//获取
说明:主要是在执行mybatis方法的时候,替换掉原生语句中的参数#{},替换成具体的值,方便在控制台直接复制到数据库查看。
以上就是SpringBoot+MyBatis+Mysql+Durid动态多数据源项目搭建的主要代码和过程。