
com.venky.swf.db.table.ModelInvocationHandler Maven / Gradle / Ivy
Show all versions of swf-db Show documentation
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.venky.swf.db.table;
import com.venky.cache.Cache;
import com.venky.core.collections.IgnoreCaseList;
import com.venky.core.collections.LowerCaseStringCache;
import com.venky.core.collections.SequenceMap;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.string.StringUtil;
import com.venky.core.util.MultiException;
import com.venky.core.util.ObjectUtil;
import com.venky.extension.Registry;
import com.venky.swf.db.Database;
import com.venky.swf.db.JdbcTypeHelper.TypeConverter;
import com.venky.swf.db.JdbcTypeHelper.TypeRef;
import com.venky.swf.db.annotations.column.COLUMN_DEF;
import com.venky.swf.db.annotations.column.IS_VIRTUAL;
import com.venky.swf.db.annotations.column.defaulting.StandardDefaulter;
import com.venky.swf.db.annotations.column.pm.PARTICIPANT;
import com.venky.swf.db.annotations.column.relationship.CONNECTED_VIA;
import com.venky.swf.db.annotations.column.validations.processors.DateFormatValidator;
import com.venky.swf.db.annotations.column.validations.processors.EnumerationValidator;
import com.venky.swf.db.annotations.column.validations.processors.ExactLengthValidator;
import com.venky.swf.db.annotations.column.validations.processors.FieldValidator;
import com.venky.swf.db.annotations.column.validations.processors.IntegerRangeValidator;
import com.venky.swf.db.annotations.column.validations.processors.MaxLengthValidator;
import com.venky.swf.db.annotations.column.validations.processors.MinLengthValidator;
import com.venky.swf.db.annotations.column.validations.processors.NotNullValidator;
import com.venky.swf.db.annotations.column.validations.processors.NumericRangeValidator;
import com.venky.swf.db.annotations.column.validations.processors.RegExValidator;
import com.venky.swf.db.annotations.model.CONFIGURATION;
import com.venky.swf.db.annotations.model.validations.ModelValidator;
import com.venky.swf.db.annotations.model.validations.UniqueKeyValidator;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.db.table.Table.ColumnDescriptor;
import com.venky.swf.exceptions.AccessDeniedException;
import com.venky.swf.routing.Config;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Delete;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Insert;
import com.venky.swf.sql.Operator;
import com.venky.swf.sql.Select;
import com.venky.swf.sql.Update;
import com.venky.swf.sql.parser.SQLExpressionParser;
import com.venky.swf.util.SharedKeys;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
/**
*
* @author venky
*/
public class ModelInvocationHandler implements InvocationHandler {
private Record record = null;
private Class extends Model> modelClass = null;
private List virtualFields = new IgnoreCaseList(false);
private String modelName = null;
private transient Model proxy = null;
private transient ModelReflector extends Model> reflector = null;
@SuppressWarnings("unchecked")
public ModelReflector getReflector() {
if (reflector == null) {
reflector = ModelReflector.instance(modelClass);
}
return (ModelReflector) reflector;
}
public String getModelName(){
return modelName;
}
public String getPool(){
return getReflector().getPool();
}
public Class extends Model> getModelClass(){
return modelClass;
}
/**
* Used for serialization.:
*/
protected ModelInvocationHandler() {
}
public ModelInvocationHandler(Class extends Model> modelClass, Record record) {
this.record = record;
this.modelClass = modelClass;
this.reflector = ModelReflector.instance(modelClass);
this.modelName = Table.getSimpleModelClassName(reflector.getTableName());
this.virtualFields = reflector.getVirtualFields();
record.startTracking(false);
}
public ModelInvocationHandler(Class extends Model> modelClass, Model proxy){
this.proxy = proxy;
this.modelClass = modelClass;
this.reflector = ModelReflector.instance(modelClass);
this.modelName = Table.getSimpleModelClassName(reflector.getTableName());
this.virtualFields = reflector.getVirtualFields();
this.record = null;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
bootStrapProxy(getModelClass().cast(proxy));
String mName = method.getName();
Class> retType = method.getReturnType();
Class>[] parameters = method.getParameterTypes();
Record record = getRawRecord();
if (getReflector().getFieldGetterSignatures().contains(getReflector().getSignature(method))) {
String fieldName = getReflector().getFieldName(method);
if (!virtualFields.contains(fieldName)){
ColumnDescriptor cd = getReflector().getColumnDescriptor(fieldName);
String columnName = cd.getName();
Object value = record.get(columnName);
TypeRef> ref =Database.getJdbcTypeHelper(getPool()).getTypeRef(retType);
TypeConverter> converter = ref.getTypeConverter();
if (value == null) {
COLUMN_DEF colDef = getReflector().getAnnotation(method, COLUMN_DEF.class);
if (colDef != null) {
value = StandardDefaulter.getDefaultValue(colDef.value(), colDef.args());
}
}
if (value == null){
if (retType.isPrimitive()){
return converter.valueOf(value);
}else {
return value;
}
} else if (retType.isInstance(value) && !ref.isLOB()) {
return value;
} else {
return converter.valueOf(value);
}
}
} else if (getReflector().getFieldSetterSignatures().contains(getReflector().getSignature(method))) {
String fieldName = StringUtil.underscorize(mName.substring(3));
if (!virtualFields.contains(fieldName)){
String columnName = getReflector().getColumnDescriptor(fieldName).getName();
return record.put(columnName, args[0]);
}
} else if (getReflector().getReferredModelGetters().contains(method)) {
if (!getReflector().isAnnotationPresent(method,IS_VIRTUAL.class)){
return getParent(method);
}
} else if (getReflector().getChildGetters().contains(method)) {
if (!getReflector().isAnnotationPresent(method,IS_VIRTUAL.class)){
CONNECTED_VIA join = getReflector().getAnnotation(method,CONNECTED_VIA.class);
if (join != null){
return getChildren(getReflector().getChildModelClass(method),join.value(),join.additional_join());
}else {
return getChildren(getReflector().getChildModelClass(method));
}
}
}
/* Optimization
for (Object impl: modelImplObjects){
try {
Method inModelImplClass = impl.getClass().getMethod(mName, parameters);
if (retType.isAssignableFrom(inModelImplClass.getReturnType())){
Timer timer = startTimer(inModelImplClass.toString());
try {
return inModelImplClass.invoke(impl, args);
}catch(InvocationTargetException ex){
throw ex.getCause();
}finally{
timer.stop();
}
}
}catch(NoSuchMethodException ex){
//
}
}
Method inCurrentClass = this.getClass().getMethod(mName, parameters);
if (retType.isAssignableFrom(inCurrentClass.getReturnType())) {
try {
return inCurrentClass.invoke(this, args);
}catch (InvocationTargetException ex){
throw ex.getCause();
}
} else {
throw new NoSuchMethodException("Donot know how to execute this method");
}
*/
Class> implClass = getMethodImplClass(method);
Object implObject = null;
if (implClass != null){
implObject = modelImplObjects.get(implClass);
}
if (implClass == null || implObject == null){
//implObject is null while constructing impls.
implClass = this.getClass();
implObject = this;
}
Method inImplClass = implClass.getMethod(mName, parameters);
if (retType.isAssignableFrom(inImplClass.getReturnType())) {
Timer timer = cat().startTimer(inImplClass.toString());
try {
if (implObject instanceof ModelInvocationHandler) {
((ModelInvocationHandler) implObject).bootStrapProxy(getModelClass().cast(proxy));
}
return inImplClass.invoke(implObject, args);
}catch (InvocationTargetException ex){
throw ex.getCause();
}finally{
timer.stop();
}
}else {
throw new NoSuchMethodException("Donot know how to execute " + getReflector().getSignature(method));
}
}
private SWFLogger cat() {
return Config.instance().getLogger(getClass().getName()+"."+getModelName());
}
@SuppressWarnings("unchecked")
public P getParent(Method parentGetter) {
Class
parentClass = (Class
) parentGetter.getReturnType();
String parentIdFieldName = StringUtil.underscorize(parentGetter.getName().substring(3) +"Id");
Method parentIdGetter = this.getReflector().getFieldGetter(parentIdFieldName);
Number parentId;
try {
parentId = (Number)parentIdGetter.invoke(proxy);
} catch (Exception e) {
throw new RuntimeException(parentIdFieldName,e);
}
P parent = null;
if (parentId != null) {
parent = Database.getTable(parentClass).get(parentId.longValue());
}
return parent;
}
public List getChildren(Class childClass){
Class extends Model> modelClass = getReflector().getModelClass();
ModelReflector> childReflector = ModelReflector.instance(childClass);
Expression expression = new Expression(childReflector.getPool(),Conjunction.OR);
for (String fieldName: childReflector.getFields()){
if (fieldName.endsWith("_ID")){
Method fieldGetter = childReflector.getFieldGetter(fieldName);
Method referredModelGetter = childReflector.getReferredModelGetterFor(fieldGetter);
if (referredModelGetter != null && ObjectUtil.equals(referredModelGetter.getReturnType().getSimpleName(),modelClass.getSimpleName())){
String columnName = childReflector.getColumnDescriptor(fieldName).getName();
expression.add(new Expression(childReflector.getPool(),columnName,Operator.EQ,proxy.getId()));
}
}
}
if (expression.isEmpty()){
throw new RuntimeException("Don;t know how to getChildren of kind " + childClass.getSimpleName() + " for " + modelClass.getSimpleName());
}
return getChildren(childClass,expression);
}
public List getChildren(Class childClass, String parentIdFieldName){
return getChildren(childClass,parentIdFieldName,null);
}
public List getChildren(Class childClass, String parentIdFieldName, String addnl_condition){
long parentId = proxy.getId();
ModelReflector childReflector = ModelReflector.instance(childClass);
String parentIdColumnName = childReflector.getColumnDescriptor(parentIdFieldName).getName();
Expression where = new Expression(getPool(),Conjunction.AND);
where.add(new Expression(getPool(),parentIdColumnName,Operator.EQ,new BindVariable(getPool(),parentId)));
if (!ObjectUtil.isVoid(addnl_condition)){
Expression addnl = new SQLExpressionParser(childClass).parse(addnl_condition);
where.add(addnl);
}
return getChildren(childClass, where);
}
public List getChildren(Class childClass, Expression expression){
Select q = new Select();
q.from(childClass);
q.where(expression);
q.orderBy(ModelReflector.instance(childClass).getOrderBy());
return q.execute(childClass);
}
public void setProxy(M proxy) {
this.proxy = proxy;
}
@SuppressWarnings("unchecked")
public M getProxy() {
return (M)proxy;
}
public boolean isAccessibleBy(User user){
return isAccessibleBy(user, getReflector().getModelClass());
}
public Set getParticipatingRoles(User user){
return getParticipatingRoles(user,getReflector().getModelClass());
}
public Set getParticipatingRoles(User user,Class extends Model> asModel){
if (!getReflector().reflects(asModel)){
throw new AccessDeniedException();
}
return getParticipatingRoles(user.getParticipationOptions(asModel,getProxy()));
}
private Set getParticipatingRoles(Cache>> pGroupOptions){
Timer timer = cat().startTimer();
try {
ModelReflector extends Model> reflector = getReflector();
Set participatingRoles = new HashSet();
for (String participantRoleGroup : pGroupOptions.keySet()){
Map> pOptions = pGroupOptions.get(participantRoleGroup);
Set participatingRolesInCurrentGroup = new HashSet();
for (String referencedModelIdFieldName :pOptions.keySet()){
Number referenceValue = reflector.get(getRawRecord(),referencedModelIdFieldName);
PARTICIPANT participant = reflector.getAnnotation(reflector.getFieldGetter(referencedModelIdFieldName), PARTICIPANT.class);
if (participant.redundant() || pOptions.get(referencedModelIdFieldName) == null || pOptions.get(referencedModelIdFieldName).contains(referenceValue)){
participatingRolesInCurrentGroup.add(reflector.getParticipatingRole(referencedModelIdFieldName));
}
}
if (!pOptions.isEmpty() && participatingRolesInCurrentGroup.isEmpty()){
throw new AccessDeniedException(); // User is not a participant on the model.
}
participatingRoles.addAll(participatingRolesInCurrentGroup);
}
return participatingRoles;
}finally{
timer.stop();
}
}
public boolean isAccessibleBy(User user,Class extends Model> asModel){
Timer timer = cat().startTimer(null,Config.instance().isTimerAdditive());
try {
if (!getReflector().reflects(asModel)){
return false;
}
Set pRoles = getParticipatingRoles(user,asModel);
return (pRoles != null);// It is always true. returning false depends on AccessDeniedException being thrown.
}catch(AccessDeniedException ex){
return false;
}finally{
timer.stop();
}
}
public Record getRawRecord(){
if (record != null){
return record;
}else if (proxy != null){
ModelInvocationHandler invocationHandler = (ModelInvocationHandler) Proxy.getInvocationHandler(proxy);
if (invocationHandler != this){
return invocationHandler.getRawRecord();
}else{
return null;
}
}else {
return null;
}
}
public void setRawRecord(Record record){
if (this.record != null){
this.record = record;
}else if (proxy != null){
ModelInvocationHandler invocationHandler = (ModelInvocationHandler) Proxy.getInvocationHandler(proxy);
if (invocationHandler != this){
invocationHandler.setRawRecord(record);
}else{
this.record = record;
}
}else {
this.record = record;
}
}
public static void dispose(){
modelImplClassesCache.clear();
methodImplClassCache.clear();
}
public static M getProxy(Class modelClass, Record record) {
ModelReflector ref = ModelReflector.instance(modelClass);
try {
ModelInvocationHandler mImpl = new ModelInvocationHandler(modelClass, record);
M m = modelClass.cast(Proxy.newProxyInstance(modelClass.getClassLoader(), ref.getClassHierarchies().toArray(new Class>[]{}), mImpl));
mImpl.bootStrapProxy(m);
return m;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void bootStrapProxy(M m) {
if (proxy == null) {
setProxy(m);
if (modelImplObjects.isEmpty()){
List> modelImplClasses = getModelImplClasses(modelClass);
for (Class> implClass: modelImplClasses){
addModelImplObject(constructImpl(implClass, m));
}
}
}
}
@SuppressWarnings("unchecked")
private static Object constructImpl(Class> implClass, M m){
if (ModelImpl.class.isAssignableFrom(implClass)){
if (ModelImpl.class.equals(implClass)) {
return new ModelImpl(m);
}else {
ParameterizedType pt = (ParameterizedType)implClass.getGenericSuperclass();
Class extends Model> modelClass = (Class extends Model>) pt.getActualTypeArguments()[0];
try {
return implClass.getConstructor(modelClass).newInstance(m);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
throw new RuntimeException("Don't know how to instantiate " + implClass.getName());
}
private SequenceMap,Object> modelImplObjects = new SequenceMap,Object>();
private void addModelImplObject(Object o){
modelImplObjects.put(o.getClass(),o);
}
private Class> getMethodImplClass(Method m){
try{
return methodImplClassCache.get(getReflector().getModelClass()).get(m);
}catch(NullPointerException ex){
Config.instance().getLogger(getClass().getName()).log(Level.SEVERE, "Method" + m.getName() + ", ModelClass:"+ modelClass);
throw ex;
}
}
private static Cache,Cache>> methodImplClassCache = new Cache, Cache>>(0,0) {
/**
*
*/
private static final long serialVersionUID = -8303755398345923039L;
@Override
protected Cache> getValue(final Class extends Model> modelClass) {
return new Cache>(0,0) {
/**
*
*/
private static final long serialVersionUID = 1322249489351360016L;
@Override
protected Class> getValue(Method method) {
String mName = method.getName();
Class> retType = method.getReturnType();
Class>[] parameters = method.getParameterTypes();
for (Class> implClass: getModelImplClasses(modelClass)){
try {
Method inModelImplClass = implClass.getMethod(mName, parameters);
if (retType.isAssignableFrom(inModelImplClass.getReturnType())){
return implClass;
}
}catch (NoSuchMethodException ex){
//
}
}
return null;
}
};
}
};
private static Cache,List>> modelImplClassesCache = new Cache, List>>(0,0) {
/**
*
*/
private static final long serialVersionUID = 7544606584634901930L;
@Override
protected List> getValue(Class extends Model> modelClass) {
SequenceSet> modelClasses = ModelReflector.instance(modelClass).getClassHierarchies();
List> modelImplClasses = new ArrayList>();
for (Class> c : modelClasses){
String modelImplClassName = c.getName()+"Impl";
try {
Class> modelImplClass = Class.forName(modelImplClassName);
if (ModelImpl.class.isAssignableFrom(modelImplClass)){
modelImplClasses.add(modelImplClass);
}else {
throw new ClassCastException(modelImplClassName + " does not extend " + ModelImpl.class.getName());
}
}catch(ClassNotFoundException ex){
// Nothing
}
}
return modelImplClasses;
}
};
private static List> getModelImplClasses(Class modelClass){
return modelImplClassesCache.get(modelClass);
}
public void save() {
save(true);
}
public void save(boolean validate) {
save(validate,false);
}
public void save(boolean validate,boolean dryRun) {
if (!isDirty()) {
return;
}
if (validate){
validate();
}
beforeSave();
Record record = getRawRecord();
if (record.isNewRecord()) {
callExtensions("before.create");
if (!dryRun){
create();
callExtensions("after.create");
}
} else {
callExtensions("before.update");
if (!dryRun){
update();
callExtensions("after.update");
}
}
if (!dryRun) {
afterSave();
}
}
public void init(){
}
private static final Cache>> _fieldValidators = new Cache>>(0,0) {
/**
*
*/
private static final long serialVersionUID = -8174150221673158116L;
@Override
protected List> getValue(String pool) {
List> fieldValidators = new ArrayList>();
fieldValidators.add(new ExactLengthValidator(pool));
fieldValidators.add(new MaxLengthValidator(pool));
fieldValidators.add(new MinLengthValidator(pool));
fieldValidators.add(new NotNullValidator(pool));
fieldValidators.add(new RegExValidator(pool));
fieldValidators.add(new EnumerationValidator(pool));
fieldValidators.add(new DateFormatValidator(pool));
fieldValidators.add(new NumericRangeValidator(pool));
fieldValidators.add(new IntegerRangeValidator(pool));
return fieldValidators;
}
};
private static final List modelValidators = new ArrayList();
static{
modelValidators.add(new UniqueKeyValidator());
}
protected boolean isModelValid(MultiException ex) {
List fields = getReflector().getEditableFields();
boolean ret = true;
for (String field : fields) {
MultiException fieldException = new MultiException();
if (!getReflector().isHouseKeepingField(field) && !isFieldValid(field,fieldException)) {
ex.add(fieldException);
ret = false;
}
}
if (ret){
for (ModelValidator v : modelValidators){
ret = v.isValid(getProxy(),ex) && ret;
}
}
return ret;
}
protected boolean isFieldValid(String field, MultiException fieldException) {
boolean ret = true;
Iterator> i = _fieldValidators.get(getPool()).iterator();
while (i.hasNext()) {
FieldValidator extends Annotation> v = i.next();
ret = v.isValid(getProxy(), field, fieldException) && ret;
}
return ret;
}
protected void validate(){
beforeValidate();
MultiException me = new MultiException();
if (!isModelValid(me)) {
throw me;
}
afterValidate();
}
private SequenceSet getExtensionPoints(Class modelClass, String extnPointNameSuffix){
SequenceSet extnPoints = new SequenceSet();
ModelReflector ref = ModelReflector.instance(modelClass);
for (Class extends Model> inHierarchy : ref.getClassHierarchies()){
String extnPoint = inHierarchy.getSimpleName() + "." + extnPointNameSuffix;
extnPoints.add(extnPoint);
}
return extnPoints;
}
private void callExtensions(String extnPointNameSuffix){
for (String extnPoint: getExtensionPoints(getReflector().getModelClass(), extnPointNameSuffix)){
Registry.instance().callExtensions(extnPoint, getProxy());
}
}
public void preValidate(){
beforeValidate();
}
protected void beforeValidate(){
defaultFields();
callExtensions("before.validate");
}
public void defaultFields(){
Record record = getRawRecord();
if (!record.isNewRecord()){
ColumnDescriptor updatedAt = getReflector().getColumnDescriptor("updated_at");
ColumnDescriptor updatorUser = getReflector().getColumnDescriptor("updater_user_id");
if (!updatedAt.isVirtual()){
proxy.setUpdatedAt(null);
}
if (!updatorUser.isVirtual()){
proxy.setUpdaterUserId(null);
}
}
ModelReflector extends Model> reflector = getReflector();
for (String field:reflector.getRealFields()){
String columnName = reflector.getColumnDescriptor(field).getName();
if (record.get(columnName) == null){
Method fieldGetter = reflector.getFieldGetter(field);
COLUMN_DEF cdef = reflector.getAnnotation(fieldGetter,COLUMN_DEF.class);
if (cdef != null){
Object defaultValue = StandardDefaulter.getDefaultValue(cdef.value(),cdef.args(),reflector.getTimeZone());
record.put(columnName,defaultValue);
}
}
}
}
protected void afterValidate(){
callExtensions("after.validate");
}
protected void beforeSave() {
callExtensions("before.save");
}
protected void afterSave() {
callExtensions("after.save");
}
protected void beforeDestory(){
callExtensions("before.destroy");
}
protected void afterDestroy(){
callExtensions("after.destroy");
}
public boolean isBeingDestroyed(){
return beingDestroyed;
}
private boolean beingDestroyed = false;
private void destroyCascade(){
ModelReflector extends Model> ref = getReflector();
for (Method childrenGetter : ref.getChildGetters()){
Class extends Model> childModelClass = ref.getChildModelClass(childrenGetter);
ModelReflector extends Model> childReflector = ModelReflector.instance(childModelClass);
List referenceFields = childReflector.getReferenceFields(ref.getModelClass());
if (ref.isAnnotationPresent(childrenGetter,IS_VIRTUAL.class)){
continue;
}
for (String referenceField: referenceFields){
try {
if (childReflector.getRealModelClass() == null){
continue;
}
if (childReflector.isFieldVirtual(referenceField)){
continue;
}
@SuppressWarnings("unchecked")
List children = (List)childrenGetter.invoke(getProxy());
for (Model child : children){
if (childReflector.isFieldMandatory(referenceField)){
child.destroy();
}else {
childReflector.set(child,referenceField,null);
child.save();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
public void destroy() {
if (isBeingDestroyed()){
return;
}
try {
beingDestroyed = true;
beforeDestory();
destroyCascade();
Delete q = new Delete(getReflector());
Expression condition = new Expression(getPool(),Conjunction.AND);
condition.add(new Expression(getPool(),getReflector().getColumnDescriptor("id").getName(),Operator.EQ,new BindVariable(getPool(),proxy.getId())));
condition.add(new Expression(getPool(),getReflector().getColumnDescriptor("lock_id").getName(),Operator.EQ,new BindVariable(getPool(),proxy.getLockId())));
q.where(condition);
if (q.executeUpdate() <= 0){
throw new RecordNotFoundException();
}
Database.getInstance().getCache(getReflector()).registerDestroy((Model)getProxy());
Database.getInstance().getCurrentTransaction().registerTableDataChanged(getReflector().getTableName());
afterDestroy();
}finally{
beingDestroyed = false;
}
}
private void update() {
Record record = getRawRecord();
Set dirtyFields = new HashSet<>(record.getDirtyFields());
encrypt();
int oldLockId = proxy.getLockId();
int newLockId = oldLockId + 1;
Update q = new Update(getReflector());
Iterator fI = dirtyFields.iterator();
while (fI.hasNext()) {
String columnName = fI.next();
String fieldName = getReflector().getFieldName(columnName);
if (getReflector().isFieldVirtual(fieldName)){
continue;
}
TypeRef> ref = Database.getJdbcTypeHelper(getPool()).getTypeRef(getReflector().getFieldGetter(fieldName).getReturnType());
q.set(columnName,new BindVariable(getPool(),record.get(columnName), ref));
}
String idColumn = getReflector().getColumnDescriptor("id").getName();
Expression condition = new Expression(getPool(),Conjunction.AND);
condition.add(new Expression(getPool(),idColumn,Operator.EQ,new BindVariable(getPool(),proxy.getId())));
ColumnDescriptor lockIdColumDescriptor = getReflector().getColumnDescriptor("lock_id");
if (!lockIdColumDescriptor.isVirtual()){
String lockidColumn = lockIdColumDescriptor.getName();
q.set(lockidColumn,new BindVariable(getPool(),newLockId));
condition.add(new Expression(getPool(),lockidColumn,Operator.EQ,new BindVariable(getPool(),oldLockId)));
}
q.where(condition);
if (q.executeUpdate() <= 0){
throw new RecordNotFoundException();
}
decrypt();
proxy.setLockId(newLockId);
record.startTracking();
if (!getReflector().isAnnotationPresent(CONFIGURATION.class)){
record.setLocked(true);
//Do only for transaction tables as config cache would need to be reset to false after commit. This is just to avoid that unwanted loop over config records cached.
}
Database.getInstance().getCache(getReflector()).registerUpdate((Model)getProxy());
Database.getInstance().getCurrentTransaction().registerTableDataChanged(getReflector().getTableName());
}
private void encrypt(){
List encryptedFields = getReflector().getEncryptedFields();
if (encryptedFields.isEmpty()){
return;
}
for (String f: encryptedFields){
String value = getReflector().get(proxy,f);
if (getRawRecord().isFieldDirty(f) && !getReflector().isVoid(value)){
getReflector().set(proxy,f, SharedKeys.getInstance().encrypt(value));
}
}
}
private void decrypt(){
List encryptedFields = getReflector().getEncryptedFields();
if (encryptedFields.isEmpty()){
return;
}
for (String f: encryptedFields){
String value = getReflector().get(proxy,f);
if (getRawRecord().isFieldDirty(f) && !getReflector().isVoid(value)) {
getReflector().set(proxy, f, SharedKeys.getInstance().decrypt(value));
}
}
}
private void create() {
encrypt();
Record record = getRawRecord();
proxy.setLockId(0);
//Table extends Model> table = Database.getTable(getReflector().getTableName());
Insert insertSQL = new Insert(getReflector());
Map values = new HashMap();
Iterator columnIterator = record.getDirtyFields().iterator();
while (columnIterator.hasNext()) {
String columnName = columnIterator.next();
String fieldName = getReflector().getFieldName(columnName);
if (fieldName == null || getReflector().isFieldVirtual(fieldName)){
continue;
}
TypeRef> ref = Database.getJdbcTypeHelper(getPool()).getTypeRef(getReflector().getFieldGetter(fieldName).getReturnType());
values.put(columnName,new BindVariable(getPool(),record.get(columnName), ref));
}
insertSQL.values(values);
Record generatedValues = new Record(getPool());
Set autoIncrementColumns = getReflector().getAutoIncrementColumns();
assert (autoIncrementColumns.size() <= 1); // atmost one auto increment id column
List generatedKeys = new ArrayList();
for (String anAutoIncrementColumn:autoIncrementColumns){
if ( Database.getJdbcTypeHelper(getPool()).isColumnNameAutoLowerCasedInDB() ){
generatedKeys.add(LowerCaseStringCache.instance().get(anAutoIncrementColumn));
}else {
generatedKeys.add(anAutoIncrementColumn);
}
}
insertSQL.executeUpdate(generatedValues, generatedKeys.toArray(new String[]{}));
if (generatedKeys.size() == 1){
if (generatedValues.getFieldNames().size() == 1){
String virtualFieldName = generatedValues.getFieldNames().iterator().next();
long id = ((Number)generatedValues.get(virtualFieldName)).longValue();
String fieldName = generatedKeys.get(0);
record.put(fieldName, id);
}
}
decrypt();
record.setNewRecord(false);
record.startTracking();
if (!getReflector().isAnnotationPresent(CONFIGURATION.class)){
record.setLocked(true);
}
Database.getInstance().getCache(getReflector()).registerInsert((Model)getProxy());
Database.getInstance().getCurrentTransaction().registerTableDataChanged(getReflector().getTableName());
}
@Override
public boolean equals(Object o){
if (o == null){
return false;
}
if (!(o instanceof ModelInvocationHandler) && !getReflector().canReflect(o)){
return false;
}
if (o instanceof ModelInvocationHandler){
return equalImpl((ModelInvocationHandler)o);
}else {
return equalsProxy((Model)o);
}
}
public int hashCode(){
return (getModelName() + ":" + getProxy().getId()).hashCode() ;
}
protected boolean equalImpl(ModelInvocationHandler anotherImpl){
return (getProxy().getId() == anotherImpl.getProxy().getId()) && getReflector().getTableName().equals(anotherImpl.getReflector().getTableName());
}
protected boolean equalsProxy(Model anotherProxy){
boolean ret = false;
if (anotherProxy != null){
ret = getProxy().getId() == anotherProxy.getId();
}
return ret;
}
@SuppressWarnings("unchecked")
public M cloneProxy(){
return (M)getRawRecord().clone().getAsProxy(getReflector().getModelClass());
}
private transient Map txnProperties = new HashMap();
public Object getTxnProperty(String name) {
return txnProperties.get(name);
}
public void setTxnProperty(String name,Object value) {
txnProperties.put(name, value);
}
public Object removeTxnProperty(String name) {
return txnProperties.remove(name);
}
public boolean isDirty(){
return !getProxy().getRawRecord().getDirtyFields().isEmpty();
}
}