add: mybatis-plus日志输出p6spy、多数据源配置

This commit is contained in:
2023-08-24 13:31:26 +08:00
parent e3670ca4d7
commit b6dc567c17
16 changed files with 386 additions and 86 deletions

View File

@@ -0,0 +1,107 @@
# mybatis多数据源配置
## dynamic-datasource
### 1、引入依赖
4.1.3版本是稳定版
```xml
<!-- dynamic-datasource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.1.3</version>
</dependency>
```
### 2、配置数据源
需要注意的是这里使用了druidp6spy
需要配置对应的数据源使用primary来指明默认的数据源
```yml
#配置数据源
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
dynamic:
primary: db1
datasource:
db1:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:stand_lms}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
username: ${DB_USER:root}
password: ${DB_PWD:12356}
type: com.alibaba.druid.pool.DruidDataSource
db2:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:13306}/${DB_NAME:test}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
username: ${DB_USER:root}
password: ${DB_PWD:12356}
type: com.alibaba.druid.pool.DruidDataSource
```
集成了druid需要去除druid配置
```yml
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
```
去除框架原来对druid数据源的配置类
```java
//import com.alibaba.druid.pool.DruidDataSource;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//
//import javax.sql.DataSource;
//
//@Configuration
//@Slf4j
//public class DataBaseConfig {
//
// @Primary
// @Bean(name = "dataSource")
// @ConfigurationProperties(prefix = "spring.datasource.druid")
// public DataSource dataSource() {
// return new DruidDataSource();
// }
//
//}
```
排除掉DruidDataSourceAutoConfigure
在启动类中需要排除掉DruidDataSourceAutoConfigure.class就是取消Druid的数据源的自动配置类。
```java
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DruidDataSourceAutoConfigure.class
})
```
也可以在配置类中
```yml
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
```
### 3、测试
使用@DS("db2")注解实现切换数据源
```java
public interface TestMapper extends BaseMapper<JSONObject> {
@DS("db2")
JSONObject getAll13();
}
```

View File

@@ -32,6 +32,19 @@
<configuration.version>1.9</configuration.version>
</properties>
<dependencies>
<!-- dynamic-datasource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
<!--重试-->
<dependency>
<groupId>org.springframework.retry</groupId>

View File

@@ -1,6 +1,7 @@
package org.nl;
import cn.dev33.satoken.annotation.SaIgnore;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import io.github.forezp.distributedlimitcore.annotation.Limit;
@@ -10,6 +11,7 @@ import org.nl.common.annotation.RepeatSubmit;
import org.nl.config.SpringContextHolder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
@@ -35,7 +37,9 @@ import java.time.LocalTime;
@RestController
@Api(hidden = true)
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DruidDataSourceAutoConfigure.class
})
@ServletComponentScan //https://blog.csdn.net/qq_36850813/article/details/101194250
@EnableTransactionManagement

View File

@@ -1,23 +1,23 @@
package org.nl.config;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
@Slf4j
public class DataBaseConfig {
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource dataSource() {
return new DruidDataSource();
}
}
//import com.alibaba.druid.pool.DruidDataSource;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//
//import javax.sql.DataSource;
//
//@Configuration
//@Slf4j
//public class DataBaseConfig {
//
// @Primary
// @Bean(name = "dataSource")
// @ConfigurationProperties(prefix = "spring.datasource.druid")
// public DataSource dataSource() {
// return new DruidDataSource();
// }
//
//}

View File

@@ -0,0 +1,71 @@
package org.nl.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import java.io.IOException;
/**
* 去除druid底部的广告配置类 https://developer.aliyun.com/article/1073552
*/
@Configuration
@ConditionalOnWebApplication
@AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true)
public class DruidConfigurer {
/**
* 除去druid页面底部的广告
*
* @param properties
* @return
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
// 创建filter进行过滤
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
// 重置缓冲区,响应头不会被重置
response.resetBuffer();
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
text = text.replaceAll("powered.*?shrek.wang</a>", "");
response.getWriter().write(text);
}
@Override
public void destroy() {
}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}

View File

@@ -0,0 +1,18 @@
package org.nl.config;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
/**
* @Author: lyd
* @Description: 自定义 P6spy SQL 打印策略
* @Date: 2023/8/23
*/
public class MyP6SpyLogger implements MessageFormattingStrategy {
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category,
String prepared, String sql, String url) {
return StringUtils.isNotBlank(sql) ? "\u001B[31m" + now + "\u001B[0m \u001B[32m执行时间" + elapsed + " ms\u001B[0m" +
" \u001B[34m执行的SQL语句\u001B[0m\u001B[36m" + sql.replaceAll("[\\s]+", " ") : "SQL为空";
}
}

