Cascade persist whole object graph #648 #647

pull/653/head
Nikita 9 years ago
parent ac7aef4706
commit 108641a23d

@ -59,16 +59,22 @@ import org.redisson.liveobject.core.LiveObjectInterceptor;
import org.redisson.liveobject.core.RExpirableInterceptor;
import org.redisson.liveobject.core.RMapInterceptor;
import org.redisson.liveobject.core.RObjectInterceptor;
import org.redisson.liveobject.core.RedissonObjectBuilder;
import org.redisson.liveobject.misc.AdvBeanCopy;
import org.redisson.liveobject.misc.ClassUtils;
import org.redisson.liveobject.misc.Introspectior;
import org.redisson.liveobject.provider.ResolverProvider;
import org.redisson.liveobject.resolver.Resolver;
import jodd.bean.BeanCopy;
import jodd.bean.BeanUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodCall;
@ -82,12 +88,14 @@ public class RedissonLiveObjectService implements RLiveObjectService {
private final RedissonClient redisson;
private final CodecProvider codecProvider;
private final ResolverProvider resolverProvider;
private final RedissonObjectBuilder objectBuilder;
public RedissonLiveObjectService(RedissonClient redisson, ConcurrentMap<Class<?>, Class<?>> classCache, CodecProvider codecProvider, ResolverProvider resolverProvider) {
this.redisson = redisson;
this.classCache = classCache;
this.codecProvider = codecProvider;
this.resolverProvider = resolverProvider;
this.objectBuilder = new RedissonObjectBuilder(redisson, codecProvider);
}
//TODO: Add ttl renewal functionality
@ -175,12 +183,20 @@ public class RedissonLiveObjectService implements RLiveObjectService {
public <T> T merge(T detachedObject) {
T attachedObject = attach(detachedObject);
getMap(attachedObject).fastPut("redisson_live_object", "1");
copy(detachedObject, attachedObject);
List<String> excludedFields = new ArrayList<String>();
String idFieldName = getRIdFieldName(detachedObject.getClass());
excludedFields.add(idFieldName);
copy(detachedObject, attachedObject, excludedFields);
return attachedObject;
}
@Override
public <T> T persist(T detachedObject) {
Map<Object, Object> alreadyPersisted = new HashMap<Object, Object>();
return persist(detachedObject, alreadyPersisted);
}
private <T> T persist(T detachedObject, Map<Object, Object> alreadyPersisted) {
String idFieldName = getRIdFieldName(detachedObject.getClass());
Object id = ClassUtils.getField(detachedObject, idFieldName);
if (id == null) {
@ -193,13 +209,85 @@ public class RedissonLiveObjectService implements RLiveObjectService {
}
T attachedObject = attach(detachedObject);
if (getMap(attachedObject).fastPut("redisson_live_object", "1")) {
copy(detachedObject, attachedObject);
alreadyPersisted.put(detachedObject, attachedObject);
RMap<String, Object> liveMap = getMap(attachedObject);
List<String> excludedFields = new ArrayList<String>();
excludedFields.add(idFieldName);
if (liveMap.fastPut("redisson_live_object", "1")) {
for (FieldDescription.InDefinedShape field : Introspectior.getFieldsDescription(detachedObject.getClass())) {
Object object = ClassUtils.getField(detachedObject, field.getName());
if (object == null) {
continue;
}
RObject rObject = objectBuilder.createObject(id, detachedObject.getClass(), object.getClass(), field.getName(), null);
if (rObject != null) {
objectBuilder.store(rObject, field.getName(), liveMap);
if (rObject instanceof SortedSet) {
((RSortedSet)rObject).trySetComparator(((SortedSet)object).comparator());
}
if (rObject instanceof Collection) {
for (Object obj : (Collection<Object>)object) {
if (obj != null && obj.getClass().isAnnotationPresent(REntity.class)) {
Object persisted = alreadyPersisted.get(obj);
if (persisted == null) {
persisted = persist(obj, alreadyPersisted);
}
obj = persisted;
}
((Collection)rObject).add(obj);
}
}
if (rObject instanceof Map) {
Map<Object, Object> rMap = (Map<Object, Object>) rObject;
Map<?, ?> map = (Map<?, ?>)rObject;
for (Entry<?, ?> entry : map.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
if (key != null && key.getClass().isAnnotationPresent(REntity.class)) {
Object persisted = alreadyPersisted.get(key);
if (persisted == null) {
persisted = persist(key, alreadyPersisted);
}
key = persisted;
}
if (value != null && value.getClass().isAnnotationPresent(REntity.class)) {
Object persisted = alreadyPersisted.get(value);
if (persisted == null) {
persisted = persist(value, alreadyPersisted);
}
value = persisted;
}
rMap.put(key, value);
}
}
excludedFields.add(field.getName());
}
if (object.getClass().isAnnotationPresent(REntity.class)) {
Object persisted = alreadyPersisted.get(object);
if (persisted == null) {
persisted = persist(object, alreadyPersisted);
}
excludedFields.add(field.getName());
BeanUtil.pojo.setSimpleProperty(attachedObject, field.getName(), persisted);
}
}
copy(detachedObject, attachedObject, excludedFields);
return attachedObject;
}
throw new IllegalArgumentException("This REntity already exists.");
}
@Override
public <T> T detach(T attachedObject) {
Map<String, Object> alreadyDetached = new HashMap<String, Object>();
@ -388,98 +476,10 @@ public class RedissonLiveObjectService implements RLiveObjectService {
return classCache.containsKey(cls) || classCache.containsValue(cls);
}
private <T> void copy(T detachedObject, T attachedObject) {
for (FieldDescription.InDefinedShape field : Introspectior.getFieldsDescription(detachedObject.getClass())) {
Object object = ClassUtils.getField(detachedObject, field.getName());
if (object != null && object.getClass().isAnnotationPresent(REntity.class)) {
throw new IllegalArgumentException("REntity should be attached to Redisson before save");
}
}
// for (FieldDescription.InDefinedShape field : Introspectior.getFieldsDescription(detachedObject.getClass())) {
// Object obj = ClassUtils.getField(detachedObject, field.getName());
// if (obj instanceof SortedSet) {
// ClassUtils.getField(detachedObject, field.getName());
// SortedSet<Object> redissonSet = (SortedSet<Object>) obj.getValue();
// Set<Object> set = new TreeSet<Object>(redissonSet.comparator());
// for (Object object : redissonSet) {
// if (isLiveObject(object)) {
// Object detachedObject = alreadyDetached.get(getMap(object).getName());
// if (detachedObject == null) {
// detachedObject = detach(object, alreadyDetached);
// }
// object = detachedObject;
// }
// set.add(object);
// }
//
// ClassUtils.setField(detached, obj.getKey(), set);
// } else if (obj.getValue() instanceof RDeque) {
// Collection<Object> redissonDeque = (Collection<Object>) obj.getValue();
// Deque<Object> deque = new LinkedList<Object>();
// for (Object object : redissonDeque) {
// if (isLiveObject(object)) {
// Object detachedObject = alreadyDetached.get(getMap(object).getName());
// if (detachedObject == null) {
// detachedObject = detach(object, alreadyDetached);
// }
// object = detachedObject;
// }
// deque.add(object);
// }
//
// ClassUtils.setField(detached, obj.getKey(), deque);
// } else if (obj.getValue() instanceof RQueue) {
// Collection<Object> redissonQueue = (Collection<Object>) obj.getValue();
// Queue<Object> queue = new LinkedList<Object>();
// for (Object object : redissonQueue) {
// if (isLiveObject(object)) {
// Object detachedObject = alreadyDetached.get(getMap(object).getName());
// if (detachedObject == null) {
// detachedObject = detach(object, alreadyDetached);
// }
// object = detachedObject;
// }
// queue.add(object);
// }
//
// ClassUtils.setField(detached, obj.getKey(), queue);
// } else if (obj.getValue() instanceof RSet) {
// Set<Object> set = new HashSet<Object>();
// Collection<Object> redissonSet = (Collection<Object>) obj.getValue();
// for (Object object : redissonSet) {
// if (isLiveObject(object)) {
// Object detachedObject = alreadyDetached.get(getMap(object).getName());
// if (detachedObject == null) {
// detachedObject = detach(object, alreadyDetached);
// }
// object = detachedObject;
// }
// set.add(object);
// }
//
// ClassUtils.setField(detached, obj.getKey(), set);
// } else if (obj.getValue() instanceof RList) {
// List<Object> list = new ArrayList<Object>();
// Collection<Object> redissonList = (Collection<Object>) obj.getValue();
// for (Object object : redissonList) {
// if (isLiveObject(object)) {
// Object detachedObject = alreadyDetached.get(getMap(object).getName());
// if (detachedObject == null) {
// detachedObject = detach(object, alreadyDetached);
// }
// object = detachedObject;
// }
// list.add(object);
// }
//
// ClassUtils.setField(detached, obj.getKey(), list);
// }
// }
String idFieldName = getRIdFieldName(detachedObject.getClass());
BeanCopy.beans(detachedObject, attachedObject)
private <T> void copy(T detachedObject, T attachedObject, List<String> excludedFields) {
new AdvBeanCopy(detachedObject, attachedObject)
.ignoreNulls(true)
.exclude(idFieldName)
.exclude(excludedFields.toArray(new String[excludedFields.size()]))
.copy();
}
@ -618,7 +618,7 @@ public class RedissonLiveObjectService implements RLiveObjectService {
.or(ElementMatchers.isSetter()))
.and(ElementMatchers.isPublic()))
.intercept(MethodDelegation.to(
new AccessorInterceptor(redisson)))
new AccessorInterceptor(redisson, objectBuilder)))
.make().load(getClass().getClassLoader(),
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();

@ -15,31 +15,12 @@
*/
package org.redisson.liveobject.core;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import org.redisson.RedissonBlockingDeque;
import org.redisson.RedissonBlockingQueue;
import org.redisson.RedissonDeque;
import org.redisson.RedissonList;
import org.redisson.RedissonMap;
import org.redisson.RedissonQueue;
import org.redisson.RedissonReference;
import org.redisson.RedissonSet;
import org.redisson.RedissonSortedSet;
import org.redisson.api.RLiveObject;
import org.redisson.api.RMap;
import org.redisson.api.RObject;
@ -47,14 +28,12 @@ import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.REntity;
import org.redisson.api.annotation.REntity.TransformationMode;
import org.redisson.api.annotation.RId;
import org.redisson.api.annotation.RObjectField;
import org.redisson.client.codec.Codec;
import org.redisson.codec.CodecProvider;
import org.redisson.liveobject.misc.Introspectior;
import org.redisson.liveobject.resolver.NamingScheme;
import org.redisson.misc.RedissonObjectFactory;
import io.netty.util.internal.PlatformDependent;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.FieldValue;
import net.bytebuddy.implementation.bind.annotation.Origin;
@ -73,31 +52,18 @@ public class AccessorInterceptor {
private final RedissonClient redisson;
private final CodecProvider codecProvider;
private final ConcurrentMap<String, NamingScheme> namingSchemeCache = PlatformDependent.newConcurrentHashMap();
private static final LinkedHashMap<Class<?>, Class<? extends RObject>> supportedClassMapping;
private final RedissonObjectBuilder objectBuilder;
public AccessorInterceptor(RedissonClient redisson) {
public AccessorInterceptor(RedissonClient redisson, RedissonObjectBuilder objectBuilder) {
this.redisson = redisson;
this.codecProvider = redisson.getCodecProvider();
this.objectBuilder = objectBuilder;
}
static {
supportedClassMapping = new LinkedHashMap<Class<?>, Class<? extends RObject>>();
supportedClassMapping.put(SortedSet.class, RedissonSortedSet.class);
supportedClassMapping.put(Set.class, RedissonSet.class);
supportedClassMapping.put(ConcurrentMap.class, RedissonMap.class);
supportedClassMapping.put(Map.class, RedissonMap.class);
supportedClassMapping.put(BlockingDeque.class, RedissonBlockingDeque.class);
supportedClassMapping.put(Deque.class, RedissonDeque.class);
supportedClassMapping.put(BlockingQueue.class, RedissonBlockingQueue.class);
supportedClassMapping.put(Queue.class, RedissonQueue.class);
supportedClassMapping.put(List.class, RedissonList.class);
}
@RuntimeType
public Object intercept(@Origin Method method, @SuperCall Callable<?> superMethod,
@AllArguments Object[] args, @This Object me,
@FieldValue("liveObjectLiveMap") RMap liveMap) throws Exception {
@FieldValue("liveObjectLiveMap") RMap<String, Object> liveMap) throws Exception {
if (isGetter(method, getREntityIdFieldName(me))) {
return ((RLiveObject) me).getLiveObjectId();
}
@ -112,26 +78,10 @@ public class AccessorInterceptor {
if (isGetter(method, fieldName)) {
Object result = liveMap.get(fieldName);
if (result == null) {
Class<? extends RObject> mappedClass = getMappedClass(fieldType);
if (mappedClass != null) {
Codec fieldCodec = getFieldCodec(me.getClass().getSuperclass(), mappedClass, fieldName);
NamingScheme fieldNamingScheme = getFieldNamingScheme(me.getClass().getSuperclass(), fieldName, fieldCodec);
RObject obj = RedissonObjectFactory
.createRObject(redisson,
mappedClass,
fieldNamingScheme.getFieldReferenceName(me.getClass().getSuperclass(),
((RLiveObject) me).getLiveObjectId(),
mappedClass,
fieldName,
null),
fieldCodec);
codecProvider.registerCodec((Class) fieldCodec.getClass(), obj, obj.getCodec());
liveMap.fastPut(fieldName,
new RedissonReference(obj.getClass(), obj.getName(), obj.getCodec()));
return obj;
RObject ar = objectBuilder.createObject(((RLiveObject) me).getLiveObjectId(), me.getClass().getSuperclass(), fieldType, fieldName, null);
if (ar != null) {
objectBuilder.store(ar, fieldName, liveMap);
return ar;
}
}
@ -159,41 +109,20 @@ public class AccessorInterceptor {
liveObject.getLiveObjectId())));
return me;
}
if (!(arg instanceof RObject)
&& (arg instanceof Collection || arg instanceof Map)
&& TransformationMode.ANNOTATION_BASED
.equals(me.getClass().getSuperclass()
.getAnnotation(REntity.class).fieldTransformation())) {
Class<? extends RObject> mappedClass = getMappedClass(arg.getClass());
if (mappedClass != null) {
Codec fieldCodec = getFieldCodec(me.getClass().getSuperclass(), mappedClass, fieldName);
NamingScheme fieldNamingScheme = getFieldNamingScheme(me.getClass().getSuperclass(), fieldName, fieldCodec);
RObject obj = RedissonObjectFactory
.createRObject(redisson,
mappedClass,
fieldNamingScheme.getFieldReferenceName(me.getClass().getSuperclass(),
((RLiveObject) me).getLiveObjectId(),
mappedClass,
fieldName,
arg),
fieldCodec);
if (obj instanceof Collection) {
((Collection) obj).addAll((Collection) arg);
} else {
((Map) obj).putAll((Map) arg);
}
arg = obj;
RObject rObject = objectBuilder.createObject(((RLiveObject) me).getLiveObjectId(), me.getClass().getSuperclass(), arg.getClass(), fieldName, arg);
if (rObject != null) {
arg = rObject;
}
}
if (arg instanceof RObject) {
RObject ar = (RObject) arg;
Codec codec = ar.getCodec();
codecProvider.registerCodec((Class) codec.getClass(), ar, codec);
liveMap.fastPut(fieldName,
new RedissonReference(ar.getClass(), ar.getName(), codec));
objectBuilder.store((RObject)arg, fieldName, liveMap);
return me;
}
liveMap.fastPut(fieldName, args[0]);
@ -216,33 +145,6 @@ public class AccessorInterceptor {
&& method.getName().endsWith(getFieldNameSuffix(fieldName));
}
/**
* WARNING: rEntity has to be the class of @This object.
*/
private Codec getFieldCodec(Class<?> rEntity, Class<? extends RObject> rObjectClass, String fieldName) throws Exception {
Field field = rEntity.getDeclaredField(fieldName);
if (field.isAnnotationPresent(RObjectField.class)) {
RObjectField anno = field.getAnnotation(RObjectField.class);
return codecProvider.getCodec(anno, rEntity, rObjectClass, fieldName);
} else {
REntity anno = rEntity.getAnnotation(REntity.class);
return codecProvider.getCodec(anno, (Class) rEntity);
}
}
/**
* WARNING: rEntity has to be the class of @This object.
*/
private NamingScheme getFieldNamingScheme(Class<?> rEntity, String fieldName, Codec c) throws Exception {
if (!namingSchemeCache.containsKey(fieldName)) {
REntity anno = rEntity.getAnnotation(REntity.class);
namingSchemeCache.putIfAbsent(fieldName, anno.namingScheme()
.getDeclaredConstructor(Codec.class)
.newInstance(c));
}
return namingSchemeCache.get(fieldName);
}
private static String getFieldNameSuffix(String fieldName) {
return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
@ -254,13 +156,4 @@ public class AccessorInterceptor {
.getName();
}
private static Class<? extends RObject> getMappedClass(Class<?> cls) {
for (Entry<Class<?>, Class<? extends RObject>> entrySet : supportedClassMapping.entrySet()) {
if (entrySet.getKey().isAssignableFrom(cls)) {
return entrySet.getValue();
}
}
return null;
}
}

@ -0,0 +1,160 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.core;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentMap;
import org.redisson.RedissonBlockingDeque;
import org.redisson.RedissonBlockingQueue;
import org.redisson.RedissonDeque;
import org.redisson.RedissonList;
import org.redisson.RedissonMap;
import org.redisson.RedissonQueue;
import org.redisson.RedissonReference;
import org.redisson.RedissonSet;
import org.redisson.RedissonSortedSet;
import org.redisson.api.RMap;
import org.redisson.api.RObject;
import org.redisson.api.RedissonClient;
import org.redisson.api.annotation.REntity;
import org.redisson.api.annotation.RObjectField;
import org.redisson.client.codec.Codec;
import org.redisson.codec.CodecProvider;
import org.redisson.liveobject.resolver.NamingScheme;
import org.redisson.misc.RedissonObjectFactory;
import io.netty.util.internal.PlatformDependent;
/**
*
* @author Rui Gu
* @author Nikita Koksharov
*
*/
public class RedissonObjectBuilder {
private static final Map<Class<?>, Class<? extends RObject>> supportedClassMapping = new LinkedHashMap<Class<?>, Class<? extends RObject>>();
ConcurrentMap<String, NamingScheme> namingSchemeCache = PlatformDependent.newConcurrentHashMap();
static {
supportedClassMapping.put(SortedSet.class, RedissonSortedSet.class);
supportedClassMapping.put(Set.class, RedissonSet.class);
supportedClassMapping.put(ConcurrentMap.class, RedissonMap.class);
supportedClassMapping.put(Map.class, RedissonMap.class);
supportedClassMapping.put(BlockingDeque.class, RedissonBlockingDeque.class);
supportedClassMapping.put(Deque.class, RedissonDeque.class);
supportedClassMapping.put(BlockingQueue.class, RedissonBlockingQueue.class);
supportedClassMapping.put(Queue.class, RedissonQueue.class);
supportedClassMapping.put(List.class, RedissonList.class);
}
private final RedissonClient redisson;
private final CodecProvider codecProvider;
public RedissonObjectBuilder(RedissonClient redisson, CodecProvider codecProvider) {
super();
this.redisson = redisson;
this.codecProvider = codecProvider;
}
public void store(RObject ar, String fieldName, RMap<String, Object> liveMap) {
Codec codec = ar.getCodec();
codecProvider.registerCodec((Class) codec.getClass(), ar, codec);
liveMap.fastPut(fieldName,
new RedissonReference(ar.getClass(), ar.getName(), codec));
}
public RObject createObject(Object id, Class<?> clazz, Class<?> fieldType, String fieldName, Object arg) {
Class<? extends RObject> mappedClass = getMappedClass(fieldType);
try {
if (mappedClass != null) {
Codec fieldCodec = getFieldCodec(clazz, mappedClass, fieldName);
NamingScheme fieldNamingScheme = getFieldNamingScheme(clazz, fieldName, fieldCodec);
RObject obj = RedissonObjectFactory
.createRObject(redisson,
mappedClass,
fieldNamingScheme.getFieldReferenceName(clazz,
id,
mappedClass,
fieldName,
arg),
fieldCodec);
if (arg != null) {
if (obj instanceof Collection) {
((Collection) obj).addAll((Collection) arg);
} else {
((Map) obj).putAll((Map) arg);
}
}
return obj;
}
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
return null;
}
/**
* WARNING: rEntity has to be the class of @This object.
*/
private Codec getFieldCodec(Class<?> rEntity, Class<? extends RObject> rObjectClass, String fieldName) throws Exception {
Field field = rEntity.getDeclaredField(fieldName);
if (field.isAnnotationPresent(RObjectField.class)) {
RObjectField anno = field.getAnnotation(RObjectField.class);
return codecProvider.getCodec(anno, rEntity, rObjectClass, fieldName);
} else {
REntity anno = rEntity.getAnnotation(REntity.class);
return codecProvider.getCodec(anno, (Class) rEntity);
}
}
/**
* WARNING: rEntity has to be the class of @This object.
*/
private NamingScheme getFieldNamingScheme(Class<?> rEntity, String fieldName, Codec c) throws Exception {
if (!namingSchemeCache.containsKey(fieldName)) {
REntity anno = rEntity.getAnnotation(REntity.class);
namingSchemeCache.putIfAbsent(fieldName, anno.namingScheme()
.getDeclaredConstructor(Codec.class)
.newInstance(c));
}
return namingSchemeCache.get(fieldName);
}
private Class<? extends RObject> getMappedClass(Class<?> cls) {
for (Entry<Class<?>, Class<? extends RObject>> entrySet : supportedClassMapping.entrySet()) {
if (entrySet.getKey().isAssignableFrom(cls)) {
return entrySet.getValue();
}
}
return null;
}
}

@ -0,0 +1,40 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.liveobject.misc;
import jodd.bean.BeanCopy;
import jodd.bean.BeanUtilBean;
/**
*
* @author Nikita Koksharov
*
*/
public class AdvBeanCopy extends BeanCopy {
public AdvBeanCopy(Object source, Object destination) {
super(source, destination);
}
@Override
public void copy() {
beanUtil = new BeanUtilBean()
.declared(declared)
.forced(forced);
visit();
}
}

@ -26,8 +26,6 @@ import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.redisson.LOTest.Customer;
import org.redisson.LOTest.Order;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDeque;
@ -553,12 +551,35 @@ public class RedissonLiveObjectServiceTest extends BaseTest {
}
}
@Test
public void testPersistList() {
Customer customer = new Customer("12");
Order order = new Order(customer);
customer.getOrders().add(order);
Order order2 = new Order(customer);
customer.getOrders().add(order2);
redisson.getLiveObjectService().persist(customer);
customer = redisson.getLiveObjectService().get(Customer.class, "12");
assertThat(customer.getOrders().size()).isEqualTo(2);
for (Order orderElement : customer.getOrders()) {
assertThat(orderElement.getId()).isNotNull();
assertThat(orderElement.getCustomer().getId()).isEqualTo("12");
}
}
@Test
public void testPersist() {
RLiveObjectService service = redisson.getLiveObjectService();
TestClass ts = new TestClass(new ObjectId(100));
ts.setValue("VALUE");
ts.setContent(new TestREntity("123"));
TestClass persisted = service.persist(ts);
assertEquals(2, redisson.getKeys().count());
assertEquals("123", ((TestREntity)persisted.getContent()).getName());
assertEquals(new ObjectId(100), persisted.getId());
assertEquals("VALUE", persisted.getValue());
try {
@ -1219,7 +1240,7 @@ public class RedissonLiveObjectServiceTest extends BaseTest {
customer.getOrders().add(order);
}
@Test(expected = IllegalArgumentException.class)
@Test
public void testObjectShouldNotBeAttached2() {
Customer customer = new Customer("12");
Order order = new Order(customer);

Loading…
Cancel
Save