622

image

image

image

image

java版集成Allure报告--注解的强大

Junit5 入门系列
Java单元测试之JUnit 5快速上手
《单元测试》Junit5入门教程——非常详细,入门即精通

mysql 汇总

基础操作

史上最全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

比较两个表

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>
posted @ 2024-09-29 08:21  潼关路边的一只野鬼  阅读(28)  评论(0)    收藏  举报