package jef.database;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.PersistenceException;
import jef.common.log.LogUtil;
import jef.database.dialect.DatabaseDialect;
import jef.database.dialect.type.AutoGuidMapping;
import jef.database.dialect.type.AutoIncrementMapping;
import jef.database.dialect.type.AutoIncrementMapping.GenerationResolution;
import jef.database.dialect.type.AutoIntMapping;
import jef.database.dialect.type.AutoLongMapping;
import jef.database.dialect.type.ColumnMapping;
import jef.database.jdbc.result.ResultSetWrapper;
import jef.database.jsqlparser.visitor.Expression;
import jef.database.meta.Feature;
import jef.database.meta.ITableMetadata;
import jef.database.meta.TupleField;
import jef.tools.Assert;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanWrapper;
/**
* 一个可以更新数据的结果集,我们可以直接在结果集上更新数据。 代码示例如下:
*
* <pre>
* <code>
*
* RecordsHolder<Root> holder = db.selectForUpdate(QB.create(Root.class).getInstance());
* System.out.println("总数:" + holder.size());
* int n = 0;
* for (Root r : holder.get()) {
* r.setName("更新第" + n + "条。"); // 修改对象中的值
* n++;
* }
* holder.delete(holder.size() - 1); // 删除结果集中的最后一条记录(序号从0开始)
* Root root = holder.newRecord(); // 创建一条新纪录
* root.setName("新插入的记录");
* holder.commit(); // 提交上述修改并关闭游标(更新、删除、添加)
* </code>
* </pre>
*
* @author jiyi
*
* @param <T>
*/
public final class RecordsHolder<T extends IQueryableEntity> implements Iterable<T> {
public static final int BEFORE_FIRST = -2;
public static final int AFTER_LAST = -3;
private DatabaseDialect profile;
private ResultSetWrapper rs;// 必须是一个允许前后滚动的结果集
private List<RecordHolder<T>> rhs;
private List<T> objs;
private boolean noHoldInsertValues = true;// HSQL has a feature, if you move
// the cursor to InsertRow, then
// you can not move it to any
// other rows. so we must pend
// inert request to last.
private boolean supportsNewRec = true;// Derby BUG,
// 用结果集直接插入记录后,Derby的自增主键不会同步增长,造成后续自增主键出现冲突。
private ITableMetadata meta;
private int index = BEFORE_FIRST;
/**
* 返回结果的条数
*
* @return
*/
public int size() {
ensureOpen();
return objs.size();
}
/**
* 返回结果集
*
* @return
*/
public List<T> get() {
ensureOpen();
return objs;
}
RecordHolder<T> get(int i) {
ensureOpen();
return rhs.get(i);
}
RecordsHolder(ITableMetadata mm) throws SQLException {
this.meta = mm;
Assert.notNull(meta);
}
void init(ResultSetWrapper holder, List<T> objs, DatabaseDialect profile) {
this.profile = profile;
this.rs = holder;
this.objs = objs;
rhs = new ArrayList<RecordHolder<T>>();
for (int i = 0; i < objs.size(); i++) {
rhs.add(new RecordHolder<T>(this, i, objs.get(i)));
}
if (holder.getProfile().has(Feature.NOT_FETCH_NEXT_AUTOINCREAMENTD)) {
for (ColumnMapping type : meta.getPKFields()) {
if (type instanceof AutoIntMapping || type instanceof AutoLongMapping) {
supportsNewRec = false;
}
}
}
noHoldInsertValues = holder.getProfile().notHas(Feature.CURSOR_ENDS_ON_INSERT_ROW);
}
/**
* 一些数据库不支持在ResultSet上新建记录,返回该标志
*
* @return
*/
public boolean supportsNewRecord() {
return supportsNewRec;
}
/**
* 创建一个新记录
*
* @return
*/
public T newRecord() {
ensureOpen();
if (!supportsNewRec) {
throw new UnsupportedOperationException("Current database " + rs.getProfile().getName() + " not support 'newRecord()'");
}
@SuppressWarnings("unchecked")
T obj = (T) meta.newInstance();
obj.startUpdate();
if (!meta.getPKFields().isEmpty()) {
for (ColumnMapping type : meta.getPKFields()) {
if (type instanceof AutoIncrementMapping) {
AutoIncrementMapping mapping = (AutoIncrementMapping) type;
mapping.getAccessor().set(obj, getNextAutoIncreament(mapping));
} else if (type instanceof AutoGuidMapping) {
BeanWrapper bean = BeanWrapper.wrap(obj, BeanWrapper.FAST);
String value = StringUtils.remove(StringUtils.generateGuid(), '-');
bean.setPropertyValue(type.fieldName(), value);
}
}
}
// 准备完成,加入
RecordHolder<T> r = new RecordHolder<T>(this, -1, obj);
this.rhs.add(r);
return obj;
}
/*
* 计算自增值
*
* @param mapping
*
* @return
*/
private long getNextAutoIncreament(AutoIncrementMapping mapping) {
GenerationResolution gtype = mapping.getGenerationType(profile);
if (gtype == GenerationResolution.SEQUENCE || gtype == GenerationResolution.TABLE) {
try {
Sequence sq = ((OperateTarget) rs.getTarget()).getSequence(mapping);
return sq.next();
} catch (SQLException e) {
throw new PersistenceException(e);
}
} else {
return profile.getColumnAutoIncreamentValue(mapping, this.rs.getTarget());
}
}
/**
* 检查指定的对象是否删除
*
* @param index
* 序号,从0开始
* @return
*/
public boolean isDeleted(int index) {
ensureOpen();
if (index < 0 || index >= size()) {
throw new IllegalArgumentException("the object you want to delete is not exist in the list");
}
RecordHolder<T> r = rhs.get(index);
return r.status == RecordHolder.DELETE || r.status == RecordHolder.DELETED;
}
/**
* 删除
*
* @param index
* 在结果集中的序号
*/
public void delete(int index) {
ensureOpen();
if (index < 0 || index >= size()) {
throw new IllegalArgumentException("the object you want to delete is not exist in the list");
}
RecordHolder<T> r = rhs.get(index);
Assert.notNull(r);
r.status = RecordHolder.DELETE;
}
/**
* 删除指定的记录
*
* @param object
* 删除结果集当中指定的对象
*/
public void delete(T object) {
ensureOpen();
int index = objs.indexOf(object);
delete(index);
}
/**
* 将修改提交(更新到数据库)
*
* @param closeit
* 提交后关闭ResultSet
* @throws SQLException
*/
private int commit(boolean closeit) throws SQLException {
ensureOpen();
moveTo(BEFORE_FIRST);
List<RecordHolder<T>> toInsert = new ArrayList<RecordHolder<T>>();
int changed = 0;
for (RecordHolder<T> r : rhs) {
try {
switch (r.getStatus()) {
case RecordHolder.CACNLED:// 不用操作
break;
case RecordHolder.DELETE:
moveTo(r.index);
rs.deleteRow();
changed++;
break;
case RecordHolder.DELETED:// 不用操作
break;
case RecordHolder.INSERT:
if (closeit || noHoldInsertValues) {
toInsert.add(r);
changed++;
}
break;
case RecordHolder.INSERTED:// 不用操作
break;
default:
if (r.get().needUpdate()) {
update(r.get(), r.index);
changed++;
}
}
} catch (SQLException e) {
LogUtil.error("Error update " + r);
throw e;
}
}
// Insert最后再操作
for (RecordHolder<T> r : toInsert) {
insert(r.get());
r.setInserted();
changed++;
}
if (closeit)
close();
return changed;
}
/**
* 将修改提交(更新到数据库)
*
* @throws SQLException
*/
public int commit() {
try {
return commit(false);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
/**
* 将修改提交到数据库
* @return
*/
public int commitAndClose() {
try {
return commit(true);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
private void update(T t, int index2) throws SQLException {
if (index2 < 0 || index2 >= size()) {
throw new SQLException("the index " + index2 + " is invalid!");
}
moveTo(index2);
boolean flag = false;
for (Field f : t.getUpdateValueMap().keySet()) {
if (f instanceof Enum<?> || f instanceof TupleField) {
Object value = t.getUpdateValueMap().get(f);
if (value instanceof Expression) {
throw new SQLException("The expression object not supported in resultSet operation model.");
}
String columnName = meta.getColumnName(f, profile, false);
if (value == null) {
rs.updateNull(columnName);
} else {
ColumnMapping column = this.meta.getColumnDef(f);
column.jdbcUpdate(rs, columnName, value, this.profile);
// rs.updateObject(columnName, value);
}
flag = true;
}
}
if (flag)
rs.updateRow();
}
private void insert(T t) throws SQLException {
rs.moveToInsertRow();
Assert.notNull(meta);
BeanWrapper bw = BeanWrapper.wrap(t);
for (ColumnMapping mType : meta.getColumns()) {
Field f = mType.field();
String columnName = mType.getColumnName(profile, false);
if (!bw.isReadableProperty(f.name()))
continue;
Object value = bw.getPropertyValue(f.name());
if (value == null) {
rs.updateNull(columnName);
} else {
rs.updateObject(columnName, value);
}
}
rs.insertRow();
rs.moveToInsertRow();
}
private void moveTo(int to) throws SQLException {
// rs.moveToInsertRow();
if (to == index)
return;
if (to == BEFORE_FIRST) {
rs.beforeFirst();
this.index = to;
} else if (to == AFTER_LAST) {
rs.afterLast();
this.index = to;
} else if (index == AFTER_LAST) {
rs.first();
this.index = 0;
} else {// to>-1
if (index == AFTER_LAST || index == BEFORE_FIRST) {
rs.first();
index = 0;
}
while (index < to) {
boolean flag = rs.next();
index++;
if (!flag)
break;
}
while (index > to) {
boolean flag = rs.previous();
index--;
if (!flag)
break;
}
if (index == to)
return;
throw new RuntimeException("Can not move to index :" + index + "->" + to);
}
}
private void ensureOpen() {
if (rs == null) {
throw new IllegalArgumentException("The result set has been closed.");
}
}
/**
* 关闭ResultSet和statement
*
* @throws SQLException
*/
public void close() {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
}
@Override
public Iterator<T> iterator() {
ensureOpen();
return objs.iterator();
}
}