Skip to content

Commit e28f7fe

Browse files
committed
Ensured virtual lists can use batches to load child objects, added tests
1 parent e180f63 commit e28f7fe

File tree

5 files changed

+370
-9
lines changed

5 files changed

+370
-9
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,18 +1617,15 @@ When mapping a Java object to Aerospike the most common operations to do are to
16171617
- Validate some of the limits, eg bin name length, set name length, etc.
16181618
- Make all maps (esp Embedded ones) K_ORDERED
16191619
- Add interface to adaptiveMap, including changing EmbedType
1620-
- Make lists of references load the data via batch loads.
16211620
- Document all parameters to annotations and examples of types
16221621
- Document enums, dates, instants.
1623-
- Test to ensure List<AerospikeRecord> / Map<AerospikeRecord> cannot have both an embed and reference annotations / multiple annotations.
16241622
- Document configuration file.
16251623
- Document creation of builder -- multiple configuration files are allowed, if the same class is declared in both the first one encountered wins.
16261624
- Document methods with 2 parameters for keys and setters, the second one either a Key or a Value
16271625
- Document subclasses and the mapping to tables + references stored as lists
16281626
- Batch load of child items on Maps and References. Ensure testing of non-parameterized classes too. Also of methods on Virtual LIsts
1629-
- Test (and fix) batch loading of children on virtual lists.
16301627
- Document batch loading
1631-
- Ensure batchloading option exists in AerospikeReference Configuration
1628+
- Ensure batch loading option exists in AerospikeReference Configuration
16321629
- handle object graph circularities (A->B->C). Be careful of: A->B(Lazy), A->C->B: B should end up fully hydrated in both instances, not lazy in both instances
16331630
- Consider the items on virtual list which return a list to be able to return a map as well (ELEMENT_LIST, ELEMENT_MAP)
16341631
- Test if map supports lazy loading of referenced objects.

