622
image



Junit5 入门系列
Java单元测试之JUnit 5快速上手
《单元测试》Junit5入门教程——非常详细,入门即精通
mysql 汇总
基础操作
表管理
查看表结构
show full columns from sys_user ;
查看表注释
select
TABLE_NAME as 表名,
TABLE_COMMENT as 表注释
from
INFORMATION_SCHEMA.TABLES
where
TABLE_SCHEMA = 'shiro_study';
查看表索引
show index from sys_user ;
基础查询
vim
.vimrc
" 开启语法高亮
syntax enable
" 开启语法高亮
syntax on
" 设置字体
set guifont=Monaco\ 12
" 检测文件类型
filetype on
" 针对不同的文件,采用不同的缩进方式
filetype indent on
" 设置取消备份,禁止临时文件生成
set nobackup
set noswapfile
" 显示当前行号和列号
set ruler
" 在状态栏显示正在输入的命令
set showcmd
" 左下角显示当前Vim模式
set showmode
" 显示状态栏
set laststatus=2
" 显示行号
set number
" 开启及时搜索(is)
set incsearch
" 设置搜索高亮(hlsearch)
set hls
" 设置搜索时忽略大小写
set ignorecase
日志
logback
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 -->
<!-- scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="LOG_PATH" value="../logs/km-ssm" />
<property name="LOG_FILE_NAME" value="app" />
<property name="PATTERN_FORMAT"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %-40.40logger{39} [tid=%X{tid}] - %msg%n" />
<!-- 输出到控制台 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN_FORMAT}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 输出到滚动文件 -->
<appender name="rolling-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
<encoder>
<pattern>${PATTERN_FORMAT}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/%d{yyyy-MM-dd}/${LOG_FILE_NAME}-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数 -->
<maxHistory>150</maxHistory>
</rollingPolicy>
</appender>
<root level="DEBUG">
<appender-ref ref="console" />
<appender-ref ref="rolling-file" />
</root>
<logger name="com.laolang.km" level="INFO" additivity="false">
<appender-ref ref="console" />
<appender-ref ref="rolling-file" />
</logger>
</configuration>
log4j2
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<properties>
<property name="LOG_HOME">../logs/jx-boot</property>
<property name="FILE_NAME">app</property>
<property name="jx.level">info</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%25t] %-5level %l - %msg%n"/>
</Console>
<RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log"
filePattern="${LOG_HOME}/${date:yyyy-MM-dd}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%25t] %-5level %l - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
<Socket name="logstash" host="localhost" port="1218" protocol="TCP">
<PatternLayout pattern="${%d{HH:mm:ss.SSS} [%25t] %-5level %l - %msg%n}"/>
</Socket>
</Appenders>
<Loggers>
<Root level="${jx.level}">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingRandomAccessFile"/>
<AppenderRef ref="logstash"/>
</Root>
<Logger name="com.laolang" level="${jx.level}" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingRandomAccessFile"/>
<AppenderRef ref="logstash"/>
</Logger>
</Loggers>
</Configuration>
mybatis
打印 sql
package com.laolang.jx.framework.mybatis.interceptor;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* 打印 sql 拦截器
*/
@Slf4j
@Intercepts({
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class MybatisPrintSqlInterceptor implements Interceptor {
/**
* mybatis 配置对象.
*/
private Configuration configuration = null;
/**
* 时间格式化.
*/
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT_THREAD_LOCAL = ThreadLocal
.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
/**
* 拦截器主方法.
*
* @param invocation invocation
* @return sql 执行结果
* @throws Throwable Throwable
*/
@SuppressWarnings("rawtypes")
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = null;
try {
result = invocation.proceed();
return result;
} finally {
try {
long endTime = System.currentTimeMillis();
long sqlCost = endTime - startTime;
StatementHandler statementHandler = getRealTarget(invocation.getTarget());
BoundSql boundSql = statementHandler.getBoundSql();
if (configuration == null) {
final ParameterHandler parameterHandler = statementHandler.getParameterHandler();
// Field configurationField = ReflectionUtils.findField(parameterHandler.getClass(), "configuration");
// ReflectionUtils.makeAccessible(configurationField);
Field configurationField = ReflectUtil.getField(parameterHandler.getClass(), "configuration");
ReflectUtil.setAccessible(configurationField);
this.configuration = (Configuration) configurationField.get(parameterHandler);
}
// 输出 mapper id
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
String id = ms.getId();
// 替换参数格式化Sql语句,去除换行符
String sql = formatSql(boundSql, configuration).concat(";");
String warning = "";
// CHECKSTYLE:OFF
if (sqlCost > 2000) {
warning = "[耗时过长]";
}
// CHECKSTYLE:ON
// 开始输出 sql
log.info("map-id: {}", id);
log.info("[ {} ] [ {} ] ms {}", sql, sqlCost, warning);
if (result instanceof List) {
log.info("Total: {}", ((List) result).size());
} else {
log.info("Updates: {}", result);
}
} catch (Exception e) {
log.error("==> 打印sql 日志异常 {0}", e);
}
}
}
/**
* <p>
* 获取真正的对象(非代理对象)
* </p>
* <p>
* 解决报错:
* <code>There is no getter for property named 'delegate' in 'class com.sun.proxy.$Proxy199'</code>
*/
@SuppressWarnings("unchecked")
public static <T> T getRealTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return getRealTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
/**
* plugin.
*
* @param target target
* @return Object
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* setProperties.
*
* @param properties properties
*/
@Override
public void setProperties(Properties properties) {
}
/**
* 获取完整的sql实体的信息.
*
* @param boundSql boundSql
* @param configuration configuration
* @return 格式化后的 sql
*/
private String formatSql(BoundSql boundSql, Configuration configuration) {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
// 输入sql字符串空判断
if (StrUtil.isBlank(sql)) {
return "";
}
if (configuration == null) {
return "";
}
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
sql = beautifySql(sql);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 参考mybatis 源码 DefaultParameterHandler
if (parameterMappings != null) {
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
String paramValueStr = "";
if (value instanceof String) {
paramValueStr = "'" + value + "'";
} else if (value instanceof Date) {
paramValueStr = "'" + DATE_FORMAT_THREAD_LOCAL.get().format(value) + "'";
} else {
paramValueStr = value + "";
}
sql = sql.replaceFirst("\\?", paramValueStr);
}
}
}
return sql;
}
/**
* 美化 sql.
*
* @param sql sql
* @return sql
*/
private String beautifySql(String sql) {
sql = sql.replaceAll("[\\s\n ]+", " ");
return sql;
}
}
资源汇总
mysql
比较两个表
package com.laolang.jx.test.base;
import static org.testng.Assert.fail;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
/**
* <p>基于 <code>spring-test</code> 实现的测试基类</p>
*/
@Slf4j
@ContextConfiguration(locations = { "classpath:spring-context.xml" })
public abstract class AbstractSpringTest extends AbstractTestNGSpringContextTests {
@Autowired
private JdbcTemplate jdbcTemplate;
private Predicate<String> sqlPredicate = t -> {
if (StrUtil.isBlank(t)) {
return false;
}
if (StrUtil.startWith(t, "--")) {
return false;
}
return true;
};
/**
* 执行初始化 sql
*/
protected void executeInitSqls(String initSqlPath) {
try {
List<String> initSqls = FileUtil.readUtf8Lines(initSqlPath).stream().filter(sqlPredicate)
.collect(Collectors.toList());
for (int i = 0; i < initSqls.size(); i++) {
jdbcTemplate.execute(initSqls.get(i));
}
} catch (Throwable throwable) {
throwable.printStackTrace();
fail("execute init sql failed");
}
}
/**
* 对比两个表的数据
*
* @param acture 实际操作的表
* @param except 对比的表
* @param compareColumns 对比的字段列表
*/
protected void validTableData(String acture, String except, List<String> compareColumns) {
String columns = String.join(",", compareColumns);
StringBuilder sb = new StringBuilder();
sb.append("select count(*) from ( select * from ( select ");
sb.append(columns);
sb.append(" from sys_dict_type union all select ");
sb.append(columns);
sb.append(" from sys_dict_type_after ) tbl group by ");
sb.append(columns);
sb.append(" having count(*) = 1 order by id ) t");
Integer count = jdbcTemplate.queryForObject(sb.toString(), Integer.class);
Assert.assertEquals(count.intValue(), 0);
}
}
allure
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.laolang.ghost</groupId>
<artifactId>spring-hello-schema</artifactId>
<version>0.1</version>
<properties>
<!-- 项目编译配置 -->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- maven 插件 -->
<maven-resources-plugin.version>2.7</maven-resources-plugin.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.2.3</maven-surefire-plugin.version>
<exec-maven-plugin.version>3.4.1</exec-maven-plugin.version>
<maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
<!-- 启动类 -->
<app.main.class>com.laolang.ghost.SpringHelloSchemaApplication</app.main.class>
<!-- spring -->
<spring.version>5.3.31</spring.version>
<!-- 日志 -->
<logback.version>1.2.12</logback.version>
<!-- 工具类库 -->
<lombok.version>1.18.30</lombok.version>
<!-- 测试相关 -->
<testng.version>6.14.3</testng.version>
<allure-testng.version>2.27.0</allure-testng.version>
<aspectj.version>1.9.6</aspectj.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- 工具类库 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>${allure-testng.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!--
mvn clean test 运行测试即可生成 allure 测试报告数据
如下命令可启动 allure 服务(注意:两个 - 要连起来)
allure serve -p 4001 - -lang zh target/allure-results
如下命令可生成 html 静态报告(注意:两个 - 要连起来),需要通过服务器查看此报告, 比如 npm 的 serve
allure generate - -lang zh target/allure-results -o target/allure-reports
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<reuseForks>true</reuseForks>
<testFailureIgnore>true</testFailureIgnore>
<argLine>
-Dfile.encoding=UTF-8
${jacocoArgLine}
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<systemProperties>
<!--定义输出在项目 target 目录-->
<property>
<name>allure.results.directory</name>
<value>target/allure-results</value>
</property>
</systemProperties>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<!--
mvn clean package 即可在 target/site/jacoco 目录下生成测试覆盖率报告
-->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin.version}</version>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jacocoArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!--
mvn clean install 即可运行程序
如果不想绑定 install ,可以删除 executions 配置
mvn clean compile exec:exec
-->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec-maven-plugin.version}</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<classpath/>
<argument>${app.main.class}</argument>
</arguments>
</configuration>
</plugin>
<!--
mvn clean package 可生成一个带所有依赖的可执行 jar 包
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${app.main.class}</Main-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-maven</artifactId>
<version>2.12.0</version>
<configuration>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

浙公网安备 33010602011771号