Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.litongjava.db.activerecord.DbPro Maven / Gradle / Ivy
package com.litongjava.db.activerecord;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.jfinal.kit.StrKit;
import com.jfinal.kit.TimeKit;
import com.litongjava.cache.ICache;
import com.litongjava.db.SqlPara;
import com.litongjava.db.activerecord.stat.ISqlStatementStat;
import com.litongjava.model.db.IAtom;
import com.litongjava.model.db.ICallback;
import com.litongjava.model.page.Page;
import lombok.extern.slf4j.Slf4j;
/**
* DbPro. Professional database query and update tool.
*/
@Slf4j
@SuppressWarnings({ "rawtypes", "unchecked" })
public class DbPro {
private String queryColumnByField = "select %s from %s where %s=?";
protected final Config config;
public DbPro() {
if (DbKit.config == null) {
throw new RuntimeException("The main config is null, initialize ActiveRecordPlugin first");
}
this.config = DbKit.config;
}
public DbPro(String configName) {
this.config = DbKit.getConfig(configName);
if (this.config == null) {
throw new IllegalArgumentException("Config not found by configName: " + configName);
}
}
public Config getConfig() {
return config;
}
protected List query(Config config, Connection conn, String sql, Object... paras) {
List result = new ArrayList();
try (PreparedStatement pst = conn.prepareStatement(sql)) {
config.dialect.fillStatement(pst, paras);
long start = System.currentTimeMillis();
try (ResultSet rs = pst.executeQuery()) {
int colAmount = rs.getMetaData().getColumnCount();
if (colAmount > 1) {
while (rs.next()) {
Object[] temp = new Object[colAmount];
for (int i = 0; i < colAmount; i++) {
temp[i] = rs.getObject(i + 1);
}
result.add(temp);
}
} else if (colAmount == 1) {
while (rs.next()) {
result.add(rs.getObject(1));
}
}
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "query", sql, paras, result.size(), start, elapsed, config.writeSync);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public List query(String sql, Object... paras) {
try (Connection conn = config.getConnection()) {
return query(config, conn, sql, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
}
}
/**
* @param sql an SQL statement
* @see #query(String, Object...)
*/
public List query(String sql) { // return List or List
return query(sql, DbKit.NULL_PARA_ARRAY);
}
/**
* 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 T queryFirst(String sql, Object... paras) {
List result = query(sql, paras);
return (result.size() > 0 ? result.get(0) : null);
}
/**
* @param sql an SQL statement
* @see #queryFirst(String, Object...)
*/
public T queryFirst(String sql) {
// return queryFirst(sql, NULL_PARA_ARRAY);
List result = query(sql, DbKit.NULL_PARA_ARRAY);
return (result.size() > 0 ? result.get(0) : null);
}
// 26 queryXxx method below -----------------------------------------------
/**
* Execute sql query just return one column.
*
* @param 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
*/
public T queryColumn(String sql, Object... paras) {
List result = query(sql, paras);
if (result.size() > 0) {
T temp = result.get(0);
if (temp instanceof Object[])
throw new ActiveRecordException("Only ONE COLUMN can be queried.");
return temp;
}
return null;
}
public T queryColumnById(String tableName, String column, Object id) {
return queryColumnByField(tableName, column, "id", id);
}
public T queryColumnByField(String tableName, String column, String field, Object value) {
String sql = String.format(queryColumnByField, column, tableName, field);
return Db.queryColumn(sql, value);
}
public String queryStr(String sql, Object... paras) {
Object s = queryColumn(sql, paras);
return s != null ? s.toString() : null;
}
public String queryStr(String sql) {
return queryStr(sql, DbKit.NULL_PARA_ARRAY);
}
public Integer queryInt(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.intValue() : null;
}
public Integer queryInt(String sql) {
return queryInt(sql, DbKit.NULL_PARA_ARRAY);
}
public Long queryLong(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.longValue() : null;
}
public Long queryLong(String sql) {
return queryLong(sql, DbKit.NULL_PARA_ARRAY);
}
public Double queryDouble(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.doubleValue() : null;
}
public Double queryDouble(String sql) {
return queryDouble(sql, DbKit.NULL_PARA_ARRAY);
}
public Float queryFloat(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.floatValue() : null;
}
public Float queryFloat(String sql) {
return queryFloat(sql, DbKit.NULL_PARA_ARRAY);
}
public BigDecimal queryBigDecimal(String sql, Object... paras) {
Object n = queryColumn(sql, paras);
if (n instanceof BigDecimal) {
return (BigDecimal) n;
} else if (n != null) {
return new BigDecimal(n.toString());
} else {
return null;
}
}
public BigDecimal queryBigDecimal(String sql) {
return queryBigDecimal(sql, DbKit.NULL_PARA_ARRAY);
}
public BigInteger queryBigInteger(String sql, Object... paras) {
Object n = queryColumn(sql, paras);
if (n instanceof BigInteger) {
return (BigInteger) n;
} else if (n != null) {
return new BigInteger(n.toString());
} else {
return null;
}
}
public BigInteger queryBigInteger(String sql) {
return queryBigInteger(sql, DbKit.NULL_PARA_ARRAY);
}
public byte[] queryBytes(String sql, Object... paras) {
return (byte[]) queryColumn(sql, paras);
}
public byte[] queryBytes(String sql) {
return (byte[]) queryColumn(sql, DbKit.NULL_PARA_ARRAY);
}
public java.util.Date queryDate(String sql, Object... paras) {
Object d = queryColumn(sql, paras);
if (d instanceof Temporal) {
if (d instanceof LocalDateTime) {
return TimeKit.toDate((LocalDateTime) d);
}
if (d instanceof LocalDate) {
return TimeKit.toDate((LocalDate) d);
}
if (d instanceof LocalTime) {
return TimeKit.toDate((LocalTime) d);
}
}
return (java.util.Date) d;
}
public java.util.Date queryDate(String sql) {
return queryDate(sql, DbKit.NULL_PARA_ARRAY);
}
public LocalDateTime queryLocalDateTime(String sql, Object... paras) {
Object d = queryColumn(sql, paras);
if (d instanceof LocalDateTime) {
return (LocalDateTime) d;
}
if (d instanceof LocalDate) {
return ((LocalDate) d).atStartOfDay();
}
if (d instanceof LocalTime) {
return LocalDateTime.of(LocalDate.now(), (LocalTime) d);
}
if (d instanceof java.util.Date) {
return TimeKit.toLocalDateTime((java.util.Date) d);
}
return (LocalDateTime) d;
}
public LocalDateTime queryLocalDateTime(String sql) {
return queryLocalDateTime(sql, DbKit.NULL_PARA_ARRAY);
}
public java.sql.Time queryTime(String sql, Object... paras) {
return (java.sql.Time) queryColumn(sql, paras);
}
public java.sql.Time queryTime(String sql) {
return (java.sql.Time) queryColumn(sql, DbKit.NULL_PARA_ARRAY);
}
public java.sql.Timestamp queryTimestamp(String sql, Object... paras) {
return (java.sql.Timestamp) queryColumn(sql, paras);
}
public java.sql.Timestamp queryTimestamp(String sql) {
return (java.sql.Timestamp) queryColumn(sql, DbKit.NULL_PARA_ARRAY);
}
public Boolean queryBoolean(String sql, Object... paras) {
return (Boolean) queryColumn(sql, paras);
}
public Boolean queryBoolean(String sql) {
return (Boolean) queryColumn(sql, DbKit.NULL_PARA_ARRAY);
}
public Short queryShort(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.shortValue() : null;
}
public Short queryShort(String sql) {
return queryShort(sql, DbKit.NULL_PARA_ARRAY);
}
public Byte queryByte(String sql, Object... paras) {
Number n = queryNumber(sql, paras);
return n != null ? n.byteValue() : null;
}
public Byte queryByte(String sql) {
return queryByte(sql, DbKit.NULL_PARA_ARRAY);
}
public Number queryNumber(String sql, Object... paras) {
return (Number) queryColumn(sql, paras);
}
public Number queryNumber(String sql) {
return (Number) queryColumn(sql, DbKit.NULL_PARA_ARRAY);
}
// 26 queryXxx method under -----------------------------------------------
/**
* Execute sql update
*/
protected int update(Config config, Connection conn, String sql, Object... paras) throws SQLException {
try (PreparedStatement pst = conn.prepareStatement(sql)) {
config.dialect.fillStatement(pst, paras);
long start = System.currentTimeMillis();
int result = pst.executeUpdate();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "update", sql, paras, result, start, elapsed, config.writeSync);
}
return result;
}
}
/**
* 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 INSERT
, UPDATE
, or
* DELETE
statements, or 0 for SQL statements that return
* nothing
*/
public int update(String sql, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
return update(config, conn, sql, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
/**
* @param sql an SQL statement
* @see #update(String, Object...)
*/
public int update(String sql) {
return update(sql, DbKit.NULL_PARA_ARRAY);
}
protected List findJsonField(Config config, Connection conn, String sql, String[] jsonFields, Object... paras) throws SQLException {
try (PreparedStatement pst = conn.prepareStatement(sql)) {
config.dialect.fillStatement(pst, paras);
List result = null;
long start = System.currentTimeMillis();
try (ResultSet rs = pst.executeQuery()) {
result = config.dialect.buildRecordListWithJsonFields(config, rs, jsonFields); // RecordBuilder.build(config, rs);
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "save", sql, paras, result.size(), start, elapsed, config.writeSync);
}
}
return result;
}
}
protected List find(Config config, Connection conn, String sql, Object... paras) throws SQLException {
try (PreparedStatement pst = conn.prepareStatement(sql)) {
config.dialect.fillStatement(pst, paras);
List result = null;
long start = System.currentTimeMillis();
try (ResultSet rs = pst.executeQuery()) {
result = config.dialect.buildRecordList(config, rs); // RecordBuilder.build(config, rs);
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "find", sql, paras, result.size(), start, elapsed, config.writeSync);
}
}
return result;
}
}
protected List find(Class clazz, Config config, Connection conn, String sql, Object... paras) {
List result = null;
try (PreparedStatement pst = conn.prepareStatement(sql)) {
config.dialect.fillStatement(pst, paras);
long start = System.currentTimeMillis();
try (ResultSet rs = pst.executeQuery()) {
result = config.dialect.buildRecordList(config, rs); // RecordBuilder.build(config, rs);
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "find", sql, paras, result.size(), start, elapsed, config.writeSync);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
List collect = new ArrayList<>(result.size());
for (Record e : result) {
collect.add(e.toBean(clazz));
}
return collect;
}
/**
* @see #findWithPrimaryKey(String, String, Object...)
*/
public List find(String sql, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
return find(config, conn, sql, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public List findJsonField(String sql, String[] jsonFields, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
return findJsonField(config, conn, sql, jsonFields, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public List findWithJsonField(String sql, String[] jsonFields, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
return findJsonField(config, conn, sql, jsonFields, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public List find(Class clazz, String sql, Object... paras) {
try (Connection conn = config.getConnection();) {
return find(clazz, config, conn, sql, paras);
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
/**
* @param sql the sql statement
* @see #findWithPrimaryKey(String, String, Object...)
*/
public List find(String sql) {
return find(sql, DbKit.NULL_PARA_ARRAY);
}
public List findWithJsonFields(String sql, String[] jsonFields) {
return findWithJsonField(sql, jsonFields, DbKit.NULL_PARA_ARRAY);
}
public List findAll(String tableName) {
String sql = config.dialect.forFindAll(tableName);
return find(sql, DbKit.NULL_PARA_ARRAY);
}
public List findAll(Class clazz, String tableName) {
String sql = config.dialect.forFindAll(tableName);
return find(clazz, sql, DbKit.NULL_PARA_ARRAY);
}
public List findColumnsAll(String tableName, String columns) {
String sql = config.dialect.forDbFindColumns(tableName, columns);
return find(sql, DbKit.NULL_PARA_ARRAY);
}
public List findColumnsAll(Class clazz, String tableName, String columns) {
String sql = config.dialect.forDbFindColumns(tableName, columns);
return find(clazz, sql, DbKit.NULL_PARA_ARRAY);
}
public List findByColumn(String tableName, String column, String value) {
String sql = config.dialect.forDbFindById(tableName, new String[] { column });
return find(sql, value);
}
/**
* 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 Record findFirst(String sql, Object... paras) {
List result = find(sql, paras);
return result.size() > 0 ? result.get(0) : null;
}
public Record findFirstJsonField(String sql, String[] jsonFields, Object... paras) {
// List result = find(sql, jsonFields, paras);
List result = findJsonField(sql, jsonFields, paras);
return result.size() > 0 ? result.get(0) : null;
}
public T findFirst(Class clazz, String sql, Object... paras) {
List result = find(clazz, sql, paras);
return result.size() > 0 ? result.get(0) : null;
}
/**
* @param sql an SQL statement
* @see #findFirst(String, Object...)
*/
public Record findFirst(String sql) {
return findFirst(sql, DbKit.NULL_PARA_ARRAY);
}
/**
* Find record by id with default primary key.
*
*
* Example:
* Record user = Db.use().findById("user", 15);
*
*
* @param tableName the table name of the table
* @param idValue the id value of the record
*/
public Record findById(String tableName, Object idValue) {
return findByIds(tableName, config.dialect.getDefaultPrimaryKey(), idValue);
}
public T findById(Class clazz, String tableName, Object idValue) {
return findByIds(clazz, tableName, config.dialect.getDefaultPrimaryKey(), idValue);
}
public Record findColumnsById(String tableName, String columns, Object idValue) {
return findColumnsById(tableName, columns, config.dialect.getDefaultPrimaryKey(), idValue);
}
public T findColumnsById(Class clazz, String tableName, String columns, Object idValue) {
return findColumnsById(clazz, tableName, columns, config.dialect.getDefaultPrimaryKey(), idValue);
}
public Record findById(String tableName, String primaryKey, Object idValue) {
return findByIds(tableName, primaryKey, idValue);
}
public T findById(Class clazz, String tableName, String primaryKey, Object idValue) {
return findByIds(clazz, tableName, primaryKey, idValue);
}
/**
* Find record by ids.
*
*
* Example:
* Record user = Db.use().findByIds("user", "user_id", 123);
* Record userRole = Db.use().findByIds("user_role", "user_id, role_id", 123, 456);
*
*
* @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 Record findByIds(String tableName, String primaryKey, Object... idValues) {
List result = findWithPrimaryKey(tableName, primaryKey, idValues);
return result.size() > 0 ? result.get(0) : null;
}
public T findByIds(Class clazz, String tableName, String primaryKey, Object... idValues) {
List result = findWithPrimaryKey(tableName, primaryKey, idValues);
List collect = result.stream().map((e) -> e.toBean(clazz)).collect(Collectors.toList());
return result.size() > 0 ? collect.get(0) : null;
}
public Record findColumnsByIds(String tableName, String columns, String primaryKey, Object... idValues) {
List result = findColumns(tableName, columns, primaryKey, idValues);
return result.size() > 0 ? result.get(0) : null;
}
public T findColumnsByIds(Class clazz, String tableName, String columns, String primaryKey, Object... idValues) {
List result = findColumns(tableName, columns, primaryKey, idValues);
List collect = result.stream().map((e) -> e.toBean(clazz)).collect(Collectors.toList());
return result.size() > 0 ? collect.get(0) : null;
}
public List findWithPrimaryKey(String tableName, String primaryKey, Object... idValues) {
String[] pKeys = primaryKey.split(",");
if (pKeys.length != idValues.length)
throw new IllegalArgumentException("primary key number must equals id value number");
String sql = config.dialect.forDbFindById(tableName, pKeys);
List result = find(sql, idValues);
return result;
}
public Record findColumnsById(String tableName, String columns, String primaryKey, Object... idValues) {
List result = findColumns(tableName, columns, primaryKey, idValues);
return result.size() > 0 ? result.get(0) : null;
}
public T findColumnsById(Class clazz, String tableName, String columns, String primaryKey, Object... idValues) {
List result = findColumns(tableName, columns, primaryKey, idValues);
List collect = result.stream().map((e) -> e.toBean(clazz)).collect(Collectors.toList());
return result.size() > 0 ? collect.get(0) : null;
}
public List findColumns(String tableName, String columns, String primaryKey, Object... idValues) {
String[] pKeys = primaryKey.split(",");
if (pKeys.length != idValues.length) {
throw new IllegalArgumentException("primary key number must equals id value number");
}
String sql = config.dialect.forDbFindColumnsById(tableName, columns, pKeys);
List result = find(sql, idValues);
return result;
}
/**
* Delete record by id with default primary key.
*
*
* Example: Db.use().deleteById("user", 15);
*
*
* @param tableName the table name of the table
* @param idValue the id value of the record
* @return true if delete succeed otherwise false
*/
public boolean deleteById(String tableName, Object idValue) {
return deleteByIds(tableName, config.dialect.getDefaultPrimaryKey(), idValue);
}
public boolean deleteById(String tableName, String primaryKey, Object idValue) {
return deleteByIds(tableName, primaryKey, idValue);
}
/**
* Delete record by ids.
*
*
* Example: Db.use().deleteByIds("user", "user_id", 15);
* Db.use().deleteByIds("user_role", "user_id, role_id", 123, 456);
*
*
* @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 boolean deleteByIds(String tableName, String primaryKey, Object... idValues) {
String[] pKeys = primaryKey.split(",");
if (pKeys.length != idValues.length)
throw new IllegalArgumentException("primary key number must equals id value number");
String sql = config.dialect.forDbDeleteById(tableName, pKeys);
return update(sql, idValues) >= 1;
}
/**
* Delete record.
*
*
* Example:
* boolean succeed = Db.use().delete("user", "id", user);
*
*
* @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 boolean delete(String tableName, String primaryKey, Record record) {
String[] pKeys = primaryKey.split(",");
if (pKeys.length <= 1) {
Object t = record.get(primaryKey); // 引入中间变量避免 JDK 8 传参有误
return deleteByIds(tableName, primaryKey, t);
}
config.dialect.trimPrimaryKeys(pKeys);
Object[] idValue = new Object[pKeys.length];
for (int i = 0; i < pKeys.length; i++) {
idValue[i] = record.get(pKeys[i]);
if (idValue[i] == null)
throw new IllegalArgumentException("The value of primary key \"" + pKeys[i] + "\" can not be null in record object");
}
return deleteByIds(tableName, primaryKey, idValue);
}
/**
*
* Example:
* boolean succeed = Db.use().delete("user", user);
*
*
* @see #delete(String, String, Record)
*/
public boolean deleteByIds(String tableName, Record record) {
String defaultPrimaryKey = config.dialect.getDefaultPrimaryKey();
Object t = record.get(defaultPrimaryKey); // 引入中间变量避免 JDK 8 传参有误
return deleteByIds(tableName, defaultPrimaryKey, t);
}
/**
*
* Example:
* String noteId="0000000";
* Record removeRecordFilter = new Record();
* removeRecordFilter.set("note_id", noteId);
* Db.delete(ENoteTableNames.ENOTE_NOTE_TAG, removeRecordFilter);
*
*/
public boolean delete(String tableName, Record record) {
// 判断record是否为空或没有字段
if (record == null) {
return false;
}
Map columns = record.getColumns();
if (columns.size() < 1) {
return false;
}
StringBuilder sql = new StringBuilder("DELETE FROM ");
sql.append(tableName);
sql.append(" WHERE ");
List paras = new ArrayList<>();
boolean isFirst = true;
// 遍历record中的所有字段,构建SQL语句和参数列表
for (Map.Entry entry : columns.entrySet()) {
if (!isFirst) {
sql.append(" AND ");
} else {
isFirst = false;
}
sql.append(entry.getKey());
sql.append(" = ?");
paras.add(entry.getValue());
}
// 调用下面的delete方法执行SQL
int result = delete(sql.toString(), paras.toArray());
// 如果受影响的行数大于0,则返回true,表示删除成功
return result > 0;
}
/**
* 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 DELETE
statements, or 0 for SQL
* statements that return nothing
*/
public int delete(String sql, Object... paras) {
return update(sql, paras);
}
/**
* @param sql an SQL statement
* @see #delete(String, Object...)
*/
public int delete(String sql) {
return update(sql);
}
private Page countPage(Config config, Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object... paras) throws SQLException {
if (pageNumber < 1 || pageSize < 1) {
throw new ActiveRecordException("pageNumber and pageSize must more than 0");
}
if (config.dialect.isTakeOverDbPaginate()) {
return config.dialect.takeOverDbPaginate(conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
}
List result = query(config, conn, totalRowSql, paras);
int size = result.size();
if (isGroupBySql == null) {
isGroupBySql = size > 1;
}
long totalRow;
if (isGroupBySql) {
totalRow = size;
} else {
totalRow = (size > 0) ? ((Number) result.get(0)).longValue() : 0;
}
if (totalRow == 0) {
return new Page(new ArrayList(0), pageNumber, pageSize, 0, 0);
}
int totalPage = (int) (totalRow / pageSize);
if (totalRow % pageSize != 0) {
totalPage++;
}
if (pageNumber > totalPage) {
return new Page(new ArrayList(0), pageNumber, pageSize, totalPage, (int) totalRow);
}
Page page = new Page(pageNumber, pageSize, totalPage, (int) totalRow);
return page;
}
/**
* 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 Page paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, paras);
}
public Page paginate(Class clazz, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object[] paras) {
return doPaginate(clazz, pageNumber, pageSize, null, select, sqlExceptSelect, paras);
}
public Page paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) {
return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginateJsonFields(int pageNumber, int pageSize, String select, String sqlExceptSelect, String[] jsonFields, Object... paras) {
return doPaginateJsonFields(pageNumber, pageSize, null, select, sqlExceptSelect, jsonFields, paras);
}
public Page paginateJsonFields(int pageNumber, int pageSize, String select, String sqlExceptSelect, String[] jsonFields) {
return doPaginateJsonFields(pageNumber, pageSize, null, select, sqlExceptSelect, jsonFields, DbKit.NULL_PARA_ARRAY);
}
public Page paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect) {
return doPaginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginate(Class clazz, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
return doPaginate(clazz, pageNumber, pageSize, null, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginate(Class clazz, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect) {
return doPaginate(clazz, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
return doPaginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
}
public Page paginate(Class clazz, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object[] paras) {
return doPaginate(clazz, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
}
protected Page doPaginateJsonFields(int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, String[] jsonFields, Object... paras) {
try (Connection conn = config.getConnection()) {
String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, null);
StringBuilder findSql = new StringBuilder();
findSql.append(select).append(' ').append(sqlExceptSelect);
return doPaginateByFullSqlWithJsonFields(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, jsonFields, paras);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
protected Page doPaginate(int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, null);
StringBuilder findSql = new StringBuilder();
findSql.append(select).append(' ').append(sqlExceptSelect);
return doPaginateByFullSql(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public Page doPaginate(Class clazz, int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, null);
StringBuilder findSql = new StringBuilder();
findSql.append(select).append(' ').append(sqlExceptSelect);
return doPaginateByFullSql(clazz, config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
protected Page doPaginateByFullSqlWithJsonFields(Config config, Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql,
String[] jsonFields, Object... paras) {
String sql = config.dialect.forPaginate(pageNumber, pageSize, findSql);
Page page = null;
try {
page = countPage(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
} catch (SQLException e) {
throw new RuntimeException(e);
}
// --------
List list = null;
try {
list = findJsonField(config, conn, sql, jsonFields, paras);
} catch (SQLException e) {
throw new RuntimeException(e);
}
page.setList(list);
return page;
}
protected Page doPaginateByFullSql(Config config, Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object... paras)
throws SQLException {
Page page = countPage(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
// --------
String sql = config.dialect.forPaginate(pageNumber, pageSize, findSql);
List list = find(config, conn, sql, paras);
page.setList(list);
return page;
}
public Page doPaginateByFullSql(Class clazz, Config config2, Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object[] paras)
throws SQLException {
Page page = countPage(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
// find with sql
String sql = config.dialect.forPaginate(pageNumber, pageSize, findSql);
List list = find(clazz, config, conn, sql, paras);
page.setList(list);
return page;
}
protected Page paginate(Config config, Connection conn, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) throws SQLException {
String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, null);
StringBuilder findSql = new StringBuilder();
findSql.append(select).append(' ').append(sqlExceptSelect);
return doPaginateByFullSql(config, conn, pageNumber, pageSize, null, totalRowSql, findSql, paras);
}
protected Page doPaginateByFullSql(int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
StringBuilder findSqlBuf = new StringBuilder().append(findSql);
return doPaginateByFullSql(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSqlBuf, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
protected Page doPaginateByFullSql(Class clazz, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
Connection conn = null;
try {
conn = config.getConnection();
StringBuilder findSqlBuf = new StringBuilder().append(findSql);
return doPaginateByFullSql(clazz, config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSqlBuf, paras);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public Page paginateByFullSql(int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
return doPaginateByFullSql(pageNumber, pageSize, null, totalRowSql, findSql, paras);
}
public Page paginateByFullSql(int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
return doPaginateByFullSql(pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
}
public Page paginateByFullSql(Class clazz, int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
return doPaginateByFullSql(clazz, pageNumber, pageSize, null, totalRowSql, findSql, paras);
}
public Page paginateByFullSql(Class clazz, int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
return doPaginateByFullSql(clazz, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
}
protected boolean save(Config config, Connection conn, String tableName, String primaryKey, Record record) {
String[] pKeys = primaryKey.split(",");
List paras = new ArrayList();
StringBuilder sql = new StringBuilder();
config.dialect.forDbSave(tableName, pKeys, record, sql, paras);
String sqlString = sql.toString();
try (PreparedStatement pst = config.dialect.isOracle() ? conn.prepareStatement(sqlString, pKeys) : conn.prepareStatement(sqlString, Statement.RETURN_GENERATED_KEYS)) {
config.dialect.fillStatement(pst, paras);
long start = System.currentTimeMillis();
int result = pst.executeUpdate();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "save", sqlString, paras.toArray(), result, start, elapsed, config.writeSync);
}
config.dialect.getRecordGeneratedKey(pst, record, pKeys);
record.clearModifyFlag();
return result >= 1;
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
protected boolean save(Config config, Connection conn, String tableName, String primaryKey, Record record, String[] jsonFields) {
String[] pKeys = primaryKey.split(",");
List paras = new ArrayList();
StringBuilder sql = new StringBuilder();
config.dialect.forDbSave(tableName, pKeys, record, sql, paras, jsonFields);
int result = 0;
String sqlString = sql.toString();
try (PreparedStatement pst = config.dialect.isOracle() ? conn.prepareStatement(sqlString, pKeys) : conn.prepareStatement(sqlString, Statement.RETURN_GENERATED_KEYS)) {
config.dialect.fillStatement(pst, paras);
long start = System.currentTimeMillis();
result = pst.executeUpdate();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "save", sqlString, paras.toArray(), result, start, elapsed, config.writeSync);
}
config.dialect.getRecordGeneratedKey(pst, record, pKeys);
} catch (SQLException e) {
throw new RuntimeException(e);
}
record.clearModifyFlag();
return result >= 1;
}
/**
* Save record.
*
*
* Example:
* Record userRole = new Record().set("user_id", 123).set("role_id", 456);
* Db.use().save("user_role", "user_id, role_id", userRole);
*
*
* @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
*/
public boolean save(String tableName, String primaryKey, Record record) {
Connection conn = null;
try {
conn = config.getConnection();
return save(config, conn, tableName, primaryKey, record);
} catch (SQLException e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public boolean save(String tableName, String primaryKey, Record record, String[] jsonFields) {
Connection conn = null;
try {
conn = config.getConnection();
return save(config, conn, tableName, primaryKey, record, jsonFields);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
config.close(conn);
}
}
/**
* @see #save(String, String, Record)
*/
public boolean save(String tableName, Record record) {
return save(tableName, config.dialect.getDefaultPrimaryKey(), record);
}
/**
* @param tableName
* @param record
* @param jsonFields
* @return
*/
public boolean save(String tableName, Record record, String[] jsonFields) {
return save(tableName, config.dialect.getDefaultPrimaryKey(), record, jsonFields);
}
protected boolean update(Config config, Connection conn, String tableName, String primaryKey, Record record) throws SQLException {
if (record.modifyFlag == null || record.modifyFlag.isEmpty()) {
return false;
}
String[] pKeys = primaryKey.split(",");
Object[] ids = new Object[pKeys.length];
for (int i = 0; i < pKeys.length; i++) {
ids[i] = record.get(pKeys[i].trim()); // .trim() is important!
if (ids[i] == null)
throw new ActiveRecordException("You can't update record without Primary Key, " + pKeys[i] + " can not be null.");
}
StringBuilder sql = new StringBuilder();
List paras = new ArrayList();
config.dialect.forDbUpdate(tableName, pKeys, ids, record, sql, paras);
if (paras.size() <= 1) { // 参数个数为 1 的情况表明只有主键,也无需更新
return false;
}
int result = update(config, conn, sql.toString(), paras.toArray());
if (result >= 1) {
record.clearModifyFlag();
return true;
}
return false;
}
protected boolean update(Config config, Connection conn, String tableName, String primaryKey, Record record, String[] jsonFields) throws SQLException {
if (record.modifyFlag == null || record.modifyFlag.isEmpty()) {
return false;
}
String[] pKeys = primaryKey.split(",");
Object[] ids = new Object[pKeys.length];
for (int i = 0; i < pKeys.length; i++) {
ids[i] = record.get(pKeys[i].trim()); // .trim() is important!
if (ids[i] == null)
throw new ActiveRecordException("You can't update record without Primary Key, " + pKeys[i] + " can not be null.");
}
StringBuilder sql = new StringBuilder();
List paras = new ArrayList();
config.dialect.forDbUpdate(tableName, pKeys, ids, record, sql, paras, jsonFields);
if (paras.size() <= 1) { // 参数个数为 1 的情况表明只有主键,也无需更新
return false;
}
int result = update(config, conn, sql.toString(), paras.toArray());
if (result >= 1) {
record.clearModifyFlag();
return true;
}
return false;
}
/**
* Update Record.
*
*
* Example: Db.use().update("user_role", "user_id, role_id", record);
*
*
* @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
*/
public boolean update(String tableName, String primaryKey, Record record) {
Connection conn = null;
try {
conn = config.getConnection();
return update(config, conn, tableName, primaryKey, record);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
public boolean update(String tableName, String primaryKey, Record record, String[] jsonFields) {
Connection conn = null;
try {
conn = config.getConnection();
return update(config, conn, tableName, primaryKey, record, jsonFields);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
/**
* Update record with default primary key.
*
*
* Example: Db.use().update("user", record);
*
*
* @see #update(String, String, Record)
*/
public boolean update(String tableName, Record record) {
return update(tableName, config.dialect.getDefaultPrimaryKey(), record);
}
/**
*
*/
public Object execute(ICallback callback) {
return execute(config, 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
*/
protected Object execute(Config config, ICallback callback) {
Connection conn = null;
try {
conn = config.getConnection();
return callback.call(conn);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
config.close(conn);
}
}
/**
* 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
*/
protected boolean tx(Config config, int transactionLevel, IAtom atom) {
Connection conn = config.getThreadLocalConnection();
if (conn != null) { // Nested transaction support
try {
if (conn.getTransactionIsolation() < transactionLevel)
conn.setTransactionIsolation(transactionLevel);
boolean result = atom.run();
if (result)
return true;
throw new NestedTransactionHelpException("Notice the outer transaction that the nested transaction return false"); // important:can not return false
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
boolean result = atom.run();
if (result)
conn.commit();
else
conn.rollback();
return result;
} catch (NestedTransactionHelpException e) {
if (conn != null)
try {
conn.rollback();
} catch (Exception e1) {
log.error(e1.getMessage(), e1);
}
// LogKit.logNothing(e);
return false;
} catch (Throwable t) {
if (conn != null)
try {
conn.rollback();
} catch (Exception e1) {
log.error(e1.getMessage(), e1);
}
throw t instanceof RuntimeException ? (RuntimeException) t : new ActiveRecordException(t);
} finally {
try {
if (conn != null) {
if (autoCommit != null)
conn.setAutoCommit(autoCommit);
conn.close();
}
} catch (Throwable t) {
log.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in
// previous catch block can not be thrown
} finally {
config.removeThreadLocalConnection(); // prevent memory leak
}
}
}
/**
* Execute transaction with default transaction level.
*
* @see #tx(int, IAtom)
*/
public boolean tx(IAtom atom) {
return tx(config, config.getTransactionLevel(), atom);
}
public boolean tx(int transactionLevel, IAtom atom) {
return tx(config, transactionLevel, atom);
}
/**
* 主要用于嵌套事务场景
*
* 实例:https://jfinal.com/feedback/4008
*
* 默认情况下嵌套事务会被合并成为一个事务,那么内层与外层任何地方回滚事务 所有嵌套层都将回滚事务,也就是说嵌套事务无法独立提交与回滚
*
* 使用 txInNewThread(...) 方法可以实现层之间的事务控制的独立性 由于事务处理是将 Connection 绑定到线程上的,所以
* txInNewThread(...) 通过建立新线程来实现嵌套事务的独立控制
*/
public Future txInNewThread(IAtom atom) {
FutureTask task = new FutureTask<>(() -> tx(config, config.getTransactionLevel(), atom));
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
return task;
}
public Future txInNewThread(int transactionLevel, IAtom atom) {
FutureTask task = new FutureTask<>(() -> tx(config, transactionLevel, atom));
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
return task;
}
/**
* Find Record by cache.
*
* @param cacheName the cache name
* @param key the key used to get date from cache
* @return the list of Record
* @see #find(String, Object...)
*/
public List findByCache(String cacheName, Object key, String sql, Object... paras) {
ICache cache = config.getCache();
List result = cache.get(cacheName, key);
if (result == null) {
result = find(sql, paras);
cache.put(cacheName, key, result);
}
return result;
}
public List findByCache(Class clazz, String cacheName, Object key, String sql, Object... paras) {
ICache cache = config.getCache();
List result = cache.get(cacheName, key);
if (result == null) {
result = find(clazz, sql, paras);
cache.put(cacheName, key, result);
}
return result;
}
/**
* @see #findByCache(String, Object, String, Object...)
*/
public List findByCache(String cacheName, Object key, String sql) {
return findByCache(cacheName, key, sql, DbKit.NULL_PARA_ARRAY);
}
public List findByCache(Class clazz, String cacheName, Object key, String sql) {
return findByCache(clazz, cacheName, key, sql, DbKit.NULL_PARA_ARRAY);
}
/**
* Find first record by cache. I recommend add "limit 1" in your sql.
*
* @param cacheName the cache name
* @param key the key used to get date from cache
* @param sql an SQL statement that may contain one or more '?' IN
* parameter placeholders
* @param paras the parameters of sql
* @return the Record object
* @see #findFirst(String, Object...)
*/
public Record findFirstByCache(String cacheName, Object key, String sql, Object... paras) {
ICache cache = config.getCache();
Record result = cache.get(cacheName, key);
if (result == null) {
result = findFirst(sql, paras);
cache.put(cacheName, key, result);
}
return result;
}
public Record findFirstByCache(String cacheName, Object key, int ttl, String sql, Object... paras) {
ICache cache = config.getCache();
Record result = cache.get(cacheName, key);
if (result == null) {
result = findFirst(sql, paras);
cache.put(cacheName, key, result, ttl);
}
return result;
}
public T findFirstByCache(Class clazz, String cacheName, Object key, String sql, Object... paras) {
ICache cache = config.getCache();
T result = cache.get(cacheName, key);
if (result == null) {
result = findFirst(clazz, sql, paras);
cache.put(cacheName, key, result);
}
return result;
}
public T findFirstByCache(Class clazz, String cacheName, Object key, int ttl, String sql, Object... paras) {
ICache cache = config.getCache();
T result = cache.get(cacheName, key);
if (result == null) {
result = findFirst(clazz, sql, paras);
cache.put(cacheName, key, result, ttl);
}
return result;
}
/**
* @see #findFirstByCache(String, Object, String, Object...)
*/
public Record findFirstByCache(String cacheName, Object key, String sql) {
return findFirstByCache(cacheName, key, sql, DbKit.NULL_PARA_ARRAY);
}
public Record findFirstByCache(String cacheName, Object key, int ttl, String sql) {
return findFirstByCache(cacheName, key, ttl, sql, DbKit.NULL_PARA_ARRAY);
}
public T findFirstByCache(Class clazz, String cacheName, Object key, String sql) {
return findFirstByCache(clazz, cacheName, key, sql, DbKit.NULL_PARA_ARRAY);
}
public T findFirstByCache(Class clazz, String cacheName, Object key, int ttl, String sql) {
return findFirstByCache(clazz, cacheName, key, sql, ttl, DbKit.NULL_PARA_ARRAY);
}
public Page doPaginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
ICache cache = config.getCache();
Page result = cache.get(cacheName, key);
if (result == null) {
result = doPaginate(clazz, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
cache.put(cacheName, key, result);
}
return result;
}
public Page doPaginateByCache(String cacheName, Object key, int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
ICache cache = config.getCache();
Page result = cache.get(cacheName, key);
if (result == null) {
result = doPaginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
cache.put(cacheName, key, result);
}
return result;
}
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
assert sqls != null;
return doPaginateByCache(cacheName, key, pageNumber, pageSize, null, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
assert sqls != null;
return doPaginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, sqls[0], sqls[1], sqlPara.getPara());
}
/**
* @see #paginateByCache(String, Object, int, int, String, String, Object...)
*/
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
return doPaginateByCache(cacheName, key, pageNumber, pageSize, null, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect) {
return doPaginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
/**
* Paginate by cache.
*
* @return Page
* @see #paginate(int, int, String, String, Object...)
*/
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
return doPaginateByCache(cacheName, key, pageNumber, pageSize, null, select, sqlExceptSelect, paras);
}
public Page paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
return doPaginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
}
public Page paginateByCacheByFullSql(String cacheName, Object key, int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
return doPaginateByCacheByFullSql(cacheName, key, pageNumber, pageSize, null, totalRowSql, findSql, paras);
}
public Page paginateByCacheByFullSql(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
return doPaginateByCacheByFullSql(cacheName, key, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
}
private Page doPaginateByCacheByFullSql(String cacheName, Object key, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
ICache cache = config.getCache();
Page result = cache.get(cacheName, key);
if (result == null) {
result = doPaginateByFullSql(pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
cache.put(cacheName, key, result);
}
return result;
}
public Page paginateByCacheByFullSql(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) {
return doPaginateByCacheByFullSql(clazz, cacheName, key, pageNumber, pageSize, null, totalRowSql, findSql, paras);
}
public Page paginateByCacheByFullSql(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) {
return doPaginateByCacheByFullSql(clazz, cacheName, key, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
}
private Page doPaginateByCacheByFullSql(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, String findSql,
Object... paras) {
ICache cache = config.getCache();
Page result = cache.get(cacheName, key);
if (result == null) {
result = doPaginateByFullSql(clazz, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras);
cache.put(cacheName, key, result);
}
return result;
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
assert sqls != null;
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, null, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
assert sqls != null;
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, isGroupBySql, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect) {
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, null, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect) {
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, DbKit.NULL_PARA_ARRAY);
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, null, select, sqlExceptSelect, paras);
}
public Page paginateByCache(Class clazz, String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
return doPaginateByCache(clazz, cacheName, key, pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras);
}
protected int[] batch(Config config, Connection conn, String sql, Object[][] paras, int batchSize) throws SQLException {
if (paras == null || paras.length == 0)
return new int[0];
if (batchSize < 1)
throw new IllegalArgumentException("The batchSize must more than 0.");
boolean isInTransaction = config.isInTransaction();
int counter = 0;
int pointer = 0;
int[] result = new int[paras.length];
try (PreparedStatement pst = conn.prepareStatement(sql)) {
for (Object[] para : paras) {
for (int j = 0; j < para.length; j++) {
Object value = para[j];
if (value instanceof java.util.Date) {
if (value instanceof java.sql.Date) {
pst.setDate(j + 1, (java.sql.Date) value);
} else if (value instanceof java.sql.Timestamp) {
pst.setTimestamp(j + 1, (java.sql.Timestamp) value);
} else {
// Oracle、SqlServer 中的 TIMESTAMP、DATE 支持 new Date() 给值
java.util.Date d = (java.util.Date) value;
pst.setTimestamp(j + 1, new java.sql.Timestamp(d.getTime()));
}
} else {
pst.setObject(j + 1, value);
}
}
pst.addBatch();
if (++counter >= batchSize) {
counter = 0;
long start = System.currentTimeMillis();
int[] r = pst.executeBatch();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "batch", sql, paras, r.length, start, elapsed, config.writeSync);
}
if (!isInTransaction)
conn.commit();
for (int i : r) {
result[pointer++] = i;
}
}
}
if (counter != 0) {
long start = System.currentTimeMillis();
int[] r = pst.executeBatch();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "batch", sql, paras, r.length, start, elapsed, config.writeSync);
}
if (!isInTransaction) {
conn.commit();
}
for (int i : r) {
result[pointer++] = i;
}
}
return result;
}
}
/**
* Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
*
*
* Example:
* String sql = "insert into user(name, cash) values(?, ?)";
* int[] result = Db.use().batch(sql, new Object[][]{{"James", 888}, {"zhanjin", 888}});
*
*
* @param sql The SQL to execute.
* @param paras An array of query replacement parameters. Each row in this array
* is one set of batch replacement values.
* @return The number of rows updated per statement
*/
public int[] batch(String sql, Object[][] paras, int batchSize) {
Connection conn = null;
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
return batch(config, conn, sql, paras, batchSize);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
if (autoCommit != null)
try {
conn.setAutoCommit(autoCommit);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
config.close(conn);
}
}
protected int[] batch(Config config, Connection conn, String sql, String columns, List list, int batchSize) throws SQLException {
if (list == null || list.size() == 0) {
return new int[0];
}
Object element = list.get(0);
if (!(element instanceof Record) && !(element instanceof Model)) {
throw new IllegalArgumentException("The element in list must be Model or Record.");
}
if (batchSize < 1) {
throw new IllegalArgumentException("The batchSize must more than 0.");
}
boolean isModel = element instanceof Model;
String[] columnArray = columns.split(",");
for (int i = 0; i < columnArray.length; i++)
columnArray[i] = columnArray[i].trim();
boolean isInTransaction = config.isInTransaction();
int counter = 0;
int pointer = 0;
int size = list.size();
int[] result = new int[size];
try (PreparedStatement pst = conn.prepareStatement(sql)) {
for (Object o : list) {
Map map = isModel ? ((Model) o)._getAttrs() : ((Record) o).getColumns();
for (int j = 0; j < columnArray.length; j++) {
Object value = map.get(columnArray[j]);
if (value instanceof java.util.Date) {
if (value instanceof java.sql.Date) {
pst.setDate(j + 1, (java.sql.Date) value);
} else if (value instanceof java.sql.Timestamp) {
pst.setTimestamp(j + 1, (java.sql.Timestamp) value);
} else {
// Oracle、SqlServer 中的 TIMESTAMP、DATE 支持 new Date() 给值
java.util.Date d = (java.util.Date) value;
pst.setTimestamp(j + 1, new java.sql.Timestamp(d.getTime()));
}
} else {
pst.setObject(j + 1, value);
}
}
pst.addBatch();
if (++counter >= batchSize) {
counter = 0;
long start = System.currentTimeMillis();
int[] r = pst.executeBatch();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "batch", sql, list.toArray(), r.length, start, elapsed, config.writeSync);
}
if (!isInTransaction)
conn.commit();
for (int i : r) {
result[pointer++] = i;
}
}
}
if (counter != 0) {
long start = System.currentTimeMillis();
int[] r = pst.executeBatch();
ISqlStatementStat stat = config.getSqlStatementStat();
if (stat != null) {
long end = System.currentTimeMillis();
long elapsed = end - start;
stat.save(config.name, "batch", sql, list.toArray(), r.length, start, elapsed, config.writeSync);
}
if (!isInTransaction)
conn.commit();
for (int i : r) {
result[pointer++] = i;
}
}
return result;
}
}
/**
* Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
*
*
* Example:
* String sql = "insert into user(name, cash) values(?, ?)";
* int[] result = Db.use().batch(sql, "name, cash", modelList, 500);
*
*
* @param sql The SQL to execute.
* @param columns the columns need be processed by sql.
* @param modelOrRecordList model or record object list.
* @param batchSize batch size.
* @return The number of rows updated per statement
*/
public int[] batch(String sql, String columns, List modelOrRecordList, int batchSize) {
Connection conn = null;
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
return batch(config, conn, sql, columns, modelOrRecordList, batchSize);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
if (autoCommit != null)
try {
conn.setAutoCommit(autoCommit);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
config.close(conn);
}
}
protected int[] batch(Config config, Connection conn, List sqlList, int batchSize) throws SQLException {
if (sqlList == null || sqlList.size() == 0)
return new int[0];
if (batchSize < 1)
throw new IllegalArgumentException("The batchSize must more than 0.");
boolean isInTransaction = config.isInTransaction();
int counter = 0;
int pointer = 0;
int size = sqlList.size();
int[] result = new int[size];
try (Statement st = conn.createStatement()) {
for (String s : sqlList) {
st.addBatch(s);
if (++counter >= batchSize) {
counter = 0;
int[] r = st.executeBatch();
if (!isInTransaction)
conn.commit();
for (int i : r) {
result[pointer++] = i;
}
}
}
if (counter != 0) {
int[] r = st.executeBatch();
if (!isInTransaction)
conn.commit();
for (int i : r) {
result[pointer++] = i;
}
}
return result;
}
}
/**
* Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
*
*
* Example:
* int[] result = Db.use().batch(sqlList, 500);
*
*
* @param sqlList The SQL list to execute.
* @param batchSize batch size.
* @return The number of rows updated per statement
*/
public int[] batch(List sqlList, int batchSize) {
Connection conn = null;
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
return batch(config, conn, sqlList, batchSize);
} catch (Exception e) {
throw new ActiveRecordException(e);
} finally {
if (autoCommit != null)
try {
conn.setAutoCommit(autoCommit);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
config.close(conn);
}
}
/**
* Batch save models using the "insert into ..." sql generated by the first
* model in modelList. Ensure all the models can use the same sql as the first
* model.
*/
public int[] batchSave(List extends Model> modelList, int batchSize) {
if (modelList == null || modelList.size() == 0) {
return new int[0];
}
Model model = modelList.get(0);
Map attrs = model._getAttrs();
int index = 0;
StringBuilder columns = new StringBuilder();
// the same as the iterator in Dialect.forModelSave() to ensure the order of the
// attrs
for (Entry e : attrs.entrySet()) {
if (config.dialect.isOracle()) { // 支持 oracle 自增主键
Object value = e.getValue();
if (value instanceof String && ((String) value).endsWith(".nextval")) {
continue;
}
}
if (index++ > 0) {
columns.append(',');
}
columns.append(e.getKey());
}
StringBuilder sql = new StringBuilder();
List parasNoUse = new ArrayList();
config.dialect.forModelSave(TableMapping.me().getTable(model.getClass()), attrs, sql, parasNoUse);
return batch(sql.toString(), columns.toString(), modelList, batchSize);
}
/**
* Batch save records using the "insert into ..." sql generated by the first
* record in recordList. Ensure all the record can use the same sql as the first
* record.
*
* @param tableName the table name
*/
public int[] batchSave(String tableName, List extends Record> recordList, int batchSize) {
if (recordList == null || recordList.size() == 0) {
return new int[0];
}
Record record = recordList.get(0);
Map cols = record.getColumns();
int index = 0;
StringBuilder columns = new StringBuilder();
// the same as the iterator in Dialect.forDbSave() to ensure the order of the
// columns
for (Entry e : cols.entrySet()) {
if (config.dialect.isOracle()) { // 支持 oracle 自增主键
Object value = e.getValue();
if (value instanceof String && ((String) value).endsWith(".nextval")) {
continue;
}
}
if (index++ > 0) {
columns.append(',');
}
columns.append(e.getKey());
}
String[] pKeysNoUse = new String[0];
StringBuilder sql = new StringBuilder();
List parasNoUse = new ArrayList();
config.dialect.forDbSave(tableName, pKeysNoUse, record, sql, parasNoUse);
return batch(sql.toString(), columns.toString(), recordList, batchSize);
}
public int[] batchDelete(String tableName, List extends Record> recordList, int batchSize) {
if (recordList == null || recordList.size() == 0) {
return new int[0];
}
Record record = recordList.get(0);
Map cols = record.getColumns();
int index = 0;
StringBuilder columns = new StringBuilder();
// the same as the iterator in Dialect.forDbSave() to ensure the order of the
// columns
for (Entry e : cols.entrySet()) {
if (config.dialect.isOracle()) { // 支持 oracle 自增主键
Object value = e.getValue();
if (value instanceof String && ((String) value).endsWith(".nextval")) {
continue;
}
}
if (index++ > 0) {
columns.append(',');
}
columns.append(e.getKey());
}
String[] pKeysNoUse = new String[0];
StringBuilder sql = new StringBuilder();
List parasNoUse = new ArrayList();
config.dialect.forDbDelete(tableName, pKeysNoUse, record, sql, parasNoUse);
return batch(sql.toString(), columns.toString(), recordList, batchSize);
}
/**
* Batch update models using the attrs names of the first model in modelList.
* Ensure all the models can use the same sql as the first model.
*/
public int[] batchUpdate(List extends Model> modelList, int batchSize) {
if (modelList == null || modelList.size() == 0)
return new int[0];
Model model = modelList.get(0);
// 新增支持 modifyFlag
if (model.modifyFlag == null || model.modifyFlag.isEmpty()) {
return new int[0];
}
Set modifyFlag = model._getModifyFlag();
Table table = TableMapping.me().getTable(model.getClass());
String[] pKeys = table.getPrimaryKey();
Map attrs = model._getAttrs();
List attrNames = new ArrayList();
// the same as the iterator in Dialect.forModelSave() to ensure the order of the
// attrs
for (Entry e : attrs.entrySet()) {
String attr = e.getKey();
if (modifyFlag.contains(attr) && !config.dialect.isPrimaryKey(attr, pKeys) && table.hasColumnLabel(attr))
attrNames.add(attr);
}
for (String pKey : pKeys)
attrNames.add(pKey);
String columns = StrKit.join(attrNames.toArray(new String[attrNames.size()]), ",");
// update all attrs of the model not use the midifyFlag of every single model
// Set modifyFlag = attrs.keySet(); // model.getModifyFlag();
StringBuilder sql = new StringBuilder();
List parasNoUse = new ArrayList();
config.dialect.forModelUpdate(TableMapping.me().getTable(model.getClass()), attrs, modifyFlag, sql, parasNoUse);
return batch(sql.toString(), columns, modelList, batchSize);
}
/**
* Batch update records using the columns names of the first record in
* recordList. Ensure all the records can use the same sql as the first record.
*
* @param tableName the table name
* @param primaryKey the primary key of the table, composite primary key is
* separated by comma character: ","
*/
public int[] batchUpdate(String tableName, String primaryKey, List extends Record> recordList, int batchSize) {
if (recordList == null || recordList.size() == 0)
return new int[0];
String[] pKeys = primaryKey.split(",");
config.dialect.trimPrimaryKeys(pKeys);
Record record = recordList.get(0);
// Record 新增支持 modifyFlag
if (record.modifyFlag == null || record.modifyFlag.isEmpty()) {
return new int[0];
}
Set modifyFlag = record._getModifyFlag();
Map cols = record.getColumns();
List colNames = new ArrayList();
// the same as the iterator in Dialect.forDbUpdate() to ensure the order of the
// columns
for (Entry e : cols.entrySet()) {
String col = e.getKey();
if (modifyFlag.contains(col) && !config.dialect.isPrimaryKey(col, pKeys))
colNames.add(col);
}
for (String pKey : pKeys)
colNames.add(pKey);
String columns = StrKit.join(colNames.toArray(new String[colNames.size()]), ",");
Object[] idsNoUse = new Object[pKeys.length];
StringBuilder sql = new StringBuilder();
List parasNoUse = new ArrayList();
config.dialect.forDbUpdate(tableName, pKeys, idsNoUse, record, sql, parasNoUse);
return batch(sql.toString(), columns, recordList, batchSize);
}
/**
* Batch update records with default primary key, using the columns names of the
* first record in recordList. Ensure all the records can use the same sql as
* the first record.
*
* @param tableName the table name
*/
public int[] batchUpdate(String tableName, List extends Record> recordList, int batchSize) {
return batchUpdate(tableName, config.dialect.getDefaultPrimaryKey(), recordList, batchSize);
}
public String getSql(String key) {
return config.getSqlKit().getSql(key);
}
// 支持传入变量用于 sql 生成。为了避免用户将参数拼接在 sql 中引起 sql 注入风险,只在 SqlKit 中开放该功能
// public String getSql(String key, Map data) {
// return config.getSqlKit().getSql(key, data);
// }
public SqlPara getSqlPara(String key, Record record) {
return getSqlPara(key, record.getColumns());
}
public SqlPara getSqlPara(String key, Model model) {
return getSqlPara(key, model._getAttrs());
}
public SqlPara getSqlPara(String key, Map data) {
return config.getSqlKit().getSqlPara(key, data);
}
public SqlPara getSqlPara(String key, Object... paras) {
return config.getSqlKit().getSqlPara(key, paras);
}
public SqlPara getSqlParaByString(String content, Map data) {
return config.getSqlKit().getSqlParaByString(content, data);
}
public SqlPara getSqlParaByString(String content, Object... paras) {
return config.getSqlKit().getSqlParaByString(content, paras);
}
public List find(SqlPara sqlPara) {
return find(sqlPara.getSql(), sqlPara.getPara());
}
public List find(Class clazz, SqlPara sqlPara) {
return find(clazz, sqlPara.getSql(), sqlPara.getPara());
}
public Record findFirst(SqlPara sqlPara) {
return findFirst(sqlPara.getSql(), sqlPara.getPara());
}
public T findFirst(Class clazz, SqlPara sqlPara) {
return findFirst(clazz, sqlPara.getSql(), sqlPara.getPara());
}
public int update(SqlPara sqlPara) {
return update(sqlPara.getSql(), sqlPara.getPara());
}
public Page paginate(int pageNumber, int pageSize, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
return doPaginate(pageNumber, pageSize, null, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginate(Class clazz, int pageNumber, int pageSize, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
return doPaginate(clazz, pageNumber, pageSize, null, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginate(int pageNumber, int pageSize, boolean isGroupBySql, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
return doPaginate(pageNumber, pageSize, isGroupBySql, sqls[0], sqls[1], sqlPara.getPara());
}
public Page paginate(Class clazz, int pageNumber, int pageSize, boolean isGroupBySql, SqlPara sqlPara) {
String[] sqls = PageSqlKit.parsePageSql(sqlPara.getSql());
return doPaginate(clazz, pageNumber, pageSize, isGroupBySql, sqls[0], sqls[1], sqlPara.getPara());
}
// ---------
/**
* 迭代处理每一个查询出来的 Record 对象
*
*