Skip to content

Commit 0d8f318

Browse files
onobcmp911de
authored andcommitted
Add Ahead of Time Repository support.
Closes: #1566 Original pull request: #1600
1 parent a79c630 commit 0d8f318

File tree

45 files changed

+4205
-162
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4205
-162
lines changed

spring-data-cassandra/pom.xml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@
5050
<artifactId>spring-tx</artifactId>
5151
</dependency>
5252

53+
<dependency>
54+
<groupId>org.springframework</groupId>
55+
<artifactId>spring-core-test</artifactId>
56+
<scope>test</scope>
57+
</dependency>
58+
5359
<!-- Spring Data -->
5460
<dependency>
5561
<groupId>${project.groupId}</groupId>
@@ -198,6 +204,13 @@
198204
<scope>test</scope>
199205
</dependency>
200206

207+
<dependency>
208+
<groupId>net.javacrumbs.json-unit</groupId>
209+
<artifactId>json-unit-assertj</artifactId>
210+
<version>4.1.0</version>
211+
<scope>test</scope>
212+
</dependency>
213+
201214
<dependency>
202215
<groupId>edu.umd.cs.mtc</groupId>
203216
<artifactId>multithreadedtc</artifactId>
@@ -227,11 +240,6 @@
227240
<artifactId>kotlinx-coroutines-reactor</artifactId>
228241
<optional>true</optional>
229242
</dependency>
230-
<dependency>
231-
<groupId>org.jspecify</groupId>
232-
<artifactId>jspecify</artifactId>
233-
<version>1.0.0</version>
234-
</dependency>
235243

236244
<dependency>
237245
<groupId>io.mockk</groupId>

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.cassandra.core;
1717

1818
import java.util.List;
19+
import java.util.Map;
1920
import java.util.function.Consumer;
2021
import java.util.function.Function;
2122
import java.util.function.Supplier;
@@ -423,6 +424,27 @@ <T, R> List<R> doSelect(Query query, Class<?> entityClass, CqlIdentifier tableNa
423424
return doQuery(select.build(), rowMapper);
424425
}
425426

