package org.zstack.core.db;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.db.DatabaseFacadeImpl.EntityInfo;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.utils.DebugUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import javax.persistence.Query;
import javax.persistence.metamodel.SingularAttribute;
import java.util.*;
/**
* Created by xing5 on 2016/6/29.
*/
@Configurable(preConstruction=true,autowire= Autowire.BY_TYPE,dependencyCheck=true)
public class UpdateQueryImpl implements UpdateQuery {
private static CLogger logger = Utils.getLogger(UpdateQueryImpl.class);
@Autowired
private DatabaseFacadeImpl dbf;
private Class entityClass;
private Map<SingularAttribute, Object> setValues = new HashMap<>();
private Map<SingularAttribute, List<Cond>> andConditions = new HashMap<>();
private class Cond {
SingularAttribute attr;
Op op;
Object val;
}
UpdateQuery entity(Class clazz) {
entityClass = clazz;
return this;
}
@Override
public UpdateQuery set(SingularAttribute attr, Object val) {
if (setValues.containsKey(attr)) {
throw new CloudRuntimeException(String.format("unable to set a column[%s] twice", attr.getName()));
}
setValues.put(attr, val);
return this;
}
@Override
public UpdateQuery condAnd(SingularAttribute attr, Op op, Object val) {
if ((op == Op.IN || op == Op.NOT_IN) && !(val instanceof Collection)) {
throw new CloudRuntimeException(String.format("for operation IN or NOT IN, a Collection value is expected, but %s got", val.getClass()));
}
Cond cond = new Cond();
cond.attr = attr;
cond.op = op;
cond.val = val;
List<Cond> conds = andConditions.get(attr);
if (conds == null) {
conds = new ArrayList<>();
andConditions.put(attr, conds);
}
conds.add(cond);
return this;
}
@Override
public UpdateQuery eq(SingularAttribute attr, Object val) {
condAnd(attr, Op.EQ, val);
return this;
}
@Override
public UpdateQuery notEq(SingularAttribute attr, Object val) {
condAnd(attr, Op.NOT_EQ, val);
return this;
}
@Override
public UpdateQuery in(SingularAttribute attr, Collection val) {
condAnd(attr, Op.IN, val);
return this;
}
@Override
public UpdateQuery notIn(SingularAttribute attr, Collection val) {
condAnd(attr, Op.NOT_IN, val);
return this;
}
@Override
public UpdateQuery isNull(SingularAttribute attr) {
condAnd(attr, Op.NULL, null);
return this;
}
@Override
public UpdateQuery notNull(SingularAttribute attr) {
condAnd(attr, Op.NOT_NULL, null);
return this;
}
@Override
public UpdateQuery gt(SingularAttribute attr, Object val) {
condAnd(attr, Op.GT, val);
return this;
}
@Override
public UpdateQuery gte(SingularAttribute attr, Object val) {
condAnd(attr, Op.GTE, val);
return this;
}
@Override
public UpdateQuery lt(SingularAttribute attr, Object val) {
condAnd(attr, Op.LT, val);
return this;
}
@Override
public UpdateQuery lte(SingularAttribute attr, Object val) {
condAnd(attr, Op.LTE, val);
return this;
}
@Override
public UpdateQuery like(SingularAttribute attr, Object val) {
condAnd(attr, Op.LIKE, val);
return this;
}
@Override
public UpdateQuery notLike(SingularAttribute attr, Object val) {
condAnd(attr, Op.NOT_LIKE, val);
return this;
}
private String where() {
if (andConditions.isEmpty()) {
return null;
}
List<String> condstrs = new ArrayList<>();
for (List<Cond> conds : andConditions.values()) {
for (int i=0; i<conds.size(); i++) {
Cond cond = conds.get(i);
String condName = String.format("cond_%s_%s", cond.attr.getName(), i);
if (Op.IN == cond.op || Op.NOT_IN == cond.op) {
condstrs.add(String.format("vo.%s %s (:%s)", cond.attr.getName(), cond.op.toString(), condName));
} else if (Op.NULL == cond.op || Op.NOT_NULL == cond.op) {
condstrs.add(String.format("vo.%s %s", cond.attr.getName(), cond.op));
} else {
condstrs.add(String.format("vo.%s %s :%s", cond.attr.getName(), cond.op.toString(), condName));
}
}
}
return StringUtils.join(condstrs, " AND ");
}
private void fillConditions(Query q) {
for (List<Cond> conds : andConditions.values()) {
for (int i=0; i<conds.size(); i++) {
Cond cond = conds.get(i);
if (Op.NULL == cond.op || Op.NOT_NULL == cond.op) {
continue;
}
q.setParameter("cond_" + cond.attr.getName() + String.format("_%s", i), cond.val);
}
}
}
@Override
@Transactional
public int hardDelete() {
DebugUtils.Assert(entityClass!=null, "entity class cannot be null");
StringBuilder sb = new StringBuilder(String.format("DELETE FROM %s vo", entityClass.getSimpleName()));
String where = where();
if (where != null) {
sb.append(String.format(" WHERE %s", where));
}
String sql = sb.toString();
if (logger.isTraceEnabled()) {
logger.trace(sql);
}
Query q = dbf.getEntityManager().createQuery(sql);
if (where != null) {
fillConditions(q);
}
int ret = q.executeUpdate();
dbf.getEntityManager().flush();
return ret;
}
@Override
@Transactional
public void delete() {
DebugUtils.Assert(entityClass!=null, "entity class cannot be null");
EntityInfo info = dbf.getEntityInfo(entityClass);
DebugUtils.Assert(entityClass!=null, "entity class cannot be null");
StringBuilder sb = new StringBuilder(String.format("SELECT vo.%s FROM %s vo", info.voPrimaryKeyField.getName(),
entityClass.getSimpleName()));
String where = where();
if (where != null) {
sb.append(String.format(" WHERE %s", where));
}
String sql = sb.toString();
if (logger.isTraceEnabled()) {
logger.trace(sql);
}
Query q = dbf.getEntityManager().createQuery(sql);
if (where != null) {
fillConditions(q);
}
List ids = q.getResultList();
if (ids.isEmpty()) {
return;
}
info.removeByPrimaryKeys(ids);
}
@Override
@Transactional
public void update() {
DebugUtils.Assert(entityClass!=null, "entity class cannot be null");
StringBuilder sb = new StringBuilder(String.format("UPDATE %s vo", entityClass.getSimpleName()));
List<String> setters = new ArrayList<>();
for (Map.Entry<SingularAttribute, Object> e : setValues.entrySet()) {
setters.add(String.format("vo.%s=:%s", e.getKey().getName(), e.getKey().getName()));
}
sb.append(String.format(" SET %s ", StringUtils.join(setters, ",")));
String where = where();
if (where != null) {
sb.append(String.format(" WHERE %s", where));
}
String sql = sb.toString();
if (logger.isTraceEnabled()) {
logger.trace(sql);
}
Query q = dbf.getEntityManager().createQuery(sql);
for (Map.Entry<SingularAttribute, Object> e : setValues.entrySet()) {
q.setParameter(e.getKey().getName(), e.getValue());
}
if (where != null) {
fillConditions(q);
}
q.executeUpdate();
dbf.getEntityManager().flush();
}
}