Skip to content

Commit 390a01c

Browse files
authored
JAVA-2935: Make GetEntity and SetEntity methods resilient to incomplete data (#1556)
1 parent 8bdd81f commit 390a01c

File tree

29 files changed

+946
-139
lines changed

29 files changed

+946
-139
lines changed

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### 4.12.0 (in progress)
66

7+
- [improvement] JAVA-2935: Make GetEntity and SetEntity methods resilient to incomplete data
78
- [improvement] JAVA-2944: Upgrade MicroProfile Metrics to 3.0
89

910
### 4.11.2 (in progress)

core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatementBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ public BoundStatementBuilder(@NonNull BoundStatement template) {
9999
this.node = template.getNode();
100100
}
101101

102+
/** The prepared statement that was used to create this statement. */
103+
@NonNull
104+
public PreparedStatement getPreparedStatement() {
105+
return preparedStatement;
106+
}
107+
102108
@NonNull
103109
@Override
104110
public List<Integer> allIndicesOf(@NonNull CqlIdentifier id) {

examples/src/main/java/com/datastax/oss/driver/examples/mapper/killrvideo/user/CreateUserQueryProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,14 @@ private boolean insertCredentialsIfNotExists(String email, char[] password, UUID
116116
UserCredentials credentials =
117117
new UserCredentials(Objects.requireNonNull(email), passwordHash, userId);
118118
BoundStatementBuilder insertCredentials = preparedInsertCredentials.boundStatementBuilder();
119-
credentialsHelper.set(credentials, insertCredentials, NullSavingStrategy.DO_NOT_SET);
119+
credentialsHelper.set(credentials, insertCredentials, NullSavingStrategy.DO_NOT_SET, false);
120120
ResultSet resultSet = session.execute(insertCredentials.build());
121121
return resultSet.wasApplied();
122122
}
123123

124124
private void insertUser(User user) {
125125
BoundStatementBuilder insertUser = preparedInsertUser.boundStatementBuilder();
126-
userHelper.set(user, insertUser, NullSavingStrategy.DO_NOT_SET);
126+
userHelper.set(user, insertUser, NullSavingStrategy.DO_NOT_SET, false);
127127
session.execute(insertUser.build());
128128
}
129129
}

examples/src/main/java/com/datastax/oss/driver/examples/mapper/killrvideo/user/LoginQueryProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Optional<User> login(String email, char[] password) {
6262
throw new IllegalStateException(
6363
"Should have found matching row for userid " + userid);
6464
} else {
65-
return Optional.of(userHelper.get(userRow));
65+
return Optional.of(userHelper.get(userRow, false));
6666
}
6767
} else {
6868
return Optional.empty();

examples/src/main/java/com/datastax/oss/driver/examples/mapper/killrvideo/video/CreateVideoQueryProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static <T> PreparedStatement prepareInsert(
9595
private static <T> BoundStatement bind(
9696
PreparedStatement preparedStatement, T entity, EntityHelper<T> entityHelper) {
9797
BoundStatementBuilder boundStatement = preparedStatement.boundStatementBuilder();
98-
entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET);
98+
entityHelper.set(entity, boundStatement, NullSavingStrategy.DO_NOT_SET, false);
9999
return boundStatement.build();
100100
}
101101

integration-tests/src/test/java/com/datastax/oss/driver/mapper/GetEntityIT.java

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.datastax.oss.driver.mapper;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.catchThrowable;
1920

2021
import com.datastax.oss.driver.api.core.CqlIdentifier;
2122
import com.datastax.oss.driver.api.core.CqlSession;
@@ -25,6 +26,8 @@
2526
import com.datastax.oss.driver.api.core.cql.ResultSet;
2627
import com.datastax.oss.driver.api.core.cql.Row;
2728
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
29+
import com.datastax.oss.driver.api.core.data.UdtValue;
30+
import com.datastax.oss.driver.api.core.type.UserDefinedType;
2831
import com.datastax.oss.driver.api.mapper.annotations.Dao;
2932
import com.datastax.oss.driver.api.mapper.annotations.DaoFactory;
3033
import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace;
@@ -38,6 +41,7 @@
3841
import com.datastax.oss.driver.categories.ParallelizableTests;
3942
import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures;
4043
import com.datastax.oss.driver.shaded.guava.common.collect.Sets;
44+
import java.util.UUID;
4145
import java.util.stream.Stream;
4246
import org.junit.BeforeClass;
4347
import org.junit.ClassRule;
@@ -56,6 +60,8 @@ public class GetEntityIT extends InventoryITBase {
5660
@ClassRule
5761
public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE);
5862

63+
private static final UUID PRODUCT_2D_ID = UUID.randomUUID();
64+
5965
private static ProductDao dao;
6066

6167
@BeforeClass
@@ -67,6 +73,18 @@ public static void setup() {
6773
SimpleStatement.builder(query).setExecutionProfile(SESSION_RULE.slowProfile()).build());
6874
}
6975

76+
UserDefinedType dimensions2d =
77+
session
78+
.getKeyspace()
79+
.flatMap(ks -> session.getMetadata().getKeyspace(ks))
80+
.flatMap(ks -> ks.getUserDefinedType("dimensions2d"))
81+
.orElseThrow(AssertionError::new);
82+
session.execute(
83+
"INSERT INTO product2d (id, description, dimensions) VALUES (?, ?, ?)",
84+
PRODUCT_2D_ID,
85+
"2D product",
86+
dimensions2d.newValue(12, 34));
87+
7088
InventoryMapper inventoryMapper = new GetEntityIT_InventoryMapperBuilder(session).build();
7189
dao = inventoryMapper.productDao(SESSION_RULE.keyspace());
7290