427+
ResultSet doSelectResultSet(Query query, Class<?> entityClass, CqlIdentifier tableName) {
428+
429+
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
430+
431+
StatementBuilder<Select> select = getStatementFactory().select(query, entity, tableName);
432+
SimpleStatement statement = select.build();
433+
434+
return queryForResultSet(statement);
435+
}
436+
437+
ResultSet queryForResultSet(Statement<?> statement) {
438+
439+
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
440+
441+
PreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
442+
return getCqlOperations().query(statementHandler, statementHandler, resultSet -> resultSet);
443+
}
444+
445+
return getCqlOperations().queryForResultSet(statement);
446+
}
447+
426448
@Override
427449
public <T> @Nullable T selectOne(Query query, Class<T> entityClass) throws DataAccessException {
428450

@@ -970,6 +992,11 @@ private <T> Function<Row, T> getMapper(EntityProjection<T, ?> projection, CqlIde
970992

971993
Class<T> targetType = projection.getMappedType().getType();
972994

995+
if (Map.class.isAssignableFrom(targetType)) {
996+
ColumnMapRowMapper columnMapRowMapper = new ColumnMapRowMapper();
997+
return row -> (T) columnMapRowMapper.mapRow(row, 0);
998+
}
999+
9731000
return row -> {
9741001

9751002
maybeEmitEvent(() -> new AfterLoadEvent<>(row, targetType, tableName));

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ExecutableSelectOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public interface ExecutableSelectOperation {
103103
*
104104
* @since 5.0
105105
*/
106-
interface UntypedSelect {
106+
interface UntypedSelect extends TerminatingProjections {
107107

108108
/**
109109
* Define the {@link Class result target type} that the Cassandra Row fields should be mapped to.

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ExecutableSelectOperationSupport.java

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.springframework.util.ObjectUtils;
3030

3131
import com.datastax.oss.driver.api.core.CqlIdentifier;
32+
import com.datastax.oss.driver.api.core.cql.ResultSet;
33+
import com.datastax.oss.driver.api.core.cql.Row;
3234
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
3335
import com.datastax.oss.driver.api.core.cql.Statement;
3436

@@ -91,6 +93,21 @@ public <T> TerminatingResults<T> map(RowMapper<T> mapper) {
9193
return new TerminatingSelectResultSupport<>(template, statement, mapper);
9294
}
9395

96+
@Override
97+
public long count() {
98+
99+
List<Row> rows = template.select(statement, Row.class);
100+
101+
if (rows.size() == 1) {
102+
103+
Object object = rows.get(0).getObject(0);
104+
105+
return ((Number) object).longValue();
106+
}
107+
108+
return 0;
109+
}
110+
94111
}
95112

96113
static class TypedSelectSupport<T> extends TerminatingSelectResultSupport<T, T> implements TerminatingResults<T> {
@@ -99,7 +116,9 @@ static class TypedSelectSupport<T> extends TerminatingSelectResultSupport<T, T>
99116

100117
TypedSelectSupport(CassandraTemplate template, Statement<?> statement, Class<T> domainType) {
101118
super(template, statement,
102-
template.getRowMapper(domainType, EntityQueryUtils.getTableName(statement), QueryResultConverter.entity()));
119+
ResultSet.class.isAssignableFrom(domainType) ? null
120+
: template.getRowMapper(domainType, EntityQueryUtils.getTableName(statement),
121+
QueryResultConverter.entity()));
103122

104123
this.domainType = domainType;
105124
}
@@ -120,9 +139,10 @@ static class TerminatingSelectResultSupport<S, T> implements TerminatingResults<
120139

121140
final Statement<?> statement;
122141

123-
final RowMapper<T> rowMapper;
142+
final @Nullable RowMapper<T> rowMapper;
124143

125-
TerminatingSelectResultSupport(CassandraTemplate template, Statement<?> statement, RowMapper<T> rowMapper) {
144+
TerminatingSelectResultSupport(CassandraTemplate template, Statement<?> statement,
145+
@Nullable RowMapper<T> rowMapper) {
126146
this.template = template;
127147
this.statement = statement;
128148
this.rowMapper = rowMapper;
@@ -156,6 +176,10 @@ public <R> TerminatingResults<R> map(QueryResultConverter<? super T, ? extends R
156176
@Override
157177
public @Nullable T oneValue() {
158178

179+
if (this.rowMapper == null) {
180+
return (T) this.template.queryForResultSet(this.statement);
181+
}
182+
159183
List<T> result = this.template.getCqlOperations().query(this.statement, this.rowMapper);
160184

161185
if (ObjectUtils.isEmpty(result)) {
@@ -271,6 +295,10 @@ public boolean exists() {
271295
@Override
272296
public @Nullable T oneValue() {
273297

298+
if (this.returnType.equals(ResultSet.class)) {
299+
return (T) this.template.doSelectResultSet(this.query.limit(2), this.domainType, getTableName());
300+
}
301+
274302
List<T> result = this.template.doSelect(this.query.limit(2), this.domainType, getTableName(), this.returnType,
275303
this.mappingFunction);
276304

@@ -279,8 +307,8 @@ public boolean exists() {
279307
}
280308

281309
if (result.size() > 1) {
282-
throw new IncorrectResultSizeDataAccessException(
283-
String.format("Query [%s] returned non unique result", this.query), 1);
310+
throw new IncorrectResultSizeDataAccessException(1, result.size());
311+
284312
}
285313

286314
return result.iterator().next();

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.jspecify.annotations.NonNull;
3131
import org.jspecify.annotations.Nullable;
32+
3233
import org.springframework.data.cassandra.core.convert.CassandraConverter;
3334
import org.springframework.data.cassandra.core.convert.QueryMapper;
3435
import org.springframework.data.cassandra.core.convert.UpdateMapper;
@@ -73,6 +74,7 @@
7374
import org.springframework.util.ClassUtils;
7475

7576
import com.datastax.oss.driver.api.core.CqlIdentifier;
77+
import com.datastax.oss.driver.api.core.cql.ResultSet;
7678
import com.datastax.oss.driver.api.core.data.CqlVector;
7779
import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder;
7880
import com.datastax.oss.driver.api.querybuilder.BindMarker;
@@ -615,7 +617,8 @@ public StatementBuilder<Delete> delete(Object entity, QueryOptions options, Enti
615617
Columns computeColumnsForProjection(EntityProjection<?, ?> projection, Columns columns,
616618
CassandraPersistentEntity<?> domainType, Class<?> returnType) {
617619

618-
if (!columns.isEmpty() || ClassUtils.isAssignable(domainType.getType(), returnType)) {
620+
if (!columns.isEmpty() || ClassUtils.isAssignable(domainType.getType(), returnType)
621+
|| ClassUtils.isAssignable(Map.class, returnType) || ClassUtils.isAssignable(ResultSet.class, returnType)) {
619622
return columns;
620623
}
621624

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/QueryMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public Filter getMappedObject(Filter filter, CassandraPersistentEntity<?> entity
145145
return Filter.from(result);
146146
}
147147

148-
private @Nullable Object getMappedValue(Field field, CriteriaDefinition.Operator operator, Object value) {
148+
protected @Nullable Object getMappedValue(Field field, CriteriaDefinition.Operator operator, Object value) {
149149

150150
if (field.getProperty().isPresent()
151151
&& field.getProperty().filter(it -> converter.getCustomConversions().hasValueConverter(it)).isPresent()) {

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Query.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ public Query pageRequest(Pageable pageable) {
232232

233233
CassandraPageRequest.validatePageable(pageable);
234234

235+
if (pageable.isUnpaged()) {
236+
return this;
237+
}
238+
235239
CassandraScrollPosition scrollPosition = getScrollPosition();
236240

237241
if (pageable instanceof CassandraPageRequest cpr) {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.cassandra.repository.aot;
17+
18+
import java.util.List;
19+
20+
import org.springframework.data.cassandra.repository.query.ParameterBinding;
21+
import org.springframework.data.domain.Limit;
22+
23+
/**
24+
* AOT query value object along with its parameter bindings.
25+
*
26+
* @author Mark Paluch
27+
* @since 5.0
28+
*/
29+
abstract class AotQuery {
30+
31+
private final List<ParameterBinding> parameterBindings;
32+
33+
AotQuery(List<ParameterBinding> parameterBindings) {
34+
this.parameterBindings = parameterBindings;
35+
}
36+
37+
/**
38+
* @return the list of parameter bindings.
39+
*/
40+
public List<ParameterBinding> getParameterBindings() {
41+
return parameterBindings;
42+
}
43+
44+
/**
45+
* @return the preliminary query limit.
46+
*/
47+
public Limit getLimit() {
48+
return Limit.unlimited();
49+
}
50+
51+
/**
52+
* @return whether the query is limited (e.g. {@code findTop10By}).
53+
*/
54+
public boolean isLimited() {
55+
return getLimit().isLimited();
56+
}
57+
58+
/**
59+
* @return whether the query a delete query.
60+
*/
61+
public boolean isDelete() {
62+
return false;
63+
}
64+
65+
/**
66+
* @return whether the query is a count query.
67+
*/
68+
public boolean isCount() {
69+
return false;
70+
}
71+
72+
/**
73+
* @return whether the query is an exists query.
74+
*/
75+
public boolean isExists() {
76+
return false;
77+
}
78+
79+
}

0 commit comments

Comments
 (0)