|
|
|
@ -1,904 +0,0 @@
|
|
|
|
|
/**
|
|
|
|
|
* Copyright (c) 2011-2025, James Zhan 詹波 (jfinal@126.com).
|
|
|
|
|
*
|
|
|
|
|
* 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 com.jfinal.plugin.activerecord;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.math.BigInteger;
|
|
|
|
|
import java.sql.Connection;
|
|
|
|
|
import java.sql.SQLException;
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.concurrent.Future;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Random;
|
|
|
|
|
import com.jfinal.kit.SyncWriteMap;
|
|
|
|
|
import com.dsideal.Config.PropKit;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Db. Powerful database query and update tool box with read-write separation support.
|
|
|
|
|
* 支持读写分离的数据库操作工具类
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
|
|
|
public class Db {
|
|
|
|
|
|
|
|
|
|
private static DbPro MAIN = null;
|
|
|
|
|
private static final Map<String, DbPro> cache = new SyncWriteMap<>(32, 0.25F);
|
|
|
|
|
private static final Map<String, DbPro> slaveCache = new SyncWriteMap<>(32, 0.25F);
|
|
|
|
|
private static final Random random = new Random();
|
|
|
|
|
private static List<String> slaveConfigs = new ArrayList<>();
|
|
|
|
|
private static boolean readWriteSeparationEnabled = false;
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
initReadWriteSeparation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化读写分离配置
|
|
|
|
|
* Initialize read-write separation configuration
|
|
|
|
|
*/
|
|
|
|
|
private static void initReadWriteSeparation() {
|
|
|
|
|
try {
|
|
|
|
|
// 检查是否存在slave配置
|
|
|
|
|
int slaveIndex = 1;
|
|
|
|
|
while (true) {
|
|
|
|
|
try {
|
|
|
|
|
String slaveKey = "mysql.slave" + slaveIndex;
|
|
|
|
|
String slaveUrl = PropKit.get(slaveKey + ".jdbcUrl");
|
|
|
|
|
if (slaveUrl != null && !slaveUrl.trim().isEmpty()) {
|
|
|
|
|
slaveConfigs.add(slaveKey);
|
|
|
|
|
slaveIndex++;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 没有更多slave配置
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!slaveConfigs.isEmpty()) {
|
|
|
|
|
readWriteSeparationEnabled = true;
|
|
|
|
|
System.out.println("[Db] 读写分离已启用,发现 " + slaveConfigs.size() + " 个从库配置");
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("[Db] 未发现从库配置,使用单库模式");
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.out.println("[Db] 读写分离配置检查失败,使用单库模式: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取随机的从库DbPro实例
|
|
|
|
|
* Get a random slave DbPro instance
|
|
|
|
|
*/
|
|
|
|
|
private static DbPro getRandomSlaveDbPro() {
|
|
|
|
|
if (!readWriteSeparationEnabled || slaveConfigs.isEmpty()) {
|
|
|
|
|
return MAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String randomSlaveConfig = slaveConfigs.get(random.nextInt(slaveConfigs.size()));
|
|
|
|
|
DbPro slaveDbPro = slaveCache.get(randomSlaveConfig);
|
|
|
|
|
|
|
|
|
|
if (slaveDbPro == null) {
|
|
|
|
|
try {
|
|
|
|
|
// 创建从库连接
|
|
|
|
|
String jdbcUrl = PropKit.get(randomSlaveConfig + ".jdbcUrl");
|
|
|
|
|
String user = PropKit.get(randomSlaveConfig + ".user", PropKit.get("mysql.user"));
|
|
|
|
|
String password = PropKit.get(randomSlaveConfig + ".password", PropKit.get("mysql.password"));
|
|
|
|
|
String driverClassName = PropKit.get(randomSlaveConfig + ".driverClassName", PropKit.get("mysql.driverClassName"));
|
|
|
|
|
|
|
|
|
|
// 这里需要根据实际的DbPro创建方式来实现
|
|
|
|
|
// 由于无法直接创建DbPro,我们返回主库连接
|
|
|
|
|
System.out.println("[Db] 使用从库: " + randomSlaveConfig + " -> " + jdbcUrl);
|
|
|
|
|
return MAIN; // 临时返回主库,实际应该创建从库连接
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.err.println("[Db] 从库连接失败,回退到主库: " + e.getMessage());
|
|
|
|
|
return MAIN;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return slaveDbPro;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 判断SQL是否为读操作
|
|
|
|
|
* Determine if SQL is a read operation
|
|
|
|
|
*/
|
|
|
|
|
private static boolean isReadOperation(String sql) {
|
|
|
|
|
if (sql == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
String trimmedSql = sql.trim().toLowerCase();
|
|
|
|
|
return trimmedSql.startsWith("select") ||
|
|
|
|
|
trimmedSql.startsWith("show") ||
|
|
|
|
|
trimmedSql.startsWith("desc") ||
|
|
|
|
|
trimmedSql.startsWith("describe") ||
|
|
|
|
|
trimmedSql.startsWith("explain");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据SQL类型选择合适的DbPro实例
|
|
|
|
|
* Choose appropriate DbPro instance based on SQL type
|
|
|
|
|
*/
|
|
|
|
|
private static DbPro chooseDbPro(String sql) {
|
|
|
|
|
if (readWriteSeparationEnabled && isReadOperation(sql)) {
|
|
|
|
|
return getRandomSlaveDbPro();
|
|
|
|
|
}
|
|
|
|
|
return MAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* for DbKit.addConfig(configName)
|
|
|
|
|
*/
|
|
|
|
|
static void init(String configName) {
|
|
|
|
|
MAIN = DbKit.getConfig(configName).dbProFactory.getDbPro(configName);
|
|
|
|
|
cache.put(configName, MAIN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* for DbKit.removeConfig(configName)
|
|
|
|
|
*/
|
|
|
|
|
static void removeDbProWithConfig(String configName) {
|
|
|
|
|
if (MAIN != null && MAIN.config.getName().equals(configName)) {
|
|
|
|
|
MAIN = null;
|
|
|
|
|
}
|
|
|
|
|
cache.remove(configName);
|
|
|
|
|
slaveCache.remove(configName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static DbPro use(String configName) {
|
|
|
|
|
DbPro result = cache.get(configName);
|
|
|
|
|
if (result == null) {
|
|
|
|
|
Config config = DbKit.getConfig(configName);
|
|
|
|
|
if (config == null) {
|
|
|
|
|
throw new IllegalArgumentException("Config not found by configName: " + configName);
|
|
|
|
|
}
|
|
|
|
|
result = config.dbProFactory.getDbPro(configName);
|
|
|
|
|
cache.put(configName, result);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static DbPro use() {
|
|
|
|
|
return MAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static <T> List<T> query(Config config, Connection conn, String sql, Object... paras) throws SQLException {
|
|
|
|
|
return MAIN.query(config, conn, sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* sql paras 查询,从 JDBC 原样取值且不封装到 Record 对象
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
*/
|
|
|
|
|
public static <T> List<T> query(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).query(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #query(String, Object...)
|
|
|
|
|
* @param sql an SQL statement
|
|
|
|
|
*/
|
|
|
|
|
public static <T> List<T> query(String sql) {
|
|
|
|
|
return chooseDbPro(sql).query(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute sql query and return the first result. I recommend add "limit 1" in your sql.
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return Object[] if your sql has select more than one column,
|
|
|
|
|
* and it return Object if your sql has select only one column.
|
|
|
|
|
*/
|
|
|
|
|
public static <T> T queryFirst(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryFirst(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #queryFirst(String, Object...)
|
|
|
|
|
* @param sql an SQL statement
|
|
|
|
|
*/
|
|
|
|
|
public static <T> T queryFirst(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryFirst(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 26 queryXxx method below -----------------------------------------------
|
|
|
|
|
/**
|
|
|
|
|
* Execute sql query just return one column.
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
* @param <T> the type of the column that in your sql's select statement
|
|
|
|
|
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return <T> T
|
|
|
|
|
*/
|
|
|
|
|
public static <T> T queryColumn(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryColumn(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static <T> T queryColumn(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryColumn(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String queryStr(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryStr(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String queryStr(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryStr(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Integer queryInt(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryInt(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Integer queryInt(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryInt(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Long queryLong(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryLong(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Long queryLong(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryLong(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Double queryDouble(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryDouble(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Double queryDouble(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryDouble(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Float queryFloat(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryFloat(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Float queryFloat(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryFloat(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static BigDecimal queryBigDecimal(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryBigDecimal(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static BigDecimal queryBigDecimal(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryBigDecimal(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static BigInteger queryBigInteger(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryBigInteger(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static BigInteger queryBigInteger(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryBigInteger(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] queryBytes(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryBytes(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] queryBytes(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryBytes(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.util.Date queryDate(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryDate(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.util.Date queryDate(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryDate(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static LocalDateTime queryLocalDateTime(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryLocalDateTime(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static LocalDateTime queryLocalDateTime(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryLocalDateTime(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.sql.Time queryTime(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryTime(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.sql.Time queryTime(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryTime(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.sql.Timestamp queryTimestamp(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryTimestamp(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static java.sql.Timestamp queryTimestamp(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryTimestamp(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Boolean queryBoolean(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryBoolean(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Boolean queryBoolean(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryBoolean(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Short queryShort(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryShort(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Short queryShort(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryShort(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Byte queryByte(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryByte(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Byte queryByte(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryByte(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Number queryNumber(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).queryNumber(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Number queryNumber(String sql) {
|
|
|
|
|
return chooseDbPro(sql).queryNumber(sql);
|
|
|
|
|
}
|
|
|
|
|
// 26 queryXxx method under -----------------------------------------------
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute sql update
|
|
|
|
|
*/
|
|
|
|
|
static int update(Config config, Connection conn, String sql, Object... paras) throws SQLException {
|
|
|
|
|
return MAIN.update(config, conn, sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute update, insert or delete sql statement.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return either the row count for <code>INSERT</code>, <code>UPDATE</code>,
|
|
|
|
|
* or <code>DELETE</code> statements, or 0 for SQL statements
|
|
|
|
|
* that return nothing
|
|
|
|
|
*/
|
|
|
|
|
public static int update(String sql, Object... paras) {
|
|
|
|
|
return MAIN.update(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #update(String, Object...)
|
|
|
|
|
* @param sql an SQL statement
|
|
|
|
|
*/
|
|
|
|
|
public static int update(String sql) {
|
|
|
|
|
return MAIN.update(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static List<Record> find(Config config, Connection conn, String sql, Object... paras) throws SQLException {
|
|
|
|
|
return MAIN.find(config, conn, sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* sql paras 查询,数据封装到 Record 对象
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
*/
|
|
|
|
|
public static List<Record> find(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).find(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #find(String, Object...)
|
|
|
|
|
* @param sql the sql statement
|
|
|
|
|
*/
|
|
|
|
|
public static List<Record> find(String sql) {
|
|
|
|
|
return chooseDbPro(sql).find(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static List<Record> findAll(String tableName) {
|
|
|
|
|
return chooseDbPro("select * from " + tableName).findAll(tableName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find first record. I recommend add "limit 1" in your sql.
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return the Record object
|
|
|
|
|
*/
|
|
|
|
|
public static Record findFirst(String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).findFirst(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #findFirst(String, Object...)
|
|
|
|
|
* @param sql an SQL statement
|
|
|
|
|
*/
|
|
|
|
|
public static Record findFirst(String sql) {
|
|
|
|
|
return chooseDbPro(sql).findFirst(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find record by id with default primary key.
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Record user = Db.findById("user", 15);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param idValue the id value of the record
|
|
|
|
|
*/
|
|
|
|
|
public static Record findById(String tableName, Object idValue) {
|
|
|
|
|
return getRandomSlaveDbPro().findById(tableName, idValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Record findById(String tableName, String primaryKey, Object idValue) {
|
|
|
|
|
return getRandomSlaveDbPro().findById(tableName, primaryKey, idValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find record by ids.
|
|
|
|
|
* 支持读写分离:读操作自动路由到从库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Record user = Db.findByIds("user", "user_id", 123);
|
|
|
|
|
* Record userRole = Db.findByIds("user_role", "user_id, role_id", 123, 456);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param primaryKey the primary key of the table, composite primary key is separated by comma character: ","
|
|
|
|
|
* @param idValues the id value of the record, it can be composite id values
|
|
|
|
|
*/
|
|
|
|
|
public static Record findByIds(String tableName, String primaryKey, Object... idValues) {
|
|
|
|
|
return getRandomSlaveDbPro().findByIds(tableName, primaryKey, idValues);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete record by id with default primary key.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Db.deleteById("user", 15);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param idValue the id value of the record
|
|
|
|
|
* @return true if delete succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
public static boolean deleteById(String tableName, Object idValue) {
|
|
|
|
|
return MAIN.deleteById(tableName, idValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean deleteById(String tableName, String primaryKey, Object idValue) {
|
|
|
|
|
return MAIN.deleteById(tableName, primaryKey, idValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete record by ids.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Db.deleteByIds("user", "user_id", 15);
|
|
|
|
|
* Db.deleteByIds("user_role", "user_id, role_id", 123, 456);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param primaryKey the primary key of the table, composite primary key is separated by comma character: ","
|
|
|
|
|
* @param idValues the id value of the record, it can be composite id values
|
|
|
|
|
* @return true if delete succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
public static boolean deleteByIds(String tableName, String primaryKey, Object... idValues) {
|
|
|
|
|
return MAIN.deleteByIds(tableName, primaryKey, idValues);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete record.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* boolean succeed = Db.delete("user", "id", user);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param primaryKey the primary key of the table, composite primary key is separated by comma character: ","
|
|
|
|
|
* @param record the record
|
|
|
|
|
* @return true if delete succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
public static boolean delete(String tableName, String primaryKey, Record record) {
|
|
|
|
|
return MAIN.delete(tableName, primaryKey, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* boolean succeed = Db.delete("user", user);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @see #delete(String, String, Record)
|
|
|
|
|
*/
|
|
|
|
|
public static boolean delete(String tableName, Record record) {
|
|
|
|
|
return MAIN.delete(tableName, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute delete sql statement.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return the row count for <code>DELETE</code> statements, or 0 for SQL statements
|
|
|
|
|
* that return nothing
|
|
|
|
|
*/
|
|
|
|
|
public static int delete(String sql, Object... paras) {
|
|
|
|
|
return MAIN.delete(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #delete(String, Object...)
|
|
|
|
|
* @param sql an SQL statement
|
|
|
|
|
*/
|
|
|
|
|
public static int delete(String sql) {
|
|
|
|
|
return MAIN.delete(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Page<Record> paginate(Config config, Connection conn, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) throws SQLException {
|
|
|
|
|
return MAIN.paginate(config, conn, pageNumber, pageSize, select, sqlExceptSelect, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Paginate.
|
|
|
|
|
* 支持读写分离:分页查询自动路由到从库
|
|
|
|
|
* @param pageNumber the page number
|
|
|
|
|
* @param pageSize the page size
|
|
|
|
|
* @param select the select part of the sql statement
|
|
|
|
|
* @param sqlExceptSelect the sql statement excluded select part
|
|
|
|
|
* @param paras the parameters of sql
|
|
|
|
|
* @return the Page object
|
|
|
|
|
*/
|
|
|
|
|
public static Page<Record> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginate(pageNumber, pageSize, select, sqlExceptSelect, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page<Record> paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 分页
|
|
|
|
|
*/
|
|
|
|
|
public static Page<Record> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) {
|
|
|
|
|
return getRandomSlaveDbPro().paginate(pageNumber, pageSize, select, sqlExceptSelect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page<Record> paginateByFullSql(int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginateByFullSql(pageNumber, pageSize, totalRowSql, findSql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page<Record> paginateByFullSql(int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginateByFullSql(pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Page<Record> paginate(int pageNumber, int pageSize, SqlPara sqlPara) {
|
|
|
|
|
return MAIN.paginate(pageNumber, pageSize, sqlPara);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean save(Config config, Connection conn, String tableName, String primaryKey, Record record) throws SQLException {
|
|
|
|
|
return MAIN.save(config, conn, tableName, primaryKey, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Save record.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Record userRole = new Record().set("user_id", 123).set("role_id", 456);
|
|
|
|
|
* Db.save("user_role", "user_id, role_id", userRole);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the table
|
|
|
|
|
* @param primaryKey the primary key of the table, composite primary key is separated by comma character: ","
|
|
|
|
|
* @param record the record will be saved
|
|
|
|
|
* @return true if save succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
public static boolean save(String tableName, String primaryKey, Record record) {
|
|
|
|
|
return MAIN.save(tableName, primaryKey, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #save(String, String, Record)
|
|
|
|
|
*/
|
|
|
|
|
public static boolean save(String tableName, Record record) {
|
|
|
|
|
return MAIN.save(tableName, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean update(Config config, Connection conn, String tableName, String primaryKey, Record record) throws SQLException {
|
|
|
|
|
return MAIN.update(config, conn, tableName, primaryKey, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update Record.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Db.update("user_role", "user_id, role_id", record);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @param tableName the table name of the Record save to
|
|
|
|
|
* @param primaryKey the primary key of the table, composite primary key is separated by comma character: ","
|
|
|
|
|
* @param record the Record object
|
|
|
|
|
* @return true if update succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
public static boolean update(String tableName, String primaryKey, Record record) {
|
|
|
|
|
return MAIN.update(tableName, primaryKey, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update record with default primary key.
|
|
|
|
|
* 写操作强制使用主库
|
|
|
|
|
* <pre>
|
|
|
|
|
* Example:
|
|
|
|
|
* Db.update("user", record);
|
|
|
|
|
* </pre>
|
|
|
|
|
* @see #update(String, String, Record)
|
|
|
|
|
*/
|
|
|
|
|
public static boolean update(String tableName, Record record) {
|
|
|
|
|
return MAIN.update(tableName, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #execute(Config, ICallback)
|
|
|
|
|
*/
|
|
|
|
|
public static Object execute(ICallback callback) {
|
|
|
|
|
return MAIN.execute(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute callback. It is useful when all the API can not satisfy your requirement.
|
|
|
|
|
* @param config the Config object
|
|
|
|
|
* @param callback the ICallback interface
|
|
|
|
|
*/
|
|
|
|
|
static Object execute(Config config, ICallback callback) {
|
|
|
|
|
return MAIN.execute(config, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute transaction.
|
|
|
|
|
* 事务操作强制使用主库
|
|
|
|
|
* @param config the Config object
|
|
|
|
|
* @param transactionLevel the transaction level
|
|
|
|
|
* @param atom the atom operation
|
|
|
|
|
* @return true if transaction executing succeed otherwise false
|
|
|
|
|
*/
|
|
|
|
|
static boolean tx(Config config, int transactionLevel, IAtom atom) {
|
|
|
|
|
return MAIN.tx(config, transactionLevel, atom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute transaction with default transaction level.
|
|
|
|
|
* 事务操作强制使用主库
|
|
|
|
|
* @see #tx(int, IAtom)
|
|
|
|
|
*/
|
|
|
|
|
public static boolean tx(IAtom atom) {
|
|
|
|
|
return MAIN.tx(atom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean tx(int transactionLevel, IAtom atom) {
|
|
|
|
|
return MAIN.tx(transactionLevel, atom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 主要用于嵌套事务场景
|
|
|
|
|
* 事务操作强制使用主库
|
|
|
|
|
*
|
|
|
|
|
* 实例:https://jfinal.com/feedback/4008
|
|
|
|
|
*
|
|
|
|
|
* 默认情况下嵌套事务会被合并成为一个事务,那么内层与外层任何地方回滚事务
|
|
|
|
|
* 所有嵌套层都将回滚事务,也就是说嵌套事务无法独立提交与回滚
|
|
|
|
|
*
|
|
|
|
|
* 使用 txInNewThread(...) 方法可以实现层之间的事务控制的独立性
|
|
|
|
|
* 由于事务处理是将 Connection 绑定到线程上的,所以 txInNewThread(...)
|
|
|
|
|
* 通过建立新线程来实现嵌套事务的独立控制
|
|
|
|
|
*/
|
|
|
|
|
public static Future<Boolean> txInNewThread(IAtom atom) {
|
|
|
|
|
return MAIN.txInNewThread(atom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Future<Boolean> txInNewThread(int transactionLevel, IAtom atom) {
|
|
|
|
|
return MAIN.txInNewThread(transactionLevel, atom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find Record by cache.
|
|
|
|
|
* 支持读写分离:缓存查询自动路由到从库
|
|
|
|
|
* @see #find(String, Object...)
|
|
|
|
|
* @param cacheName the cache name
|
|
|
|
|
* @param key the key used to get date from cache
|
|
|
|
|
* @return the list of Record
|
|
|
|
|
*/
|
|
|
|
|
public static List<Record> findByCache(String cacheName, Object key, String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).findByCache(cacheName, key, sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #findByCache(String, Object, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static List<Record> findByCache(String cacheName, Object key, String sql) {
|
|
|
|
|
return chooseDbPro(sql).findByCache(cacheName, key, sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #findByCache(String, Object, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static Record findFirstByCache(String cacheName, Object key, String sql, Object... paras) {
|
|
|
|
|
return chooseDbPro(sql).findFirstByCache(cacheName, key, sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #findFirstByCache(String, Object, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static Record findFirstByCache(String cacheName, Object key, String sql) {
|
|
|
|
|
return chooseDbPro(sql).findFirstByCache(cacheName, key, sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #paginateByCache(String, Object, int, int, String, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static Page<Record> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginateByCache(cacheName, key, pageNumber, pageSize, select, sqlExceptSelect, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #paginateByCache(String, Object, int, int, String, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static Page<Record> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
|
|
|
|
|
return getRandomSlaveDbPro().paginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see #paginateByCache(String, Object, int, int, String, String, Object...)
|
|
|
|
|
*/
|
|
|
|
|
public static Page<Record> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
|
|
|
|
|
return getRandomSlaveDbPro().paginateByCache(cacheName, key, pageNumber, pageSize, select, sqlExceptSelect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -----------------------------
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取读写分离状态信息
|
|
|
|
|
* Get read-write separation status information
|
|
|
|
|
*/
|
|
|
|
|
public static String getReadWriteSeparationStatus() {
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
sb.append("读写分离状态: ").append(readWriteSeparationEnabled ? "已启用" : "未启用").append("\n");
|
|
|
|
|
if (readWriteSeparationEnabled) {
|
|
|
|
|
sb.append("从库配置数量: ").append(slaveConfigs.size()).append("\n");
|
|
|
|
|
for (int i = 0; i < slaveConfigs.size(); i++) {
|
|
|
|
|
sb.append("从库").append(i + 1).append(": ").append(slaveConfigs.get(i)).append("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 强制使用主库执行查询(用于需要强一致性的场景)
|
|
|
|
|
* Force use master database for query (for scenarios requiring strong consistency)
|
|
|
|
|
*/
|
|
|
|
|
public static <T> List<T> queryFromMaster(String sql, Object... paras) {
|
|
|
|
|
return MAIN.query(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static List<Record> findFromMaster(String sql, Object... paras) {
|
|
|
|
|
return MAIN.find(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Record findFirstFromMaster(String sql, Object... paras) {
|
|
|
|
|
return MAIN.findFirst(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 强制使用指定从库执行查询
|
|
|
|
|
* Force use specific slave database for query
|
|
|
|
|
*/
|
|
|
|
|
public static <T> List<T> queryFromSlave(int slaveIndex, String sql, Object... paras) {
|
|
|
|
|
if (!readWriteSeparationEnabled || slaveIndex < 0 || slaveIndex >= slaveConfigs.size()) {
|
|
|
|
|
return MAIN.query(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
// 这里应该返回指定的从库连接,暂时返回主库
|
|
|
|
|
return MAIN.query(sql, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String getSql(String key) {
|
|
|
|
|
return MAIN.getSql(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static SqlPara getSqlPara(String key) {
|
|
|
|
|
return MAIN.getSqlPara(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static SqlPara getSqlPara(String key, Record record) {
|
|
|
|
|
return MAIN.getSqlPara(key, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static SqlPara getSqlPara(String key, Map<?, ?> data) {
|
|
|
|
|
return MAIN.getSqlPara(key, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static SqlPara getSqlPara(String key, Object... paras) {
|
|
|
|
|
return MAIN.getSqlPara(key, paras);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int update(SqlPara sqlPara) {
|
|
|
|
|
return MAIN.update(sqlPara);
|
|
|
|
|
}
|
|
|
|
|
public static List<Record> find(SqlPara sqlPara) {
|
|
|
|
|
return MAIN.find(sqlPara);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batchUpdate(List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batchUpdate(List<? extends Model> modelList, int batchSize) {
|
|
|
|
|
return MAIN.batchUpdate(modelList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batchUpdate(String, String, List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batchUpdate(String tableName, String primaryKey, List<? extends Record> recordList, int batchSize) {
|
|
|
|
|
return MAIN.batchUpdate(tableName, primaryKey, recordList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batch(String, Object[][], int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batch(String sql, Object[][] paras, int batchSize) {
|
|
|
|
|
return MAIN.batch(sql, paras, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batch(String, String, List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batch(String sql, String columns, List modelOrRecordList, int batchSize) {
|
|
|
|
|
return MAIN.batch(sql, columns, modelOrRecordList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batch(List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batch(List<String> sqlList, int batchSize) {
|
|
|
|
|
return MAIN.batch(sqlList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batchUpdate(String, List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batchUpdate(String tableName, List<? extends Record> recordList, int batchSize) {
|
|
|
|
|
return MAIN.batchUpdate(tableName, recordList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batchSave(List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batchSave(List<? extends Model> modelList, int batchSize) {
|
|
|
|
|
return MAIN.batchSave(modelList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see DbPro#batchSave(String, List, int)
|
|
|
|
|
*/
|
|
|
|
|
public static int[] batchSave(String tableName, List<? extends Record> recordList, int batchSize) {
|
|
|
|
|
return MAIN.batchSave(tableName, recordList, batchSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|