mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix:避免调用方 显示调用API 触发查找树 优化;并通过内置锁,避免因并行树优化 可能造成的不可预知结果 和 无效重复的 树优化操作
This commit is contained in:
commit
2b2c0d001c
@ -8,7 +8,7 @@ import cn.hutool.core.collection.partition.RandomAccessPartition;
|
|||||||
import cn.hutool.core.comparator.PinyinComparator;
|
import cn.hutool.core.comparator.PinyinComparator;
|
||||||
import cn.hutool.core.comparator.PropertyComparator;
|
import cn.hutool.core.comparator.PropertyComparator;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.math.PageInfo;
|
import cn.hutool.core.lang.page.PageInfo;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class StringConverter extends AbstractConverter {
|
|||||||
* @param stringFunction 序列化函数
|
* @param stringFunction 序列化函数
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public StringConverter putStringer(Class<?> clazz, Function<Object, String> stringFunction){
|
public StringConverter putStringer(final Class<?> clazz, final Function<Object, String> stringFunction){
|
||||||
if(null == stringer){
|
if(null == stringer){
|
||||||
stringer = new HashMap<>();
|
stringer = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ public class SerializeUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
|
final FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
|
||||||
IoUtil.writeObjects(byteOut, false, (Serializable) obj);
|
IoUtil.writeObjects(byteOut, false, obj);
|
||||||
return byteOut.toByteArray();
|
return byteOut.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,12 @@ import cn.hutool.core.lang.mutable.MutableEntry;
|
|||||||
import cn.hutool.core.map.WeakConcurrentMap;
|
import cn.hutool.core.map.WeakConcurrentMap;
|
||||||
import cn.hutool.core.reflect.LookupFactory;
|
import cn.hutool.core.reflect.LookupFactory;
|
||||||
import cn.hutool.core.reflect.MethodUtil;
|
import cn.hutool.core.reflect.MethodUtil;
|
||||||
|
import cn.hutool.core.reflect.ReflectUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.invoke.*;
|
import java.lang.invoke.*;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -31,7 +34,7 @@ public class LambdaFactory {
|
|||||||
throw new IllegalAccessException();
|
throw new IllegalAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<MutableEntry<Class<?>, Method>, Object> CACHE = new WeakConcurrentMap<>();
|
private static final Map<MutableEntry<Class<?>, Executable>, Object> CACHE = new WeakConcurrentMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建Lambda
|
* 构建Lambda
|
||||||
@ -61,30 +64,37 @@ public class LambdaFactory {
|
|||||||
* 构建Lambda
|
* 构建Lambda
|
||||||
*
|
*
|
||||||
* @param functionInterfaceType 接受Lambda的函数式接口类型
|
* @param functionInterfaceType 接受Lambda的函数式接口类型
|
||||||
* @param method 方法对象
|
* @param executable 方法对象,支持构造器
|
||||||
|
* @param <F> Function类型
|
||||||
* @return 接受Lambda的函数式接口对象
|
* @return 接受Lambda的函数式接口对象
|
||||||
* @param <F> Function类型
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <F> F build(final Class<F> functionInterfaceType, final Method method) {
|
public static <F> F build(final Class<F> functionInterfaceType, final Executable executable) {
|
||||||
Assert.notNull(functionInterfaceType);
|
Assert.notNull(functionInterfaceType);
|
||||||
Assert.notNull(method);
|
Assert.notNull(executable);
|
||||||
final MutableEntry<Class<?>, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method);
|
final MutableEntry<Class<?>, Executable> cacheKey = new MutableEntry<>(functionInterfaceType, executable);
|
||||||
return (F) CACHE.computeIfAbsent(cacheKey, key -> {
|
return (F) CACHE.computeIfAbsent(cacheKey, key -> {
|
||||||
final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
|
final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
|
||||||
.filter(m -> Modifier.isAbstract(m.getModifiers()))
|
.filter(m -> Modifier.isAbstract(m.getModifiers()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口");
|
Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口");
|
||||||
if (!method.isAccessible()) {
|
ReflectUtil.setAccessible(executable);
|
||||||
method.setAccessible(true);
|
|
||||||
}
|
|
||||||
final Method invokeMethod = abstractMethods.get(0);
|
final Method invokeMethod = abstractMethods.get(0);
|
||||||
final MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass());
|
final MethodHandles.Lookup caller = LookupFactory.lookup(executable.getDeclaringClass());
|
||||||
final String invokeName = invokeMethod.getName();
|
final String invokeName = invokeMethod.getName();
|
||||||
final MethodType invokedType = methodType(functionInterfaceType);
|
final MethodType invokedType = methodType(functionInterfaceType);
|
||||||
final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes());
|
final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes());
|
||||||
final MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get();
|
final MethodHandle implMethod;
|
||||||
final MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
|
final MethodType instantiatedMethodType;
|
||||||
|
if (executable instanceof Method) {
|
||||||
|
final Method method = (Method) executable;
|
||||||
|
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflect(method)).get();
|
||||||
|
instantiatedMethodType = MethodType.methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
|
||||||
|
} else {
|
||||||
|
final Constructor<?> constructor = (Constructor<?>) executable;
|
||||||
|
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflectConstructor(constructor)).get();
|
||||||
|
instantiatedMethodType = MethodType.methodType(constructor.getDeclaringClass(), constructor.getParameterTypes());
|
||||||
|
}
|
||||||
final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType);
|
final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType);
|
||||||
try {
|
try {
|
||||||
final CallSite callSite = isSerializable ?
|
final CallSite callSite = isSerializable ?
|
||||||
@ -94,7 +104,7 @@ public class LambdaFactory {
|
|||||||
invokedType,
|
invokedType,
|
||||||
samMethodType,
|
samMethodType,
|
||||||
implMethod,
|
implMethod,
|
||||||
insMethodType,
|
instantiatedMethodType,
|
||||||
FLAG_SERIALIZABLE
|
FLAG_SERIALIZABLE
|
||||||
) :
|
) :
|
||||||
LambdaMetafactory.metafactory(
|
LambdaMetafactory.metafactory(
|
||||||
@ -103,7 +113,7 @@ public class LambdaFactory {
|
|||||||
invokedType,
|
invokedType,
|
||||||
samMethodType,
|
samMethodType,
|
||||||
implMethod,
|
implMethod,
|
||||||
insMethodType
|
instantiatedMethodType
|
||||||
);
|
);
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (F) callSite.getTarget().invoke();
|
return (F) callSite.getTarget().invoke();
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
package cn.hutool.core.lang.page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航分页信息类<br>
|
||||||
|
* 根据提供的总页数、每页记录数、导航页码数等信息,生成导航数组。
|
||||||
|
* <pre>
|
||||||
|
* [1] 2 3 4 5 >>
|
||||||
|
* << 1 [2] 3 4 5 >>
|
||||||
|
* << 1 2 3 4 [5]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author 莫取网名
|
||||||
|
*/
|
||||||
|
public class NavigatePageInfo extends PageInfo {
|
||||||
|
|
||||||
|
private final int navigatePageCount; //导航页码数
|
||||||
|
private int[] navigatePageNumbers; //所有导航页号
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param total 总记录数
|
||||||
|
* @param pageSize 每页显示记录数
|
||||||
|
* @param navigatePageCount 导航页码数
|
||||||
|
*/
|
||||||
|
public NavigatePageInfo(final int total, final int pageSize, final int navigatePageCount) {
|
||||||
|
super(total, pageSize);
|
||||||
|
this.navigatePageCount = navigatePageCount;
|
||||||
|
|
||||||
|
//基本参数设定之后进行导航页面的计算
|
||||||
|
calcNavigatePageNumbers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到所有导航页号
|
||||||
|
*
|
||||||
|
* @return {int[]}
|
||||||
|
*/
|
||||||
|
public int[] getNavigatePageNumbers() {
|
||||||
|
return navigatePageNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigatePageInfo setFirstPageNo(final int firstPageNo) {
|
||||||
|
super.setFirstPageNo(firstPageNo);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigatePageInfo setPageNo(final int pageNo) {
|
||||||
|
super.setPageNo(pageNo);
|
||||||
|
// 重新计算导航
|
||||||
|
calcNavigatePageNumbers();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder str = new StringBuilder();
|
||||||
|
|
||||||
|
if (false == isFirstPage()) {
|
||||||
|
str.append("<< ");
|
||||||
|
}
|
||||||
|
if (navigatePageNumbers.length > 0) {
|
||||||
|
str.append(wrapForDisplay(navigatePageNumbers[0]));
|
||||||
|
}
|
||||||
|
for (int i = 1; i < navigatePageNumbers.length; i++) {
|
||||||
|
str.append(" ").append(wrapForDisplay(navigatePageNumbers[i]));
|
||||||
|
}
|
||||||
|
if (false == isLastPage()) {
|
||||||
|
str.append(" >>");
|
||||||
|
}
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// region ----- private methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于显示的包装<br>
|
||||||
|
* 当前页显示'[pageNumber]',否则直接显示
|
||||||
|
*
|
||||||
|
* @param pageNumber 页码
|
||||||
|
* @return 包装的页码
|
||||||
|
*/
|
||||||
|
private String wrapForDisplay(final int pageNumber) {
|
||||||
|
if (this.pageNo == pageNumber) {
|
||||||
|
return "[" + pageNumber + "]";
|
||||||
|
}
|
||||||
|
return String.valueOf(pageNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算导航页
|
||||||
|
*/
|
||||||
|
private void calcNavigatePageNumbers() {
|
||||||
|
//当总页数小于或等于导航页码数时,全部显示
|
||||||
|
if (pageCount <= navigatePageCount) {
|
||||||
|
navigatePageNumbers = new int[pageCount];
|
||||||
|
for (int i = 0; i < pageCount; i++) {
|
||||||
|
navigatePageNumbers[i] = i + 1;
|
||||||
|
}
|
||||||
|
} else { //当总页数大于导航页码数时,部分显示
|
||||||
|
navigatePageNumbers = new int[navigatePageCount];
|
||||||
|
int startNum = pageNo - navigatePageCount / 2;
|
||||||
|
int endNum = pageNo + navigatePageCount / 2;
|
||||||
|
|
||||||
|
if (startNum < 1) {
|
||||||
|
startNum = 1;
|
||||||
|
//(最前navPageCount页
|
||||||
|
for (int i = 0; i < navigatePageCount; i++) {
|
||||||
|
navigatePageNumbers[i] = startNum++;
|
||||||
|
}
|
||||||
|
} else if (endNum > pageCount) {
|
||||||
|
endNum = pageCount;
|
||||||
|
//最后navPageCount页
|
||||||
|
for (int i = navigatePageCount - 1; i >= 0; i--) {
|
||||||
|
navigatePageNumbers[i] = endNum--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//所有中间页
|
||||||
|
for (int i = 0; i < navigatePageCount; i++) {
|
||||||
|
navigatePageNumbers[i] = startNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cn.hutool.core.math;
|
package cn.hutool.core.lang.page;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.lang.DefaultSegment;
|
import cn.hutool.core.lang.DefaultSegment;
|
||||||
@ -36,7 +36,7 @@ public class PageInfo {
|
|||||||
/**
|
/**
|
||||||
* 总页数
|
* 总页数
|
||||||
*/
|
*/
|
||||||
int pages;
|
int pageCount;
|
||||||
/**
|
/**
|
||||||
* 首页标识
|
* 首页标识
|
||||||
*/
|
*/
|
||||||
@ -74,7 +74,7 @@ public class PageInfo {
|
|||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
// 因为总条数除以页大小的最大余数是页大小数-1,
|
// 因为总条数除以页大小的最大余数是页大小数-1,
|
||||||
// 因此加一个最大余数,保证舍弃的余数与最大余数凑1.x,就是一旦有余数则+1页
|
// 因此加一个最大余数,保证舍弃的余数与最大余数凑1.x,就是一旦有余数则+1页
|
||||||
this.pages = (total + pageSize - 1) / pageSize;
|
this.pageCount = (total + pageSize - 1) / pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,8 +100,8 @@ public class PageInfo {
|
|||||||
*
|
*
|
||||||
* @return {int}
|
* @return {int}
|
||||||
*/
|
*/
|
||||||
public int getPages() {
|
public int getPageCount() {
|
||||||
return pages;
|
return pageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,7 +129,7 @@ public class PageInfo {
|
|||||||
* @return 是否尾页
|
* @return 是否尾页
|
||||||
*/
|
*/
|
||||||
public boolean isLastPage() {
|
public boolean isLastPage() {
|
||||||
return getPageIndexBase1() == this.pages;
|
return getPageIndexBase1() == this.pageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +147,7 @@ public class PageInfo {
|
|||||||
* @return 是否有下一页
|
* @return 是否有下一页
|
||||||
*/
|
*/
|
||||||
public boolean hasNextPage() {
|
public boolean hasNextPage() {
|
||||||
return getBeginIndex() < this.pages;
|
return getBeginIndex() < this.pageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +155,7 @@ public class PageInfo {
|
|||||||
* @return 是否可用
|
* @return 是否可用
|
||||||
*/
|
*/
|
||||||
public boolean isValidPage(){
|
public boolean isValidPage(){
|
||||||
return this.getPageIndexBase1() <= this.pages;
|
return this.getPageIndexBase1() <= this.pageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,7 +227,8 @@ public class PageInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置首页编号,即以数字几为第一页标志<br>
|
* 设置首页编号,即以数字几为第一页标志<br>
|
||||||
* 如设置0,则0表示第一页,1表示第二页
|
* 如设置0,则0表示第一页,1表示第二页<br>
|
||||||
|
* 设置此参数后,须调用{@link #setPageNo(int)} 重新设置当前页的页码
|
||||||
*
|
*
|
||||||
* @param firstPageNo 首页编号
|
* @param firstPageNo 首页编号
|
||||||
* @return this
|
* @return this
|
||||||
@ -238,7 +239,9 @@ public class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前页码
|
* 设置当前页码,具体这个页码代表实际页,取决于{@link #setFirstPageNo(int)}设置的值。
|
||||||
|
* 例如当{@link #setFirstPageNo(int)}设置为1时,1表示首页;设置为0时,0表示首页,依次类推。<br>
|
||||||
|
* 当设置页码小于{@link #getFirstPageNo()}值时,始终为{@link #getFirstPageNo()}
|
||||||
*
|
*
|
||||||
* @param pageNo 当前页码
|
* @param pageNo 当前页码
|
||||||
* @return this
|
* @return this
|
||||||
@ -251,7 +254,8 @@ public class PageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下一页,即当前页码+1
|
* 下一页,即当前页码+1<br>
|
||||||
|
* 当超过末页时,此方法指向的页码值始终为{@link #getPageCount()} + 1,即最后一页后的空白页。
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
@ -264,14 +268,14 @@ public class PageInfo {
|
|||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public PageInfo PreviousPage() {
|
public PageInfo previousPage() {
|
||||||
return setPageNo(this.pageNo - 1);
|
return setPageNo(this.pageNo - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "{" +
|
return "{" +
|
||||||
"total=" + total +
|
"total=" + total +
|
||||||
",pages=" + pages +
|
",pages=" + pageCount +
|
||||||
",pageNumber=" + pageNo +
|
",pageNumber=" + pageNo +
|
||||||
",limit=" + pageSize +
|
",limit=" + pageSize +
|
||||||
",isFirstPage=" + isFirstPage() +
|
",isFirstPage=" + isFirstPage() +
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 提供分页信息封装,主要包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link cn.hutool.core.lang.page.PageInfo}: 提供分页信息。</li>
|
||||||
|
* <li>{@link cn.hutool.core.lang.page.NavigatePageInfo}: 提供分页导航信息。</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package cn.hutool.core.lang.page;
|
@ -1,71 +0,0 @@
|
|||||||
package cn.hutool.core.math;
|
|
||||||
|
|
||||||
public class NavigatePageInfo extends PageInfo{
|
|
||||||
|
|
||||||
private final int navigatePages = 8; //导航页码数
|
|
||||||
private int[] navigatePageNumbers; //所有导航页号
|
|
||||||
|
|
||||||
public NavigatePageInfo(final int total, final int pageSize) {
|
|
||||||
super(total, pageSize);
|
|
||||||
|
|
||||||
//基本参数设定之后进行导航页面的计算
|
|
||||||
calcNavigatePageNumbers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到所有导航页号
|
|
||||||
*
|
|
||||||
* @return {int[]}
|
|
||||||
*/
|
|
||||||
public int[] getNavigatePageNumbers() {
|
|
||||||
return navigatePageNumbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
final StringBuilder str = new StringBuilder(super.toString());
|
|
||||||
str.append(", {navigatePageNumbers=");
|
|
||||||
final int len = navigatePageNumbers.length;
|
|
||||||
if (len > 0) str.append(navigatePageNumbers[0]);
|
|
||||||
for (int i = 1; i < len; i++) {
|
|
||||||
str.append(" ").append(navigatePageNumbers[i]);
|
|
||||||
}
|
|
||||||
str.append("}");
|
|
||||||
return str.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算导航页
|
|
||||||
*/
|
|
||||||
private void calcNavigatePageNumbers() {
|
|
||||||
//当总页数小于或等于导航页码数时
|
|
||||||
if (pages <= navigatePages) {
|
|
||||||
navigatePageNumbers = new int[pages];
|
|
||||||
for (int i = 0; i < pages; i++) {
|
|
||||||
navigatePageNumbers[i] = i + 1;
|
|
||||||
}
|
|
||||||
} else { //当总页数大于导航页码数时
|
|
||||||
navigatePageNumbers = new int[navigatePages];
|
|
||||||
int startNum = pageNo - navigatePages / 2;
|
|
||||||
int endNum = pageNo + navigatePages / 2;
|
|
||||||
|
|
||||||
if (startNum < 1) {
|
|
||||||
startNum = 1;
|
|
||||||
//(最前navPageCount页
|
|
||||||
for (int i = 0; i < navigatePages; i++) {
|
|
||||||
navigatePageNumbers[i] = startNum++;
|
|
||||||
}
|
|
||||||
} else if (endNum > pages) {
|
|
||||||
endNum = pages;
|
|
||||||
//最后navPageCount页
|
|
||||||
for (int i = navigatePages - 1; i >= 0; i--) {
|
|
||||||
navigatePageNumbers[i] = endNum--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//所有中间页
|
|
||||||
for (int i = 0; i < navigatePages; i++) {
|
|
||||||
navigatePageNumbers[i] = startNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ import java.util.*;
|
|||||||
*
|
*
|
||||||
* @author renyp
|
* @author renyp
|
||||||
*/
|
*/
|
||||||
public class Automaton {
|
public class NFA {
|
||||||
/**
|
/**
|
||||||
* AC树的根节点
|
* AC树的根节点
|
||||||
*/
|
*/
|
||||||
@ -17,20 +17,26 @@ public class Automaton {
|
|||||||
/**
|
/**
|
||||||
* 标记是否需要构建AC自动机,做树优化
|
* 标记是否需要构建AC自动机,做树优化
|
||||||
*/
|
*/
|
||||||
private volatile boolean needBuildAC;
|
private volatile boolean needBuildAc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内置锁,防止并发场景,并行建AC树,造成不可预知结果
|
* 内置锁,防止并发场景,并行建AC树,造成不可预知结果
|
||||||
*/
|
*/
|
||||||
private final Object lock;
|
private final Object buildAcLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内置锁,防止并行插入,新节点建立后,被挂载到树上前 被篡改
|
||||||
|
*/
|
||||||
|
private final Object insertTreeLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认构造
|
* 默认构造
|
||||||
*/
|
*/
|
||||||
public Automaton() {
|
public NFA() {
|
||||||
this.root = new Node();
|
this.root = new Node();
|
||||||
this.needBuildAC = true;
|
this.needBuildAc = true;
|
||||||
this.lock = new Object();
|
this.buildAcLock = new Object();
|
||||||
|
this.insertTreeLock = new Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +44,7 @@ public class Automaton {
|
|||||||
*
|
*
|
||||||
* @param words 添加的新词
|
* @param words 添加的新词
|
||||||
*/
|
*/
|
||||||
public Automaton(String... words) {
|
public NFA(String... words) {
|
||||||
this();
|
this();
|
||||||
this.insert(words);
|
this.insert(words);
|
||||||
}
|
}
|
||||||
@ -49,17 +55,19 @@ public class Automaton {
|
|||||||
* @param word 添加的新词
|
* @param word 添加的新词
|
||||||
*/
|
*/
|
||||||
public void insert(String word) {
|
public void insert(String word) {
|
||||||
needBuildAC = true;
|
synchronized (insertTreeLock) {
|
||||||
Node p = root;
|
needBuildAc = true;
|
||||||
for (char curr : word.toCharArray()) {
|
Node p = root;
|
||||||
int ind = curr;
|
for (char curr : word.toCharArray()) {
|
||||||
if (p.next.get(ind) == null) {
|
int ind = curr;
|
||||||
p.next.put(ind, new Node());
|
if (p.next.get(ind) == null) {
|
||||||
|
p.next.put(ind, new Node());
|
||||||
|
}
|
||||||
|
p = p.next.get(ind);
|
||||||
}
|
}
|
||||||
p = p.next.get(ind);
|
p.flag = true;
|
||||||
|
p.str = word;
|
||||||
}
|
}
|
||||||
p.flag = true;
|
|
||||||
p.str = word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +109,7 @@ public class Automaton {
|
|||||||
queue.offer(curr.next.get(key));
|
queue.offer(curr.next.get(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
needBuildAC = false;
|
needBuildAc = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,9 +125,9 @@ public class Automaton {
|
|||||||
*/
|
*/
|
||||||
public List<FoundWord> find(String text, boolean isDensityMatch) {
|
public List<FoundWord> find(String text, boolean isDensityMatch) {
|
||||||
// double check,防止重复无用的 buildAC
|
// double check,防止重复无用的 buildAC
|
||||||
if (needBuildAC) {
|
if (needBuildAc) {
|
||||||
synchronized (lock) {
|
synchronized (buildAcLock) {
|
||||||
if (needBuildAC) {
|
if (needBuildAc) {
|
||||||
this.buildAc();
|
this.buildAc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,6 +161,7 @@ public class Automaton {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class Node {
|
private static class Node {
|
||||||
|
|
||||||
/**
|
/**
|
@ -2,7 +2,7 @@ package cn.hutool.core.collection;
|
|||||||
|
|
||||||
import cn.hutool.core.date.StopWatch;
|
import cn.hutool.core.date.StopWatch;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.math.PageInfo;
|
import cn.hutool.core.lang.page.PageInfo;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
@ -12,11 +12,8 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.lang.invoke.LambdaConversionException;
|
import java.lang.invoke.*;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.invoke.MethodHandleProxies;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -24,6 +21,7 @@ import java.util.Comparator;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nasodaengineer
|
* @author nasodaengineer
|
||||||
@ -160,6 +158,15 @@ public class LambdaFactoryTest {
|
|||||||
loop(count, tasks);
|
loop(count, tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructor() {
|
||||||
|
Constructor<Something> constructor = ((SerSupplier<Constructor<Something>>) Something.class::getConstructor).get();
|
||||||
|
Supplier<Something> constructorLambda = LambdaFactory.build(Supplier.class, constructor);
|
||||||
|
// constructorLambda can be cache or transfer
|
||||||
|
Something something = constructorLambda.get();
|
||||||
|
Assert.assertEquals(Something.class, something.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>hardCode 运行1次耗时 7600 NANOSECONDS
|
* <p>hardCode 运行1次耗时 7600 NANOSECONDS
|
||||||
* <p>lambda 运行1次耗时 12400 NANOSECONDS
|
* <p>lambda 运行1次耗时 12400 NANOSECONDS
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package cn.hutool.core.lang.page;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class NavigatePageInfoTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void naviTest1(){
|
||||||
|
// 首页
|
||||||
|
final NavigatePageInfo navigatePageInfo = new NavigatePageInfo(10, 2, 6);
|
||||||
|
Assert.assertEquals("[1] 2 3 4 5 >>", navigatePageInfo.toString());
|
||||||
|
|
||||||
|
// 中间页
|
||||||
|
navigatePageInfo.nextPage();
|
||||||
|
Assert.assertEquals("<< 1 [2] 3 4 5 >>", navigatePageInfo.toString());
|
||||||
|
|
||||||
|
// 尾页
|
||||||
|
navigatePageInfo.setPageNo(5);
|
||||||
|
Assert.assertEquals("<< 1 2 3 4 [5]", navigatePageInfo.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void naviTest2(){
|
||||||
|
// 首页
|
||||||
|
final NavigatePageInfo navigatePageInfo = new NavigatePageInfo(10, 2, 4);
|
||||||
|
Assert.assertEquals("[1] 2 3 4 >>", navigatePageInfo.toString());
|
||||||
|
|
||||||
|
// 中间页
|
||||||
|
navigatePageInfo.nextPage();
|
||||||
|
Assert.assertEquals("<< 1 [2] 3 4 >>", navigatePageInfo.toString());
|
||||||
|
|
||||||
|
// 尾页
|
||||||
|
navigatePageInfo.setPageNo(5);
|
||||||
|
Assert.assertEquals("<< 2 3 4 [5]", navigatePageInfo.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cn.hutool.core.math;
|
package cn.hutool.core.lang.page;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -7,10 +7,10 @@ public class PageInfoTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pagesTest() {
|
public void pagesTest() {
|
||||||
PageInfo pageInfo = new PageInfo(20, 3);
|
PageInfo pageInfo = new PageInfo(20, 3);
|
||||||
Assert.assertEquals(7, pageInfo.getPages());
|
Assert.assertEquals(7, pageInfo.getPageCount());
|
||||||
|
|
||||||
pageInfo = new PageInfo(20, 4);
|
pageInfo = new PageInfo(20, 4);
|
||||||
Assert.assertEquals(5, pageInfo.getPages());
|
Assert.assertEquals(5, pageInfo.getPageCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
@ -1,225 +0,0 @@
|
|||||||
package cn.hutool.core.text.dfa;
|
|
||||||
|
|
||||||
import cn.hutool.core.date.StopWatch;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class AutomatonTest extends TestCase {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密集匹配 测试查找结果,并与WordTree对比效率
|
|
||||||
*/
|
|
||||||
public void testFind() {
|
|
||||||
Automaton automaton = new Automaton();
|
|
||||||
WordTree wordTree = new WordTree();
|
|
||||||
automaton.insert("say", "her", "he", "she", "shr");
|
|
||||||
// automaton.buildAc();
|
|
||||||
wordTree.addWords("say", "her", "he", "she", "shr");
|
|
||||||
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "sasherhsay";
|
|
||||||
|
|
||||||
stopWatch.start("automaton_char_find");
|
|
||||||
List<FoundWord> ans1 = automaton.find(input);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(3), ans1.get(1).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(4), ans1.get(1).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(3), ans1.get(2).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(5), ans1.get(2).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(7), ans1.get(3).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(9), ans1.get(3).getEndIndex());
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_char_find");
|
|
||||||
List<String> ans2 = wordTree.matchAll(input, -1, true, true);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,he,her,say", String.join(",", ans2));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 非密集匹配 测试查找结果,并与WordTree对比效率
|
|
||||||
*/
|
|
||||||
public void testFindNotDensity() {
|
|
||||||
Automaton automaton = new Automaton();
|
|
||||||
WordTree wordTree = new WordTree();
|
|
||||||
automaton.insert("say", "her", "he", "she", "shr");
|
|
||||||
// automaton.buildAc();
|
|
||||||
wordTree.addWords("say", "her", "he", "she", "shr");
|
|
||||||
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "sasherhsay";
|
|
||||||
|
|
||||||
stopWatch.start("automaton_char_find_not_density");
|
|
||||||
List<FoundWord> ans1 = automaton.find(input, false);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(7), ans1.get(1).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(9), ans1.get(1).getEndIndex());
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_char_find_not_density");
|
|
||||||
List<String> ans2 = wordTree.matchAll(input, -1, false, true);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,say", String.join(",", ans2));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密集匹配 测试建树和查找,并与WordTree对比效率
|
|
||||||
*/
|
|
||||||
public void testBuildAndFind() {
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "sasherhsay";
|
|
||||||
|
|
||||||
stopWatch.start("automaton_char_buid_find");
|
|
||||||
Automaton automatonLocal = new Automaton();
|
|
||||||
automatonLocal.insert("say", "her", "he", "she", "shr");
|
|
||||||
// automatonLocal.buildAc();
|
|
||||||
List<FoundWord> ans1 = automatonLocal.find(input);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(2), ans1.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(4), ans1.get(0).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(3), ans1.get(1).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(4), ans1.get(1).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(3), ans1.get(2).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(5), ans1.get(2).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(7), ans1.get(3).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(9), ans1.get(3).getEndIndex());
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_char_build_find");
|
|
||||||
WordTree wordTreeLocal = new WordTree();
|
|
||||||
wordTreeLocal.addWords("say", "her", "he", "she", "shr");
|
|
||||||
List<String> ans2 = wordTreeLocal.matchAll(input, -1, true, true);
|
|
||||||
stopWatch.stop();
|
|
||||||
assertEquals("she,he,her,say", String.join(",", ans2));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密集匹配 构建树和查找 测试中文字符,并与wordTree对比效率
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testBuildFindCnChar() {
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "赵啊三在做什么";
|
|
||||||
|
|
||||||
stopWatch.start("automaton_cn_build_find");
|
|
||||||
Automaton automatonLocal = new Automaton();
|
|
||||||
automatonLocal.insert("赵", "赵啊", "赵啊三");
|
|
||||||
// automatonLocal.buildAc();
|
|
||||||
|
|
||||||
final List<FoundWord> result = automatonLocal.find(input);
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(3, result.size());
|
|
||||||
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(1).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(2).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_cn_build_find");
|
|
||||||
WordTree wordTreeLocal = new WordTree();
|
|
||||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
|
||||||
|
|
||||||
final List<String> result1 = wordTreeLocal.matchAll(input, -1, true, true);
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(3, result1.size());
|
|
||||||
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testFindCNChar() {
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "赵啊三在做什么";
|
|
||||||
|
|
||||||
Automaton automatonLocal = new Automaton();
|
|
||||||
automatonLocal.insert("赵", "赵啊", "赵啊三");
|
|
||||||
// automatonLocal.buildAc();
|
|
||||||
|
|
||||||
stopWatch.start("automaton_cn_find");
|
|
||||||
final List<FoundWord> result = automatonLocal.find(input);
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(3, result.size());
|
|
||||||
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(1).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(2).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
|
||||||
|
|
||||||
WordTree wordTreeLocal = new WordTree();
|
|
||||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_cn_find");
|
|
||||||
final List<String> result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(3, result1.size());
|
|
||||||
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 非密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率,
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testFindCNCharNotDensity() {
|
|
||||||
StopWatch stopWatch = new StopWatch();
|
|
||||||
String input = "赵啊三在做什么";
|
|
||||||
|
|
||||||
Automaton automatonLocal = new Automaton();
|
|
||||||
automatonLocal.insert("赵", "赵啊", "赵啊三");
|
|
||||||
// automatonLocal.buildAc();
|
|
||||||
|
|
||||||
stopWatch.start("automaton_cn_find_not_density");
|
|
||||||
final List<FoundWord> result = automatonLocal.find(input, false);
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(1, result.size());
|
|
||||||
Assert.assertEquals("赵", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getStartIndex());
|
|
||||||
assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
|
||||||
|
|
||||||
WordTree wordTreeLocal = new WordTree();
|
|
||||||
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
|
||||||
|
|
||||||
stopWatch.start("wordtree_cn_find_not_density");
|
|
||||||
final List<String> result1 =
|
|
||||||
wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
stopWatch.stop();
|
|
||||||
|
|
||||||
Assert.assertEquals(1, result1.size());
|
|
||||||
Assert.assertEquals("赵", String.join(",", result1));
|
|
||||||
|
|
||||||
System.out.println(stopWatch.prettyPrint());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
228
hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java
Normal file
228
hutool-core/src/test/java/cn/hutool/core/text/dfa/NFATest.java
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package cn.hutool.core.text.dfa;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.StopWatch;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class NFATest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密集匹配 测试查找结果,并与WordTree对比效率
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFind() {
|
||||||
|
final NFA NFA = new NFA();
|
||||||
|
NFA.insert("say", "her", "he", "she", "shr");
|
||||||
|
// NFA.buildAc();
|
||||||
|
|
||||||
|
final WordTree wordTree = new WordTree();
|
||||||
|
wordTree.addWords("say", "her", "he", "she", "shr");
|
||||||
|
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "sasherhsay";
|
||||||
|
|
||||||
|
stopWatch.start("automaton_char_find");
|
||||||
|
final List<FoundWord> ans1 = NFA.find(input);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_char_find");
|
||||||
|
final List<String> ans2 = wordTree.matchAll(input, -1, true, true);
|
||||||
|
stopWatch.stop();
|
||||||
|
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非密集匹配 测试查找结果,并与WordTree对比效率
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFindNotDensity() {
|
||||||
|
final NFA NFA = new NFA();
|
||||||
|
NFA.insert("say", "her", "he", "she", "shr");
|
||||||
|
// NFA.buildAc();
|
||||||
|
|
||||||
|
final WordTree wordTree = new WordTree();
|
||||||
|
wordTree.addWords("say", "her", "he", "she", "shr");
|
||||||
|
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "sasherhsay";
|
||||||
|
|
||||||
|
stopWatch.start("automaton_char_find_not_density");
|
||||||
|
final List<FoundWord> ans1 = NFA.find(input, false);
|
||||||
|
stopWatch.stop();
|
||||||
|
Assert.assertEquals("she,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(7, ans1.get(1).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(9, ans1.get(1).getEndIndex().intValue());
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_char_find_not_density");
|
||||||
|
final List<String> ans2 = wordTree.matchAll(input, -1, false, true);
|
||||||
|
stopWatch.stop();
|
||||||
|
Assert.assertEquals("she,say", String.join(",", ans2));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密集匹配 测试建树和查找,并与WordTree对比效率
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBuildAndFind() {
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "sasherhsay";
|
||||||
|
|
||||||
|
stopWatch.start("automaton_char_buid_find");
|
||||||
|
final NFA NFALocal = new NFA();
|
||||||
|
NFALocal.insert("say", "her", "he", "she", "shr");
|
||||||
|
// NFALocal.buildAc();
|
||||||
|
final List<FoundWord> ans1 = NFALocal.find(input);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals("she,he,her,say", ans1.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(2, ans1.get(0).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(4, ans1.get(0).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(3, ans1.get(1).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(4, ans1.get(1).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(3, ans1.get(2).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(5, ans1.get(2).getEndIndex().intValue());
|
||||||
|
Assert.assertEquals(7, ans1.get(3).getBeginIndex().intValue());
|
||||||
|
Assert.assertEquals(9, ans1.get(3).getEndIndex().intValue());
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_char_build_find");
|
||||||
|
final WordTree wordTreeLocal = new WordTree();
|
||||||
|
wordTreeLocal.addWords("say", "her", "he", "she", "shr");
|
||||||
|
final List<String> ans2 = wordTreeLocal.matchAll(input, -1, true, true);
|
||||||
|
stopWatch.stop();
|
||||||
|
Assert.assertEquals("she,he,her,say", String.join(",", ans2));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密集匹配 构建树和查找 测试中文字符,并与wordTree对比效率
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void buildFindCnCharTest() {
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "赵啊三在做什么";
|
||||||
|
|
||||||
|
stopWatch.start("automaton_cn_build_find");
|
||||||
|
final NFA NFALocal = new NFA();
|
||||||
|
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||||
|
// NFALocal.buildAc();
|
||||||
|
|
||||||
|
final List<FoundWord> result = NFALocal.find(input);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(3, result.size());
|
||||||
|
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_cn_build_find");
|
||||||
|
final WordTree wordTreeLocal = new WordTree();
|
||||||
|
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||||
|
|
||||||
|
final List<String> result1 = wordTreeLocal.matchAll(input, -1, true, true);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(3, result1.size());
|
||||||
|
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFindCNChar() {
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "赵啊三在做什么";
|
||||||
|
|
||||||
|
final NFA NFALocal = new NFA();
|
||||||
|
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||||
|
// NFALocal.buildAc();
|
||||||
|
|
||||||
|
stopWatch.start("automaton_cn_find");
|
||||||
|
final List<FoundWord> result = NFALocal.find(input);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(3, result.size());
|
||||||
|
Assert.assertEquals("赵,赵啊,赵啊三", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(1).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(1), result.get(1).getEndIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(2).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(2), result.get(2).getEndIndex());
|
||||||
|
|
||||||
|
final WordTree wordTreeLocal = new WordTree();
|
||||||
|
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_cn_find");
|
||||||
|
final List<String> result1 = wordTreeLocal.matchAllWords(input, -1, true, true).stream().map(FoundWord::getWord)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(3, result1.size());
|
||||||
|
Assert.assertEquals("赵,赵啊,赵啊三", String.join(",", result1));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非密集匹配 测试构建树和查找 中文字符,并与wordTree对比效率,
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFindCNCharNotDensity() {
|
||||||
|
final StopWatch stopWatch = new StopWatch();
|
||||||
|
final String input = "赵啊三在做什么";
|
||||||
|
|
||||||
|
final NFA NFALocal = new NFA();
|
||||||
|
NFALocal.insert("赵", "赵啊", "赵啊三");
|
||||||
|
// NFALocal.buildAc();
|
||||||
|
|
||||||
|
stopWatch.start("automaton_cn_find_not_density");
|
||||||
|
final List<FoundWord> result = NFALocal.find(input, false);
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
Assert.assertEquals("赵", result.stream().map(FoundWord::getWord).collect(Collectors.joining(",")));
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getBeginIndex());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), result.get(0).getEndIndex());
|
||||||
|
|
||||||
|
final WordTree wordTreeLocal = new WordTree();
|
||||||
|
wordTreeLocal.addWords("赵", "赵啊", "赵啊三");
|
||||||
|
|
||||||
|
stopWatch.start("wordtree_cn_find_not_density");
|
||||||
|
final List<String> result1 =
|
||||||
|
wordTreeLocal.matchAllWords(input, -1, false, true).stream().map(FoundWord::getWord)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
|
Assert.assertEquals(1, result1.size());
|
||||||
|
Assert.assertEquals("赵", String.join(",", result1));
|
||||||
|
|
||||||
|
//Console.log(stopWatch.prettyPrint());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package cn.hutool.core.tree;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IssueI6NR2ZTest {
|
||||||
|
@Test
|
||||||
|
public void getNodeTest() {
|
||||||
|
final List<TreeNode<Integer>> list = new ArrayList<>();
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode1 = new TreeNode<>();
|
||||||
|
treeNode1.setId(1);
|
||||||
|
treeNode1.setParentId(0);
|
||||||
|
list.add(treeNode1);
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode2 = new TreeNode<>();
|
||||||
|
treeNode2.setId(2);
|
||||||
|
treeNode2.setParentId(1);
|
||||||
|
list.add(treeNode2);
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode3 = new TreeNode<>();
|
||||||
|
treeNode3.setId(3);
|
||||||
|
treeNode3.setParentId(1);
|
||||||
|
list.add(treeNode3);
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode4 = new TreeNode<>();
|
||||||
|
treeNode4.setId(21);
|
||||||
|
treeNode4.setParentId(2);
|
||||||
|
list.add(treeNode4);
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode5 = new TreeNode<>();
|
||||||
|
treeNode5.setId(31);
|
||||||
|
treeNode5.setParentId(3);
|
||||||
|
list.add(treeNode5);
|
||||||
|
|
||||||
|
final TreeNode<Integer> treeNode6 = new TreeNode<>();
|
||||||
|
treeNode6.setId(211);
|
||||||
|
treeNode6.setParentId(21);
|
||||||
|
list.add(treeNode6);
|
||||||
|
|
||||||
|
final List<MapTree<Integer>> build = TreeUtil.build(list);
|
||||||
|
final MapTree<Integer> node = TreeUtil.getNode(build.get(0), 31);
|
||||||
|
Assert.notNull(node);
|
||||||
|
}
|
||||||
|
}
|
40
hutool-db/README.md
Normal file
40
hutool-db/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://hutool.cn/"><img src="https://plus.hutool.cn/images/hutool.svg" width="45%"></a>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<strong>🍬A set of tools that keep Java sweet.</strong>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
👉 <a href="https://hutool.cn">https://hutool.cn/</a> 👈
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 📚Hutool-db 模块介绍
|
||||||
|
|
||||||
|
`Hutool-db`是一个在JDBC基础上封装的数据库操作工具类。通过包装,使用ActiveRecord思想操作数据库。
|
||||||
|
在Hutool-db中,使用Entity(本质上是个Map)代替Bean来使数据库操作更加灵活,同时提供Bean和Entity的转换提供传统ORM的兼容支持。
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 🛠️包含内容
|
||||||
|
|
||||||
|
### 数据库方言(dialect)
|
||||||
|
通过抽象CRUD方法,针对不同数据库,封装对应的查询、分页等SQL转换功能,入口为`DialectFactory`。
|
||||||
|
|
||||||
|
### 数据源(ds)
|
||||||
|
提供常用数据库连接池的门面支持,支持顺序为:
|
||||||
|
|
||||||
|
- Hikari > Druid > Tomcat > BeeCP > Dbcp > C3p0 > Hutool Pooled
|
||||||
|
|
||||||
|
### SQL相关工具(sql)
|
||||||
|
提供SQL相关功能,包括SQL变量替换(NamedSql),通过对象完成SQL构建(SqlBuilder)等。
|
||||||
|
|
||||||
|
`SqlSqlExecutor`提供SQL执行的静态方法。
|
||||||
|
|
||||||
|
### 数据库元信息(meta)
|
||||||
|
通过`MetaUtil`提供数据库表、字段等信息的读取操作。
|
||||||
|
|
||||||
|
### 增删改查(Db、Session)
|
||||||
|
提供增删改查的汇总方法类。
|
||||||
|
|
||||||
|
### 结果转换(handler)
|
||||||
|
通过封装`RsHandler`,提供将ResultSet转换为需要的对象的功能。
|
@ -51,6 +51,7 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
}
|
}
|
||||||
// ------------------------------------------------------- Constructor end
|
// ------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
|
// region ----- query
|
||||||
/**
|
/**
|
||||||
* 查询
|
* 查询
|
||||||
*
|
*
|
||||||
@ -189,7 +190,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region ----- execute
|
||||||
/**
|
/**
|
||||||
* 执行非查询语句<br>
|
* 执行非查询语句<br>
|
||||||
* 语句包括 插入、更新、删除
|
* 语句包括 插入、更新、删除
|
||||||
@ -282,9 +285,11 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- CRUD start
|
// ---------------------------------------------------------------------------- CRUD start
|
||||||
|
|
||||||
|
// region ----- insert
|
||||||
/**
|
/**
|
||||||
* 插入数据
|
* 插入数据
|
||||||
*
|
*
|
||||||
@ -395,7 +400,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region ----- del
|
||||||
/**
|
/**
|
||||||
* 删除数据
|
* 删除数据
|
||||||
*
|
*
|
||||||
@ -425,7 +432,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region ----- update
|
||||||
/**
|
/**
|
||||||
* 更新数据<br>
|
* 更新数据<br>
|
||||||
* 更新条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
* 更新条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
||||||
@ -444,8 +453,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
// ------------------------------------------------------------- Get start
|
// region ----- get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据某个字段(最好是唯一字段)查询单个记录<br>
|
* 根据某个字段(最好是唯一字段)查询单个记录<br>
|
||||||
@ -473,8 +483,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
return find(where.getFieldNames(), where, new EntityHandler(this.caseInsensitive));
|
return find(where.getFieldNames(), where, new EntityHandler(this.caseInsensitive));
|
||||||
|
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------- Get end
|
// endregion
|
||||||
|
|
||||||
|
// region ----- find
|
||||||
/**
|
/**
|
||||||
* 查询<br>
|
* 查询<br>
|
||||||
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
||||||
@ -487,13 +498,7 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
* @throws DbRuntimeException SQL执行异常
|
* @throws DbRuntimeException SQL执行异常
|
||||||
*/
|
*/
|
||||||
public <T> T find(final Collection<String> fields, final Entity where, final RsHandler<T> rsh) throws DbRuntimeException {
|
public <T> T find(final Collection<String> fields, final Entity where, final RsHandler<T> rsh) throws DbRuntimeException {
|
||||||
Connection conn = null;
|
return find(Query.of(where).setFields(fields), rsh);
|
||||||
try {
|
|
||||||
conn = this.getConnection();
|
|
||||||
return runner.find(conn, Query.of(where).setFields(fields), rsh);
|
|
||||||
} finally {
|
|
||||||
this.closeConnection(conn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -652,7 +657,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
public List<Entity> findLike(final String tableName, final String field, final String value, final LikeType likeType) throws DbRuntimeException {
|
public List<Entity> findLike(final String tableName, final String field, final String value, final LikeType likeType) throws DbRuntimeException {
|
||||||
return findAll(Entity.of(tableName).set(field, SqlUtil.buildLikeValue(value, likeType, true)));
|
return findAll(Entity.of(tableName).set(field, SqlUtil.buildLikeValue(value, likeType, true)));
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region ----- count
|
||||||
/**
|
/**
|
||||||
* 结果的条目数
|
* 结果的条目数
|
||||||
*
|
*
|
||||||
@ -705,7 +712,9 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
this.closeConnection(conn);
|
this.closeConnection(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region ----- page
|
||||||
/**
|
/**
|
||||||
* 分页查询,结果为Entity列表,不计算总数<br>
|
* 分页查询,结果为Entity列表,不计算总数<br>
|
||||||
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
||||||
@ -867,7 +876,7 @@ public abstract class AbstractDb<R extends AbstractDb<R>> extends DefaultConnect
|
|||||||
public PageResult<Entity> page(final Entity where, final Page page) throws DbRuntimeException {
|
public PageResult<Entity> page(final Entity where, final Page page) throws DbRuntimeException {
|
||||||
return this.page(where.getFieldNames(), where, page);
|
return this.page(where.getFieldNames(), where, page);
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------------------- CRUD end
|
// endregion
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- Getters and Setters start
|
// ---------------------------------------------------------------------------- Getters and Setters start
|
||||||
|
|
||||||
|
@ -13,7 +13,12 @@ public class DefaultConnectionHolder implements ConnectionHolder {
|
|||||||
|
|
||||||
protected final DataSource ds;
|
protected final DataSource ds;
|
||||||
|
|
||||||
public DefaultConnectionHolder(DataSource ds) {
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param ds {@link DataSource}
|
||||||
|
*/
|
||||||
|
public DefaultConnectionHolder(final DataSource ds) {
|
||||||
this.ds = ds;
|
this.ds = ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.db;
|
package cn.hutool.db;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Segment;
|
import cn.hutool.core.lang.Segment;
|
||||||
import cn.hutool.core.math.PageInfo;
|
import cn.hutool.core.lang.page.PageInfo;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.db.sql.Order;
|
import cn.hutool.db.sql.Order;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package cn.hutool.db;
|
package cn.hutool.db;
|
||||||
|
|
||||||
import cn.hutool.core.math.PageInfo;
|
import cn.hutool.core.lang.page.PageInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ public class PageResult<T> extends ArrayList<T> {
|
|||||||
this(page, pageSize);
|
this(page, pageSize);
|
||||||
|
|
||||||
this.total = total;
|
this.total = total;
|
||||||
this.totalPage = PageInfo.of(total, pageSize).getPages();
|
this.totalPage = PageInfo.of(total, pageSize).getPageCount();
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------- Constructor end
|
//---------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ public class StatementUtil {
|
|||||||
*
|
*
|
||||||
* @param conn 数据库连接
|
* @param conn 数据库连接
|
||||||
* @param sql SQL语句,使用"?"做为占位符
|
* @param sql SQL语句,使用"?"做为占位符
|
||||||
* @param params "?"对应参数列表
|
* @param params "?"对应参数列表或者Map表示命名参数
|
||||||
* @return {@link PreparedStatement}
|
* @return {@link PreparedStatement}
|
||||||
* @throws SQLException SQL异常
|
* @throws SQLException SQL异常
|
||||||
* @since 3.2.3
|
* @since 3.2.3
|
||||||
|
@ -115,9 +115,18 @@ public class SqlBuilder implements Builder<String> {
|
|||||||
private QuoteWrapper quoteWrapper;
|
private QuoteWrapper quoteWrapper;
|
||||||
|
|
||||||
// --------------------------------------------------------------- Constructor start
|
// --------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
public SqlBuilder() {
|
public SqlBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param quoteWrapper 包装器
|
||||||
|
*/
|
||||||
public SqlBuilder(final QuoteWrapper quoteWrapper) {
|
public SqlBuilder(final QuoteWrapper quoteWrapper) {
|
||||||
this.quoteWrapper = quoteWrapper;
|
this.quoteWrapper = quoteWrapper;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,13 @@ public class CRUDTest {
|
|||||||
Assert.assertEquals(2, results.size());
|
Assert.assertEquals(2, results.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findInTest4() {
|
||||||
|
final List<Entity> results = db.findAll(Entity.of("user")
|
||||||
|
.set("id", new String[]{"1", "2", "3"}));
|
||||||
|
Assert.assertEquals(2, results.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findAllTest() {
|
public void findAllTest() {
|
||||||
final List<Entity> results = db.findAll("user");
|
final List<Entity> results = db.findAll("user");
|
||||||
|
@ -55,6 +55,19 @@ public class DbTest {
|
|||||||
Assert.assertEquals(1, page1.size());
|
Assert.assertEquals(1, page1.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pageBySqlWithInTest() {
|
||||||
|
// in和其他条件混用
|
||||||
|
final String sql = "select * from user where age > :age and name in (:names) order by name";
|
||||||
|
// 测试数据库中一共4条数据,第0页有3条,第1页有1条
|
||||||
|
final List<Entity> page0 = Db.of().page(
|
||||||
|
sql, Page.of(0, 3),
|
||||||
|
Entity.of().set("age", 12)
|
||||||
|
.set("names", new String[]{"张三", "王五"})
|
||||||
|
);
|
||||||
|
Assert.assertEquals(1, page0.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pageWithParamsTest() {
|
public void pageWithParamsTest() {
|
||||||
final String sql = "select * from user where name = ?";
|
final String sql = "select * from user where name = ?";
|
||||||
|
@ -30,7 +30,9 @@ public class ClientConfig {
|
|||||||
* 默认读取超时
|
* 默认读取超时
|
||||||
*/
|
*/
|
||||||
private int readTimeout;
|
private int readTimeout;
|
||||||
|
/**
|
||||||
|
* SSL相关配置
|
||||||
|
*/
|
||||||
private SSLInfo sslInfo;
|
private SSLInfo sslInfo;
|
||||||
/**
|
/**
|
||||||
* 是否禁用缓存
|
* 是否禁用缓存
|
||||||
|
@ -2,6 +2,7 @@ package cn.hutool.json;
|
|||||||
|
|
||||||
import cn.hutool.core.io.file.FileUtil;
|
import cn.hutool.core.io.file.FileUtil;
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.reflect.TypeReference;
|
import cn.hutool.core.reflect.TypeReference;
|
||||||
import cn.hutool.core.text.StrUtil;
|
import cn.hutool.core.text.StrUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
@ -364,6 +365,7 @@ public class JSONUtil {
|
|||||||
* @since 4.6.2
|
* @since 4.6.2
|
||||||
*/
|
*/
|
||||||
public static <T> T toBean(final Object json, final TypeReference<T> typeReference) {
|
public static <T> T toBean(final Object json, final TypeReference<T> typeReference) {
|
||||||
|
Assert.notNull(typeReference);
|
||||||
return toBean(json, typeReference.getType());
|
return toBean(json, typeReference.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user