mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
Merge remote-tracking branch 'upstream/v5-dev' into v5-dev
This commit is contained in:
commit
169d7b8307
@ -3,12 +3,13 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 5.5.3 (2020-12-05)
|
||||
# 5.5.3 (2020-12-06)
|
||||
|
||||
### 新特性
|
||||
* 【core 】 IdcardUtil增加行政区划83(issue#1277@Github)
|
||||
* 【core 】 multipart中int改为long,解决大文件上传越界问题(issue#I27WZ3@Gitee)
|
||||
* 【core 】 ListUtil.page增加检查(pr#224@Gitee)
|
||||
* 【db 】 Db增加使用sql的page方法(issue#247@Gitee)
|
||||
|
||||
### Bug修复
|
||||
* 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github)
|
||||
|
@ -45,6 +45,7 @@ public class Hutool {
|
||||
/**
|
||||
* 显示Hutool所有的工具类
|
||||
*
|
||||
* @return 工具类名集合
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static Set<Class<?>> getAllUtils() {
|
||||
|
@ -26,7 +26,7 @@ public class URLEncoder implements Serializable {
|
||||
|
||||
// --------------------------------------------------------------------------------------------- Static method start
|
||||
/**
|
||||
* 默认{@link URLEncoder}<br>
|
||||
* 默认URLEncoder<br>
|
||||
* 默认的编码器针对URI路径编码,定义如下:
|
||||
*
|
||||
* <pre>
|
||||
@ -38,7 +38,7 @@ public class URLEncoder implements Serializable {
|
||||
public static final URLEncoder DEFAULT = createDefault();
|
||||
|
||||
/**
|
||||
* 用于查询语句的{@link URLEncoder}<br>
|
||||
* 用于查询语句的URLEncoder<br>
|
||||
* 编码器针对URI路径编码,定义如下:
|
||||
*
|
||||
* <pre>
|
||||
@ -53,7 +53,7 @@ public class URLEncoder implements Serializable {
|
||||
public static final URLEncoder QUERY = createQuery();
|
||||
|
||||
/**
|
||||
* 全编码的{@link URLEncoder}<br>
|
||||
* 全编码的URLEncoder<br>
|
||||
* <pre>
|
||||
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
|
||||
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' 不编码
|
||||
@ -63,7 +63,7 @@ public class URLEncoder implements Serializable {
|
||||
public static final URLEncoder ALL = createAll();
|
||||
|
||||
/**
|
||||
* 创建默认{@link URLEncoder}<br>
|
||||
* 创建默认URLEncoder<br>
|
||||
* 默认的编码器针对URI路径编码,定义如下:
|
||||
*
|
||||
* <pre>
|
||||
@ -72,7 +72,7 @@ public class URLEncoder implements Serializable {
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
||||
* </pre>
|
||||
*
|
||||
* @return {@link URLEncoder}
|
||||
* @return URLEncoder
|
||||
*/
|
||||
public static URLEncoder createDefault() {
|
||||
final URLEncoder encoder = new URLEncoder();
|
||||
@ -102,7 +102,7 @@ public class URLEncoder implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用于查询语句的{@link URLEncoder}<br>
|
||||
* 创建用于查询语句的URLEncoder<br>
|
||||
* 编码器针对URI路径编码,定义如下:
|
||||
*
|
||||
* <pre>
|
||||
@ -114,7 +114,7 @@ public class URLEncoder implements Serializable {
|
||||
* <p>
|
||||
* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
|
||||
*
|
||||
* @return {@link URLEncoder}
|
||||
* @return URLEncoder
|
||||
*/
|
||||
public static URLEncoder createQuery() {
|
||||
final URLEncoder encoder = new URLEncoder();
|
||||
@ -133,7 +133,7 @@ public class URLEncoder implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link URLEncoder}<br>
|
||||
* 创建URLEncoder<br>
|
||||
* 编码器针对URI路径编码,定义如下:
|
||||
*
|
||||
* <pre>
|
||||
@ -144,7 +144,7 @@ public class URLEncoder implements Serializable {
|
||||
* <p>
|
||||
* 详细见:https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
|
||||
*
|
||||
* @return {@link URLEncoder}
|
||||
* @return URLEncoder
|
||||
*/
|
||||
public static URLEncoder createAll() {
|
||||
final URLEncoder encoder = new URLEncoder();
|
||||
|
@ -12,6 +12,7 @@ import cn.hutool.db.sql.Condition;
|
||||
import cn.hutool.db.sql.Condition.LikeType;
|
||||
import cn.hutool.db.sql.LogicalOperator;
|
||||
import cn.hutool.db.sql.Query;
|
||||
import cn.hutool.db.sql.SqlBuilder;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import cn.hutool.db.sql.SqlUtil;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
@ -660,7 +661,7 @@ public abstract class AbstractDb implements Serializable {
|
||||
* @return 复合条件的结果数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int count(Entity where) throws SQLException {
|
||||
public long count(Entity where) throws SQLException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
@ -670,6 +671,23 @@ public abstract class AbstractDb implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结果的条目数
|
||||
*
|
||||
* @param selectSql 查询SQL语句
|
||||
* @return 复合条件的结果数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public long count(CharSequence selectSql) throws SQLException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
return runner.count(conn, selectSql);
|
||||
} finally {
|
||||
this.closeConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
||||
@ -777,25 +795,59 @@ public abstract class AbstractDb implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param sql SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL
|
||||
* @param page 分页对象
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public <T> T page(CharSequence sql, Page page, RsHandler<T> rsh) throws SQLException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
return runner.page(conn, SqlBuilder.of(sql), page, rsh);
|
||||
} finally {
|
||||
this.closeConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param sql SQL语句字符串
|
||||
* @param page 分页对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public PageResult<Entity> page(CharSequence sql, Page page) throws SQLException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
return runner.page(conn, SqlBuilder.of(sql), page);
|
||||
} finally {
|
||||
this.closeConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 查询条件为多个key value对表示,默认key = value,如果使用其它条件可以使用:where.put("key", " > 1"),value也可以传Condition对象,key被忽略
|
||||
*
|
||||
* @param fields 返回的字段列表,null则返回所有字段
|
||||
* @param where 条件实体类(包含表名)
|
||||
* @param page 分页对象
|
||||
* @param numPerPage 每页条目数
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页结果数
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public PageResult<Entity> page(Collection<String> fields, Entity where, int page, int numPerPage) throws SQLException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
return runner.page(conn, fields, where, page, numPerPage);
|
||||
} finally {
|
||||
this.closeConnection(conn);
|
||||
}
|
||||
public PageResult<Entity> page(Collection<String> fields, Entity where, int pageNumber, int pageSize) throws SQLException {
|
||||
return page(fields, where, new Page(pageNumber, pageSize));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -326,7 +326,7 @@ public class DaoTemplate {
|
||||
* @return 数量
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int count(Entity where) throws SQLException{
|
||||
public long count(Entity where) throws SQLException{
|
||||
return db.count(fixEntity(where));
|
||||
}
|
||||
|
||||
|
299
hutool-db/src/main/java/cn/hutool/db/DialectRunner.java
Normal file
299
hutool-db/src/main/java/cn/hutool/db/DialectRunner.java
Normal file
@ -0,0 +1,299 @@
|
||||
package cn.hutool.db;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.dialect.Dialect;
|
||||
import cn.hutool.db.dialect.DialectFactory;
|
||||
import cn.hutool.db.handler.NumberHandler;
|
||||
import cn.hutool.db.handler.RsHandler;
|
||||
import cn.hutool.db.sql.Query;
|
||||
import cn.hutool.db.sql.SqlBuilder;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import cn.hutool.db.sql.SqlUtil;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class DialectRunner implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Dialect dialect;
|
||||
/**
|
||||
* 是否大小写不敏感(默认大小写不敏感)
|
||||
*/
|
||||
protected boolean caseInsensitive = GlobalDbConfig.caseInsensitive;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param dialect 方言
|
||||
*/
|
||||
public DialectRunner(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param driverClassName 驱动类名,用于识别方言
|
||||
*/
|
||||
public DialectRunner(String driverClassName) {
|
||||
this(DialectFactory.newDialect(driverClassName));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------- CRUD start
|
||||
/**
|
||||
* 批量插入数据<br>
|
||||
* 批量插入必须严格保持Entity的结构一致,不一致会导致插入数据出现不可预知的结果<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param records 记录列表,记录KV必须严格一致
|
||||
* @return 插入行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int[] insert(Connection conn, Entity... records) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (ArrayUtil.isEmpty(records)) {
|
||||
return new int[]{0};
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
if(1 == records.length){
|
||||
//单条单独处理
|
||||
ps = dialect.psForInsert(conn, records[0]);
|
||||
return new int[]{ps.executeUpdate()};
|
||||
}
|
||||
|
||||
// 批量
|
||||
ps = dialect.psForInsertBatch(conn, records);
|
||||
return ps.executeBatch();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param record 记录
|
||||
* @param generatedKeysHandler 自增主键处理器,用于定义返回自增主键的范围和类型
|
||||
* @return 主键列表
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T insert(Connection conn, Entity record, RsHandler<T> generatedKeysHandler) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForInsert(conn, record);
|
||||
ps.executeUpdate();
|
||||
if(null == generatedKeysHandler){
|
||||
return null;
|
||||
}
|
||||
return StatementUtil.getGeneratedKeys(ps, generatedKeysHandler);
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param where 条件
|
||||
* @return 影响行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int del(Connection conn, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(where)) {
|
||||
//不允许做全表删除
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForDelete(conn, Query.of(where));
|
||||
return ps.executeUpdate();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param record 记录
|
||||
* @param where 条件
|
||||
* @return 影响行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int update(Connection conn, Entity record, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
if (CollUtil.isEmpty(where)) {
|
||||
//不允许做全表更新
|
||||
throw new SQLException("Empty where provided!");
|
||||
}
|
||||
|
||||
//表名可以从被更新记录的Entity中获得,也可以从Where中获得
|
||||
String tableName = record.getTableName();
|
||||
if (StrUtil.isBlank(tableName)) {
|
||||
tableName = where.getTableName();
|
||||
record.setTableName(tableName);
|
||||
}
|
||||
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), tableName);
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForUpdate(conn, record, query);
|
||||
return ps.executeUpdate();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param query {@link Query}
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T find(Connection conn, Query query, RsHandler<T> rsh) throws SQLException {
|
||||
checkConn(conn);
|
||||
Assert.notNull(query, "[query] is null !");
|
||||
return SqlExecutor.queryAndClosePs(dialect.psForFind(conn, query), rsh);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结果总数,生成类似于select count(1) from XXX wher XXX=? and YYY=?
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param where 查询条件
|
||||
* @return 复合条件的结果数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public long count(Connection conn, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
return SqlExecutor.queryAndClosePs(dialect.psForCount(conn, Query.of(where)), new NumberHandler()).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param query 查询条件(包含表名)
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T page(Connection conn, Query query, RsHandler<T> rsh) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (null == query.getPage()) {
|
||||
return this.find(conn, query, rsh);
|
||||
}
|
||||
|
||||
return SqlExecutor.queryAndClosePs(dialect.psForPage(conn, query), rsh);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param sqlBuilder SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL
|
||||
* @param page 分页对象
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public <T> T page(Connection conn, SqlBuilder sqlBuilder, Page page, RsHandler<T> rsh) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (null == page) {
|
||||
return SqlExecutor.query(conn, sqlBuilder, rsh);
|
||||
}
|
||||
|
||||
return SqlExecutor.queryAndClosePs(dialect.psForPage(conn, sqlBuilder, page), rsh);
|
||||
}
|
||||
//---------------------------------------------------------------------------- CRUD end
|
||||
|
||||
//---------------------------------------------------------------------------- Getters and Setters start
|
||||
|
||||
/**
|
||||
* 设置是否在结果中忽略大小写<br>
|
||||
* 如果忽略,则在Entity中调用getXXX时,字段值忽略大小写,默认忽略
|
||||
*
|
||||
* @param caseInsensitive 否在结果中忽略大小写
|
||||
* @since 5.2.4
|
||||
*/
|
||||
public void setCaseInsensitive(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SQL方言
|
||||
*/
|
||||
public Dialect getDialect() {
|
||||
return dialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SQL方言
|
||||
*
|
||||
* @param dialect 方言
|
||||
*/
|
||||
public void setDialect(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
|
||||
*
|
||||
* @param wrapperChar 包装字符,字符会在SQL生成时位于表名和字段名两边,null时表示取消包装
|
||||
*/
|
||||
public void setWrapper(Character wrapperChar) {
|
||||
setWrapper(new Wrapper(wrapperChar));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
|
||||
*
|
||||
* @param wrapper 包装器,null表示取消包装
|
||||
*/
|
||||
public void setWrapper(Wrapper wrapper) {
|
||||
this.dialect.setWrapper(wrapper);
|
||||
}
|
||||
//---------------------------------------------------------------------------- Getters and Setters end
|
||||
|
||||
//---------------------------------------------------------------------------- Private method start
|
||||
private void checkConn(Connection conn) {
|
||||
Assert.notNull(conn, "Connection object must be not null!");
|
||||
}
|
||||
//---------------------------------------------------------------------------- Private method start
|
||||
}
|
@ -11,21 +11,39 @@ import java.util.Arrays;
|
||||
* 分页对象
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class Page implements Serializable {
|
||||
private static final long serialVersionUID = 97792549823353462L;
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/** 页码,0表示第一页 */
|
||||
/**
|
||||
* 页码,0表示第一页
|
||||
*/
|
||||
private int pageNumber;
|
||||
/** 每页结果数 */
|
||||
/**
|
||||
* 每页结果数
|
||||
*/
|
||||
private int pageSize;
|
||||
/** 排序 */
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Order[] orders;
|
||||
|
||||
/**
|
||||
* 创建Page对象
|
||||
*
|
||||
* @param pageNumber 页码,0表示第一页
|
||||
* @param pageSize 每页结果数
|
||||
* @return Page
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static Page of(int pageNumber, int pageSize) {
|
||||
return new Page(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- Constructor start
|
||||
|
||||
/**
|
||||
* 构造,默认第0页,每页{@value #DEFAULT_PAGE_SIZE} 条
|
||||
*
|
||||
@ -39,7 +57,7 @@ public class Page implements Serializable {
|
||||
* 构造
|
||||
*
|
||||
* @param pageNumber 页码,0表示第一页
|
||||
* @param pageSize 每页结果数
|
||||
* @param pageSize 每页结果数
|
||||
*/
|
||||
public Page(int pageNumber, int pageSize) {
|
||||
this.pageNumber = Math.max(pageNumber, 0);
|
||||
@ -50,16 +68,17 @@ public class Page implements Serializable {
|
||||
* 构造
|
||||
*
|
||||
* @param pageNumber 页码,0表示第一页
|
||||
* @param pageSize 每页结果数
|
||||
* @param order 排序对象
|
||||
* @param pageSize 每页结果数
|
||||
* @param order 排序对象
|
||||
*/
|
||||
public Page(int pageNumber, int pageSize, Order order) {
|
||||
this(pageNumber, pageSize);
|
||||
this.orders = new Order[] { order };
|
||||
this.orders = new Order[]{order};
|
||||
}
|
||||
// ---------------------------------------------------------- Constructor start
|
||||
|
||||
// ---------------------------------------------------------- Getters and Setters start
|
||||
|
||||
/**
|
||||
* @return 页码,0表示第一页
|
||||
*/
|
||||
|
@ -31,7 +31,7 @@ public class Session extends AbstractDb implements Closeable {
|
||||
/**
|
||||
* 创建默认数据源会话
|
||||
*
|
||||
* @return {@link Session}
|
||||
* @return Session
|
||||
* @since 3.2.3
|
||||
*/
|
||||
public static Session create() {
|
||||
@ -42,7 +42,7 @@ public class Session extends AbstractDb implements Closeable {
|
||||
* 创建会话
|
||||
*
|
||||
* @param group 分组
|
||||
* @return {@link Session}
|
||||
* @return Session
|
||||
* @since 4.0.11
|
||||
*/
|
||||
public static Session create(String group) {
|
||||
@ -53,7 +53,7 @@ public class Session extends AbstractDb implements Closeable {
|
||||
* 创建会话
|
||||
*
|
||||
* @param ds 数据源
|
||||
* @return {@link Session}
|
||||
* @return Session
|
||||
*/
|
||||
public static Session create(DataSource ds) {
|
||||
return new Session(ds);
|
||||
|
@ -1,26 +1,21 @@
|
||||
package cn.hutool.db;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.dialect.Dialect;
|
||||
import cn.hutool.db.dialect.DialectFactory;
|
||||
import cn.hutool.db.handler.EntityListHandler;
|
||||
import cn.hutool.db.handler.HandleHelper;
|
||||
import cn.hutool.db.handler.NumberHandler;
|
||||
import cn.hutool.db.handler.PageResultHandler;
|
||||
import cn.hutool.db.handler.RsHandler;
|
||||
import cn.hutool.db.sql.Condition.LikeType;
|
||||
import cn.hutool.db.sql.Query;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import cn.hutool.db.sql.SqlBuilder;
|
||||
import cn.hutool.db.sql.SqlUtil;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -32,15 +27,9 @@ import java.util.List;
|
||||
*
|
||||
* @author Luxiaolei
|
||||
*/
|
||||
public class SqlConnRunner implements Serializable {
|
||||
public class SqlConnRunner extends DialectRunner {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Dialect dialect;
|
||||
/**
|
||||
* 是否大小写不敏感(默认大小写不敏感)
|
||||
*/
|
||||
protected boolean caseInsensitive = GlobalDbConfig.caseInsensitive;
|
||||
|
||||
/**
|
||||
* 实例化一个新的SQL运行对象
|
||||
*
|
||||
@ -79,7 +68,7 @@ public class SqlConnRunner implements Serializable {
|
||||
* @param dialect 方言
|
||||
*/
|
||||
public SqlConnRunner(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
super(dialect);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,35 +77,12 @@ public class SqlConnRunner implements Serializable {
|
||||
* @param driverClassName 驱动类名,,用于识别方言
|
||||
*/
|
||||
public SqlConnRunner(String driverClassName) {
|
||||
this(DialectFactory.newDialect(driverClassName));
|
||||
super(driverClassName);
|
||||
}
|
||||
//------------------------------------------------------- Constructor end
|
||||
|
||||
//---------------------------------------------------------------------------- CRUD start
|
||||
|
||||
/**
|
||||
* 插入数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param record 记录
|
||||
* @return 插入行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int insert(Connection conn, Entity record) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForInsert(conn, record);
|
||||
return ps.executeUpdate();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入或更新数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
@ -152,33 +118,16 @@ public class SqlConnRunner implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入数据<br>
|
||||
* 批量插入必须严格保持Entity的结构一致,不一致会导致插入数据出现不可预知的结果<br>
|
||||
* 插入数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param records 记录列表,记录KV必须严格一致
|
||||
* @param conn 数据库连接
|
||||
* @param record 记录
|
||||
* @return 插入行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int[] insert(Connection conn, Entity... records) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (ArrayUtil.isEmpty(records)) {
|
||||
return new int[]{0};
|
||||
}
|
||||
|
||||
//单条单独处理
|
||||
if (1 == records.length) {
|
||||
return new int[]{insert(conn, records[0])};
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForInsertBatch(conn, records);
|
||||
return ps.executeBatch();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
public int insert(Connection conn, Entity record) throws SQLException {
|
||||
return insert(conn, new Entity[]{record})[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,19 +140,7 @@ public class SqlConnRunner implements Serializable {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public List<Object> insertForGeneratedKeys(Connection conn, Entity record) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForInsert(conn, record);
|
||||
ps.executeUpdate();
|
||||
return StatementUtil.getGeneratedKeys(ps);
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
return insert(conn, record, HandleHelper::handleRowToList);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,106 +153,17 @@ public class SqlConnRunner implements Serializable {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public Long insertForGeneratedKey(Connection conn, Entity record) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForInsert(conn, record);
|
||||
ps.executeUpdate();
|
||||
return StatementUtil.getGeneratedKeyOfLong(ps);
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param where 条件
|
||||
* @return 影响行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int del(Connection conn, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(where)) {
|
||||
//不允许做全表删除
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), where.getTableName());
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForDelete(conn, query);
|
||||
return ps.executeUpdate();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param record 记录
|
||||
* @param where 条件
|
||||
* @return 影响行数
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public int update(Connection conn, Entity record, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (CollUtil.isEmpty(record)) {
|
||||
throw new SQLException("Empty entity provided!");
|
||||
}
|
||||
if (CollUtil.isEmpty(where)) {
|
||||
//不允许做全表更新
|
||||
throw new SQLException("Empty where provided!");
|
||||
}
|
||||
|
||||
//表名可以从被更新记录的Entity中获得,也可以从Where中获得
|
||||
String tableName = record.getTableName();
|
||||
if (StrUtil.isBlank(tableName)) {
|
||||
tableName = where.getTableName();
|
||||
record.setTableName(tableName);
|
||||
}
|
||||
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), tableName);
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForUpdate(conn, record, query);
|
||||
return ps.executeUpdate();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param query {@link Query}
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T find(Connection conn, Query query, RsHandler<T> rsh) throws SQLException {
|
||||
checkConn(conn);
|
||||
Assert.notNull(query, "[query] is null !");
|
||||
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForFind(conn, query);
|
||||
return SqlExecutor.query(ps, rsh);
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
return insert(conn, record, (rs)->{
|
||||
Long generatedKey = null;
|
||||
if (rs != null && rs.next()) {
|
||||
try {
|
||||
generatedKey = rs.getLong(1);
|
||||
} catch (SQLException e) {
|
||||
// 自增主键不为数字或者为Oracle的rowid,跳过
|
||||
}
|
||||
}
|
||||
return generatedKey;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,9 +179,7 @@ public class SqlConnRunner implements Serializable {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T find(Connection conn, Collection<String> fields, Entity where, RsHandler<T> rsh) throws SQLException {
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), where.getTableName());
|
||||
query.setFields(fields);
|
||||
return find(conn, query, rsh);
|
||||
return find(conn, Query.of(where).setFields(fields), rsh);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -433,24 +279,16 @@ public class SqlConnRunner implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 结果的条目数
|
||||
* 获取查询结果总数,生成类似于 SELECT count(1) from (sql)
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param where 查询条件
|
||||
* @return 复合条件的结果数
|
||||
* @throws SQLException SQL执行异常
|
||||
* @param conn 数据库连接对象
|
||||
* @param selectSql 查询语句
|
||||
* @return 结果数
|
||||
* @throws SQLException SQL异常
|
||||
*/
|
||||
public int count(Connection conn, Entity where) throws SQLException {
|
||||
checkConn(conn);
|
||||
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), where.getTableName());
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = dialect.psForCount(conn, query);
|
||||
return SqlExecutor.query(ps, new NumberHandler()).intValue();
|
||||
} finally {
|
||||
DbUtil.close(ps);
|
||||
}
|
||||
public long count(Connection conn, CharSequence selectSql) throws SQLException {
|
||||
SqlBuilder sqlBuilder = SqlBuilder.of(selectSql).insertPreFragment("SELECT count(1) from(").append(")");
|
||||
return page(conn, sqlBuilder, null, new NumberHandler()).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -468,32 +306,25 @@ public class SqlConnRunner implements Serializable {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T page(Connection conn, Collection<String> fields, Entity where, int pageNumber, int numPerPage, RsHandler<T> rsh) throws SQLException {
|
||||
return page(conn, fields, where, new Page(pageNumber, numPerPage), rsh);
|
||||
return page(conn, Query.of(where).setFields(fields).setPage(new Page(pageNumber, numPerPage)), rsh);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 结果对象类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param fields 返回的字段列表,null则返回所有字段
|
||||
* @param where 条件实体类(包含表名)
|
||||
* @param page 分页对象
|
||||
* @param rsh 结果集处理对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param sqlBuilder SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL
|
||||
* @param page 分页对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public <T> T page(Connection conn, Collection<String> fields, Entity where, Page page, RsHandler<T> rsh) throws SQLException {
|
||||
checkConn(conn);
|
||||
if (null == page) {
|
||||
return this.find(conn, fields, where, rsh);
|
||||
}
|
||||
|
||||
final Query query = new Query(SqlUtil.buildConditions(where), where.getTableName());
|
||||
query.setFields(fields);
|
||||
query.setPage(page);
|
||||
return SqlExecutor.queryAndClosePs(dialect.psForPage(conn, query), rsh);
|
||||
public PageResult<Entity> page(Connection conn, SqlBuilder sqlBuilder, Page page) throws SQLException {
|
||||
final PageResultHandler pageResultHandler = new PageResultHandler(
|
||||
new PageResult<>(page.getPageNumber(), page.getPageSize(), (int)count(conn, sqlBuilder.build())),
|
||||
this.caseInsensitive);
|
||||
return page(conn, sqlBuilder, page, pageResultHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -509,38 +340,7 @@ public class SqlConnRunner implements Serializable {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public PageResult<Entity> page(Connection conn, Collection<String> fields, Entity where, int page, int numPerPage) throws SQLException {
|
||||
checkConn(conn);
|
||||
|
||||
final int count = count(conn, where);
|
||||
final PageResultHandler pageResultHandler = new PageResultHandler(new PageResult<>(page, numPerPage, count), this.caseInsensitive);
|
||||
return this.page(conn, fields, where, page, numPerPage, pageResultHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param fields 返回的字段列表,null则返回所有字段
|
||||
* @param where 条件实体类(包含表名)
|
||||
* @param page 分页对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public PageResult<Entity> page(Connection conn, Collection<String> fields, Entity where, Page page) throws SQLException {
|
||||
checkConn(conn);
|
||||
|
||||
//查询全部
|
||||
if (null == page) {
|
||||
List<Entity> entityList = this.find(conn, fields, where, new EntityListHandler(GlobalDbConfig.caseInsensitive));
|
||||
final PageResult<Entity> pageResult = new PageResult<>(0, entityList.size(), entityList.size());
|
||||
pageResult.addAll(entityList);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
final int count = count(conn, where);
|
||||
PageResultHandler pageResultHandler = new PageResultHandler(new PageResult<>(page.getPageNumber(), page.getPageSize(), count), this.caseInsensitive);
|
||||
return this.page(conn, fields, where, page, pageResultHandler);
|
||||
return page(conn, fields, where, new Page(page, numPerPage));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -556,68 +356,39 @@ public class SqlConnRunner implements Serializable {
|
||||
public PageResult<Entity> page(Connection conn, Entity where, Page page) throws SQLException {
|
||||
return this.page(conn, null, where, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param fields 返回的字段列表,null则返回所有字段
|
||||
* @param where 条件实体类(包含表名)
|
||||
* @param page 分页对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public PageResult<Entity> page(Connection conn, Collection<String> fields, Entity where, Page page) throws SQLException {
|
||||
final PageResultHandler pageResultHandler = new PageResultHandler(
|
||||
new PageResult<>(page.getPageNumber(), page.getPageSize(), (int)count(conn, where)),
|
||||
this.caseInsensitive);
|
||||
return page(conn, fields, where, page, pageResultHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param fields 返回的字段列表,null则返回所有字段
|
||||
* @param where 条件实体类(包含表名)
|
||||
* @param page 分页对象
|
||||
* @param handler 结果集处理器
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public <T> T page(Connection conn, Collection<String> fields, Entity where, Page page, RsHandler<T> handler) throws SQLException {
|
||||
return this.page(conn, Query.of(where).setFields(fields).setPage(page), handler);
|
||||
}
|
||||
//---------------------------------------------------------------------------- CRUD end
|
||||
|
||||
//---------------------------------------------------------------------------- Getters and Setters end
|
||||
|
||||
/**
|
||||
* 设置是否在结果中忽略大小写<br>
|
||||
* 如果忽略,则在Entity中调用getXXX时,字段值忽略大小写,默认忽略
|
||||
*
|
||||
* @param caseInsensitive 否在结果中忽略大小写
|
||||
* @since 5.2.4
|
||||
*/
|
||||
public void setCaseInsensitive(boolean caseInsensitive) {
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SQL方言
|
||||
*/
|
||||
public Dialect getDialect() {
|
||||
return dialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SQL方言
|
||||
*
|
||||
* @param dialect 方言
|
||||
* @return this
|
||||
*/
|
||||
public SqlConnRunner setDialect(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
|
||||
*
|
||||
* @param wrapperChar 包装字符,字符会在SQL生成时位于表名和字段名两边,null时表示取消包装
|
||||
* @return this
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public SqlConnRunner setWrapper(Character wrapperChar) {
|
||||
return setWrapper(new Wrapper(wrapperChar));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突
|
||||
*
|
||||
* @param wrapper 包装器,null表示取消包装
|
||||
* @return this
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public SqlConnRunner setWrapper(Wrapper wrapper) {
|
||||
this.dialect.setWrapper(wrapper);
|
||||
return this;
|
||||
}
|
||||
//---------------------------------------------------------------------------- Getters and Setters end
|
||||
|
||||
//---------------------------------------------------------------------------- Private method start
|
||||
private void checkConn(Connection conn) {
|
||||
if (null == conn) {
|
||||
throw new NullPointerException("Connection object is null!");
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------- Private method start
|
||||
}
|
@ -6,6 +6,8 @@ import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.handler.HandleHelper;
|
||||
import cn.hutool.db.handler.RsHandler;
|
||||
import cn.hutool.db.sql.NamedSql;
|
||||
import cn.hutool.db.sql.SqlBuilder;
|
||||
import cn.hutool.db.sql.SqlLog;
|
||||
@ -21,7 +23,6 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -230,14 +231,14 @@ public class StatementUtil {
|
||||
|
||||
/**
|
||||
* 获得自增键的值<br>
|
||||
* 此方法对于Oracle无效
|
||||
* 此方法对于Oracle无效(返回null)
|
||||
*
|
||||
* @param ps PreparedStatement
|
||||
* @return 自增键的值
|
||||
* @return 自增键的值,不存在返回null
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public static Long getGeneratedKeyOfLong(Statement ps) throws SQLException {
|
||||
try (final ResultSet rs = ps.getGeneratedKeys()) {
|
||||
return getGeneratedKeys(ps, (rs)->{
|
||||
Long generatedKey = null;
|
||||
if (rs != null && rs.next()) {
|
||||
try {
|
||||
@ -247,7 +248,7 @@ public class StatementUtil {
|
||||
}
|
||||
}
|
||||
return generatedKey;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,15 +259,21 @@ public class StatementUtil {
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
public static List<Object> getGeneratedKeys(Statement ps) throws SQLException {
|
||||
final List<Object> keys = new ArrayList<>();
|
||||
try (final ResultSet rs = ps.getGeneratedKeys()) {
|
||||
if (null != rs) {
|
||||
int i = 1;
|
||||
while (rs.next()) {
|
||||
keys.add(rs.getObject(i++));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
return getGeneratedKeys(ps, HandleHelper::handleRowToList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键,并使用{@link RsHandler} 处理后返回
|
||||
* @param statement {@link Statement}
|
||||
* @param rsHandler 主键结果集处理器
|
||||
* @param <T> 自定义主键类型
|
||||
* @return 主键
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static <T> T getGeneratedKeys(Statement statement, RsHandler<T> rsHandler) throws SQLException {
|
||||
try (final ResultSet rs = statement.getGeneratedKeys()) {
|
||||
return rsHandler.handle(rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,26 @@
|
||||
package cn.hutool.db.dialect;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.Page;
|
||||
import cn.hutool.db.sql.Order;
|
||||
import cn.hutool.db.sql.Query;
|
||||
import cn.hutool.db.sql.SqlBuilder;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.sql.Query;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
|
||||
/**
|
||||
* SQL方言,不同的数据库由于在某些SQL上有所区别,故为每种数据库配置不同的方言。<br>
|
||||
* 由于不同数据库间SQL语句的差异,导致无法统一拼接SQL,<br>
|
||||
* Dialect接口旨在根据不同的数据库,使用不同的方言实现类,来拼接对应的SQL,并将SQL和参数放入PreparedStatement中
|
||||
*
|
||||
* @author loolly
|
||||
*
|
||||
*/
|
||||
public interface Dialect extends Serializable{
|
||||
public interface Dialect extends Serializable {
|
||||
|
||||
/**
|
||||
* @return 包装器
|
||||
@ -32,10 +35,11 @@ public interface Dialect extends Serializable{
|
||||
void setWrapper(Wrapper wrapper);
|
||||
|
||||
// -------------------------------------------- Execute
|
||||
|
||||
/**
|
||||
* 构建用于插入的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param entity 数据实体类(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
@ -45,7 +49,7 @@ public interface Dialect extends Serializable{
|
||||
/**
|
||||
* 构建用于批量插入的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param entities 数据实体,实体的结构必须全部一致,否则插入结果将不可预知
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
@ -55,7 +59,7 @@ public interface Dialect extends Serializable{
|
||||
/**
|
||||
* 构建用于删除的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param query 查找条件(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
@ -65,19 +69,20 @@ public interface Dialect extends Serializable{
|
||||
/**
|
||||
* 构建用于更新的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param entity 数据实体类(包含表名)
|
||||
* @param query 查找条件(包含表名)
|
||||
* @param query 查找条件(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
PreparedStatement psForUpdate(Connection conn, Entity entity, Query query) throws SQLException;
|
||||
|
||||
// -------------------------------------------- Query
|
||||
|
||||
/**
|
||||
* 构建用于获取多条记录的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param query 查询条件(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
@ -87,27 +92,45 @@ public interface Dialect extends Serializable{
|
||||
/**
|
||||
* 构建用于分页查询的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param query 查询条件(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
PreparedStatement psForPage(Connection conn, Query query) throws SQLException;
|
||||
|
||||
/**
|
||||
* 构建用于分页查询的PreparedStatement<br>
|
||||
* 可以在此方法中使用{@link SqlBuilder#orderBy(Order...)}方法加入排序信息,
|
||||
* 排序信息通过{@link Page#getOrders()}获取
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param sqlBuilder SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL
|
||||
* @param page 分页对象
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
PreparedStatement psForPage(Connection conn, SqlBuilder sqlBuilder, Page page) throws SQLException;
|
||||
|
||||
/**
|
||||
* 构建用于查询行数的PreparedStatement
|
||||
*
|
||||
* @param conn 数据库连接对象
|
||||
* @param conn 数据库连接对象
|
||||
* @param query 查询条件(包含表名)
|
||||
* @return PreparedStatement
|
||||
* @throws SQLException SQL执行异常
|
||||
*/
|
||||
PreparedStatement psForCount(Connection conn, Query query) throws SQLException;
|
||||
default PreparedStatement psForCount(Connection conn, Query query) throws SQLException{
|
||||
query.setFields(ListUtil.toList("count(1)"));
|
||||
return psForFind(conn, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方言名
|
||||
*
|
||||
* @return 方言名
|
||||
* @since 5.5.3
|
||||
*/
|
||||
DialectName dialectName();
|
||||
String dialectName();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.hutool.db.dialect.impl;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@ -59,7 +58,7 @@ public class AnsiSqlDialect implements Dialect {
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForDelete(Connection conn, Query query) throws SQLException {
|
||||
Assert.notNull(query, "query must not be null !");
|
||||
Assert.notNull(query, "query must be not null !");
|
||||
|
||||
final Condition[] where = query.getWhere();
|
||||
if (ArrayUtil.isEmpty(where)) {
|
||||
@ -73,7 +72,7 @@ public class AnsiSqlDialect implements Dialect {
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForUpdate(Connection conn, Entity entity, Query query) throws SQLException {
|
||||
Assert.notNull(query, "query must not be null !");
|
||||
Assert.notNull(query, "query must be not null !");
|
||||
|
||||
final Condition[] where = query.getWhere();
|
||||
if (ArrayUtil.isEmpty(where)) {
|
||||
@ -88,32 +87,27 @@ public class AnsiSqlDialect implements Dialect {
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForFind(Connection conn, Query query) throws SQLException {
|
||||
Assert.notNull(query, "query must not be null !");
|
||||
|
||||
final SqlBuilder find = SqlBuilder.create(wrapper).query(query);
|
||||
|
||||
return StatementUtil.prepareStatement(conn, find);
|
||||
return psForPage(conn, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForPage(Connection conn, Query query) throws SQLException {
|
||||
// 验证
|
||||
if (query == null || StrUtil.hasBlank(query.getTableNames())) {
|
||||
throw new DbRuntimeException("Table name must not be null !");
|
||||
Assert.notNull(query, "query must be not null !");
|
||||
if (StrUtil.hasBlank(query.getTableNames())) {
|
||||
throw new DbRuntimeException("Table name must be not empty !");
|
||||
}
|
||||
|
||||
final Page page = query.getPage();
|
||||
if (null == page) {
|
||||
// 无分页信息默认使用find
|
||||
return this.psForFind(conn, query);
|
||||
}
|
||||
|
||||
SqlBuilder find = SqlBuilder.create(wrapper).query(query).orderBy(page.getOrders());
|
||||
final SqlBuilder find = SqlBuilder.create(wrapper).query(query);
|
||||
return psForPage(conn, find, query.getPage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForPage(Connection conn, SqlBuilder sqlBuilder, Page page) throws SQLException {
|
||||
// 根据不同数据库在查询SQL语句基础上包装其分页的语句
|
||||
find = wrapPageSql(find, page);
|
||||
|
||||
return StatementUtil.prepareStatement(conn, find);
|
||||
if(null != page){
|
||||
sqlBuilder = wrapPageSql(sqlBuilder.orderBy(page.getOrders()), page);
|
||||
}
|
||||
return StatementUtil.prepareStatement(conn, sqlBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,18 +121,16 @@ public class AnsiSqlDialect implements Dialect {
|
||||
*/
|
||||
protected SqlBuilder wrapPageSql(SqlBuilder find, Page page) {
|
||||
// limit A offset B 表示:A就是你需要多少行,B就是查询的起点位置。
|
||||
return find.append(" limit ").append(page.getPageSize()).append(" offset ").append(page.getStartPosition());
|
||||
return find
|
||||
.append(" limit ")
|
||||
.append(page.getPageSize())
|
||||
.append(" offset ")
|
||||
.append(page.getStartPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement psForCount(Connection conn, Query query) throws SQLException {
|
||||
query.setFields(ListUtil.toList("count(1)"));
|
||||
return psForFind(conn, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.ANSI;
|
||||
public String dialectName() {
|
||||
return DialectName.ANSI.name();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------- Protected method start
|
||||
|
@ -17,8 +17,8 @@ public class H2Dialect extends AnsiSqlDialect {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.H2;
|
||||
public String dialectName() {
|
||||
return DialectName.H2.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,7 +23,7 @@ public class MysqlDialect extends AnsiSqlDialect{
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.MYSQL;
|
||||
public String dialectName() {
|
||||
return DialectName.MYSQL.toString();
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ public class OracleDialect extends AnsiSqlDialect{
|
||||
private static final long serialVersionUID = 6122761762247483015L;
|
||||
|
||||
public OracleDialect() {
|
||||
// wrapper = new Wrapper('"'); //Oracle所有字段名用双引号包围,防止字段名或表名与系统关键字冲突
|
||||
//Oracle所有字段名用双引号包围,防止字段名或表名与系统关键字冲突
|
||||
//wrapper = new Wrapper('"');
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -27,7 +28,7 @@ public class OracleDialect extends AnsiSqlDialect{
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.ORACLE;
|
||||
public String dialectName() {
|
||||
return DialectName.ORACLE.name();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public class PostgresqlDialect extends AnsiSqlDialect{
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.POSTGREESQL;
|
||||
public String dialectName() {
|
||||
return DialectName.POSTGREESQL.name();
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class SqlServer2012Dialect extends AnsiSqlDialect {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.SQLSERVER2012;
|
||||
public String dialectName() {
|
||||
return DialectName.SQLSERVER2012.name();
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public class Sqlite3Dialect extends AnsiSqlDialect{
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialectName dialectName() {
|
||||
return DialectName.SQLITE3;
|
||||
public String dialectName() {
|
||||
return DialectName.SQLITE3.name();
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,6 @@ public class NumberHandler implements RsHandler<Number>{
|
||||
|
||||
@Override
|
||||
public Number handle(ResultSet rs) throws SQLException {
|
||||
return rs.next() ? rs.getBigDecimal(1) : null;
|
||||
return (null != rs && rs.next()) ? rs.getBigDecimal(1) : null;
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
package cn.hutool.db.sql;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.db.DbRuntimeException;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.Page;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 查询对象,用于传递查询所需的字段值<br>
|
||||
* 查询对象根据表名(可以多个),多个条件 {@link Condition} 构建查询对象完成查询。<br>
|
||||
@ -26,6 +27,16 @@ public class Query {
|
||||
/** 分页对象 */
|
||||
Page page;
|
||||
|
||||
/**
|
||||
* 从{@link Entity}构建Query
|
||||
* @param where 条件查询{@link Entity},包含条件Map和表名
|
||||
* @return Query
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static Query of(Entity where){
|
||||
return new Query(SqlUtil.buildConditions(where), where.getTableName());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Constructor start
|
||||
/**
|
||||
* 构造
|
||||
@ -147,9 +158,9 @@ public class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得分页对象
|
||||
* 获得分页对象,无分页返回{@code null}
|
||||
*
|
||||
* @return 分页对象
|
||||
* @return 分页对象 or {@code null}
|
||||
*/
|
||||
public Page getPage() {
|
||||
return page;
|
||||
|
@ -3,7 +3,6 @@ package cn.hutool.db.sql;
|
||||
import cn.hutool.core.builder.Builder;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.DbRuntimeException;
|
||||
import cn.hutool.db.Entity;
|
||||
@ -12,6 +11,7 @@ import cn.hutool.db.dialect.DialectName;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@ -46,6 +46,16 @@ public class SqlBuilder implements Builder<String>{
|
||||
return new SqlBuilder(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从已有的SQL中构建一个SqlBuilder
|
||||
* @param sql SQL语句
|
||||
* @return SqlBuilder
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static SqlBuilder of(CharSequence sql){
|
||||
return create().append(sql);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Static methods end
|
||||
|
||||
// --------------------------------------------------------------- Enums start
|
||||
@ -105,6 +115,19 @@ public class SqlBuilder implements Builder<String>{
|
||||
* @return 自己
|
||||
*/
|
||||
public SqlBuilder insert(Entity entity, DialectName dialectName) {
|
||||
return insert(entity, dialectName.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入<br>
|
||||
* 插入会忽略空的字段名及其对应值,但是对于有字段名对应值为{@code null}的情况不忽略
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param dialectName 方言名,用于对特殊数据库特殊处理
|
||||
* @return 自己
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public SqlBuilder insert(Entity entity, String dialectName) {
|
||||
// 验证
|
||||
validateEntity(entity);
|
||||
|
||||
@ -114,7 +137,7 @@ public class SqlBuilder implements Builder<String>{
|
||||
entity.setTableName(wrapper.wrap(entity.getTableName()));
|
||||
}
|
||||
|
||||
final boolean isOracle = ObjectUtil.equal(dialectName, DialectName.ORACLE);// 对Oracle的特殊处理
|
||||
final boolean isOracle = StrUtil.equalsAnyIgnoreCase(dialectName, DialectName.ORACLE.name());// 对Oracle的特殊处理
|
||||
final StringBuilder fieldsPart = new StringBuilder();
|
||||
final StringBuilder placeHolder = new StringBuilder();
|
||||
|
||||
@ -519,7 +542,14 @@ public class SqlBuilder implements Builder<String>{
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加SQL其它部分片段
|
||||
* 追加SQL其它部分片段,此方法只是简单的追加SQL字符串,空格需手动加入,例如:
|
||||
*
|
||||
* <pre>
|
||||
* SqlBuilder builder = SqlBuilder.of("select *");
|
||||
* builder.append(" from ").append("user");
|
||||
* </pre>
|
||||
*
|
||||
* 如果需要追加带占位符的片段,需调用{@link #addParams(Object...)} 方法加入对应参数值。
|
||||
*
|
||||
* @param sqlFragment SQL其它部分片段
|
||||
* @return this
|
||||
@ -531,6 +561,26 @@ public class SqlBuilder implements Builder<String>{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动增加参数,调用此方法前需确认SQL中有对应占位符,主要用于自定义SQL片段中有占位符的情况,例如:
|
||||
*
|
||||
* <pre>
|
||||
* SqlBuilder builder = SqlBuilder.of("select * from user where id=?");
|
||||
* builder.append(" and name=?")
|
||||
* builder.addParams(1, "looly");
|
||||
* </pre>
|
||||
*
|
||||
* @param params 参数列表
|
||||
* @return this
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public SqlBuilder addParams(Object... params){
|
||||
if(ArrayUtil.isNotEmpty(params)){
|
||||
Collections.addAll(this.paramValues, params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询SQL
|
||||
*
|
||||
|
@ -259,6 +259,22 @@ public class SqlExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询语句<br>
|
||||
* 此方法不会关闭Connection
|
||||
*
|
||||
* @param <T> 处理结果类型
|
||||
* @param conn 数据库连接对象
|
||||
* @param sqlBuilder SQL构建器,包含参数
|
||||
* @param rsh 结果集处理对象
|
||||
* @return 结果对象
|
||||
* @throws SQLException SQL执行异常
|
||||
* @since 5.5.3
|
||||
*/
|
||||
public static <T> T query(Connection conn, SqlBuilder sqlBuilder, RsHandler<T> rsh) throws SQLException {
|
||||
return query(conn, sqlBuilder.build(), rsh, sqlBuilder.getParamValueArray());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------- Execute With PreparedStatement
|
||||
/**
|
||||
* 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。<br>
|
||||
|
@ -4,6 +4,7 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* SQL格式化器 from Hibernate
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class SqlFormatter {
|
||||
@ -64,7 +65,7 @@ public class SqlFormatter {
|
||||
boolean beginLine = true;
|
||||
boolean afterBeginBeforeEnd = false;
|
||||
boolean afterByOrSetOrFromOrSelect = false;
|
||||
// boolean afterValues = false;
|
||||
// boolean afterValues = false;
|
||||
boolean afterOn = false;
|
||||
boolean afterBetween = false;
|
||||
boolean afterInsert = false;
|
||||
|
@ -37,6 +37,25 @@ public class DbTest {
|
||||
Assert.assertEquals(1, page1.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pageTest2() throws SQLException {
|
||||
String sql = "select * from user";
|
||||
// 测试数据库中一共4条数据,第0页有3条,第1页有1条
|
||||
List<Entity> page0 = Db.use().page(
|
||||
sql, Page.of(0, 3));
|
||||
Assert.assertEquals(3, page0.size());
|
||||
|
||||
List<Entity> page1 = Db.use().page(
|
||||
sql, Page.of(1, 3));
|
||||
Assert.assertEquals(1, page1.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countTest() throws SQLException {
|
||||
final long count = Db.use().count("select * from user");
|
||||
Assert.assertEquals(4, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findLikeTest() throws SQLException {
|
||||
// 方式1
|
||||
|
Loading…
x
Reference in New Issue
Block a user