首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,611 阅读
2
Mac打印机设置黑白打印
4,952 阅读
3
修改elementUI中el-table树形结构图标
4,896 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,675 阅读
5
intelliJ Idea 2022.2.X破解
4,358 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
224
篇与
的结果
2021-06-06
Java线程间的通信之锁与同步
在Java中,锁的概念都是基于对象的,所以我们⼜经常称它为对象锁。线程和锁的关系,我们可以⽤婚姻关系来理解。⼀个锁同⼀时间只能被⼀个线程持有。也就是说,⼀个锁如果和⼀个线程“结婚”(持有),那其他线程如果需要得到这个锁,就得等这个线程和这个锁“离婚”(释放)。在我们的线程之间,有⼀个同步的概念。什么是同步呢,假如我们现在有2位正在抄暑假作业答案的同学:线程A和线程B。当他们正在抄的时候,⽼师突然来修改了⼀些答案,可能A和B最后写出的暑假作业就不⼀样。我们为了A,B能写出2本相同的暑假作业,我们就需要让⽼师先修改答案,然后A,B同学再抄。或者A,B同学先抄完,⽼师再修改答案。这就是线程A,线程B的线程同步。可以以解释为:线程同步是线程之间按照⼀定的顺序执⾏。为了达到线程同步,我们可以使⽤锁来实现它。我们先来看看⼀个⽆锁的程序:package com.company; import java.util.stream.IntStream; public class NoneLock { static class ThreadA implements Runnable { @Override public void run() { IntStream.range(1, 100).forEach(i -> { System.out.println("threadA:" + i); }); } } static class ThreadB implements Runnable { @Override public void run() { IntStream.range(1, 100).forEach(i -> { System.out.println("threadB:" + i); }); } } public static void main(String[] args) { new Thread(new ThreadA()).start(); new Thread(new ThreadB()).start(); } } 某次的运行结果那我现在有⼀个需求,我想等A先执⾏完之后,再由B去执⾏,怎么办呢?最简单的⽅式就是使⽤⼀个“对象锁”:package com.company; import java.util.stream.IntStream; public class ObjectLock { private static final Object lock = new Object(); static class ThreadA implements Runnable { @Override public void run() { synchronized (lock) { IntStream.range(1, 100).forEach(i -> { System.out.println("threadA:" + i); }); } } } static class ThreadB implements Runnable { @Override public void run() { synchronized (lock) { IntStream.range(1, 100).forEach(i -> { System.out.println("threadB:" + i); }); } } } public static void main(String[] args) throws InterruptedException { new Thread(new ThreadA()).start(); Thread.sleep(10); new Thread(new ThreadB()).start(); } }
2021年06月06日
919 阅读
0 评论
0 点赞
2021-05-29
spring boot整合log4j2使用日志切面
在spring boot 整合log4j2中,我们在spring boot中整合了log4j,这篇文章,我们通过增加切面,实现自动记录日志。增加切面依赖<!-- 增加切面依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>增加切面@Aspect @Component @Slf4j public class LogAspect { /** * 定义切点 * 所有controller包下的public方法 */ @Pointcut("execution(public * cc.lisen.log4j.controller..*.*(..))") public void autoLog(){}; /** * 方法执行前 * @param joinPoint 切点 */ @Before("autoLog()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert servletRequestAttributes != null; HttpServletRequest request = servletRequestAttributes.getRequest(); log.info("method---"+request.getMethod()); } }
2021年05月29日
963 阅读
2 评论
1 点赞
2021-05-29
spring boot 整合log4j2
log4j2的优点相比与其他的日志系统,log4j2丢数据的情况少;在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生。使用添加依赖引入依赖时,需要首先排除spring boot自带的logback,然后在添加log4j2的依赖。<!--排除 Spring-boot-starter 默认的日志配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!--增加日志依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>{message type="success" content="推荐如上方式排除默认的logback依赖,而不是放到某一个starter依赖中"/}添加log4j2的配置文件修改默认配置文件名log4j2默认配置文件名为log4j2-spring.xml,如果想自定义配置文件,可以在`application.yamllogging: config: classpath:log4j2.xml这样我们在项目resources文件夹下创建文件log4j2.xml配置文件模板log4j2.xml配置模板内容如下:<?xml version="1.0" encoding="UTF-8"?> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration monitorInterval="5"> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--变量配置--> <Properties> <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符--> <!-- %logger{36} 表示 Logger 名字最长36个字符 --> <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" /> <!-- 定义日志存储的路径 --> <property name="FILE_PATH" value="log" /> <property name="FILE_NAME" value="log4j2" /> </Properties> <appenders> <console name="Console" target="SYSTEM_OUT"> <!--输出日志的格式--> <PatternLayout pattern="${LOG_PATTERN}"/> <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> </console> <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false"> <PatternLayout pattern="${LOG_PATTERN}"/> </File> <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="${LOG_PATTERN}"/> <Policies> <!--interval属性用来指定多久滚动一次,默认是1 hour--> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖--> <DefaultRolloverStrategy max="15"/> </RollingFile> </appenders> <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--> <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--> <loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.mybatis" level="info" additivity="false"> <AppenderRef ref="Console"/> </logger> <!--监控系统信息--> <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。--> <Logger name="org.springframework" level="info" additivity="false"> <AppenderRef ref="Console"/> </Logger> <root level="info"> <appender-ref ref="Console"/> <appender-ref ref="Filelog"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>配置参数介绍日志级别机制:如果一条日志信息的级别大于等于配置文件的级别,就记录。trace:追踪,就是程序推进一下,可以写个trace输出debug:调试,一般作为最低级别,trace基本不用。info:输出重要的信息,使用较多warn:警告,有些信息不是错误信息,但也要给程序员一些提示。error:错误信息。用的也很多。fatal:致命错误。输出源CONSOLE(输出到控制台)FILE(输出到文件)格式SimpleLayout:以简单的形式显示HTMLLayout:以HTML表格显示PatternLayout:自定义形式显示PatternLayout自定义日志布局:%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间%-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0%c : logger的名称(%logger)%t : 输出当前线程名称%p : 日志输出格式%m : 日志内容,即 logger.info("message")%n : 换行符%C : Java类名(%F)%L : 行号%M : 方法名%l : 输出语句所在的行数, 包括类名、方法名、文件名、行数hostName : 本地机器名hostAddress : 本地ip地址简单测试lombok就是一个注解工具jar包,能帮助我们省略一繁杂的代码。为了方便使用,我们可以在需要打印日志的类上添加@Slf4j注解,然后通过log.输出日志,如下/** * 测试log4j2 * * @author laughing * @date 2021.5.29 */ @RequestMapping("") @RestController @Slf4j public class IndexController { @GetMapping public String index() { log.info("测试日志"); return "测试成功"; } }
2021年05月29日
951 阅读
0 评论
1 点赞
2021-05-28
spring boot mybatis配置主从数据库(多数据源)
MyBatis配置多数据源基本步骤:预制两个测试的数据库master和cluster添加mybatis及druid依赖配置文件配置两个数据源(配置数据源时,必须要有一个主数据源)测试代码预制数据库表预制两个数据库,主库为master,并创建表test,从库为cluster,并创建表user。主库从库创建表语句:CREATE TABLE `test` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制数据INSERT INTO cluster.user (id, username) VALUES (1, '李四');从库从库创建表语句CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制语句INSERT INTO cluster.user (id, username) VALUES (1, '李四');添加项目依赖创建spring boot项目,并添加如下依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.4</version> </dependency>添加配置数据库连接在application.properties中添加两个数据库的配置文件#主库配置 master.datasource.url=jdbc:mysql://IP:3306/master?useSSL=false&characterEncoding=utf8 master.datasource.username=master master.datasource.password=master master.datasource.driver=com.mysql.jdbc.Driver #从库配置 cluster.datasource.url=jdbc:mysql://IP:3306/cluster?useSSL=false&characterEncoding=utf8 cluster.datasource.username=cluster cluster.datasource.password=cluster cluster.datasource.driver=com.mysql.jdbc.Driver添加数据源主库添加主库数据源MasterDataSourceConfig.javapackage cc.lisen.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; 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 org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 主库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { final static String PACKAGE = "cc.lisen.mybatsmultidatasource.dao.master"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${master.datasource.url}") private String url; @Value("${master.datasource.username}") private String username; @Value("${master.datasource.password}") private String password; @Value("${master.datasource.driver}") private String driver; @Bean(name = "masterDataSource") @Primary public DataSource masterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "masterDataSourceTransactionManager") @Primary public DataSourceTransactionManager masterDataSourceTransactionManager() { return new DataSourceTransactionManager(masterDataSource()); } @Bean(name = "masterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 从库添加从库数据源ClusterDataSourceConfig.javapackage cc.lisen.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; 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.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 从库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "clusterSqlSessionFactory") public class ClusterDataSourceConfig { final static String PACKAGE = "cc.lisen.mybatsmultidatasource.dao.cluster"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${cluster.datasource.url}") private String url; @Value("${cluster.datasource.username}") private String username; @Value("${cluster.datasource.password}") private String password; @Value("${cluster.datasource.driver}") private String driver; @Bean(name = "clusterDataSource") @Primary public DataSource clusterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "clusterDataSourceTransactionManager") @Primary public DataSourceTransactionManager clusterDataSourceTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "clusterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ClusterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 主库、从库的数据源通过@MapperScan注解注入不同数据库mapper所在的包。创建DAO主库主库的包位于cc.lisen.mybatsmultidatasource.dao.master与@MapperScan配置的位置要保持一致。主库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库Test表的所有数据。从库从库的包位于cc.lisen.mybatsmultidatasource.dao.cluster与@MapperScan配置的位置要保持一致。从库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库User表的所有数据。创建Service代码就不罗列了,也是有一个selectAll()方法。测试创建controller分别查询主库及从库的数据,如下@RestController public class UserController { @Resource private ITestService testService; @Resource private IUserService userService; @GetMapping("/master") public List<Test> getMasterAll(){ return testService.selectAll(); } @GetMapping("/cluster") public List<User> getClusterAll(){ return userService.selectAll(); } }
2021年05月28日
1,614 阅读
0 评论
0 点赞
2021-05-17
MQTT使用三之Spring Boot集成MQTT简单使用
Spring Boot集成MQTT支持动态创建topic。添加依赖 <!-- MQTT --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mqtt</artifactId> </dependency>添加配置文件server: port: 9999 #mqtt的配置 mqtt: server: url: tcp://ip:1883 port: 1883 username: 用户名 password: 密码 client: consumerId: consumerCo publishId: publishCo default: topic: topic completionTimeout: 3000MQTT配置文件package cc.lisen.mqtt.config; import cc.lisen.mqtt.utils.MqttReceiveHandle; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageProducer; import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; import org.springframework.integration.mqtt.core.MqttPahoClientFactory; import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.Arrays; import java.util.List; /** * Description:消息订阅配置 * * @author : laughing * DateTime: 2021-05-18 13:31 */ @Configuration public class MqttConfig { public final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final byte[] WILL_DATA; static { WILL_DATA = "offline".getBytes(); } @Resource private MqttReceiveHandle mqttReceiveHandle; @Value("${mqtt.server.url}") private final String url = "tcp://139.198.172.114:1883"; @Value("${mqtt.server.port}") private final String port = "1883"; @Value("${mqtt.server.username}") private final String username = "admin"; @Value("${mqtt.server.password}") private final String password = "public"; @Value("${mqtt.client.consumerId}") private final String consumerId = "consumerClient"; @Value("${mqtt.client.publishId}") private final String publishId = "publishClient"; @Value("${mqtt.default.topic}") private final String topic = "topic"; @Value("${mqtt.default.completionTimeout}") private final Integer completionTimeout = 3000; //消息驱动 private MqttPahoMessageDrivenChannelAdapter adapter; //订阅的主题列表 private String listenTopics = ""; // //mqtt消息接收接口 // private MqttReceiveService mqttReceiveService; // // public void setMqttReceiveService(MqttReceiveService mqttReceiveService){ // this.mqttReceiveService = mqttReceiveService; // } /** * MQTT连接器选项 * **/ @Bean(value = "getMqttConnectOptions") public MqttConnectOptions getMqttConnectOptions(){ MqttConnectOptions mqttConnectOptions=new MqttConnectOptions(); // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 mqttConnectOptions.setCleanSession(true); // 设置超时时间 单位为秒 mqttConnectOptions.setConnectionTimeout(10); mqttConnectOptions.setAutomaticReconnect(true); mqttConnectOptions.setUserName(username); mqttConnectOptions.setPassword(password.toCharArray()); mqttConnectOptions.setServerURIs(new String[]{url}); // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线,但这个方法并没有重连的机制 mqttConnectOptions.setKeepAliveInterval(10); // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。 mqttConnectOptions.setWill("willTopic", WILL_DATA, 2, false); return mqttConnectOptions; } /** * MQTT工厂 * **/ @Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); factory.setConnectionOptions(getMqttConnectOptions()); return factory; } /** * MQTT信息通道(生产者) * **/ @Bean public MessageChannel mqttOutboundChannel() { return new DirectChannel(); } /** * MQTT消息处理器(生产者) * **/ @Bean @ServiceActivator(inputChannel = "mqttOutboundChannel") public MessageHandler mqttOutbound() { MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(publishId, mqttClientFactory()); messageHandler.setAsync(true); messageHandler.setDefaultTopic(topic); return messageHandler; } /** * 配置client,监听的topic * MQTT消息订阅绑定(消费者) * **/ @Bean public MessageProducer inbound() { if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } String [] topics = listenTopics.split(","); for(String topic: topics){ if(!StringUtils.isEmpty(topic)){ adapter.addTopic(topic,1); } } adapter.setCompletionTimeout(completionTimeout); adapter.setConverter(new DefaultPahoMessageConverter()); adapter.setQos(2); adapter.setOutputChannel(mqttInputChannel()); return adapter; } /** * 增加监听的topic * @param topicArr 消息列表 * @return 结果 */ public List<String> addListenTopic(String [] topicArr){ if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } List<String> listTopic = Arrays.asList(adapter.getTopic()); for(String topic: topicArr){ if(!StringUtils.isEmpty(topic)){ if(!listTopic.contains(topic)){ adapter.addTopic(topic,1); } } } return Arrays.asList(adapter.getTopic()); } /** * 移除一个监听的topic * @param topic * @return */ public List<String> removeListenTopic(String topic){ if(adapter == null){ adapter = new MqttPahoMessageDrivenChannelAdapter(consumerId, mqttClientFactory(), topic); } List<String> listTopic = Arrays.asList(adapter.getTopic()); if(listTopic.contains(topic)){ adapter.removeTopic(topic); } return Arrays.asList(adapter.getTopic()); } /** * MQTT信息通道(消费者) * **/ @Bean public MessageChannel mqttInputChannel() { return new DirectChannel(); } /** * MQTT消息处理器(消费者) * **/ @Bean @ServiceActivator(inputChannel = "mqttInputChannel") public MessageHandler handler() { return new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { //处理接收消息 mqttReceiveHandle.handle(message); //String topic = message.getHeaders().get("mqtt_receivedTopic").toString(); //String msg = ((String) message.getPayload()).toString(); //mqttReceiveService.handlerMqttMessage(topic,msg); } }; } } 消息处理package cc.lisen.mqtt.utils; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; @Component public class MqttReceiveHandle implements MqttCallback { private final Logger logger = LoggerFactory.getLogger(MqttReceiveHandle.class); public void handle(Message<?> message) { try { logger.info("{},客户端号:{},主题:{},QOS:{},消息接收到的数据:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), message.getHeaders().get(MqttHeaders.ID), message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC), message.getHeaders().get(MqttHeaders.RECEIVED_QOS), message.getPayload()); //处理mqtt数据 this.handle(message.getPayload().toString()); } catch (Exception e) { e.printStackTrace(); logger.error("处理错误" + e.getMessage()); } } private void handle(String str) throws Exception { logger.info(str); } @Override public void connectionLost(Throwable throwable) { logger.warn("连接丢失"); } @Override public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { logger.info("消息到达:" + topic + "\n" + "消息内容:" + new String(mqttMessage.getPayload()) + "\nclientId:" + mqttMessage.getId()); } @Override public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { logger.info("clientId:" + iMqttDeliveryToken.getClient().getClientId()); } }消息发送package cc.lisen.mqtt.utils; import org.springframework.integration.annotation.MessagingGateway; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.handler.annotation.Header; /** * Description: * * @author : laughing * DateTime: 2021-05-18 13:44 */ @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") public interface MqttGateway { /** * 发送信息到MQTT服务器 * * @param data 发送的文本 */ void sendToMqtt(String data); /** * 发送信息到MQTT服务器 * * @param topic 主题 * @param payload 消息主体 */ void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload); /** * 发送信息到MQTT服务器 * * @param topic 主题 * @param qos 对消息处理的几种机制。 * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。 * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。 * 2 多了一次去重的动作,确保订阅者收到的消息有一次。 * @param payload 消息主体 */ void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload); }测试package cc.lisen.mqtt.controller; import cc.lisen.mqtt.config.MqttConfig; import cc.lisen.mqtt.utils.MqttGateway; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController public class MqttController { @Resource private MqttGateway mqttGateway; @Resource private MqttConfig mqttConfig; @GetMapping("/add/{topic}") public String addTopic(@PathVariable("topic") String topic) { String[] topics = {topic}; List<String> list = mqttConfig.addListenTopic(topics); return list.toString(); } @GetMapping("/pub") public String pubTopic() { String topic = "temperature1"; String msg = "client msg at: " + String.valueOf(System.currentTimeMillis()); mqttGateway.sendToMqtt(topic, 2, msg); return "OK"; } @GetMapping("/del/{topic}") public String delTopic(@PathVariable("topic") String topic) { List<String> list = mqttConfig.removeListenTopic(topic); return list.toString(); } }
2021年05月17日
1,377 阅读
2 评论
0 点赞
2021-05-17
MQTT使用二之EMQ X的安装及使用
EMQ X (Erlang/Enterprise/Elastic MQTT Broker) 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器。Erlang/OTP是出色的软实时 (Soft-Realtime)、低延时 (Low-Latency)、分布式 (Distributed)的语言平台。MQTT 是轻量的 (Lightweight)、发布订阅模式 (PubSub) 的物联网消息协议。EMQ X 设计目标是实现高可靠,并支持承载海量物联网终端的MQTT连接,支持在海量物联网设备间低延时消息路由:稳定承载大规模的 MQTT 客户端连接,单服务器节点支持50万到100万连接。分布式节点集群,快速低延时的消息路由,单集群支持1000万规模的路由。消息服务器内扩展,支持定制多种认证方式、高效存储消息到后端数据库。完整物联网协议支持,MQTT、MQTT-SN、CoAP、LwM2M、WebSocket 或私有协议支持。服务器支持EMQ X 目前支持的操作系统:CentOS 6CentOS 7CentOS 8OpenSUSE tumbleweedDebian 9Debian 10Ubuntu 16.04Ubuntu 18.04Ubuntu 20.04macOS 10.13macOS 10.14macOS 10.15Windows Server 2019{message type="success" content="产品部署建议 Linux 服务器,不推荐 Windows 服务器。"/}Shell 脚本一键安装 (Linux)curl https://repos.emqx.io/install_emqx.sh | bash包管理器安装(CentOS)安装所需要的依赖包sudo yum install -y yum-utils device-mapper-persistent-data lvm2使用以下命令设置稳定存储库,以 CentOS 7 为例sudo yum-config-manager --add-repo https://repos.emqx.io/emqx-ce/redhat/centos/7/emqx-ce.repo安装最新版本的 EMQ X Brokersudo yum install emqx启动直接启动 EMQ X Brokeremqx startsystemctl 启动sudo systemctl start emqx 查看状态emqx_ctl status停止 EMQ X Brokeremqx stop卸载sudo yum remove emqxDashboard安装完成后,通过ip:18083,打开出现界面代表安装成功。默认用户名是 admin,密码是 public更多安装方式可以参考:https://docs.emqx.cn/broker/v4.3/getting-started/install.html#
2021年05月17日
1,428 阅读
0 评论
1 点赞
2021-05-17
MQTT使用一之MQTT介绍
之前其实也没有用过MQTT,最近接触MQTT是因为有几个项目涉及传感器,在与传感器对接的过程中,通过MQTT完成数据的交互。简介MQTT(message queuing telemetry transport)是IBM开发的即时通讯协议,是一种发布/订阅极其轻量级的消息传输协议,专门为网络受限设备、低宽带以及高延迟和不可靠的网络而设计的。由于以上轻量级的特点,是实现智能家居的首选传输协议,相比于XMPP,更加轻量级而且占用宽带低。特点由于采用发布/订阅的消息模式,可以提供一对多的消息发布轻量级,网络开销小对负载内容会有屏蔽的消息传输有三种消息发布质量(Qos): qos=0:“至多一次”,这一级别会发生消息丢失或重复,消息发布依赖于TCP/IP网络 qos=1:“至少一次”,确保消息到达,但消息重复可能会发生 qos=2:“只有一次”,确保消息到达一次通知机制,异常中断时会通知双方原理MQTT协议有三种身份:发布者、代理、订阅者,发布者和订阅者都为客户端,代理为服务器,同时消息的发布者也可以是订阅者(为了节约内存和流量发布者和订阅者一般都会定义在一起)。MQTT传输的消息分为主题(Topic,可理解为消息的类型,订阅者订阅后,就会收到该主题的消息内容(payload))和负载(payload,可以理解为消息的内容)两部分。
2021年05月17日
1,104 阅读
0 评论
1 点赞
2021-05-11
mybatis-plus逻辑删除不生效的解决办法
我们在使用mybatis-plus时,一般设备逻辑删除是非常简单的,基本上在yaml等配置文件中做一下配置。然后在字段上注解@TableLogic就可以了。有不清楚的,可以参考https://lisen.cc/back/mybatis-plus-logical-deletion.html但是今天在项目中,发现一个问题,就是明明也正确的进行了配置,但是在进行数据库操作时,发现逻辑删除并没有生效。问题描述先说一下问题先想,数据库指定的字段可以使用,但是指定是否逻辑删除的值时还是mybatis-plus默认的0和1,并不是我指定的N和Y。配置文件先来看下我的配置文件。mybatisPlus: # 搜索指定包别名 typeAliasesPackage: cc.lisen.**.domain # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml global-config: db-config: # 配置逻辑删除 logic-delete-field: del_flag logic-not-delete-value: N logic-delete-value: Y通过配置文件,我指定数据库标记逻辑删除的字段为del_flag,如果已经删除,标记为Y,如果没有删除(默认值)就是N。实体通过提取的公共实体,标记逻辑删除字段,如下@Data public class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 搜索值 */ private String searchValue; /** * 创建者 */ @TableField(fill = FieldFill.INSERT) private Long createBy; /** * 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 更新者 */ @TableField(fill = FieldFill.UPDATE) private Long updateBy; /** * 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.UPDATE) private Date updateTime; /** * 删除标志(Y-已删除,N-未删除) */ @TableLogic private String delFlag; /** * 开始时间 */ @JsonIgnore @TableField(exist = false) private String beginTime; /** * 结束时间 */ @JsonIgnore @TableField(exist = false) private String endTime; }使用调用了一个update方法postMapper.updatePost(post);在进行更新操作时,mybatis-plus会追加where条件防止更新到已删除数据,且使用wrapper.entity生成的where条件会忽略该字段。也就是说,我本来的方法对应的sql可能是update xx set xx where xx=xx如果我配置的逻辑删除没有问题的话,mybatis-plus生成的sql应该是update xx set xx where xx=xx and del_flag = 'N'但是实际我测试发现,生成的sql却是update xx set xx where xx=xx and del_flag = '0'可以看到,虽然逻辑删除的字段是对的,但是实际上,对应字段是否删除的值还是mybatis-plus默认的,并不是我们设置的。问题分析其实这个问题之前还是好的,让我想到应该是最近配置的SqlSessionFactory的问题。 @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); return sessionFactory.getObject(); }我这里重新注入了MybatisSqlSessionFactoryBean,但是并没有对它的配置进行修改,这就导致了我配置文件里的东西并没有加载。解决解决办法也很简单,两种方式我们分别说下。方式一方式一是在我们实体逻辑删除的注解上加上删除和未删除对应的值。 /** * 删除标志(Y-已删除,N-未删除) */ @TableLogic(value = "N",delval = "Y") private String delFlag;方式二方式二就是,我们在MybatisSqlSessionFactoryBean的bean里,把我们配置文件里的配置加上。对SqlSessionFactory改造如下 @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); sessionFactory.setGlobalConfig(globalConfig()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } /** * 逻辑删除插件 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setLogicDeleteValue("Y"); dbConfig.setLogicNotDeleteValue("N"); globalConfig.setDbConfig(dbConfig); return globalConfig; }
2021年05月11日
2,322 阅读
0 评论
1 点赞
2021-05-11
mybatis plus 出现 Invalid bound statement (not found)
使用mybatis-plus时不能使用自带的SqlSessionFactory,要使用MybatisSqlSessionFactory,在配置类中加入如下配置(springboot) @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatisPlus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatisPlus.mapperLocations"); String configLocation = env.getProperty("mybatisPlus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); return sessionFactory.getObject(); }
2021年05月11日
1,400 阅读
0 评论
2 点赞
2021-05-07
mybatis-plus自动注入sql语句
我们在日常开发中,一般会在表里面增加创建人(create_by)、创建时间(create_time)、修改人(update_by)、修改时间(update_time)四个字段。创建人:新增时,通过上下文获取用户信息。创建时间:新增时,获取系统当前时间。修改人:修改时,通过上下文获取用户信息。修改时间:修改时,获取系统当前时间。对于这种公共的字段,如果每次都在Sql中进行处理,那是相当繁琐也是没必要的。如果你用的是MyBatis-Plus那么一切就变的简单了。继承mybatis的Interceptor拦截器/** * Description: mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间 * * @author : laughing * DateTime: 2021-05-06 17:13 */ @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) @Slf4j @SuppressWarnings("all") public class MybatisSqlInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String sqlId = mappedStatement.getId(); SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Object parameter = invocation.getArgs()[1]; if (parameter == null) { return invocation.proceed(); } if(SysOperateLog.class.isAssignableFrom(parameter.getClass()) || SysLoginInfo.class.isAssignableFrom(parameter.getClass())){ return invocation.proceed(); } if (SqlCommandType.INSERT == sqlCommandType) { Field[] fields = ConvertUtils.getAllFields(parameter); Long userId = SecurityUtils.getUserId(); for (Field field : fields) { try { if ("createBy".equals(field.getName())) { field.setAccessible(true); Object local_createBy = field.get(parameter); field.setAccessible(false); if (local_createBy == null || local_createBy.equals("")) { // 登录人账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } } // 注入创建时间 if ("createTime".equals(field.getName())) { field.setAccessible(true); Object local_createDate = field.get(parameter); field.setAccessible(false); if (local_createDate == null || local_createDate.equals("")) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } } catch (Exception e) { } } } if (SqlCommandType.UPDATE == sqlCommandType) { Long userId = SecurityUtils.getUserId(); Field[] fields = null; if (parameter instanceof MapperMethod.ParamMap) { MapperMethod.ParamMap<?> p = (MapperMethod.ParamMap<?>) parameter; if (p.containsKey("et")) { parameter = p.get("et"); } else { parameter = p.get("param1"); } if (parameter == null) { return invocation.proceed(); } fields = ConvertUtils.getAllFields(parameter); } else { fields = ConvertUtils.getAllFields(parameter); } for (Field field : fields) { try { if ("updateBy".equals(field.getName())) { //获取登录用户信息 // 登录账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } if ("updateTime".equals(field.getName())) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } } } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } }通过SqlSessionFactory加载插件/** * Mybatis-Plus配置 * * @author leeframe */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || LeeFrameConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatisPlus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatisPlus.mapperLocations"); String configLocation = env.getProperty("mybatisPlus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } }
2021年05月07日
1,469 阅读
0 评论
0 点赞
2021-05-07
Spring Boot yaml文件配置列表
我们在Spring Boot的yaml配置文件中,一般配置的都是一些文本(字符串)。那么我们在yaml文件中如何配置列表或者数组呢。场景试想一下我们的场景:我们系统涉及到租户,数据库采用行级别的隔离,也就是说表里面有一个org_code列,作为租户之间数据隔离的条件,类似于where org_code = 'xx'。但是,我们不是所有的表都要进行,那么我们在过滤某些表不进行过滤时,首先肯定想到的就是在yaml文件中配置需要过滤的表名。敲代码yaml设置# 项目相关配置 leeframe: # 过滤表名,不进行租户的过滤 filterTableList: - sys_dict_type - sys_dict_data - sys_config - sys_organization - sys_job - sys_user_role配置映射@Component @ConfigurationProperties(prefix = "leeframe") public class LeeFrameConfig { /** * 过滤表名,不进行租户的过滤 */ private static List<String> filterTableList; public static List<String> getFilterTableList() { return filterTableList; } public void setFilterTableList(List<String> filterTableList) { LeeFrameConfig.filterTableList = filterTableList; } }使用LeeFrameConfig.getFilterTableList()
2021年05月07日
1,305 阅读
0 评论
1 点赞
2021-05-07
Spring Boot alibaba druid配置输出sql
用的slf4j打印的sql# 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver druid: # 主库数据源 master: url: jdbc:mysql://localhost:3306/leeframe?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: root # 从库数据源 slave: # 从数据源开关/默认关闭 enabled: false url: username: password: # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最大生存的时间,单位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置检测连接是否有效 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 设置白名单,不填则允许所有访问 allow: url-pattern: /druid/* # 控制台管理用户名和密码 login-username: login-password: filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true slf4j: enabled: true statement-create-after-log-enabled: false statement-log-enabled: true statement-executable-sql-log-enable: true statement-log-error-enabled: true result-set-log-enabled: false logging: level: druid: sql: Statement: DEBUG
2021年05月07日
1,348 阅读
0 评论
1 点赞
1
...
9
10
11
...
19