View File

@@ -0,0 +1,13 @@
package org.nl.config;
/**
* @Author: lyd
* @Description: 输出到控制台
* @Date: 2023/8/23
*/
public class MyStdoutLogger extends com.p6spy.engine.spy.appender.StdoutLogger {
@Override
public void logText(String text) {
System.out.println(text);
}
}

View File

@@ -0,0 +1,18 @@
package org.nl.config;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
/**
* @Author: lyd
* @Description:
* @Date: 2023/8/23
*/
public class P6SpyLoggerLogback implements MessageFormattingStrategy {
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category,
String prepared, String sql, String url) {
return StringUtils.isNotBlank(sql) ? "\u001B[32m执行时长\u001B[0m" + elapsed + " ms" +
" \u001B[33mSQL语句\u001B[0m\u001B[36m" + sql.replaceAll("[\\s]+", " ") : "SQL为空";
}
}

View File

@@ -2,6 +2,7 @@ package org.nl.wms.sch.task_manage.task.tasks.mapper;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.HashMap;
@@ -37,4 +38,7 @@ public interface TestMapper extends BaseMapper<JSONObject> {
JSONArray getAll11(JSONObject map);
JSONArray getAll12(List<String> list);
@DS("db2")
JSONObject getAll13();
}

View File

@@ -80,6 +80,10 @@
</foreach>
</where>
</select>
<select id="getAll13" resultType="com.alibaba.fastjson.JSONObject">
SELECT id, name
FROM user
</select>
</mapper>

View File

@@ -3,60 +3,25 @@ server:
limit.type: redis
#配置数据源
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
# url: jdbc:log4jdbc:mysql://${DB_HOST:192.168.81.252}:${DB_PORT:3306}/${DB_NAME:nl-sso-server}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
# url: jdbc:log4jdbc:mysql://${DB_HOST:47.111.78.178}:${DB_PORT:3306}/${DB_NAME:stand_lms}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
# username: ${DB_USER:root}
# password: ${DB_PWD:P@ssw0rd}
# password: ${DB_PWD:P@ssw0rd}
url: jdbc:log4jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:stand_lms}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
username: ${DB_USER:root}
# password: ${DB_PWD:Root.123456}
password: ${DB_PWD:12356}
# 初始连接数
initial-size: 5
# 最小连接数
min-idle: 15
# 最大连接数
max-active: 30
# 超时时间(以秒数为单位)
remove-abandoned-timeout: 180
# 获取连接超时时间
max-wait: 3000
# 连接有效性检测时间
time-between-eviction-runs-millis: 60000
# 连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
# 连接在池中最大生存的时间
max-evictable-idle-time-millis: 900000
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
test-while-idle: true
# 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个
test-on-borrow: true
# 是否在归还到池中前进行检验
test-on-return: false
# 检测连接是否有效
validation-query: select 1
# 配置监控统计
webStatFilter:
enabled: true
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
filter:
stat:
enabled: true
# 记录慢SQL
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic:
primary: db1
datasource:
db1:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:stand_lms}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
username: ${DB_USER:root}
password: ${DB_PWD:12356}
type: com.alibaba.druid.pool.DruidDataSource
db2:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:13306}/${DB_NAME:test}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true
username: ${DB_USER:root}
password: ${DB_PWD:12356}
type: com.alibaba.druid.pool.DruidDataSource
flyway:
# 是否启用flyway
enabled: true

View File

