Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ public Children isNotNull(boolean condition, R column) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL));
}

public Children in(boolean condition, List<R> columns, List<List<?>> values) {
return maybeDo(condition, () -> appendSqlSegments(buildMulSqlSegment(columns, IN, values)));
}

@Override
public Children in(boolean condition, R column, Collection<?> coll) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll)));
Expand All @@ -298,6 +302,11 @@ public Children in(boolean condition, R column, Object... values) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values)));
}

@Override
public Children notIn(boolean condition, List<R> columns, List<List<?>> values) {
return maybeDo(condition, () -> appendSqlSegments(buildMulSqlSegment(columns, NOT_IN, values)));
}

@Override
public Children notIn(boolean condition, R column, Collection<?> coll) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll)));
Expand Down Expand Up @@ -560,6 +569,38 @@ protected ISqlSegment inExpression(Collection<?> value) {
.collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
}

private ISqlSegment[] buildMulSqlSegment(List<R> columns, SqlKeyword sqlKeyword, List<List<?>> values) {
Assert.notEmpty(columns, "columns can not be empty!");
Assert.notEmpty(values, "values can not be empty!");

List<ISqlSegment> segments = new ArrayList<>();
segments.add(LEFT_BRACKET);
columns.stream().map(this::columnToSqlSegment).forEach(e -> {
segments.add(e);
segments.add(COMMA);
});
segments.remove(segments.size() - 1);
segments.add(RIGHT_BRACKET);
segments.add(sqlKeyword);
segments.addAll(inMulExpression(values));
return segments.toArray(new ISqlSegment[0]);
}

protected List<ISqlSegment> inMulExpression(List<List<?>> value) {
if (CollectionUtils.isEmpty(value)) {
return Collections.singletonList(() -> "()");
}
List<ISqlSegment> sqlSegments = new ArrayList<>();
sqlSegments.add(0, LEFT_BRACKET);
value.stream().map(this::inExpression).forEach(e -> {
sqlSegments.add(e);
sqlSegments.add(COMMA);
});
sqlSegments.remove(sqlSegments.size() - 1);
sqlSegments.add(RIGHT_BRACKET);
return sqlSegments;
}

/**
* 获取in表达式 包含括号
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ default Children isNotNull(R column) {
* 字段 IN (value.get(0), value.get(1), ...)
* <p>例: in("id", Arrays.asList(1, 2, 3, 4, 5))</p>
*
* <li> 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错</li>
* <li> 注意!当集合为值是空或null时,或者字段和值长度不一致时,会报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param column 字段
Expand All @@ -102,6 +102,64 @@ default Children in(R column, Collection<?> coll) {
*/
Children in(boolean condition, R column, Collection<?> coll);

/**
* (grade, age) IN (('A', 1), ('B', 2))
* <p>例: in("id", Arrays.asList(Arrays.asList('A', 1), Arrays.asList('B', 2)))</p>
*
* <li> 注意!当集合为值是空或null时,或者字段和值长度不一致时,会报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param columns 字段
* @param values 数据集合
* @return children
*/
default Children in(List<R> columns, List<List<?>> values) {
return in(true, columns, values);
}

/**
* (grade, age) IN (('A', 1), ('B', 2))
* <p>例: in("id", Arrays.asList(Arrays.asList('A', 1), Arrays.asList('B', 2)))</p>
*
* <li> 注意!当集合为值是空或null时,或者字段和值长度不一致时,会报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param columns 字段
* @param values 数据集合
* @return children
*/
Children in(boolean condition, List<R> columns, List<List<?>> values);

/**
* (grade, age) NOT IN (('A', 1), ('B', 2))
* <p>例: notIn("id", Arrays.asList(Arrays.asList('A', 1), Arrays.asList('B', 2)))</p>
*
* <li> 注意!当集合为值是空或null时,或者字段和值长度不一致时,会报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param columns 字段
* @param values 数据集合
* @return children
*/
default Children notIn(List<R> columns, List<List<?>> values) {
return notIn(true, columns, values);
}

/**
* (grade, age) NOT IN (('A', 1), ('B', 2))
* <p>例: notIn("id", Arrays.asList(Arrays.asList('A', 1), Arrays.asList('B', 2)))</p>
*
* <li> 注意!当集合为值是空或null时,或者字段和值长度不一致时,会报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param columns 字段
* @param values 数据集合
* @return children
*/
Children notIn(boolean condition, List<R> columns, List<List<?>> values);

