package jef.database;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.FetchType;
import jef.database.annotation.Cascade;
import jef.database.dialect.type.ColumnMapping;
import jef.database.meta.AbstractRefField;
import jef.database.meta.ISelectProvider;
import jef.database.meta.ITableMetadata;
import jef.database.meta.JoinKey;
import jef.database.meta.JoinPath;
import jef.database.meta.MetaHolder;
import jef.database.meta.Reference;
import jef.database.meta.TupleMetadata;
import jef.database.query.ReferenceType;
import jef.tools.Assert;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanWrapper;
import com.google.common.base.Objects;
final class CascadeUtil {
static int deleteWithRefInTransaction(IQueryableEntity source, Session trans, int minPriority) throws SQLException {
return deleteCascadeByQuery(source, trans, true, true, minPriority);
}
// 删除单个对象或请求和其所有级联引用
private static int deleteCascadeByQuery(IQueryableEntity source, Session trans, boolean doSelect, boolean delSubFirst, int minPriority) throws SQLException {
ITableMetadata meta = MetaHolder.getMeta(source);
List<Reference> delrefs = new ArrayList<Reference>();
for (AbstractRefField f : meta.getRefFieldsByName().values()) {
if (!f.canDelete() || f.getPriority() < minPriority) {
continue;
}
Reference ref = f.getReference();
if (ref.getType() == ReferenceType.ONE_TO_MANY || ref.getType() == ReferenceType.ONE_TO_ONE) {
delrefs.add(ref);
} else if (ref.getHint() != null && ref.getHint().getRelationTable() != null) {
delrefs.add(ref);
}
}
if (delrefs.isEmpty()) {
return trans.delete0(source.getQuery()); // 没有需级联删除的引用,当做普通删除即可
}
// 要不要做
List<IQueryableEntity> objs;
if (doSelect) {
source.getQuery().setCascade(false);
objs = trans.select(source);
} else {
objs = Arrays.asList(source);
}
if (delSubFirst) {
@SuppressWarnings("unused")
int n = deleteChildren(objs, trans, delrefs);
return trans.delete0(source.getQuery());
} else {
int n = trans.delete0(source.getQuery()); // 先删除自身,在删除引用,防止在解析引用时出现循环引用又来删除自身
deleteChildren(objs, trans, delrefs);
return n;
}
}
private static int deleteChildren(List<IQueryableEntity> objs, Session trans, List<Reference> refs) throws SQLException {
int count = 0;
for (IQueryableEntity obj : objs) {
for (Reference ref : refs) {
// 前面已经检查过,无需再次检查
// if (ref.getType() == ReferenceType.ONE_TO_MANY ||
// ref.getType() == ReferenceType.ONE_TO_ONE) {
BeanWrapper bean = BeanWrapper.wrap(obj);
count += doDeleteRef(trans, bean, ref);
// }
}
}
return count;
}
/**
* 目的是清理掉对象内的延迟加载上下文,并将延迟加载未处理的字段记录下来
*
* 修改原因:算法优化——对于没有调用过延迟加载的关系,认为是无需参加级联操作的关系。
* 修改前:没有调用过的延迟加载,在级联触发之前会自动调用,从而先加载,然后再次加载,并且进行比对,比对后发现没有修改过,然后什么也不做。
* 改用这种优化算法后,对于没有触发的延迟加载最少可以省去两次查询操作。
*
* @param obj
* @return
*/
private static Set<String> clearLazy(DataObject obj) {
// return Collections.emptySet();
Set<String> unloaded;
if (obj.lazyload != null) {
unloaded = new HashSet<String>();
Map<String, Integer> fields = obj.lazyload.getProcessor().getOnFields();
for (String s : fields.keySet()) {
if (obj.lazyload.needLoad(s) > -1) {// 将尚未执行过延迟加载的字段记录下来,后续忽略更新
unloaded.add(s);
}
}
} else {
unloaded = Collections.emptySet();
}
obj.clearQuery();
return unloaded;
}
/*
* smartMode: 智能模式,当开启后自动忽略掉那些没有set过的property
*/
static void insertWithRefInTransaction(List<IQueryableEntity> list, Session trans, boolean smartMode, int minPriority) throws SQLException {
if (list.isEmpty())
return;
boolean single = list.size() == 1;
ITableMetadata meta = MetaHolder.getMeta(list.get(0));
for (IQueryableEntity obj : list) {
// 在维护端操作之前
Set<String> lazyloadSkip = clearLazy((DataObject) obj);
BeanWrapper bean = BeanWrapper.wrap(obj);
for (AbstractRefField f : meta.getRefFieldsByName().values()) {
if (lazyloadSkip.contains(f.getName()))
continue;
// 无需执行级联操作
if (!f.canInsert() || f.getPriority() < minPriority) {
continue;
}
Reference ref = f.getReference();
Object value = f.getField().get(obj);
if (value != null && ref.getType() == ReferenceType.MANY_TO_ONE) { // 多对一的话,提前维护子表
doInsertRef1(trans, value, bean, ref, false);
}
}
}
if (single) {
trans.insert0(list.get(0), null, smartMode);
} else {
trans.batchInsert(list, smartMode);
}
for (IQueryableEntity obj : list) {
BeanWrapper bean = BeanWrapper.wrap(obj);
// 在维护端操作之后
for (AbstractRefField f : meta.getRefFieldsByName().values()) {
if (!f.canInsert() || f.getPriority() < minPriority)
continue;
Reference ref = f.getReference();
Object value = f.getField().get(obj);
if (value == null)
continue;
// 其他几种情况,维护子表
switch (ref.getType()) {
case ONE_TO_ONE:
doInsertRef1(trans, value, bean, ref, true);
break;
case ONE_TO_MANY:
doInsertRefN(trans, value, bean, f);
break;
case MANY_TO_MANY:
doInsertRefNN(trans, value, bean, f);
default:
}
}
}
}
static int updateWithRefInTransaction(IQueryableEntity obj, Session trans, int minPriority) throws SQLException {
Collection<AbstractRefField> refs = MetaHolder.getMeta(obj).getRefFieldsByName().values();
Set<String> lazyloadSkip = clearLazy((DataObject) obj);
int result = 0;
// 在维护端操作之前
for (AbstractRefField f : refs) {
if (lazyloadSkip.contains(f.getName()))
continue;
if (!f.canUpdate() || f.getPriority() < minPriority) {
continue;
}
Reference ref = f.getReference();
Object value = f.getField().get(obj);
if (ref.getType() == ReferenceType.MANY_TO_ONE) { // 多对一的话,提前维护子表
doUpdateRef1(trans, value, BeanWrapper.wrap(obj), ref, false);
}
}
// 维护端操作
if (obj.needUpdate())
result = trans.update0(obj, null);
// 维护端操作之后
for (AbstractRefField f : refs) {
// 无需执行级联操作
if (lazyloadSkip.contains(f.getName()))
continue;
if (!f.canUpdate() || f.getPriority() < minPriority) {
continue;
}
Reference ref = f.getReference();
Object value = f.getField().get(obj);
BeanWrapper bean = BeanWrapper.wrap(obj);
switch (ref.getType()) {
case ONE_TO_MANY:
doUpdateRefN(trans, value, bean, f, true);
break;
case MANY_TO_MANY:
doUpdateRefNN(trans, value, bean, f);
break;
case ONE_TO_ONE:
doUpdateRef1(trans, value, bean, ref, true);
default:
break;
}
}
return result;
}
// 维护插入操作的子表(对一操作,此处不分多对一还是单对一。因此有些问题。)
/**
*
* @param trans
* @param value
* @param bean
* @param ref
* @param reverse
* 反向。 (当子表先操作,父表后操作(此处一般指insert或update操作),称为正向。)
* 当先操作父表,后操作字表,称为反向。正向情况下,要将子表操作完成后 关联键值赋值到父表中。
* 反向情况下,在操作完成前,就将父表关键字值赋值给子表
*
* @throws SQLException
*/
private static void doInsertRef1(Session trans, Object value, BeanWrapper bean, Reference ref, boolean reverse) throws SQLException {
IQueryableEntity d = cast(value, ref.getTargetType());
BeanWrapper bwSub = BeanWrapper.wrap(d);
JoinPath jp = ref.toJoinPath();
if (reverse) {// 反向维护关联键值
for (JoinKey jk : jp.getJoinKeys()) {
Object parentKey = bean.getPropertyValue(jk.getLeft().name());
Object subKey = bwSub.getPropertyValue(jk.getRightAsField().name());
if (!Objects.equal(parentKey, subKey)) {
bwSub.setPropertyValue(jk.getRightAsField().name(), parentKey);
}
}
}
checkAndInsert(trans, Arrays.asList(d), true);
if (!reverse) {// 正向维护关联键值
for (JoinKey jk : jp.getJoinKeys()) {
Object subKey = bwSub.getPropertyValue(jk.getRightAsField().name());
Object parentKey = bean.getPropertyValue(jk.getLeft().name());
if (!Objects.equal(parentKey, subKey)) {
bean.setPropertyValue(jk.getLeft().name(), subKey);
}
}
}
}
/**
* 对多关系,无论是一对多还是多对多,目前都是先插入父表,再插入子表的……因此其实都是反向模式
*
* @param trans
* 事务
* @param value
* 级联对象
* @param bean
* 父对象反射包装
* @param f
* 引用关系
* @throws SQLException
*/
private static void doInsertRefN(Session trans, Object value, BeanWrapper bean, AbstractRefField f) throws SQLException {
Reference ref = f.getReference();
Map<String, Object> map = new HashMap<String, Object>();
for (JoinKey jk : ref.toJoinPath().getJoinKeys()) {
if (bean.isReadableProperty(jk.getLeft().name())) {
Object refValue = bean.getPropertyValue(jk.getLeft().name());
map.put(jk.getRightAsField().name(), refValue);
}
}
Collection<? extends IQueryableEntity> list = castToList(value, f);
for (IQueryableEntity d : list) {
BeanWrapper bwSub = BeanWrapper.wrap(d);
for (Entry<String, Object> e : map.entrySet()) {
Object newValue = e.getValue();
Object oldValue = bwSub.getPropertyValue(e.getKey());
if (!Objects.equal(oldValue, newValue)) {
bwSub.setPropertyValue(e.getKey(), newValue);
}
}
}
checkAndInsert(trans, list, true);
}
/**
* 对多关系,无论是一对多还是多对多,目前都是先插入父表,再插入子表的……因此其实都是反向模式
*
* @param trans
* 事务
* @param value
* 级联对象
* @param bean
* 父对象反射包装
* @param f
* 引用关系
* @throws SQLException
*/
private static void doInsertRefNN(Session trans, Object value, BeanWrapper bean, AbstractRefField f) throws SQLException {
Reference ref = f.getReference();
if (ref.getHint() != null && ref.getHint().getRelationTable() == null) {
doInsertRefN(trans, value, bean, f);
return;
}
// 对于用过中间表的多对多关系,其级联对象关联位于一张隐含的表中,和当前对象的单表字段没有关系。
// 故修改数值一致性操作省略
Collection<? extends IQueryableEntity> list = castToList(value, f);
// 目标表处理
checkAndInsert(trans, list, true);
// 关系表处理
List<VarObject> relations = generateRelationData(ref, list, bean);
checkAndInsert(trans, relations, false);
}
/**
* 产生关系表数据
*/
private static List<VarObject> generateRelationData(Reference ref, Collection<? extends IQueryableEntity> list, BeanWrapper bean) {
List<VarObject> relations = new ArrayList<VarObject>();
JoinPath path = ref.getHint();
TupleMetadata meta = path.getRelationTable();
JoinPath path2 = path.getRelationToTarget();
for (IQueryableEntity d : list) {
VarObject obj = meta.newInstance();
// 主对象赋值
for (JoinKey jk : path.getJoinKeys()) {
obj.put(jk.getRightAsField().name(), bean.getPropertyValue(jk.getLeft().name()));
}
// 级联对象赋值
BeanWrapper sub = BeanWrapper.wrap(d);
for (JoinKey jk : path2.getJoinKeys()) {
obj.put(jk.getLeft().name(), sub.getPropertyValue(jk.getRightAsField().name()));
}
relations.add(obj);
}
return relations;
}
private static int doDeleteRef(Session trans, BeanWrapper bean, Reference ref) throws SQLException {
IQueryableEntity rObj;
if (ref.getHint() != null && ref.getHint().getRelationTable() != null) {
rObj = ref.getHint().getRelationTable().newInstance();
} else {
rObj = ref.getTargetType().newInstance();
}
for (JoinKey r : ref.toJoinPath().getJoinKeys()) {
rObj.getQuery().addCondition(r.getRightAsField(), bean.getPropertyValue(r.getLeft().name()));
}
return deleteCascadeByQuery(rObj, trans, true, false, 0);
}
private static Map<List<?>, IQueryableEntity> doSelectRef(Session trans, BeanWrapper bean, Reference ref) throws SQLException {
Map<List<?>, IQueryableEntity> result = new HashMap<List<?>, IQueryableEntity>();
IQueryableEntity rObj = ref.getTargetType().newInstance();
for (JoinKey r : ref.toJoinPath().getJoinKeys()) {
rObj.getQuery().addCondition(r.getRightAsField(), bean.getPropertyValue(r.getLeft().name()));
}
List<? extends IQueryableEntity> list = trans.select(rObj);// 查出旧的引用对象
for (IQueryableEntity e : list) {
List<Object> key = DbUtils.getPrimaryKeyValue(e);
Assert.notNull(key);
result.put(key, e);
}
return result;// 按主键为key记录每个引用的对象
}
// 维护更新操作的子表
private static void doUpdateRef1(Session trans, Object value, BeanWrapper bean, Reference ref, boolean doDelete) throws SQLException {
if (value == null) {
if (doDelete) {
doDeleteRef(trans, bean, ref);
}
return;
}
IQueryableEntity d = cast(value, ref.getTargetType());
checkAndInsert(trans, Arrays.asList(d), true);
BeanWrapper bwSub = BeanWrapper.wrap(d);
JoinPath jp = ref.toJoinPath();
for (JoinKey jk : jp.getJoinKeys()) {
Object newValue = bwSub.getPropertyValue(jk.getRightAsField().name());
Object oldValue = bean.getPropertyValue(jk.getLeft().name());
if (!Objects.equal(oldValue, newValue)) {
bean.setPropertyValue(jk.getLeft().name(), newValue);
}
}
}
static <T extends IQueryableEntity> T compareToNewUpdateMap(T changedObj, T oldObj) {
Assert.isTrue(Objects.equal(DbUtils.getPrimaryKeyValue(changedObj), DbUtils.getPKValueSafe(oldObj)), "For consistence, the two parameter must hava equally primary keys.");
ITableMetadata m = MetaHolder.getMeta(oldObj);
Map<Field, Object> used = null;
boolean safeMerge = ORMConfig.getInstance().isSafeMerge();
if (safeMerge) {
used = new HashMap<Field, Object>(changedObj.getUpdateValueMap());
}
changedObj.getUpdateValueMap().clear();
for (ColumnMapping mType : m.getColumns()) {
Field field = mType.field();
if (mType.isPk()) {
continue;
}
boolean notUsed=!used.containsKey(field);
if(mType.isGenerated() && notUsed){
continue;
}
if (safeMerge && notUsed) {// 智能更新下,发现字段未被设过值,就不予更新
continue;
}
Object valueNew = mType.getFieldAccessor().get(changedObj);
Object valueOld =mType.getFieldAccessor().get(oldObj);
if (!Objects.equal(valueNew, valueOld)) {
changedObj.prepareUpdate(field, valueNew, true);
}
}
return changedObj;
}
private static void doUpdateRefN(Session trans, Object value, BeanWrapper bean, AbstractRefField f, boolean doDeletion) throws SQLException {
// 2011-12-22:refactor logic, avoid to use delete-insert algorithm.
// 2014-5-1: add rule, if refByMany then no deletion
// 取得新旧的引用关系List.查出旧的引用数据集合,并按主键存放
Reference ref = f.getReference();
Map<List<?>, IQueryableEntity> olds = doSelectRef(trans, bean, ref);
// 新的引用关系
Collection<? extends IQueryableEntity> list = castToList(value, f);
// 计算要更新要子表的字段和数值
Map<String, Object> map = new HashMap<String, Object>();
for (JoinKey jk : ref.toJoinPath().getJoinKeys()) {
if (bean.isReadableProperty(jk.getLeft().name())) { // JoinKey的左边,就是主表中的值
Object refValue = bean.getPropertyValue(jk.getLeft().name()); // 得到主表中的引用键值
map.put(jk.getRightAsField().name(), refValue); // 记录要更新到子表记录中的字段和值,引用键值和自表中的字段名对应
}
}
List<IQueryableEntity> toAdd = new ArrayList<IQueryableEntity>();
// 更新新的子表数据到
for (IQueryableEntity d : list) {
BeanWrapper bwSub = BeanWrapper.wrap(d);
// 更新内存数据(将新的引用关系中的引用键更新为何主表记录一致。)修复数据一致性。
for (Entry<String, Object> e : map.entrySet()) {
Object newValue = e.getValue();
Object oldValue = bwSub.getPropertyValue(e.getKey());
if (!Objects.equal(oldValue, newValue)) {
bwSub.setPropertyValue(e.getKey(), newValue);
}
}
// 对照旧值进行插入或更新
List<Object> pks = DbUtils.getPrimaryKeyValue(d);
IQueryableEntity old = null;
if (pks != null) {
old = olds.remove(pks);
}
if (old != null) {// 存在旧值,更新处理
DbUtils.compareToUpdateMap(d, old);
if (old.needUpdate()) {
updateWithRefInTransaction(old, trans, 0);
}
} else {
toAdd.add(d);// 无旧值,插入处理
}
}
insertWithRefInTransaction(toAdd, trans, true, 0);
// 旧值中有而新值中没有,删除处理
if (doDeletion) {
// 将剩余的子表数据删掉
for (IQueryableEntity d : olds.values()) {
deleteCascadeByQuery(d, trans, false, true, 0);
}
}
}
/**
* 多对多的更新操作
*
* @param trans
* 事务
* @param value
* 级联对象
* @param bean
* 父对象
* @param f
* 引用字段
* @param b
* 是否执行删除
* @throws SQLException
*/
private static void doUpdateRefNN(Session trans, Object value, BeanWrapper bean, AbstractRefField f) throws SQLException {
Reference ref = f.getReference();
if (ref.getHint() != null && ref.getHint().getRelationTable() == null) {
doUpdateRefN(trans, value, bean, f, false);
return;
}
// 检查目标表
// 对于用过中间表的多对多关系,其级联对象关联位于一张隐含的表中,和当前对象的单表字段没有关系。
// 故修改数值一致性操作省略
Collection<? extends IQueryableEntity> list = castToList(value, f);
// 目标表处理
checkAndInsert(trans, list, true);
// 维护关系表
List<VarObject> relations = generateRelationData(ref, list, bean);
doDeleteRef(trans, bean, ref);
trans.batchInsert(relations);
}
/**
* 按主键去检查每条字表记录,有的就更新,没有的就插入
*
* @param trans
* @param subs
* 级联对象
* @param doUpdate
* 执行更新
* @throws SQLException
*/
private static void checkAndInsert(Session trans, Collection<? extends IQueryableEntity> subs, boolean doUpdate) throws SQLException {
if (subs == null)
return;
List<IQueryableEntity> toAdd = new ArrayList<IQueryableEntity>();
for (IQueryableEntity d : subs) {
if (DbUtils.getPrimaryKeyValue(d) != null) {
d.getQuery().clearQuery();
d.getQuery().setCascadeViaOuterJoin(false);
List<IQueryableEntity> oldValue = trans.select(d);
if (oldValue.size() > 0) {// 更新
if (doUpdate) {
IQueryableEntity old = oldValue.get(0);
DbUtils.compareToUpdateMap(d, old);
if (old.needUpdate()) {
updateWithRefInTransaction(old, trans, 0);
}
}
continue;
}
}
toAdd.add(d);
}
// 插入
insertWithRefInTransaction(toAdd, trans, ORMConfig.getInstance().isDynamicInsert(), 0);
}
@SuppressWarnings("unchecked")
private static <T extends IQueryableEntity> T cast(Object obj, ITableMetadata c) {
if (obj == null)
return null;
if (c.getThisType().isAssignableFrom(obj.getClass())) {
return (T) obj;
} else {
throw new ClassCastException(obj.getClass().getName() + " can not cast to " + c.getName());
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static <T extends IQueryableEntity> Collection<T> castToList(Object obj, AbstractRefField ref) {
if (obj == null)
return Collections.EMPTY_LIST;
ITableMetadata c = ref.getReference().getTargetType();
if (obj instanceof Collection<?>) {// 其他的Clection
Collection<?> collection = (Collection<?>) obj;
List<T> list = new ArrayList<T>();
for (Object o : collection) {
if (o == null)
continue;
if (c.getThisType().isAssignableFrom(o.getClass())) {
list.add((T) o);
} else {
throw new IllegalArgumentException("There's a value can't cast to class:" + c);
}
}
return list;
} else if (obj.getClass().isArray()) {
List<T> list = new ArrayList<T>();
for (Object element : (Object[]) obj) {
if (c.getThisType().isAssignableFrom(element.getClass())) {
list.add((T) element);
} else {
throw new IllegalArgumentException("There's a value can't cast to class:" + c);
}
}
return list;
} else if (obj instanceof Map) {
Cascade cascade = ref.getCascadeInfo();
if (cascade == null || StringUtils.isEmpty(cascade.valueOfMap())) {
Collection<T> collection = ((Map) obj).values();
return collection;
} else {
List<T> result = new ArrayList<T>();
for (Map.Entry<String, ?> entry : ((Map<String, ?>) obj).entrySet()) {
Object target = c.newInstance();
BeanWrapper bw = BeanWrapper.wrap(target);
bw.setPropertyValue(cascade.keyOfMap(), entry.getKey());
bw.setPropertyValue(cascade.valueOfMap(), String.valueOf(entry.getValue()));
result.add((T) target);
}
return result;
}
}
throw new IllegalArgumentException("Unknow set class:" + obj.getClass());
}
public static ReverseReferenceProcessor getReverseProcessor(Reference ref) {
List<Reference> reverses = ref.getExistReverseReference();
if (reverses == null || reverses.isEmpty())
return null;
for (Iterator<Reference> iter = reverses.iterator(); iter.hasNext();) {
// 凡是走到这里的都是 对多关系的反向关系,因此如果是 nv1才处理,nvn暂时不处理
// 应该说只剩下一种关系,那就是 Nv1关系
Reference reverse = iter.next();
if (!reverse.getType().isToOne()) {
// == ReferenceType.MANY_TO_MANY ||== ReferenceType.ONE_TO_MANY
iter.remove();
}
}
return new ReverseReferenceProcessor(reverses);
}
// 填充1vsN的字段
// 每次处理一个关系: JEF中一个关系允许有多个字段被填充
// <T extends DataObject> is true
// .get(entry.getKey())
protected static <T> void fillOneVsManyReference(List<T> list, Map.Entry<Reference, List<AbstractRefField>> entry, Map<Reference, List<Condition>> filters, Session session) throws SQLException {
if (list.isEmpty())
return;
CascadeLoaderTask task = new CascadeLoaderTask(entry, filters);
if (list.size() > 1000 || lazy(entry.getValue())) {// 不对超过1000个元素进行一对多填充//必须使用延迟加载
markTask(task, list, session);
} else {
for (T obj : list) {
task.process(session, obj);
}
}
}
static void markTask(LazyLoadTask task, List<?> objs, Session session) {
if (objs.isEmpty())
return;
DataObject obj = (DataObject) objs.get(0);
if (obj.lazyload != null) {
obj.lazyload.getProcessor().register(task);
return;
}
LazyLoadProcessor processor = new LazyLoadProcessor(task, session);
for (Object o : objs) {
DataObject dobj = (DataObject) o;
dobj.lazyload = new LazyLoadContext(processor);
}
}
static private boolean lazy(List<AbstractRefField> value) {
if (ORMConfig.getInstance().isEnableLazyLoad()) {
for (ISelectProvider prov : value) {
AbstractRefField refd = (AbstractRefField) prov;
if (refd.getFetch() == FetchType.EAGER) {
return false;
}
}
return true;
}
return false;
}
}