Merge remote-tracking branch 'upstream/v5-dev' into v5-dev

This commit is contained in:
lzpeng723 2020-12-07 12:57:42 +08:00
commit 169d7b8307
24 changed files with 714 additions and 451 deletions

View File

@ -3,12 +3,13 @@
-------------------------------------------------------------------------------------------------------------
# 5.5.3 (2020-12-05)
# 5.5.3 (2020-12-06)
### 新特性
* 【core 】 IdcardUtil增加行政区划83issue#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

View File

@ -45,6 +45,7 @@ public class Hutool {
/**
* 显示Hutool所有的工具类
*
* @return 工具类名集合
* @since 5.5.2
*/
public static Set<Class<?>> getAllUtils() {

View File

@ -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 = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </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();

View File

@ -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", " &gt; 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", " &gt; 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));
}
/**

View File

@ -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));
}

View 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
}

View File

@ -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表示第一页
*/

View File

@ -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);

View File

@ -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
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -17,8 +17,8 @@ public class H2Dialect extends AnsiSqlDialect {
}
@Override
public DialectName dialectName() {
return DialectName.H2;
public String dialectName() {
return DialectName.H2.name();
}
@Override

View File

@ -23,7 +23,7 @@ public class MysqlDialect extends AnsiSqlDialect{
}
@Override
public DialectName dialectName() {
return DialectName.MYSQL;
public String dialectName() {
return DialectName.MYSQL.toString();
}
}

View File

@ -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();
}
}

View File

@ -17,7 +17,7 @@ public class PostgresqlDialect extends AnsiSqlDialect{
}
@Override
public DialectName dialectName() {
return DialectName.POSTGREESQL;
public String dialectName() {
return DialectName.POSTGREESQL.name();
}
}

View File

@ -34,7 +34,7 @@ public class SqlServer2012Dialect extends AnsiSqlDialect {
}
@Override
public DialectName dialectName() {
return DialectName.SQLSERVER2012;
public String dialectName() {
return DialectName.SQLSERVER2012.name();
}
}

View File

@ -16,7 +16,7 @@ public class Sqlite3Dialect extends AnsiSqlDialect{
}
@Override
public DialectName dialectName() {
return DialectName.SQLITE3;
public String dialectName() {
return DialectName.SQLITE3.name();
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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
*

View File

@ -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
/**
* 用于执行 INSERTUPDATE DELETE 语句以及 SQL DDL数据定义语言语句例如 CREATE TABLE DROP TABLE<br>

View File

@ -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;

View File

@ -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