/**
* 字段 IN (v0, v1, ...)
* <p>例: in("id", 1, 2, 3, 4, 5)</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ public enum SqlKeyword implements ISqlSegment {
BETWEEN("BETWEEN"),
NOT_BETWEEN("NOT BETWEEN"),
ASC("ASC"),
DESC("DESC");

DESC("DESC"),
COMMA(StringPool.COMMA),
LEFT_BRACKET(StringPool.LEFT_BRACKET),
RIGHT_BRACKET(StringPool.RIGHT_BRACKET),
;
private final String keyword;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ public Children isNotNull(boolean condition, R column) {
return typedThis;
}

@Override
public Children in(boolean condition, List<R> columns, List<List<?>> values) {
getWrapper().in(condition, columns, values);
return typedThis;
}

@Override
public Children in(boolean condition, R column, Collection<?> coll) {
getWrapper().in(condition, column, coll);
Expand All @@ -189,6 +195,12 @@ public Children in(boolean condition, R column, Object... values) {
return typedThis;
}

@Override
public Children notIn(boolean condition, List<R> columns, List<List<?>> values) {
getWrapper().notIn(condition, columns, values);
return typedThis;
}

@Override
public Children notIn(boolean condition, R column, Collection<?> coll) {
getWrapper().notIn(condition, column, coll);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand All @@ -39,6 +39,131 @@
*/
class DbTest extends BaseDbTest<EntityMapper> {

@Test
void testMulIn() {
List<List<?>> values = new ArrayList<>();
values.add(Arrays.asList("1", "ruben"));
values.add(Arrays.asList("2", "chocolate"));

// 测试 1:匹配一个不存在的组合,预期结果为空
List<Entity> list = Db.lambdaQuery(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), List.of(Arrays.asList("2", "ruben")))
.list();
assertEquals(0, list.size());

// 测试 2:根据单个字段 id 匹配 "2",预期返回一条记录
list = Db.lambdaQuery(Entity.class)
.in(List.of(Entity::getId), List.of(List.of("2")))
.list();
assertEquals(1, list.size());

// 测试 3:匹配组合条件,预期返回一条记录
list = Db.lambdaQuery(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), List.of(Arrays.asList("1", "ruben")))
.list();
assertEquals(1, list.size());

// 测试 4:匹配 values 列表中的两个组合值,预期返回两条记录
list = Db.lambdaQuery(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), values)
.list();
assertEquals(2, list.size());

// 测试 5:普通 Query 方式实现相同的 IN 查询(字段名写法),预期同样返回两条记录
list = Db.query(Entity.class)
.in(true, List.of("id", "name"), values)
.list();
assertEquals(2, list.size());

// 测试 6:抛出异常的情况
assertThrows(Exception.class, () -> Db.lambdaQuery(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), new ArrayList<>())
.list()
);

assertThrows(Exception.class, () -> Db.lambdaQuery(Entity.class)
.in(true, new ArrayList<>(), new ArrayList<>())
.list()
);

// 字段数量和值数量不匹配的情况
assertThrows(Exception.class, () -> Db.lambdaQuery(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), List.of(List.of("1")))
.list()
);

// 测试 7:Lambda Update 使用 in 进行更新操作,将匹配 values 中的组合记录的 name 改为 "hjl"
boolean update = Db.lambdaUpdate(Entity.class)
.in(true, List.of(Entity::getId, Entity::getName), values)
.set(Entity::getName, "hjl")
.update();
assertTrue(update);

// 测试 8:普通 Update 使用 in 进行更新操作,上面的name已被更新, 所以预期更新失败
update = Db.update(Entity.class)
.in(true, List.of("id", "name"), values)
.set("name", "robot")
.update();
assertFalse(update);
}

@Test
void testMulNotIn() {
List<List<?>> values = new ArrayList<>();
values.add(Arrays.asList("1", "ruben"));
values.add(Arrays.asList("2", "chocolate"));

// 测试 1:排除一个不存在的组合 ("2", "ruben"),预期返回所有其他记录
List<Entity> list = Db.lambdaQuery(Entity.class)
.notIn(true, List.of(Entity::getId, Entity::getName), List.of(Arrays.asList("2", "ruben")))
.list();
assertEquals(2, list.size());

// 测试 2:排除单个字段 id 为 "2" 的记录,预期返回除该记录外的所有数据
list = Db.lambdaQuery(Entity.class)
.notIn(List.of(Entity::getId), List.of(List.of("2")))
.list();
assertEquals(1, list.size());

// 测试 3:排除组合条件,预期返回除该记录外的所有数据
list = Db.lambdaQuery(Entity.class)
.notIn(true, List.of(Entity::getId, Entity::getName), List.of(Arrays.asList("1", "ruben")))
.list();
assertEquals(1, list.size());

// 测试 4:排除 values 列表中的两个组合值,预期返回除这两条外的所有数据
list = Db.lambdaQuery(Entity.class)
.notIn(true, List.of(Entity::getId, Entity::getName), values)
.list();
assertEquals(0, list.size());

// 测试 5:普通 Query 方式实现相同的 NOT IN 查询(字段名写法),预期结果一致
list = Db.query(Entity.class)
.notIn(true, List.of("id", "name"), values)
.list();
assertEquals(0, list.size());

// 测试 6:当查询值为空时,会抛出异常,和in查询一个效果
assertThrows(Exception.class, () -> Db.lambdaQuery(Entity.class)
.notIn(true, List.of(Entity::getId, Entity::getName), new ArrayList<>())
.list()
);

// 测试 7:LambdaUpdate,排除单个记录
boolean update = Db.lambdaUpdate(Entity.class)
.notIn(true, List.of(Entity::getId, Entity::getName), List.of(Arrays.asList("1", "ruben")))
.set(Entity::getName, "hjl")
.update();
assertTrue(update);

// 测试 8:普通 Update
update = Db.update(Entity.class)
.notIn(true, List.of("id", "name"), values)
.set("name", "robot")
.update();
assertTrue(update);
}

@Test
void testSave() {
Entity entity = new Entity();
Expand Down