@@ -1,4 +1,35 @@
spring:
datasource:
druid:
initial-size: 5 #初始化时建立物理连接的个数
min-idle: 15 #最小连接池数量
maxActive: 30 #最大连接池数量
maxWait: 3000 #获取连接时最大等待时间,单位毫秒
#申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效。
test-while-idle: true
time-between-eviction-runs-millis: 300000 #既作为检测的间隔时间又作为test-while-idle执行的依据
min-evictable-idle-time-millis: 900000 #销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
#用来检测连接是否有效的sql
#mysql中为 select 'x'
#oracle中为 select 1 from dual
validation-query: SELECT 'x'
test-on-borrow: false #申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
test-on-return: false #归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
exception-sorter: true #当数据库抛出不可恢复的异常时,抛弃该连接
pool-prepared-statements: true #是否缓存preparedStatement,mysql5.5+建议开启
max-pool-prepared-statement-per-connection-size: 20 #当值大于20时poolPreparedStatements会自动修改为true
#通过connectProperties属性来打开mergeSql功能慢SQL记录
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
use-global-data-source-stat: true #合并多个DruidDataSource的监控数据
#filters通过别名的方式配置扩展插件常用的插件有
#监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
filters: stat,wall,log4j
#设置访问druid监控页面的拦截路径及账号和密码,默认没有
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin
freemarker:
check-template-location: false
profiles:
@@ -9,16 +40,6 @@ spring:
redis:
repositories:
enabled: false
#配置 Jpa
jpa:
hibernate:
ddl-auto: none
open-in-view: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
enable_lazy_load_no_trans: true
task:
pool:
# 核心线程池大小
@@ -82,7 +103,7 @@ security:
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations:
- classpath:org.nl.**.mapper/*.xml
global-config:

View File

@@ -0,0 +1,24 @@
#3.2.1????
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# ???????
#logMessageFormat=org.nl.config.MyP6SpyLogger
logMessageFormat=org.nl.config.P6SpyLoggerLogback
#????????
#appender=org.nl.config.MyStdoutLogger
#appender=com.p6spy.engine.spy.appender.StdoutLogger
# ???????? sql
appender=com.p6spy.engine.spy.appender.Slf4JLogger
# ?? p6spy driver ??
deregisterdrivers=true
# ??JDBC URL??
useprefix=true
# ???? Log ??,????????error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# ????
dateformat=yyyy-MM-dd HH:mm:ss
# ???????
driverlist=com.mysql.cj.jdbc.Driver
# ?????SQL??
outagedetection=true
# ?SQL???? 2 ?
outagedetectioninterval=2

View File

@@ -1,9 +1,14 @@
package org.nl;
import cn.hutool.core.date.DateUtil;
import org.junit.jupiter.api.Test;
import org.nl.system.service.user.ISysUserService;
import org.nl.wms.sch.point.service.ISchBasePointService;
import org.nl.wms.sch.region.service.ISchBaseRegionService;
import org.nl.wms.sch.region.service.dao.SchBaseRegion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
/**
* @Author: lyd
@@ -14,8 +19,28 @@ import org.springframework.boot.test.context.SpringBootTest;
public class ApplicationTest {
@Autowired
private ISysUserService userService;
@Autowired
private ISchBaseRegionService regionService;
@Autowired
private ISchBasePointService pointService;
@Test
void contextLoads() {
System.out.println(userService.list());
}
/**
* 测试多数据源情况的事务
*/
@Test
@Transactional
void contextLoads1() {
SchBaseRegion region = new SchBaseRegion();
region.setRegion_code("ee");
region.setRegion_name("sss");
region.setCreate_id("1");
region.setCreate_name("sss");
region.setCreate_time(DateUtil.now());
regionService.save(region);
int n = 1 / 0;
}
}

View File

@@ -8,6 +8,7 @@ import org.nl.config.MapOf;
import org.nl.wms.sch.task_manage.task.tasks.mapper.TestMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
@@ -165,4 +166,14 @@ public class MapperTest {
System.out.println(SecureUtil.md5("123456"));
}
}
/**
* 使用第二数据源
*/
@Test
@Transactional
void testMapper13() {
JSONObject all13 = testMapper.getAll13();
System.out.println("结果集:" + all13);
}
}

View File

@@ -19,6 +19,8 @@ module.exports = {
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {
allowedHosts: ['all'],
disableHostCheck: true,
port: port,
open: false,
overlay: {