All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dromara.hutool.db.DialectRunner Maven / Gradle / Ivy

There is a newer version: 6.0.0.M3
Show newest version
/*
 * Copyright (c) 2013-2024 Hutool Team and hutool.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dromara.hutool.db;

import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.regex.PatternPool;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.config.DbConfig;
import org.dromara.hutool.db.dialect.Dialect;
import org.dromara.hutool.db.handler.NumberHandler;
import org.dromara.hutool.db.handler.PageResultHandler;
import org.dromara.hutool.db.handler.RsHandler;
import org.dromara.hutool.db.sql.*;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 提供基于方言的原始增删改查执行封装
 *
 * @author looly
 * @since 5.5.3
 */
public class DialectRunner implements Serializable {
	private static final long serialVersionUID = 1L;

	private final DbConfig config;
	private final Dialect dialect;

	/**
	 * 构造
	 *
	 * @param config  数据库配置
	 * @param dialect 方言
	 */
	public DialectRunner(final DbConfig config, final Dialect dialect) {
		this.config = config;
		this.dialect = dialect;
	}
	//---------------------------------------------------------------------------- CRUD start

	/**
	 * 批量插入数据
* 批量插入必须严格保持Entity的结构一致,不一致会导致插入数据出现不可预知的结果
* 此方法不会关闭Connection * * @param conn 数据库连接 * @param records 记录列表,记录KV必须严格一致 * @return 插入行数 * @throws DbException SQL执行异常 */ public int[] insert(final Connection conn, final Entity... records) throws DbException { checkConn(conn); if (ArrayUtil.isEmpty(records)) { return new int[]{0}; } PreparedStatement ps = null; try { if (1 == records.length) { //单条单独处理 ps = dialect.psForInsert(false, conn, records[0]); return new int[]{ps.executeUpdate()}; } // 批量 ps = dialect.psForInsertBatch(conn, records); return ps.executeBatch(); } catch (final SQLException e) { throw new DbException(e); } finally { IoUtil.closeQuietly(ps); } } /** * 更新或插入数据
* 此方法不会关闭Connection * 如果方言未实现此方法则内部自动使用insertOrUpdate来替代功能 * * @param conn 数据库连接 * @param record 记录 * @param keys 需要检查唯一性的字段 * @return 插入行数 * @throws DbException SQL执行异常 * @since 5.7.20 */ public int upsert(final Connection conn, final Entity record, final String... keys) throws DbException { PreparedStatement ps = this.dialect.psForUpsert(conn, record, keys); try { ps = this.dialect.psForUpsert(conn, record, keys); } catch (final DbException ignore) { // 方言不支持,使用默认 } if (null != ps) { try { return ps.executeUpdate(); } catch (final SQLException e) { throw new DbException(e); } finally { IoUtil.closeQuietly(ps); } } else { return insertOrUpdate(conn, record, keys); } } /** * 插入或更新数据
* 此方法不会关闭Connection * * @param conn 数据库连接 * @param record 记录 * @param keys 需要检查唯一性的字段 * @return 插入行数 * @throws DbException SQL执行异常 */ public int insertOrUpdate(final Connection conn, final Entity record, final String... keys) throws DbException { final Entity where = record.filterNew(keys); if (MapUtil.isNotEmpty(where) && count(conn, Query.of(where)) > 0) { return update(conn, record.removeNew(keys), where); } else { return insert(conn, record)[0]; } } /** * 插入数据
* 此方法不会关闭Connection * * @param 主键类型,可能为数字或对象列表 * @param conn 数据库连接 * @param record 记录 * @param generatedKeysHandler 自增主键处理器,用于定义返回自增主键的范围和类型 * @return 主键列表 * @throws DbException SQL执行异常 */ public T insert(final Connection conn, final Entity record, final RsHandler generatedKeysHandler) throws DbException { checkConn(conn); if (MapUtil.isEmpty(record)) { throw new DbException("Empty entity provided!"); } PreparedStatement ps = null; try { ps = dialect.psForInsert(true, conn, record); ps.executeUpdate(); if (null == generatedKeysHandler) { return null; } return StatementUtil.getGeneratedKeys(ps, generatedKeysHandler); } catch (final SQLException e) { throw new DbException(e); } finally { IoUtil.closeQuietly(ps); } } /** * 删除数据
* 此方法不会关闭Connection * * @param conn 数据库连接 * @param where 条件 * @return 影响行数 * @throws DbException SQL执行异常 */ public int del(final Connection conn, final Entity where) throws DbException { checkConn(conn); if (MapUtil.isEmpty(where)) { //不允许做全表删除 throw new DbException("Empty entity provided!"); } PreparedStatement ps = null; try { ps = dialect.psForDelete(conn, Query.of(where)); return ps.executeUpdate(); } catch (final SQLException e) { throw new DbException(e); } finally { IoUtil.closeQuietly(ps); } } /** * 更新数据
* 此方法不会关闭Connection * * @param conn 数据库连接 * @param record 记录 * @param where 条件 * @return 影响行数 * @throws DbException SQL执行异常 */ public int update(final Connection conn, final Entity record, final Entity where) throws DbException { checkConn(conn); if (MapUtil.isEmpty(record)) { throw new DbException("Empty entity provided!"); } if (MapUtil.isEmpty(where)) { //不允许做全表更新 throw new DbException("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(); } catch (final SQLException e) { throw new DbException(e); } finally { IoUtil.closeQuietly(ps); } } /** * 查询
* 此方法不会关闭Connection * * @param 结果对象类型 * @param conn 数据库连接对象 * @param query {@link Query} * @param rsh 结果集处理对象 * @return 结果对象 * @throws DbException SQL执行异常 */ public T find(final Connection conn, final Query query, final RsHandler rsh) throws DbException { checkConn(conn); Assert.notNull(query, "[query] is null !"); return StatementUtil.executeQuery(dialect.psForFind(conn, query), rsh); } /** * 获取结果总数,生成类似于select count(1) from XXX wher XXX=? and YYY=? * * @param conn 数据库连接对象 * @param query 查询 * @return 复合条件的结果数 * @throws DbException SQL执行异常 */ public long count(final Connection conn, final Query query) throws DbException { checkConn(conn); return StatementUtil.executeQuery(dialect.psForCount(conn, query), NumberHandler.INSTANCE).longValue(); } /** * 获取查询结果总数,生成类似于 SELECT count(1) from (sql) hutool_alias_count_
* 此方法会重新构建{@link SqlBuilder},并去除末尾的order by子句 * * @param conn 数据库连接对象 * @param sqlBuilder 查询语句 * @return 复合条件的结果数 * @throws DbException SQL执行异常 * @since 5.7.2 */ public long count(final Connection conn, final SqlBuilder sqlBuilder) throws DbException { checkConn(conn); String selectSql = sqlBuilder.build(); // 去除order by 子句 final Pattern pattern = PatternPool.get("(.*?)[\\s]order[\\s]by[\\s][^\\s]+\\s(asc|desc)?", Pattern.CASE_INSENSITIVE); final Matcher matcher = pattern.matcher(selectSql); if (matcher.matches()) { selectSql = matcher.group(1); } return StatementUtil.executeQuery(dialect.psForCount(conn, SqlBuilder.of(selectSql).addParams(sqlBuilder.getParamValueArray())), NumberHandler.INSTANCE).longValue(); } /** * 分页查询
* 此方法不会关闭Connection * * @param conn 数据库连接对象 * @param query 查询 * @return 结果对象 * @throws DbException SQL执行异常 */ public PageResult page(final Connection conn, final Query query) throws DbException { final Page page = query.getPage(); final PageResultHandler entityResultHandler = PageResultHandler.of( // 分页查询中总数的查询要去掉分页信息 new PageResult<>(page, (int) count(conn, query.clone().setPage(null)))); return page(conn, query, entityResultHandler.setCaseInsensitive(this.config.isCaseInsensitive())); } /** * 分页查询
* 此方法不会关闭Connection * * @param 结果对象类型 * @param conn 数据库连接对象 * @param query 查询条件(包含表名) * @param rsh 结果集处理对象 * @return 结果对象 * @throws DbException SQL执行异常 */ public T page(final Connection conn, final Query query, final RsHandler rsh) throws DbException { checkConn(conn); if (null == query.getPage()) { return this.find(conn, query, rsh); } return StatementUtil.executeQuery(dialect.psForPage(conn, query), rsh); } /** * 分页查询
* 此方法不会关闭Connection * * @param conn 数据库连接对象 * @param sqlBuilder SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL * @param page 分页对象 * @return 结果对象 * @throws DbException SQL执行异常 */ public PageResult page(final Connection conn, final SqlBuilder sqlBuilder, final Page page) throws DbException { final PageResultHandler entityResultHandler = PageResultHandler.of( new PageResult<>(page, (int) count(conn, sqlBuilder))); return page(conn, sqlBuilder, page, entityResultHandler.setCaseInsensitive(this.config.isCaseInsensitive())); } /** * 分页查询
* 此方法不会关闭Connection * * @param 结果对象类型 * @param conn 数据库连接对象 * @param sqlBuilder SQL构建器,可以使用{@link SqlBuilder#of(CharSequence)} 包装普通SQL * @param page 分页对象 * @param rsh 结果集处理对象 * @return 结果对象 * @throws DbException SQL执行异常 * @since 5.5.3 */ public T page(final Connection conn, final SqlBuilder sqlBuilder, final Page page, final RsHandler rsh) throws DbException { checkConn(conn); return StatementUtil.executeQuery(dialect.psForPage(conn, sqlBuilder, page), rsh); } //---------------------------------------------------------------------------- CRUD end /** * 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突 * * @param wrapperChar 包装字符,字符会在SQL生成时位于表名和字段名两边,null时表示取消包装 */ public void setWrapper(final Character wrapperChar) { setWrapper(new QuoteWrapper(wrapperChar)); } /** * 设置包装器,包装器用于对表名、字段名进行符号包装(例如双引号),防止关键字与这些表名或字段冲突 * * @param quoteWrapper 包装器,null表示取消包装 */ public void setWrapper(final QuoteWrapper quoteWrapper) { this.dialect.setWrapper(quoteWrapper); } /** * 检查{@link Connection} 可用性 * @param conn 数据库连接 */ private void checkConn(final Connection conn) { Assert.notNull(conn, "Connection object must be not null!"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy