private Prop findRelProperty(Class<?> fromCls, String rel, Class<?> toCls) { Object entity = !fromCls.isInterface() ? data.constructor.create(fromCls) : null; for (Prop prop : Beany.propertiesOf(fromCls).select(data.relPropSelector)) { String relName = null; if (!fromCls.isInterface()) { Object value = prop.getRaw(entity); if (hasEntityLinks(value)) { EntityLinks links = entityLinks(value); relName = links.relationName(); } } else { Rel relation = prop.getAnnotation(Rel.class); if (relation != null) { relName = relation.value(); } } if (relName != null && relName.equals(rel)) { if (prop.getRawTypeArg(0).equals(toCls)) { return prop; } } } Log.warn( "Didn't find inverse relation property!", "relation", rel, "from", fromCls, "to", toCls); return null; }
private void deleteRelsFor(Object entity) { for (Prop prop : Beany.propertiesOf(entity).select(data.relPropSelector)) { Object value = prop.getRaw(entity); if (hasEntityLinks(value)) { EntityLinks links = entityLinks(value); long fromId = links.fromId(); propageteRelChanges(entity, prop, links, fromId, null, links.allRelIds()); } } }
private RelPair getRelPair(Object entity, Prop prop, String rel, boolean inverse) { Class<?> cls = prop.getRawTypeArg(0); Class<? extends Object> entCls = Cls.unproxy(entity.getClass()); Class<?> srcType = inverse ? cls : entCls; Class<?> destType = inverse ? entCls : cls; Tuple key = new Tuple(rel, srcType, destType); RelPair relPair = data.relPairs.get(key); Prop srcProp, destProp; if (relPair != null) { srcProp = relPair.srcProp; destProp = relPair.destProp; } else { String invRel = inverse ? rel : "^" + rel; Prop p = findRelProperty(cls, invRel, entCls); srcProp = inverse ? p : prop; destProp = inverse ? prop : p; relPair = new RelPair(rel, srcType, destType, srcProp, destProp); data.relPairs.putIfAbsent(key, relPair); if (srcType == null || srcProp == null || destType == null || destProp == null) { Log.warn("Incomplete relation pair!", "relation", relPair); } } return relPair; }
private void relUnlink(String relation, Prop srcProp, long fromId, Prop destProp, long toId) { if (srcProp != null && destProp != null) { Object from = get_(fromId, true); EntityLinks srcRels = entityLinks(srcProp.getRaw(from)); srcRels.removeRelTo(toId); update_(fromId, from, false, false); } }
private void update_(long id, Object record, boolean reflectRelChanges, boolean checkSecurity) { failIfReadonlyTx(); validateId(id); Rec old = data.data.get(id); Object entity = obj(old); if (checkSecurity) { secureUpdate(entity); } // Optimistic concurrency control through the "version" property Long oldVersion = U.or(Beany.getPropValueOfType(entity, VERSION, Long.class, null), 0L); Long recordVersion = U.or(Beany.getPropValueOfType(record, VERSION, Long.class, null), 0L); occErrorIf( !U.eq(oldVersion, recordVersion), "Concurrent modification occured while updating the object with ID=%s!", id); Beany.setId(record, id); if (!sudo && checkSecurity) { boolean canUpdate = false; for (Prop prop : Beany.propertiesOf(record)) { if (!Secure.getPropertyPermissions(username(), entity.getClass(), entity, prop.getName()) .change) { prop.set(record, prop.get(entity)); } else { canUpdate = true; } } U.secure( canUpdate, "Not enough privileges to update any column of %s!", entity.getClass().getSimpleName()); } // Optimistic concurrency control through the "version" property if (Beany.hasProperty(record, VERSION)) { Beany.setPropValue(record, VERSION, oldVersion + 1); } if (checkSecurity) { secureUpdate(record); } if (Beany.hasProperty(record, LAST_UPDATED_BY)) { Beany.setPropValue(record, LAST_UPDATED_BY, username()); } if (Beany.hasProperty(record, LAST_UPDATED_ON)) { Beany.setPropValue(record, LAST_UPDATED_ON, new Date()); } boolean updated = data.data.replace(id, old, rec(record)); occErrorIf( !updated, "Concurrent modification occured while updating the object with ID=%s!", id); if (data.insideTx.get()) { data.txChanges.putIfAbsent(id, old); } if (old == null) { throw new IllegalStateException("Cannot update non-existing record with ID=" + id); } if (reflectRelChanges) { updateChangesFromRels(record); } data.lastChangedOn.set(System.currentTimeMillis()); Log.audit("Updated DB record", "id", id); }
@Override public boolean eval(Prop prop) throws Exception { return Cls.isAssignableTo( prop.getType(), Number.class, String.class, Boolean.class, Enum.class, Date.class); }