@@ -75,19 +93,98 @@ public static void setup() {
7593
}
7694

7795
@Test
78-
public void should_get_entity_from_row() {
96+
public void should_get_entity_from_complete_row() {
7997
CqlSession session = SESSION_RULE.session();
8098
ResultSet rs =
8199
session.execute(
82100
SimpleStatement.newInstance(
83-
"SELECT * FROM product WHERE id = ?", FLAMETHROWER.getId()));
101+
"SELECT id, description, dimensions, now() FROM product WHERE id = ?",
102+
FLAMETHROWER.getId()));
84103
Row row = rs.one();
85104
assertThat(row).isNotNull();
86105

87106
Product product = dao.get(row);
88107
assertThat(product).isEqualTo(FLAMETHROWER);
89108
}
90109

110+
@Test
111+
public void should_not_get_entity_from_partial_row_when_not_lenient() {
112+
CqlSession session = SESSION_RULE.session();
113+
ResultSet rs =
114+
session.execute(
115+
SimpleStatement.newInstance(
116+
"SELECT id, description, now() FROM product WHERE id = ?", FLAMETHROWER.getId()));
117+
Row row = rs.one();
118+
assertThat(row).isNotNull();
119+
120+
Throwable error = catchThrowable(() -> dao.get(row));
121+
assertThat(error).hasMessage("dimensions is not a column in this row");
122+
}
123+
124+
@Test
125+
public void should_get_entity_from_partial_row_when_lenient() {
126+
CqlSession session = SESSION_RULE.session();
127+
ResultSet rs =
128+
session.execute(
129+
SimpleStatement.newInstance(
130+
"SELECT id, dimensions FROM product2d WHERE id = ?", PRODUCT_2D_ID));
131+
Row row = rs.one();
132+
assertThat(row).isNotNull();
133+
134+
Product product = dao.getLenient(row);
135+
assertThat(product.getId()).isEqualTo(PRODUCT_2D_ID);
136+
assertThat(product.getDescription()).isNull();
137+
assertThat(product.getDimensions()).isNotNull();
138+
assertThat(product.getDimensions().getWidth()).isEqualTo(12);
139+
assertThat(product.getDimensions().getHeight()).isEqualTo(34);
140+
assertThat(product.getDimensions().getLength()).isZero();
141+
}
142+
143+
@Test
144+
public void should_get_entity_from_complete_udt_value() {
145+
CqlSession session = SESSION_RULE.session();
146+
ResultSet rs =
147+
session.execute(
148+
SimpleStatement.newInstance(
149+
"SELECT dimensions FROM product WHERE id = ?", FLAMETHROWER.getId()));
150+
Row row = rs.one();
151+
assertThat(row).isNotNull();
152+
153+
Dimensions dimensions = dao.get(row.getUdtValue(0));
154+
assertThat(dimensions).isEqualTo(FLAMETHROWER.getDimensions());
155+
}
156+
157+
@Test
158+
public void should_not_get_entity_from_partial_udt_value_when_not_lenient() {
159+
CqlSession session = SESSION_RULE.session();
160+
ResultSet rs =
161+
session.execute(
162+
SimpleStatement.newInstance(
163+
"SELECT dimensions FROM product2d WHERE id = ?", PRODUCT_2D_ID));
164+
Row row = rs.one();
165+
assertThat(row).isNotNull();
166+
167+
Throwable error = catchThrowable(() -> dao.get(row.getUdtValue(0)));
168+
assertThat(error).hasMessage("length is not a field in this UDT");
169+
}
170+
171+
@Test
172+
public void should_get_entity_from_partial_udt_value_when_lenient() {
173+
CqlSession session = SESSION_RULE.session();
174+
ResultSet rs =
175+
session.execute(
176+
SimpleStatement.newInstance(
177+
"SELECT dimensions FROM product2d WHERE id = ?", PRODUCT_2D_ID));
178+
Row row = rs.one();
179+
assertThat(row).isNotNull();
180+
181+
Dimensions dimensions = dao.getLenient(row.getUdtValue(0));
182+
assertThat(dimensions).isNotNull();
183+
assertThat(dimensions.getWidth()).isEqualTo(12);
184+
assertThat(dimensions.getHeight()).isEqualTo(34);
185+
assertThat(dimensions.getLength()).isZero();
186+
}
187+
91188
@Test
92189
public void should_get_entity_from_first_row_of_result_set() {
93190
CqlSession session = SESSION_RULE.session();
@@ -144,9 +241,19 @@ public interface InventoryMapper {
144241
@Dao
145242
@DefaultNullSavingStrategy(NullSavingStrategy.SET_TO_NULL)
146243
public interface ProductDao {
244+
147245
@GetEntity
148246
Product get(Row row);
149247

248+
@GetEntity(lenient = true)
249+
Product getLenient(Row row);
250+
251+
@GetEntity
252+
Dimensions get(UdtValue row);
253+
254+
@GetEntity(lenient = true)
255+
Dimensions getLenient(UdtValue row);
256+
150257
@GetEntity
151258
PagingIterable<Product> get(ResultSet resultSet);
152259

integration-tests/src/test/java/com/datastax/oss/driver/mapper/GuavaFutureProducerService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected Object convert(
106106
@NonNull AsyncResultSet resultSet, @Nullable EntityHelper<?> entityHelper) {
107107
assert entityHelper != null;
108108
Row row = resultSet.one();
109-
return (row == null) ? null : entityHelper.get(row);
109+
return (row == null) ? null : entityHelper.get(row, false);
110110
}
111111
}
112112
}

0 commit comments

Comments
 (0)