src/main/java/com/aerospike/mapper/tools/AeroMapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private <T> ClassCacheEntry<T> getEntryAndValidateNamespace(Class<T> clazz) {
184184
namespace = entry.getNamespace();
185185
}
186186
if (StringUtils.isBlank(namespace)) {
187-
throw new AerospikeException("Namespace not specified to save a record of type " + clazz.getName());
187+
throw new AerospikeException("Namespace not specified to perform database operation on a record of type " + clazz.getName());
188188
}
189189
return entry;
190190
}
@@ -563,7 +563,7 @@ public <T> Map<String, Object> convertToMap(@NotNull T instance) {
563563
* the list of deferred objects is empty. The deferred objects are stored in a <pre>ThreadLocalData<pre> list, so are thread safe
564564
* @param parentEntity - the ClassCacheEntry of the parent entity. This is used to get the batch policy to use.
565565
*/
566-
private void resolveDependencies(ClassCacheEntry<?> parentEntity) {
566+
void resolveDependencies(ClassCacheEntry<?> parentEntity) {
567567
List<DeferredObjectSetter> deferredObjects = DeferredObjectLoader.getAndClear();
568568

569569
if (deferredObjects.size() == 0) {
@@ -583,7 +583,7 @@ private void resolveDependencies(ClassCacheEntry<?> parentEntity) {
583583
DeferredObjectSetter thisObjectSetter = deferredObjects.get(i);
584584
DeferredObject deferredObject = thisObjectSetter.getObject();
585585
Class<?> clazz = deferredObject.getType();
586-
ClassCacheEntry<?> entry = (ClassCacheEntry<?>) ClassCache.getInstance().loadClass(clazz, this);
586+
ClassCacheEntry<?> entry = getEntryAndValidateNamespace(clazz);
587587
classCaches[i] = entry;
588588

589589
if (deferredObject.isDigest()) {

src/main/java/com/aerospike/mapper/tools/VirtualList.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.aerospike.mapper.tools;
22

33
import java.util.ArrayList;
4+
import java.util.Collection;
45
import java.util.List;
56
import java.util.Map;
67
import java.util.Map.Entry;
@@ -178,6 +179,14 @@ else if (this.indexToReturn >= 0) {
178179
* @return
179180
*/
180181
public Object end() {
182+
return end(null);
183+
}
184+
185+
/**
186+
* Finish the multi operation and process it.
187+
* @return
188+
*/
189+
public <T> T end(Class<T> resultType) {
181190
if (this.interactions.isEmpty()) {
182191
return null;
183192
}
@@ -202,9 +211,10 @@ public Object end() {
202211

203212
Record record = this.virtualList.mapper.mClient.operate(writePolicy, key, operations);
204213

214+
T result;
205215
if (count == 1) {
206216
Object resultObj = record.getValue(binName);
207-
return this.interactions.get(0).getResult(resultObj);
217+
result = (T)this.interactions.get(0).getResult(resultObj);
208218
}
209219
else {
210220
List<?> resultList = record.getList(binName);
@@ -218,8 +228,17 @@ public Object end() {
218228
}
219229
}
220230
}
221-
return this.interactions.get(indexToReturn).getResult(resultList.get(indexToReturn));
231+
result = (T)this.interactions.get(indexToReturn).getResult(resultList.get(indexToReturn));
222232
}
233+
if (result != null) {
234+
Object object = result;
235+
if (result instanceof Collection) {
236+
Collection<T> collection = (Collection<T>)result;
237+
object = collection.isEmpty() ? null : collection.iterator().next();
238+
}
239+
mapper.resolveDependencies(ClassCache.getInstance().loadClass(object.getClass(), mapper));
240+
}
241+
return result;
223242
}
224243
}
225244

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.aerospike.mapper;
2+
3+
import static org.junit.Assert.assertTrue;
4+
import static org.junit.Assert.fail;
5+
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import org.junit.Test;
10+
11+
import com.aerospike.client.AerospikeException;
12+
import com.aerospike.mapper.annotations.AerospikeEmbed;
13+
import com.aerospike.mapper.annotations.AerospikeKey;
14+
import com.aerospike.mapper.annotations.AerospikeRecord;
15+
import com.aerospike.mapper.annotations.AerospikeReference;
16+
import com.aerospike.mapper.tools.AeroMapper;
17+
18+
public class CompetingAnnotationsTest extends AeroMapperBaseTest {
19+
20+
@AerospikeRecord(namespace = "test", set = "A")
21+
public static class A {
22+
@AerospikeKey
23+
int a;
24+
String b;
25+
}
26+
27+
@AerospikeRecord(namespace = "test", set = "B")
28+
public static class CompetingReferenceAndEmbedAnnotations {
29+
@AerospikeKey
30+
int key;
31+
@AerospikeEmbed
32+
@AerospikeReference
33+
A a;
34+
}
35+
36+
@AerospikeRecord(namespace = "test", set = "B")
37+
public static class CompetingListReferenceAndEmbedAnnotations {
38+
@AerospikeKey
39+
int key;
40+
@AerospikeEmbed
41+
@AerospikeReference
42+
List<A> a;
43+
}
44+
45+
@AerospikeRecord(namespace = "test", set = "B")
46+
public static class CompetingMapReferenceAndEmbedAnnotations {
47+
@AerospikeKey
48+
int key;
49+
@AerospikeEmbed
50+
@AerospikeReference
51+
Map<Integer, A> a;
52+
}
53+
54+
@AerospikeRecord(namespace = "test", set = "B")
55+
public static class CompetingArrayReferenceAndEmbedAnnotations {
56+
@AerospikeKey
57+
int key;
58+
@AerospikeEmbed
59+
@AerospikeReference
60+
A[] a;
61+
}
62+
63+
@Test
64+
public void testCompetingReferenceAndEmbedAnnotations() {
65+
AeroMapper mapper = new AeroMapper.Builder(client).build();
66+
67+
A a = new A();
68+
a.a = 2;
69+
a.b = "hello";
70+
71+
CompetingReferenceAndEmbedAnnotations b = new CompetingReferenceAndEmbedAnnotations();
72+
b.key = 1;
73+
b.a = a;
74+
75+
try {
76+
mapper.save(b);
77+
fail();
78+
}
79+
catch (AerospikeException ae) {
80+
assertTrue(true);
81+
}
82+
}
83+
84+
@Test
85+
public void testCompetingListReferenceAndEmbedAnnotations() {
86+
AeroMapper mapper = new AeroMapper.Builder(client).build();
87+
88+
CompetingListReferenceAndEmbedAnnotations b = new CompetingListReferenceAndEmbedAnnotations();
89+
b.key = 1;
90+
91+
try {
92+
mapper.save(b);
93+
fail();
94+
}
95+
catch (AerospikeException ae) {
96+
assertTrue(true);
97+
}
98+
}
99+
100+
@Test
101+
public void testMapCompetingReferenceAndEmbedAnnotations() {
102+
AeroMapper mapper = new AeroMapper.Builder(client).build();
103+
104+
CompetingMapReferenceAndEmbedAnnotations b = new CompetingMapReferenceAndEmbedAnnotations();
105+
b.key = 1;
106+
try {
107+
mapper.save(b);
108+
fail();
109+
}
110+
catch (AerospikeException ae) {
111+
assertTrue(true);
112+
}
113+
}
114+
115+
@Test
116+
public void testCompetingArrayReferenceAndEmbedAnnotations() {
117+
AeroMapper mapper = new AeroMapper.Builder(client).build();
118+
119+
CompetingArrayReferenceAndEmbedAnnotations b = new CompetingArrayReferenceAndEmbedAnnotations();
120+
b.key = 1;
121+
122+
try {
123+
mapper.save(b);
124+
fail();
125+
}
126+
catch (AerospikeException ae) {
127+
assertTrue(true);
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)