
cn.handyplus.lib.db.DbExecution Maven / Gradle / Ivy
The newest version!
package cn.handyplus.lib.db;
import cn.handyplus.lib.constants.BaseConstants;
import cn.handyplus.lib.core.BeanUtil;
import cn.handyplus.lib.core.DateUtil;
import cn.handyplus.lib.core.NumberUtil;
import cn.handyplus.lib.core.StrUtil;
import cn.handyplus.lib.db.enter.Page;
import cn.handyplus.lib.db.enums.DbTypeEnum;
import cn.handyplus.lib.db.enums.FieldTypeEnum;
import cn.handyplus.lib.db.enums.IndexEnum;
import cn.handyplus.lib.db.enums.SqlKeyword;
import cn.handyplus.lib.db.param.FieldInfoParam;
import cn.handyplus.lib.db.param.TableInfoParam;
import cn.handyplus.lib.util.MessageUtil;
import org.bukkit.Bukkit;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
* db执行器
*
* @author handy
* @since 1.4.8
*/
public class DbExecution implements BaseMapper {
private final Class clazz;
private final DbSql dbSql;
private final String storageMethod;
private final boolean isMysql;
private final Connection conn;
protected DbExecution(DbSql dbSql, Class clazz) {
this.dbSql = dbSql;
this.clazz = clazz;
this.storageMethod = SqlManagerUtil.getInstance().getStorageMethod();
this.isMysql = !DbTypeEnum.SQLite.getType().equalsIgnoreCase(storageMethod);
this.conn = SqlManagerUtil.getInstance().getConnection(storageMethod);
}
protected DbExecution(DbSql dbSql, Class clazz, boolean initConn) {
this.dbSql = dbSql;
this.clazz = clazz;
this.storageMethod = SqlManagerUtil.getInstance().getStorageMethod();
this.isMysql = !DbTypeEnum.SQLite.getType().equalsIgnoreCase(storageMethod);
this.conn = initConn ? SqlManagerUtil.getInstance().getConnection(storageMethod) : null;
}
protected DbExecution(DbSql dbSql, Class clazz, Connection conn) {
this.dbSql = dbSql;
this.clazz = clazz;
this.storageMethod = SqlManagerUtil.getInstance().getStorageMethod();
this.isMysql = !DbTypeEnum.SQLite.getType().equalsIgnoreCase(storageMethod);
this.conn = conn;
}
protected DbExecution(DbSql dbSql, Class clazz, String storageMethod) {
this.dbSql = dbSql;
this.clazz = clazz;
this.storageMethod = storageMethod;
this.isMysql = !DbTypeEnum.SQLite.getType().equalsIgnoreCase(storageMethod);
this.conn = SqlManagerUtil.getInstance().getConnection(storageMethod);
}
/**
* 建表
*/
protected void create() {
// 新增表
TableInfoParam tableInfoParam = dbSql.getTableInfoParam();
String createTable = isMysql ? DbConstant.CREATE_TABLE : DbConstant.SQLITE_CREATE_TABLE;
String createTableSql = String.format(createTable, tableInfoParam.getTableName());
// 打印执行sql
this.printSql(createTableSql, "createTableSql: ");
SqlService.getInstance().executionSql(createTableSql, storageMethod);
// 新增表注释
if (isMysql) {
String tableCommentSql = String.format(DbConstant.TABLE_COMMENT, tableInfoParam.getTableName(), tableInfoParam.getTableComment());
this.printSql(tableCommentSql, "tableCommentSql: ");
SqlService.getInstance().executionSql(tableCommentSql, storageMethod);
}
// 新增字段
this.addColumn(tableInfoParam.getTableName(), dbSql.getFieldInfoMap());
// 新增索引
this.addIndex(tableInfoParam.getTableName());
}
/**
* 新增字段
*
* @param tableName 表名
* @param fieldInfoMap 字段信息
* @since 3.4.3
*/
private void addColumn(String tableName, LinkedHashMap fieldInfoMap) {
// 现有字段
String sql;
if (isMysql) {
String database = BaseConstants.STORAGE_CONFIG.getString("MySQL.Database");
sql = String.format(DbConstant.TABLE_INFO, tableName, database);
} else {
sql = String.format(DbConstant.SQLITE_TABLE_INFO, tableName);
}
this.printSql(sql, "addColumnSelectSql: ");
List fieldNameList = SqlService.getInstance().getTableInfo(sql, storageMethod);
// 新增字段
for (String fieldName : fieldInfoMap.keySet()) {
// 新增字段
FieldInfoParam fieldInfoParam = fieldInfoMap.get(fieldName);
FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getEnum(fieldInfoParam);
if (!fieldNameList.contains(fieldName)) {
String addColumn = isMysql ? DbConstant.ADD_COLUMN : DbConstant.SQLITE_ADD_COLUMN;
String fieldSql;
if (isMysql) {
// mysql处理
fieldSql = fieldInfoParam.getFieldNotNull() ? DbConstant.NOT_NULL : "";
if (StrUtil.isNotEmpty(fieldInfoParam.getFieldDefault())) {
fieldSql += String.format(DbConstant.DEFAULT, fieldInfoParam.getFieldDefault());
}
} else {
// sqlite not null 必须有默认值
fieldSql = fieldInfoParam.getFieldNotNull() ? DbConstant.NOT_NULL : "";
if (StrUtil.isNotEmpty(fieldInfoParam.getFieldDefault())) {
fieldSql += String.format(DbConstant.DEFAULT, fieldInfoParam.getFieldDefault());
} else if (fieldInfoParam.getFieldNotNull()) {
fieldSql += String.format(DbConstant.DEFAULT, "");
}
}
Integer fieldLength = fieldInfoParam.getFieldLength() != 0 ? fieldInfoParam.getFieldLength() : fieldTypeEnum.getLength();
String mysqlType = fieldTypeEnum.getMysqlType();
String fieldLengthStr = fieldLength.toString();
if (FieldTypeEnum.DOUBLE.getMysqlType().equals(mysqlType) || FieldTypeEnum.BASIC_DOUBLE.getMysqlType().equals(mysqlType)) {
fieldLengthStr = fieldLength + ", 2";
}
String createFieldSql = String.format(addColumn, tableName, fieldInfoParam.getFieldName(), mysqlType, fieldLengthStr, fieldSql);
createFieldSql = createFieldSql.replace("(0)", "");
this.printSql(createFieldSql, "addColumnSql: ");
SqlService.getInstance().executionSql(createFieldSql, storageMethod);
}
if (isMysql) {
// 修改字段信息
String fieldSql = fieldInfoParam.getFieldNotNull() ? DbConstant.NOT_NULL : "";
if ("id".equals(fieldName)) {
fieldSql = DbConstant.NOT_NULL + DbConstant.AUTO_INCREMENT;
}
if (StrUtil.isNotEmpty(fieldInfoParam.getFieldDefault())) {
fieldSql += String.format(DbConstant.DEFAULT, fieldInfoParam.getFieldDefault());
}
if (StrUtil.isNotEmpty(fieldInfoParam.getFieldComment())) {
fieldSql += String.format(DbConstant.COMMENT, fieldInfoParam.getFieldComment());
}
Integer fieldLength = fieldInfoParam.getFieldLength() != 0 ? fieldInfoParam.getFieldLength() : fieldTypeEnum.getLength();
String mysqlType = fieldTypeEnum.getMysqlType();
String fieldLengthStr = fieldLength.toString();
if (FieldTypeEnum.DOUBLE.getMysqlType().equals(mysqlType) || FieldTypeEnum.BASIC_DOUBLE.getMysqlType().equals(mysqlType)) {
fieldLengthStr = fieldLength + ", 2";
}
String fieldCommentSql = String.format(DbConstant.ADD_COLUMN_COMMENT, tableName, fieldInfoParam.getFieldName(), fieldTypeEnum.getMysqlType(), fieldLengthStr, fieldSql);
fieldCommentSql = fieldCommentSql.replace("(0)", "");
this.printSql(fieldCommentSql, "addColumnCommentSql: ");
SqlService.getInstance().executionSql(fieldCommentSql, storageMethod);
}
}
}
/**
* 根据id查询
*
* @param id id
* @return T
*/
@Override
public Optional selectById(Integer id) {
dbSql.addCondition(true, "id", SqlKeyword.EQ, id);
return this.selectOne();
}
/**
* 批量id查询
*
* @param ids ids
* @return List
*/
@Override
public List selectBatchIds(List ids) {
dbSql.addInCondition(true, "id", SqlKeyword.IN, ids);
return this.list();
}
/**
* 查询
*
* @return T
*/
@Override
public Optional selectOne() {
PreparedStatement ps = null;
ResultSet rst = null;
try {
String sql = dbSql.selectDataSql();
ps = this.conn.prepareStatement(sql);
// 处理占位符参数
this.psSetVal(ps);
// 打印执行sql
this.printSql(sql, "selectOne: ");
rst = ps.executeQuery();
if (!rst.isBeforeFirst()) {
return Optional.empty();
}
Constructor> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
T newInstance = (T) constructor.newInstance();
LinkedHashMap fieldInfoMap = dbSql.getFieldInfoMap();
while (rst.next()) {
for (String fieldName : fieldInfoMap.keySet()) {
FieldInfoParam fieldInfoParam = fieldInfoMap.get(fieldName);
Object obj = rst.getObject(fieldInfoParam.getFieldName());
if (obj == null) {
continue;
}
// 特殊字段类型处理
obj = specialHandling(fieldInfoParam, obj);
Field field = clazz.getDeclaredField(fieldInfoParam.getFieldRealName());
field.setAccessible(true);
field.set(newInstance, obj);
}
}
return Optional.of(newInstance);
} catch (SQLException | NoSuchFieldException | NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException e) {
Bukkit.getLogger().log(Level.SEVERE, "selectOne 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(this.conn, ps, rst);
}
return Optional.empty();
}
/**
* 查询总数-count(*)
*
* @return 总数
*/
@Override
public int count() {
return count(null);
}
/**
* 查询总数-COUNT(DISTINCT `field`)
*
* @param field 过滤字段
* @return 总数
* @since 3.7.6
*/
@Override
public int count(String field) {
return count(field, true);
}
/**
* 查询总数-COUNT(DISTINCT `field`)
*
* @param field 过滤字段
* @param isClose 是否关闭
* @return 总数
* @since 3.10.8
*/
private int count(String field, boolean isClose) {
PreparedStatement ps = null;
ResultSet rst = null;
int count = 0;
try {
String sql = dbSql.selectCountSql(field);
ps = this.conn.prepareStatement(sql);
// 处理占位符参数
this.psSetVal(ps);
// 打印执行sql
this.printSql(sql, "count: ");
rst = ps.executeQuery();
while (rst.next()) {
count = rst.getInt(1);
}
} catch (SQLException e) {
Bukkit.getLogger().log(Level.SEVERE, "count 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(isClose ? this.conn : null, ps, rst);
}
return count;
}
/**
* 查询列表
*
* @return 列表
*/
@Override
public List list() {
return list(true);
}
/**
* 查询列表
*
* @return 列表
*/
private List list(boolean isClose) {
PreparedStatement ps = null;
ResultSet rst = null;
List list = new ArrayList<>();
try {
String sql = dbSql.selectDataSql();
ps = this.conn.prepareStatement(sql);
// 处理占位符参数
this.psSetVal(ps);
// 打印执行sql
this.printSql(sql, "list: ");
rst = ps.executeQuery();
if (!rst.isBeforeFirst()) {
return list;
}
LinkedHashMap fieldInfoMap = dbSql.getFieldInfoMap();
while (rst.next()) {
Constructor> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
T newInstance = (T) constructor.newInstance();
for (String fieldName : fieldInfoMap.keySet()) {
FieldInfoParam fieldInfoParam = fieldInfoMap.get(fieldName);
Object obj = rst.getObject(fieldInfoParam.getFieldName());
if (obj == null) {
continue;
}
// 特殊字段类型处理
obj = specialHandling(fieldInfoParam, obj);
Field field = clazz.getDeclaredField(fieldInfoParam.getFieldRealName());
field.setAccessible(true);
field.set(newInstance, obj);
}
list.add(newInstance);
}
return list;
} catch (SQLException | NoSuchFieldException | NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException e) {
Bukkit.getLogger().log(Level.SEVERE, "list 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(isClose ? this.conn : null, ps, rst);
}
return list;
}
/**
* 分页
*
* @return 集合
*/
@Override
public Page page() {
try {
int count = this.count(null, false);
List list = new ArrayList<>();
if (count > 0) {
list = this.list(false);
}
return new Page<>(count, list);
} finally {
SqlManagerUtil.getInstance().closeSql(this.conn, null, null);
}
}
/**
* 新增
*
* @param obj 入参
* @return ID
*/
@Override
public int insert(T obj) {
if (isMysql) {
return baseInsert(obj);
}
try {
boolean lock = DbConstant.SQLITE_LOCK.tryLock(1, TimeUnit.MINUTES);
if (lock) {
return baseInsert(obj);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
DbConstant.SQLITE_LOCK.unlock();
}
return 0;
}
/**
* 批量新增
*
* @param objList 入参集合
* @return true成功
*/
@Override
public boolean insertBatch(List objList) {
if (isMysql) {
return baseInsertBatch(objList);
}
try {
boolean lock = DbConstant.SQLITE_LOCK.tryLock(1, TimeUnit.MINUTES);
if (lock) {
return baseInsertBatch(objList);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
DbConstant.SQLITE_LOCK.unlock();
}
return false;
}
/**
* 根据id更新
*
* @param id id
* @return 结果
*/
@Override
public int updateById(Integer id) {
dbSql.addCondition(true, "id", SqlKeyword.EQ, id);
return this.update();
}
/**
* 更新
*
* @return 条数
*/
@Override
public int update() {
if (isMysql) {
return baseUpdate();
}
try {
boolean lock = DbConstant.SQLITE_LOCK.tryLock(1, TimeUnit.MINUTES);
if (lock) {
return baseUpdate();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
DbConstant.SQLITE_LOCK.unlock();
}
return 0;
}
/**
* 根据id删除
*
* @param id id
* @return 结果
*/
@Override
public int deleteById(Integer id) {
dbSql.addCondition(true, "id", SqlKeyword.EQ, id);
return this.delete();
}
/**
* 根据ids删除
*
* @param ids ids
* @return 结果
*/
@Override
public int deleteBatchIds(List ids) {
dbSql.addInCondition(true, "id", SqlKeyword.IN, ids);
return this.delete();
}
/**
* 删除
*
* @return 条数
*/
@Override
public int delete() {
if (isMysql) {
return baseDelete();
}
try {
boolean lock = DbConstant.SQLITE_LOCK.tryLock(1, TimeUnit.MINUTES);
if (lock) {
return baseDelete();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
DbConstant.SQLITE_LOCK.unlock();
}
return 0;
}
/**
* 特殊字段类型处理
*
* @param fieldInfoParam 字段信息
* @param obj 值
* @return 新值
*/
private Object specialHandling(FieldInfoParam fieldInfoParam, Object obj) {
FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getEnum(fieldInfoParam);
switch (fieldTypeEnum) {
// date处理
case DATE:
if (!isMysql) {
String str = obj.toString();
obj = new Date(Long.parseLong(str));
} else {
if (obj instanceof LocalDateTime) {
obj = DateUtil.toDate((LocalDateTime) obj);
}
}
break;
// 布尔处理
case BOOLEAN:
case BASIC_BOOLEAN:
if (obj instanceof Integer) {
Integer bool = (Integer) obj;
obj = bool == 1;
}
break;
// Long处理
case LONG:
case BASIC_LONG:
if (obj instanceof Integer) {
Integer number = (Integer) obj;
obj = number.longValue();
}
break;
// BigDecimal处理
case BIG_DECIMAL:
Optional bigDecimalOptional = NumberUtil.isNumericToBigDecimal(String.valueOf(obj), BigDecimal.ZERO);
if (bigDecimalOptional.isPresent()) {
obj = bigDecimalOptional.get();
}
break;
default:
break;
}
return obj;
}
/**
* 新增索引
*
* @param tableName 表名
*/
private void addIndex(String tableName) {
if (!isMysql) {
return;
}
String showIndexSql = String.format(DbConstant.SHOW_INDEX, tableName);
this.printSql(showIndexSql, "showIndexSql: ");
List mysqlTableIndexList = SqlService.getInstance().getMysqlTableIndex(showIndexSql, storageMethod);
for (String fieldName : dbSql.getFieldInfoMap().keySet()) {
FieldInfoParam fieldInfoParam = dbSql.getFieldInfoMap().get(fieldName);
if (IndexEnum.NOT.equals(fieldInfoParam.getIndexEnum())) {
continue;
}
if (mysqlTableIndexList.contains(fieldName)) {
continue;
}
String indexName = fieldName;
if (IndexEnum.UNIQUE.equals(fieldInfoParam.getIndexEnum())) {
indexName = "un_" + indexName;
} else {
indexName = "idx_" + indexName;
}
String addIndexSql = String.format(DbConstant.ADD_INDEX, tableName, indexName, fieldName);
this.printSql(addIndexSql, "addIndexSql: ");
SqlService.getInstance().executionSql(addIndexSql, storageMethod);
}
}
/**
* 内部新增方法
*
* @param obj 参数
* @return id
* @since 3.6.8
*/
private int baseInsert(T obj) {
PreparedStatement ps = null;
ResultSet rst = null;
try {
String sql = dbSql.insertDataSql();
MessageUtil.sendConsoleDebugMessage("insert: " + sql);
ps = this.conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 获取字段信息
Map fieldInfoParamMap = dbSql.getFieldInfoMap().values().stream().collect(Collectors.groupingBy(FieldInfoParam::getFieldRealName, Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));
Map paramMap = BeanUtil.beanToMap(obj);
// 赋值
for (String key : paramMap.keySet()) {
FieldInfoParam fieldInfoParam = fieldInfoParamMap.get(key);
if (fieldInfoParam == null) {
continue;
}
ps.setObject(fieldInfoParam.getFieldIndex(), paramMap.get(key));
}
ps.executeUpdate();
//获取自增id
int id = 0;
rst = ps.getGeneratedKeys();
if (rst.next()) {
id = rst.getInt(1);
}
return id;
} catch (SQLException e) {
Bukkit.getLogger().log(Level.SEVERE, "baseInsert 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(this.conn, ps, rst);
}
return 0;
}
/**
* 内部批量新增方法
*
* @param objList 批量参数
* @return true成功
* @since 3.6.8
*/
private boolean baseInsertBatch(List objList) {
PreparedStatement ps = null;
boolean autoCommit = true;
try {
String sql = dbSql.insertDataSql();
MessageUtil.sendConsoleDebugMessage("insertBatch: " + sql);
// 开始事物
autoCommit = this.conn.getAutoCommit();
if (autoCommit) {
this.conn.setAutoCommit(false);
}
ps = this.conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 获取字段信息
Map fieldInfoParamMap = dbSql.getFieldInfoMap().values().stream().collect(Collectors.groupingBy(FieldInfoParam::getFieldRealName, Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));
for (int i = 0; i < objList.size(); i++) {
Map paramMap = BeanUtil.beanToMap(objList.get(i));
// 赋值
for (String key : paramMap.keySet()) {
FieldInfoParam fieldInfoParam = fieldInfoParamMap.get(key);
if (fieldInfoParam == null) {
continue;
}
ps.setObject(fieldInfoParam.getFieldIndex(), paramMap.get(key));
}
ps.addBatch();
// 如果大于500一批就进行执行
if (i != 0 && (i + 1) % 500 == 0) {
ps.executeBatch();
ps.clearBatch();
}
}
ps.executeBatch();
if (autoCommit) {
this.conn.commit();
}
return true;
} catch (SQLException e) {
try {
if (autoCommit) {
this.conn.rollback();
}
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
Bukkit.getLogger().log(Level.SEVERE, "baseInsertBatch 发生异常", e);
} finally {
if (autoCommit) {
SqlManagerUtil.getInstance().quietSetAutoCommit(this.conn, autoCommit);
}
SqlManagerUtil.getInstance().closeSql(this.conn, ps, null);
}
return false;
}
/**
* 内部更新方法
*
* @return 条数
* @since 3.6.8
*/
private int baseUpdate() {
PreparedStatement ps = null;
try {
String sql = dbSql.updateDataSql();
ps = this.conn.prepareStatement(sql);
// 获取字段信息
LinkedHashMap updateFieldMap = dbSql.getUpdateFieldMap();
for (Integer index : updateFieldMap.keySet()) {
ps.setObject(index, updateFieldMap.get(index));
}
// 处理占位符参数
this.psSetVal(ps, updateFieldMap.size());
// 打印执行sql
this.printSql(sql, "update: ");
return ps.executeUpdate();
} catch (SQLException e) {
Bukkit.getLogger().log(Level.SEVERE, "baseUpdate 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(this.conn, ps, null);
}
return 0;
}
/**
* 内部删除方法
*
* @return 条数
* @since 3.6.8
*/
private int baseDelete() {
PreparedStatement ps = null;
try {
String sql = dbSql.deleteDataSql();
MessageUtil.sendConsoleDebugMessage("delete: " + sql);
ps = this.conn.prepareStatement(sql);
// 处理占位符参数
this.psSetVal(ps);
// 打印执行sql
this.printSql(sql, "delete: ");
return ps.executeUpdate();
} catch (SQLException e) {
Bukkit.getLogger().log(Level.SEVERE, "baseDelete 发生异常", e);
} finally {
SqlManagerUtil.getInstance().closeSql(this.conn, ps, null);
}
return 0;
}
/**
* 设置参数
*
* @param ps 参数
* @throws SQLException sql异常
* @since 3.10.7
*/
private void psSetVal(PreparedStatement ps) throws SQLException {
psSetVal(ps, 0);
}
/**
* 设置参数
*
* @param ps 参数
* @param size 偏移位置
* @throws SQLException sql异常
* @since 3.10.7
*/
private void psSetVal(PreparedStatement ps, Integer size) throws SQLException {
if (dbSql.getWhereMap() == null) {
return;
}
for (Integer index : dbSql.getWhereMap().keySet()) {
ps.setObject(index + size, dbSql.getWhereMap().get(index));
}
}
/**
* 打印sql
*
* @param sql sql
* @param method 方法
*/
private void printSql(String sql, String method) {
if (!BaseConstants.DEBUG) {
return;
}
LinkedHashMap whereMap = dbSql.getWhereMap();
LinkedHashMap updateFieldMap = dbSql.getUpdateFieldMap();
if (updateFieldMap != null) {
for (Integer index : updateFieldMap.keySet()) {
sql = sql.replaceFirst("\\?", updateFieldMap.get(index).toString());
}
}
if (whereMap != null) {
for (Integer index : whereMap.keySet()) {
sql = sql.replaceFirst("\\?", whereMap.get(index).toString());
}
}
// 打印最终的 SQL 查询
MessageUtil.sendConsoleDebugMessage(method + sql);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy