Skip to content

Commit f87f057

Browse files
Alexandre Dutraolim7t
authored andcommitted
JAVA-984: Allow non-void setters in object mapping.
1 parent a53e2a3 commit f87f057

6 files changed

Lines changed: 62 additions & 10 deletions

File tree

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [improvement] JAVA-1192: Make EventDebouncer settings updatable at runtime.
1313
- [new feature] JAVA-541: Add polymorphism support to object mapper.
1414
- [new feature] JAVA-636: Allow @Column annotations on getters/setters as well as fields.
15+
- [new feature] JAVA-984: Allow non-void setters in object mapping.
1516

1617
Merged from 3.0.x branch:
1718

driver-mapping/src/main/java/com/datastax/driver/mapping/AnnotationParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ static <T> EntityMapper<T> parseEntity(final Class<T> entityClass, MappingManage
114114
? "col" + columnCounter.incrementAndGet()
115115
: null;
116116

117-
PropertyMapper propertyMapper = new PropertyMapper(propertyName, alias, field, property);
117+
PropertyMapper propertyMapper = new PropertyMapper(entityClass, propertyName, alias, field, property);
118118

119119
if (mappingManager.isCassandraV1 && propertyMapper.isComputed())
120120
throw new UnsupportedOperationException("Computed properties are not supported with native protocol v1");
@@ -180,7 +180,7 @@ static <T> MappedUDTCodec<T> parseUDT(Class<T> udtClass, MappingManager mappingM
180180
java.lang.reflect.Field field = (java.lang.reflect.Field) entry.getValue()[0];
181181
PropertyDescriptor property = (PropertyDescriptor) entry.getValue()[1];
182182

183-
PropertyMapper propertyMapper = new PropertyMapper(propertyName, null, field, property);
183+
PropertyMapper propertyMapper = new PropertyMapper(udtClass, propertyName, null, field, property);
184184

185185
AnnotationChecks.validateAnnotations(propertyMapper, VALID_FIELD_ANNOTATIONS);
186186

driver-mapping/src/main/java/com/datastax/driver/mapping/PropertyMapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ class PropertyMapper {
5151
private final Method setter;
5252
private final Map<Class<? extends Annotation>, Annotation> annotations;
5353

54-
PropertyMapper(String propertyName, String alias, Field field, PropertyDescriptor property) {
54+
PropertyMapper(Class<?> baseClass, String propertyName, String alias, Field field, PropertyDescriptor property) {
5555
this.propertyName = propertyName;
5656
this.alias = alias;
5757
this.field = field;
5858
getter = ReflectionUtils.findGetter(property);
59-
setter = ReflectionUtils.findSetter(property);
59+
setter = ReflectionUtils.findSetter(baseClass, property);
6060
annotations = ReflectionUtils.scanPropertyAnnotations(field, property);
6161
if (field != null)
6262
ReflectionUtils.tryMakeAccessible(field);

driver-mapping/src/main/java/com/datastax/driver/mapping/ReflectionUtils.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private static <T> Map<String, Field> scanFields(Class<T> baseClass) {
7575
HashMap<String, Field> fields = new HashMap<String, Field>();
7676
for (Class<?> clazz = baseClass; !clazz.equals(Object.class); clazz = clazz.getSuperclass()) {
7777
for (Field field : clazz.getDeclaredFields()) {
78-
if (field.getName().equals("class") || field.isSynthetic() || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC)
78+
if (field.getName().equals("class") || field.isSynthetic() || Modifier.isStatic(field.getModifiers()))
7979
continue;
8080
// never override a more specific field masking another one declared in a superclass
8181
if (!fields.containsKey(field.getName()))
@@ -160,13 +160,24 @@ static Method findGetter(PropertyDescriptor property) {
160160
return getter;
161161
}
162162

163-
static Method findSetter(PropertyDescriptor property) {
163+
static Method findSetter(Class<?> baseClass, PropertyDescriptor property) {
164164
if (property == null)
165165
return null;
166166
Method setter = property.getWriteMethod();
167-
if (setter == null)
168-
return null;
169-
return setter;
167+
if (setter != null)
168+
return setter;
169+
String propertyName = property.getName();
170+
String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
171+
// JAVA-984: look for a "relaxed" setter, ie. a setter whose return type may be anything
172+
try {
173+
setter = baseClass.getMethod(setterName, property.getPropertyType());
174+
if (!Modifier.isStatic(setter.getModifiers())) {
175+
return setter;
176+
}
177+
} catch (NoSuchMethodException e) {
178+
// ok
179+
}
180+
return null;
170181
}
171182

172183
static void tryMakeAccessible(AccessibleObject object) {

driver-mapping/src/test/java/com/datastax/driver/mapping/MapperPolymorphismTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,11 @@ public double getRadius() {
243243
return _radius;
244244
}
245245

246-
public void setRadius(double radius) {
246+
// builder-style setter; because the field isn't named in a standard way,
247+
// if this setter is not detected the test would fail
248+
public Circle setRadius(double radius) {
247249
this._radius = radius;
250+
return this;
248251
}
249252

250253
@Override
@@ -430,6 +433,13 @@ public void setCenter(Point3D center) {
430433
this.center = center;
431434
}
432435

436+
// overridden builder-style setter
437+
@Override
438+
public Sphere setRadius(double radius) {
439+
super.setRadius(radius);
440+
return this;
441+
}
442+
433443
@Override
434444
public boolean equals(Object o) {
435445
if (this == o) return true;

manual/object_mapper/creating/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,36 @@ A mapped field is thus allowed to be non-public if:
119119
1. Reads and writes are done through its public getters and setters; or
120120
2. The security manager, if any, grants the mapper access to it via [reflection][set-accessible].
121121

122+
Note that, according to the [Java Beans specification][java-beans], a setter
123+
must have a `void` return type; the driver, however, will consider as a setter any public method
124+
having a matching signature (i.e., name and parameter types match those expected),
125+
but having a different return type. This allows the usage of "fluent" setters that can
126+
be chained together in a builder-pattern style:
127+
128+
```java
129+
@Table(name = "users")
130+
public static class User {
131+
132+
// fluent setters
133+
134+
public User setId(UUID id) {
135+
this.id = id;
136+
return this;
137+
}
138+
139+
public User setName(String name) {
140+
this.name = name;
141+
return this;
142+
}
143+
144+
}
145+
146+
// chained calls
147+
User user = new User()
148+
.setId(UUIDs.random())
149+
.setName("John Doe");
150+
```
151+
122152
[table]:http://docs.datastax.com/en/drivers/java/3.0/com/datastax/driver/mapping/annotations/Table.html
123153
[case-sensitive]:http://docs.datastax.com/en/cql/3.3/cql/cql_reference/ucase-lcase_r.html
124154
[consistency level]:http://docs.datastax.com/en/drivers/java/3.0/com/datastax/driver/core/ConsistencyLevel.html

0 commit comments

Comments
 (0)