fix: toMany join性能优化

@RelationOneToMany和@RelationManyToMany的实现存在严重的性能问题。内存join算法的复杂度高达n^2
测试sql脚本init中设置编码格式避免中文乱码
This commit is contained in:
SWQXDBA 2024-02-28 14:02:28 +08:00
parent 83c3afe542
commit c61ff5865c
27 changed files with 197 additions and 126 deletions

View File

@ -26,9 +26,13 @@ import java.util.*;
class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> { class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
protected String mapKeyField; protected String mapKeyField;
protected FieldWrapper mapKeyFieldWrapper; protected FieldWrapper mapKeyFieldWrapper;
protected String orderBy; protected String orderBy;
protected long limit = 0; protected long limit = 0;
protected String selfValueSplitBy; protected String selfValueSplitBy;
@ -37,12 +41,20 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField, String dataSource, Class<SelfEntity> selfEntityClass, Field relationField,
String extraCondition, String[] selectColumns) { String extraCondition, String[] selectColumns) {
super(selfField, targetSchema, targetTable, targetField, valueField, super(selfField, targetSchema, targetTable, targetField, valueField,
joinTable, joinSelfColumn, joinTargetColumn, joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField, dataSource, selfEntityClass, relationField,
extraCondition, selectColumns extraCondition, selectColumns
); );
} }
public static Class<? extends Map> getMapWrapType(Class<?> type) {
if (ClassUtil.canInstance(type.getModifiers())) {
return (Class<? extends Map>) type;
}
return HashMap.class;
}
/** /**
* 构建查询目标对象的 QueryWrapper * 构建查询目标对象的 QueryWrapper
* *
@ -71,7 +83,6 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
return super.buildQueryWrapper(targetValues); return super.buildQueryWrapper(targetValues);
} }
@Override @Override
public void customizeQueryWrapper(QueryWrapper queryWrapper) { public void customizeQueryWrapper(QueryWrapper queryWrapper) {
if (StringUtil.isNotBlank(orderBy)) { if (StringUtil.isNotBlank(orderBy)) {
@ -86,67 +97,131 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows) { public void join(List<SelfEntity> selfEntities, List<?> targetObjectList, List<Row> mappingRows) {
selfEntities.forEach(selfEntity -> {
//目标表关联字段->目标表对象
Map<String, List<Object>> leftFieldToRightTableMap = new HashMap<>(targetObjectList.size());
for (Object targetObject : targetObjectList) {
Object targetJoinFieldValue = targetFieldWrapper.get(targetObject);
if (targetJoinFieldValue != null) {
leftFieldToRightTableMap.computeIfAbsent(targetJoinFieldValue.toString(), k -> new ArrayList<>()).add(targetObject);
}
}
//通过中间表
if (mappingRows != null) {
//当使用中间表时需要重新映射关联关系
Map<String, List<Object>> temp = new HashMap<>(selfEntities.size());
for (Row mappingRow : mappingRows) {
Object midTableJoinSelfValue = mappingRow.getIgnoreCase(joinSelfColumn);
if (midTableJoinSelfValue == null) {
continue;
}
Object midTableJoinTargetValue = mappingRow.getIgnoreCase(joinTargetColumn);
if (midTableJoinTargetValue == null) {
continue;
}
List<Object> targetObjects = leftFieldToRightTableMap.get(midTableJoinTargetValue.toString());
if (targetObjects == null) {
continue;
}
temp.computeIfAbsent(midTableJoinSelfValue.toString(), k -> new ArrayList<>(targetObjects.size())).addAll(targetObjects);
}
leftFieldToRightTableMap = temp;
}
//关联集合的类型
Class<?> fieldType = relationFieldWrapper.getFieldType();
Class<?> wrapType;
if (Map.class.isAssignableFrom(fieldType)) {
wrapType = getMapWrapType(fieldType);
} else {
wrapType = MapperUtil.getCollectionWrapType(fieldType);
}
for (SelfEntity selfEntity : selfEntities) {
if (selfEntity == null) {
continue;
}
Object selfValue = selfFieldWrapper.get(selfEntity); Object selfValue = selfFieldWrapper.get(selfEntity);
if (selfValue != null) { if (selfValue == null) {
selfValue = selfValue.toString(); continue;
Set<String> targetMappingValues = new HashSet<>(); }
if (mappingRows != null) { selfValue = selfValue.toString();
for (Row mappingRow : mappingRows) { Set<String> targetMappingValues;//只有当splitBy不为空时才会有多个值
if (selfValue.equals(String.valueOf(mappingRow.getIgnoreCase(joinSelfColumn)))) { boolean splitMode = StringUtil.isNotBlank(selfValueSplitBy);
Object joinValue = mappingRow.getIgnoreCase(joinTargetColumn); if (splitMode) {
if (joinValue != null) { String[] splitValues = ((String) selfValue).split(selfValueSplitBy);
targetMappingValues.add(joinValue.toString()); targetMappingValues = new LinkedHashSet<>(Arrays.asList(splitValues));
} } else {
targetMappingValues = new HashSet<>(1);
targetMappingValues.add((String) selfValue);
}
if (targetMappingValues.isEmpty()) {
return;
}
//map
if (Map.class.isAssignableFrom(fieldType)) {
Map map = (Map) ClassUtil.newInstance(wrapType);
Set<Object> validateCountSet = new HashSet<>(targetMappingValues.size());
for (String targetMappingValue : targetMappingValues) {
List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
//如果非真实外键约束 可能没有对应的对象
if (targetObjects == null) {
continue;
}
for (Object targetObject : targetObjects) {
Object keyValue = mapKeyFieldWrapper.get(targetObject);
Object needKeyValue = ConvertUtil.convert(keyValue, relationFieldWrapper.getKeyType());
if (validateCountSet.contains(needKeyValue) ) {
//当字段类型为Map时一个key对应的value只能有一个
throw FlexExceptions.wrap("When fieldType is Map, the target entity can only be one,\n" +
" current entity type is : " + selfEntity + "\n" +
" relation field name is : " + relationField.getName() + "\n" +
" target entity is : " + targetObjects);
}
validateCountSet.add(needKeyValue);
map.put(needKeyValue, targetObject);
}
}
if (!map.isEmpty()) {
relationFieldWrapper.set(map, selfEntity);
}
}
//集合
else {
Collection collection = (Collection) ClassUtil.newInstance(wrapType);
if (onlyQueryValueField) {
Object first = targetObjectList.iterator().next();
//将getter方法用单独的变量存储 FieldWrapper.of虽然有缓存 但每次调用至少有一个HashMap的get开销
FieldWrapper fieldValueFieldWrapper = FieldWrapper.of(first.getClass(), valueField);
for (String targetMappingValue : targetMappingValues) {
List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
if (targetObjects == null) {
continue;
}
for (Object targetObject : targetObjects) {
//仅绑定某个字段
collection.add(fieldValueFieldWrapper.get(targetObject));
} }
} }
} else { } else {
if (StringUtil.isNotBlank(selfValueSplitBy)) { for (String targetMappingValue : targetMappingValues) {
String[] splitValues = ((String) selfValue).split(selfValueSplitBy); List<Object> targetObjects = leftFieldToRightTableMap.get(targetMappingValue);
targetMappingValues.addAll(Arrays.asList(splitValues)); if (targetObjects == null) {
} else { continue;
targetMappingValues.add((String) selfValue);
}
}
if (targetMappingValues.isEmpty()) {
return;
}
Class<?> fieldType = relationFieldWrapper.getFieldType();
//map
if (Map.class.isAssignableFrom(fieldType)) {
Class<?> wrapType = getMapWrapType(fieldType);
Map map = (Map) ClassUtil.newInstance(wrapType);
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
Object keyValue = mapKeyFieldWrapper.get(targetObject);
Object needKeyValue = ConvertUtil.convert(keyValue, relationFieldWrapper.getKeyType());
map.put(needKeyValue, targetObject);
} }
collection.addAll(targetObjects);
} }
relationFieldWrapper.set(map, selfEntity);
}
//集合
else {
Class<?> wrapType = MapperUtil.getCollectionWrapType(fieldType);
Collection collection = (Collection) ClassUtil.newInstance(wrapType);
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
if (onlyQueryValueField) {
//仅绑定某个字段
collection.add(FieldWrapper.of(targetObject.getClass(), valueField).get(targetObject));
} else {
collection.add(targetObject);
}
}
}
relationFieldWrapper.set(collection, selfEntity);
} }
relationFieldWrapper.set(collection, selfEntity);
} }
}); }
} }
public void setMapKeyField(String mapKeyField) { public void setMapKeyField(String mapKeyField) {
@ -160,12 +235,4 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
} }
} }
public static Class<? extends Map> getMapWrapType(Class<?> type) {
if (ClassUtil.canInstance(type.getModifiers())) {
return (Class<? extends Map>) type;
}
return HashMap.class;
}
} }

View File

@ -45,7 +45,7 @@ public class ArticleTester {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -43,7 +43,7 @@ public class BatchTester {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap.getInstance()

View File

@ -38,7 +38,7 @@ public class CursorTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -49,7 +49,7 @@ public class DbTest {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -48,7 +48,7 @@ public class DbTest273 {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema_273.sql") .addScript("schema_273.sql")
.addScript("data273.sql") .addScript("data273.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -29,7 +29,7 @@ public class DbTestStarter {
public static void main(String[] args) { public static void main(String[] args) {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql").setScriptEncoding("UTF-8")
// .addScript("data.sql") // .addScript("data.sql")
.build(); .build();

View File

@ -40,7 +40,7 @@ public class EntityTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -32,7 +32,7 @@ public class InsertWithPkTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -32,7 +32,7 @@ public class JoinTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -33,7 +33,7 @@ public class JoinTester {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -41,7 +41,7 @@ public class JoinWithDeleteColumnTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -33,7 +33,7 @@ public class MapperProxyCacheTestStarter {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -30,7 +30,7 @@ public class MaskManagerTest {
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.setName("db1") .setName("db1")
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap.getInstance()

View File

@ -38,7 +38,7 @@ public class MultiDataSourceTester {
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.setName("db1") .setName("db1")
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
HikariDataSource dataSource2 = new HikariDataSource(); HikariDataSource dataSource2 = new HikariDataSource();

View File

@ -28,6 +28,7 @@ public class MultiThreadsTest {
.setName("db1") .setName("db1")
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql")
.setScriptEncoding("UTF-8")
.build(); .build();
HikariDataSource dataSource2 = new HikariDataSource(); HikariDataSource dataSource2 = new HikariDataSource();

View File

@ -36,7 +36,7 @@ public class UpdateWrapperTest {
DataSource dataSource = new EmbeddedDatabaseBuilder() DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -42,7 +42,7 @@ public class Account6Test implements WithAssertions {
this.dataSource = new EmbeddedDatabaseBuilder() this.dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("none_key_schema.sql") .addScript("none_key_schema.sql")
.addScript("none_key_data.sql") .addScript("none_key_data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap() MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()

View File

@ -44,7 +44,7 @@ public class Account7Test implements WithAssertions {
this.dataSource = new EmbeddedDatabaseBuilder() this.dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("generate_key_schema.sql") .addScript("generate_key_schema.sql")
.addScript("generate_key_data.sql") .addScript("generate_key_data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap() MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()

View File

@ -48,7 +48,7 @@ public class AccountInsertWithArrayAttrTest implements WithAssertions {
this.dataSource = new EmbeddedDatabaseBuilder() this.dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema05.sql") .addScript("schema05.sql")
.addScript("data05.sql") .addScript("data05.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap() MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()

View File

@ -67,7 +67,7 @@ public class AccountNativeTest implements WithAssertions {
this.dataSource = new EmbeddedDatabaseBuilder() this.dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("auto_increment_key_schema.sql") .addScript("auto_increment_key_schema.sql")
.addScript("auto_increment_key_data.sql") .addScript("auto_increment_key_data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap() MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()

View File

@ -59,7 +59,7 @@ public class DbChainTest implements WithAssertions {
this.database = new EmbeddedDatabaseBuilder() this.database = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("auto_increment_key_schema.sql") .addScript("auto_increment_key_schema.sql")
.addScript("auto_increment_key_data.sql") .addScript("auto_increment_key_data.sql").setScriptEncoding("UTF-8")
.build(); .build();
// Environment environment = new Environment(ENVIRONMENT_ID, new JdbcTransactionFactory(), this.database); // Environment environment = new Environment(ENVIRONMENT_ID, new JdbcTransactionFactory(), this.database);

View File

@ -47,7 +47,7 @@ public class ListenerTest implements WithAssertions {
public void init() { public void init() {
dataSource = new EmbeddedDatabaseBuilder() dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("auto_increment_key_schema.sql") .addScript("auto_increment_key_schema.sql").setScriptEncoding("UTF-8")
.build(); .build();
// 注册全局监听器 // 注册全局监听器
FlexGlobalConfig defaultConfig = FlexGlobalConfig.getDefaultConfig(); FlexGlobalConfig defaultConfig = FlexGlobalConfig.getDefaultConfig();

View File

@ -70,7 +70,7 @@ public class RelationsTest implements WithAssertions {
dataSource = new EmbeddedDatabaseBuilder() dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("relation/onetoone/schema.sql") .addScript("relation/onetoone/schema.sql")
.addScript("relation/onetoone/data.sql") .addScript("relation/onetoone/data.sql").setScriptEncoding("UTF-8")
.build(); .build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance() MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()

View File

@ -57,6 +57,7 @@ public class RowTest implements WithAssertions {
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema_row.sql") .addScript("schema_row.sql")
.addScript("data_row.sql") .addScript("data_row.sql")
.setScriptEncoding("UTF-8")
.build(); .build();
new MybatisFlexBootstrap().setDataSource(DATA_SOURCE_KEY, dataSource) new MybatisFlexBootstrap().setDataSource(DATA_SOURCE_KEY, dataSource)

View File

@ -42,11 +42,12 @@ import static com.mybatisflex.test.table.ArticleTableDef.ARTICLE;
public class UpdateChainTest implements WithAssertions { public class UpdateChainTest implements WithAssertions {
private AccountMapper accountMapper;
private EmbeddedDatabase dataSource;
private static final String DATA_SOURCE_KEY = "ds2"; private static final String DATA_SOURCE_KEY = "ds2";
private AccountMapper accountMapper;
private EmbeddedDatabase dataSource;
@BeforeClass @BeforeClass
public static void enableAudit() { public static void enableAudit() {
AuditManager.setAuditEnable(true); AuditManager.setAuditEnable(true);
@ -56,16 +57,17 @@ public class UpdateChainTest implements WithAssertions {
@Before @Before
public void init() { public void init() {
this.dataSource = new EmbeddedDatabaseBuilder() this.dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql")
.build(); .setScriptEncoding("UTF-8")
.build();
MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap() MybatisFlexBootstrap bootstrap = new MybatisFlexBootstrap()
.setDataSource(DATA_SOURCE_KEY, this.dataSource) .setDataSource(DATA_SOURCE_KEY, this.dataSource)
.setLogImpl(StdOutImpl.class) .setLogImpl(StdOutImpl.class)
.addMapper(AccountMapper.class) .addMapper(AccountMapper.class)
.start(); .start();
DataSourceKey.use(DATA_SOURCE_KEY); DataSourceKey.use(DATA_SOURCE_KEY);
accountMapper = bootstrap.getMapper(AccountMapper.class); accountMapper = bootstrap.getMapper(AccountMapper.class);
@ -81,53 +83,53 @@ public class UpdateChainTest implements WithAssertions {
@SneakyThrows @SneakyThrows
public void testUpdateChain() { public void testUpdateChain() {
UpdateChain.of(Account.class) UpdateChain.of(Account.class)
.set(Account::getUserName, "张三") .set(Account::getUserName, "张三")
.setRaw(Account::getAge, "age + 1") .setRaw(Account::getAge, "age + 1")
.where(Account::getId).eq(1) .where(Account::getId).eq(1)
.update(); .update();
Account account = accountMapper.selectOneById(1); Account account = accountMapper.selectOneById(1);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String toParse = "2020-01-11"; String toParse = "2020-01-11";
assertThat(account).isNotNull() assertThat(account).isNotNull()
.extracting( .extracting(
Account::getUserName, Account::getAge, Account::getUserName, Account::getAge,
Account::getSex, Account::getBirthday, Account::getSex, Account::getBirthday,
Account::getOptions, Account::getDelete, Account::getOptions, Account::getDelete,
Account::getArticles, Account::getTitle) Account::getArticles, Account::getTitle)
.containsExactly( .containsExactly(
"张*", 19, "张*", 19,
SexEnum.TYPE1, format.parse(toParse), SexEnum.TYPE1, format.parse(toParse),
Collections.singletonMap("key", "value1"), false, Collections.singletonMap("key", "value1"), false,
Collections.emptyList(), null); Collections.emptyList(), null);
} }
@Test @Test
public void testUpdateChain1() { public void testUpdateChain1() {
UpdateChain.of(Account.class) UpdateChain.of(Account.class)
.set(Account::getAge, ACCOUNT.AGE.add(1)) .set(Account::getAge, ACCOUNT.AGE.add(1))
.where(Account::getId).ge(100) .where(Account::getId).ge(100)
.and(Account::getAge).eq(18) .and(Account::getAge).eq(18)
.update(); .update();
List<Account> list = QueryChain.of(accountMapper).list(); List<Account> list = QueryChain.of(accountMapper).list();
assertThat(list).hasSize(2) assertThat(list).hasSize(2)
.extracting(Account::getId, Account::getUserName, Account::getAge) .extracting(Account::getId, Account::getUserName, Account::getAge)
.containsExactly(tuple(1L, "张*", 18), tuple(2L, "王麻**叔", 19)); .containsExactly(tuple(1L, "张*", 18), tuple(2L, "王麻**叔", 19));
} }
@Test @Test
public void testUpdateChainToSql() { public void testUpdateChainToSql() {
String sql = UpdateChain.of(Account.class) String sql = UpdateChain.of(Account.class)
.set(ACCOUNT.AGE, 18) .set(ACCOUNT.AGE, 18)
.set(Article::getAccountId, 4) .set(Article::getAccountId, 4)
.leftJoin(ARTICLE).as("ar").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID)) .leftJoin(ARTICLE).as("ar").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
.where(ACCOUNT.ID.eq(4)) .where(ACCOUNT.ID.eq(4))
.toSQL(); .toSQL();
String expectSQL = "UPDATE `tb_account` " + String expectSQL = "UPDATE `tb_account` " +
"LEFT JOIN `tb_article` AS `ar` ON `tb_account`.`id` = `ar`.`account_id` " + "LEFT JOIN `tb_article` AS `ar` ON `tb_account`.`id` = `ar`.`account_id` " +
"SET `age` = 18 , `accountId` = 4 WHERE `id` = 4"; "SET `age` = 18 , `accountId` = 4 WHERE `id` = 4";
assertThat(sql).isEqualTo(expectSQL); assertThat(sql).isEqualTo(expectSQL);
} }

View File

@ -41,7 +41,7 @@ public class AppConfig implements ApplicationListener<ContextRefreshedEvent> {
return new EmbeddedDatabaseBuilder() return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2) .setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql") .addScript("schema.sql")
.addScript("data.sql") .addScript("data.sql").setScriptEncoding("UTF-8")
.build(); .build();
} }