feat: 添加测试用例,对比性能

This commit is contained in:
wangyu 2022-12-08 17:15:50 +08:00
parent 8329fd17a9
commit 53961b00ac
13 changed files with 474 additions and 34 deletions

View File

@ -6,6 +6,7 @@ import group.flyfish.fluent.chain.common.PreSqlChain;
import group.flyfish.fluent.chain.select.AfterOrderSqlChain; import group.flyfish.fluent.chain.select.AfterOrderSqlChain;
import group.flyfish.fluent.chain.select.AfterWhereSqlChain; import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
import group.flyfish.fluent.chain.update.AfterSetSqlChain; import group.flyfish.fluent.chain.update.AfterSetSqlChain;
import group.flyfish.fluent.debug.FluentSqlDebugger;
import group.flyfish.fluent.entity.SQLEntity; import group.flyfish.fluent.entity.SQLEntity;
import group.flyfish.fluent.operations.FluentSQLOperations; import group.flyfish.fluent.operations.FluentSQLOperations;
import group.flyfish.fluent.query.JoinCandidate; import group.flyfish.fluent.query.JoinCandidate;
@ -38,9 +39,6 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
// 参数map有序 // 参数map有序
private final List<Object> parameters = new ArrayList<>(); private final List<Object> parameters = new ArrayList<>();
// 调试标识
private final boolean debug = false;
// 主表class默认是第一个from的表为主表 // 主表class默认是第一个from的表为主表
private Class<?> primaryClass; private Class<?> primaryClass;
@ -229,7 +227,7 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
private String sql() { private String sql() {
Assert.notNull(SHARED_OPERATIONS, "未指定执行数据源!"); Assert.notNull(SHARED_OPERATIONS, "未指定执行数据源!");
String sql = segments.stream().map(SQLSegment::get).collect(Collectors.joining(" ")); String sql = segments.stream().map(SQLSegment::get).collect(Collectors.joining(" "));
if (debug) { if (FluentSqlDebugger.enabled()) {
System.out.println("prepared sql: " + sql); System.out.println("prepared sql: " + sql);
System.out.println("prepared args:" + parameters.stream().map(ParameterUtils::convert).map(String::valueOf) System.out.println("prepared args:" + parameters.stream().map(ParameterUtils::convert).map(String::valueOf)
.collect(Collectors.joining(","))); .collect(Collectors.joining(",")));

View File

@ -0,0 +1,17 @@
package group.flyfish.fluent.debug;
/**
* 调试器
*/
public class FluentSqlDebugger {
private static boolean debug = false;
public static void enable() {
debug = true;
}
public static boolean enabled() {
return debug;
}
}

View File

@ -30,6 +30,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
@ -40,5 +45,26 @@
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<!--包含测试mapper-->
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!-- 是否替换资源中的属性-->
<filtering>false</filtering>
</testResource>
</testResources>
</build>
</project> </project>

View File

@ -4,25 +4,19 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.mysql.cj.jdbc.Driver; import com.mysql.cj.jdbc.Driver;
import group.flyfish.fluent.operations.FluentSQLOperations; import group.flyfish.fluent.operations.FluentSQLOperations;
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations; import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
import group.flyfish.fluent.utils.data.ObjectMappers; import group.flyfish.framework.cases.FluentSqlTestCase;
import group.flyfish.framework.entity.SaasOrder; import group.flyfish.framework.cases.JdbcTestCase;
import group.flyfish.framework.entity.SaasPlan; import group.flyfish.framework.cases.MybatisTestCase;
import group.flyfish.framework.entity.SaasTenant;
import group.flyfish.framework.vo.TenantContext;
import org.junit.Test; import org.junit.Test;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static group.flyfish.fluent.chain.SQL.select;
import static group.flyfish.fluent.chain.select.SelectComposite.composite;
import static group.flyfish.fluent.query.Query.where;
/** /**
* 链式jdbc测试 * 链式jdbc测试
* *
@ -42,27 +36,16 @@ public class FluentJdbcTest {
new Driver(), new Driver(),
"jdbc:mysql://127.0.0.1:3306/epi_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=Asia/Shanghai", "jdbc:mysql://127.0.0.1:3306/epi_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=Asia/Shanghai",
"root", "root",
"oI3WtMO8h%mSYARp" "Unicom#2018"
); );
// 基于构造器自动绑定注册在实际应用中使用@Bean声明即可可参考下面的demo // 准备待测试用例
new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource)); List<TestCase<?>> cases = Arrays.asList(
new JdbcTestCase(dataSource),
// 一个平平无奇的查询 new MybatisTestCase(dataSource),
List<TenantContext> list = select( new FluentSqlTestCase(dataSource)
// 查询租户全量字段 );
composite(SaasTenant::getId, SaasTenant::getName, SaasTenant::getIdentifier, SaasTenant::getDatasource, // 执行测试
SaasTenant::getStorage, SaasTenant::getStatus, SaasTenant::getEnable), cases.forEach(TestCase::test);
// 查询套餐
composite(SaasOrder::getQuotaConfig, SaasOrder::getOrderTime, SaasOrder::getExpireTime,
SaasOrder::getOrderType))
.from(SaasTenant.class)
.leftJoin(SaasOrder.class).on(where(SaasOrder::getTenantId).eq(SaasTenant::getId))
.leftJoin(SaasPlan.class).on(where(SaasPlan::getId).eq(SaasOrder::getPlanId))
.matching(where(SaasTenant::getEnable).eq(true))
.list(TenantContext.class);
// 打印效果
System.out.println(ObjectMappers.shared().writeValueAsString(list));
} }
/** /**

View File

@ -0,0 +1,38 @@
package group.flyfish.framework;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 测试器
*
* @author wangyu
*/
public interface TestCase<T> {
/**
* 初始化
*
* @throws Exception 异常
*/
void initialize() throws Exception;
/**
* 执行并获取结果
*
* @return 结果
*/
T test();
/**
* 测试用例名称
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Name {
String value();
}
}

View File

@ -0,0 +1,62 @@
package group.flyfish.framework.cases;
import group.flyfish.framework.TestCase;
import javax.sql.DataSource;
import static group.flyfish.framework.utils.LogUtils.print;
import static group.flyfish.framework.utils.LogUtils.printResult;
/**
* 抽象测试用例
*
* @param <T> 泛型
*/
public abstract class AbstractTestCase<T> implements TestCase<T> {
protected DataSource dataSource;
protected AbstractTestCase(DataSource dataSource) {
this.dataSource = dataSource;
try {
this.initialize();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 测试运行逻辑
*
* @return 运行结果
* @throws Exception 异常
*/
public abstract T run() throws Exception;
/**
* 测试逻辑
*
* @return 测试结果值
*/
@Override
public T test() {
Name anno = getClass().getAnnotation(Name.class);
assert anno != null;
String name = anno.value();
long current = System.currentTimeMillis();
T result = null;
try {
result = run();
print("【初次执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
current = System.currentTimeMillis();
result = run();
return result;
} catch (Exception e) {
print("执行失败!{0}", e.getMessage());
throw new RuntimeException(e);
} finally {
print("【正常执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
printResult(result);
}
}
}

View File

@ -0,0 +1,61 @@
package group.flyfish.framework.cases;
import group.flyfish.fluent.debug.FluentSqlDebugger;
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
import group.flyfish.framework.TestCase;
import group.flyfish.framework.entity.SaasOrder;
import group.flyfish.framework.entity.SaasPlan;
import group.flyfish.framework.entity.SaasTenant;
import group.flyfish.framework.vo.TenantContext;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.List;
import static group.flyfish.fluent.chain.SQL.select;
import static group.flyfish.fluent.chain.select.SelectComposite.composite;
import static group.flyfish.fluent.query.Query.where;
@TestCase.Name("使用fluent-sql")
public class FluentSqlTestCase extends AbstractTestCase<List<TenantContext>> {
public FluentSqlTestCase(DataSource dataSource) {
super(dataSource);
}
/**
* 初始化
*
* @throws Exception 异常
*/
@Override
public void initialize() throws Exception {
// 基于构造器自动绑定注册在实际应用中使用@Bean声明即可可参考下面的demo
new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource));
// 启用调试
FluentSqlDebugger.enable();
}
/**
* 测试运行逻辑
*
* @return 运行结果
* @throws Exception 异常
*/
@Override
public List<TenantContext> run() throws Exception {
// 一个平平无奇的查询
return select(
// 查询租户全量字段
composite(SaasTenant::getId, SaasTenant::getName, SaasTenant::getIdentifier, SaasTenant::getDatasource,
SaasTenant::getStorage, SaasTenant::getStatus, SaasTenant::getEnable),
// 查询套餐
composite(SaasOrder::getQuotaConfig, SaasOrder::getOrderTime, SaasOrder::getExpireTime,
SaasOrder::getOrderType))
.from(SaasTenant.class)
.leftJoin(SaasOrder.class).on(where(SaasOrder::getTenantId).eq(SaasTenant::getId))
.leftJoin(SaasPlan.class).on(where(SaasPlan::getId).eq(SaasOrder::getPlanId))
.matching(where(SaasTenant::getEnable).eq(true))
.list(TenantContext.class);
}
}

View File

@ -0,0 +1,99 @@
package group.flyfish.framework.cases;
import com.fasterxml.jackson.core.JsonProcessingException;
import group.flyfish.fluent.utils.data.ObjectMappers;
import group.flyfish.framework.TestCase;
import group.flyfish.framework.entity.SaasOrder;
import group.flyfish.framework.entity.SaasQuota;
import group.flyfish.framework.entity.SaasTenant;
import group.flyfish.framework.vo.TenantContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@TestCase.Name("使用JDBC")
public class JdbcTestCase extends AbstractTestCase<List<TenantContext>> {
public JdbcTestCase(DataSource dataSource) {
super(dataSource);
}
/**
* 初始化
*
* @throws Exception 异常
*/
@Override
public void initialize() throws Exception {
// do nothing
}
/**
* 测试运行逻辑
*
* @return 运行结果
* @throws Exception 异常
*/
@Override
public List<TenantContext> run() throws Exception {
String statement = "SELECT t1.`id` as `id`,t1.`name` as `name`,t1.`identifier` as `identifier`,t1.`datasource` as `datasource`,t1.`storage` as `storage`,t1.`status` as `status`,t1.`enable` as `enable`,t2.`quota_config` as `quotaConfig`,t2.`order_time` as `orderTime`,t2.`expire_time` as `expireTime`,t2.`order_type` as `orderType` FROM saas_tenant `t1` LEFT JOIN saas_order `t2` ON t2.`tenant_id` = t1.`id` LEFT JOIN saas_plan `t3` ON t3.`id` = t2.`plan_id` WHERE t1.`enable` = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement pst = connection.prepareStatement(statement);
ResultSet rs = execute(pst, true)
) {
return extract(rs);
}
}
/**
* 执行prepared statement并绑定值
*
* @param statement 已经准备好的statement
* @param args 参数
* @return 结果集
* @throws SQLException 异常
*/
private ResultSet execute(PreparedStatement statement, Object... args) throws SQLException {
statement.setBoolean(1, (Boolean) args[0]);
return statement.executeQuery();
}
/**
* 从结果集取得对象
*
* @param resultSet 结果集
* @return 结果
*/
private List<TenantContext> extract(ResultSet resultSet) throws SQLException {
List<TenantContext> list = new ArrayList<>();
while (resultSet.next()) {
TenantContext context = new TenantContext();
context.setId(resultSet.getString("id"));
context.setName(resultSet.getString("name"));
context.setIdentifier(resultSet.getString("identifier"));
context.setDatasource(readValue(resultSet.getString("datasource"), SaasTenant.DataSourceConfig.class));
context.setStorage(readValue(resultSet.getString("storage"), SaasTenant.StorageConfig.class));
context.setStatus(SaasTenant.Status.valueOf(resultSet.getString("status")));
context.setEnable(resultSet.getBoolean("enable"));
context.setQuota(readValue(resultSet.getString("quotaConfig"), SaasQuota.class));
context.setOrderTime(resultSet.getDate("orderTime"));
context.setExpireTime(resultSet.getDate("expireTime"));
context.setOrderType(SaasOrder.Type.valueOf(resultSet.getString("orderType")));
list.add(context);
}
return list;
}
private <T> T readValue(String value, Class<T> clazz) {
try {
return ObjectMappers.shared().readValue(value, clazz);
} catch (JsonProcessingException e) {
return null;
}
}
}

View File

@ -0,0 +1,62 @@
package group.flyfish.framework.cases;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import group.flyfish.framework.TestCase;
import group.flyfish.framework.entity.SaasQuota;
import group.flyfish.framework.entity.SaasTenant;
import group.flyfish.framework.mapper.TenantContextMapper;
import group.flyfish.framework.vo.TenantContext;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.TypeHandlerRegistry;
import javax.sql.DataSource;
import java.util.List;
@TestCase.Name("使用mybatis执行")
public class MybatisTestCase extends AbstractTestCase<List<TenantContext>> {
private SqlSessionFactory sqlSessionFactory;
public MybatisTestCase(DataSource dataSource) {
super(dataSource);
}
/**
* 初始化
*
* @throws Exception 异常
*/
@Override
public void initialize() throws Exception {
// 使用mybatis执行
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(TenantContextMapper.class);
TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
registry.register(SaasTenant.DataSourceConfig.class, JacksonTypeHandler.class);
registry.register(SaasQuota.class, JacksonTypeHandler.class);
registry.register(SaasTenant.StorageConfig.class, JacksonTypeHandler.class);
this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
}
/**
* 测试运行逻辑
*
* @return 运行结果
* @throws Exception 异常
*/
@Override
public List<TenantContext> run() throws Exception {
try (SqlSession session = sqlSessionFactory.openSession()) {
TenantContextMapper mapper = session.getMapper(TenantContextMapper.class);
return mapper.selectList(true);
}
}
}

View File

@ -0,0 +1,23 @@
package group.flyfish.framework.mapper;
import group.flyfish.framework.vo.TenantContext;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 测试用的mapper
*
* @author wangyu
*/
@Mapper
public interface TenantContextMapper {
/**
* 查询列表
*
* @param enable 启用与否
* @return 结果
*/
List<TenantContext> selectList(Boolean enable);
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="group.flyfish.framework.mapper.TenantContextMapper">
<resultMap id="Result" type="group.flyfish.framework.vo.TenantContext">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="identifier" property="identifier"/>
<result column="datasource" property="datasource"
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
<result column="storage" property="storage" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
<result column="status" property="status" />
<result column="enable" property="enable" />
<result column="quotaConfig" property="quota" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
<result column="orderTime" property="orderTime" />
<result column="expireTime" property="expireTime" />
<result column="orderType" property="orderType" />
</resultMap>
<select id="selectList" resultType="group.flyfish.framework.vo.TenantContext">
SELECT t1.`id` as `id`,
t1.`name` as `name`,
t1.`identifier` as `identifier`,
t1.`datasource` as `datasource`,
t1.`storage` as `storage`,
t1.`status` as `status`,
t1.`enable` as `enable`,
t2.`quota_config` as `quotaConfig`,
t2.`order_time` as `orderTime`,
t2.`expire_time` as `expireTime`,
t2.`order_type` as `orderType`
FROM saas_tenant `t1`
LEFT JOIN saas_order `t2` ON t2.`tenant_id` = t1.`id`
LEFT JOIN saas_plan `t3` ON t3.`id` = t2.`plan_id`
WHERE t1.`enable` = #{enable}
</select>
</mapper>

View File

@ -0,0 +1,25 @@
package group.flyfish.framework.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import group.flyfish.fluent.utils.data.ObjectMappers;
import java.text.MessageFormat;
public class LogUtils {
public static void print(String message, Object... args) {
System.out.println(MessageFormat.format(message, args));
}
public static void printResult(Object value) {
if (null == value) {
print("执行结果为null");
} else {
try {
print("执行结果为{0}", ObjectMappers.shared().writeValueAsString(value));
} catch (JsonProcessingException e) {
print("执行结果为{0}", value);
}
}
}
}

10
pom.xml
View File

@ -121,6 +121,16 @@
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
<version>${spring.version}</version> <version>${spring.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.2</version>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>