Skip to content

Commit e0b54e2

Browse files
committed
Merge pull request #44291 from nosan
* pr/44291: Polish "Auto-configure CqlTemplate and ReactiveCqlTemplate" Auto-configure CqlTemplate and ReactiveCqlTemplate Closes gh-44291
2 parents e884aa3 + 8d713fb commit e0b54e2

File tree

5 files changed

+106
-88
lines changed

5 files changed

+106
-88
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@
4545
import org.springframework.data.cassandra.core.convert.CassandraConverter;
4646
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
4747
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
48+
import org.springframework.data.cassandra.core.cql.CqlOperations;
49+
import org.springframework.data.cassandra.core.cql.CqlTemplate;
4850
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
4951
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
5052

@@ -114,10 +116,16 @@ public SessionFactoryFactoryBean cassandraSessionFactory(Environment environment
114116
return session;
115117
}
116118

119+
@Bean
120+
@ConditionalOnMissingBean(CqlOperations.class)
121+
public CqlTemplate cqlTemplate(SessionFactory sessionFactory) {
122+
return new CqlTemplate(sessionFactory);
123+
}
124+
117125
@Bean
118126
@ConditionalOnMissingBean(CassandraOperations.class)
119-
public CassandraTemplate cassandraTemplate(SessionFactory sessionFactory, CassandraConverter converter) {
120-
return new CassandraTemplate(sessionFactory, converter);
127+
public CassandraTemplate cassandraTemplate(CqlTemplate cqlTemplate, CassandraConverter converter) {
128+
return new CassandraTemplate(cqlTemplate, converter);
121129
}
122130

123131
@Bean

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveDataAutoConfiguration.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@
3030
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
3131
import org.springframework.data.cassandra.core.ReactiveCassandraTemplate;
3232
import org.springframework.data.cassandra.core.convert.CassandraConverter;
33+
import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations;
34+
import org.springframework.data.cassandra.core.cql.ReactiveCqlTemplate;
3335
import org.springframework.data.cassandra.core.cql.session.DefaultBridgedReactiveSession;
3436
import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory;
3537

@@ -58,11 +60,17 @@ public ReactiveSessionFactory reactiveCassandraSessionFactory(ReactiveSession re
5860
return new DefaultReactiveSessionFactory(reactiveCassandraSession);
5961
}
6062

63+
@Bean
64+
@ConditionalOnMissingBean(ReactiveCqlOperations.class)
65+
public ReactiveCqlTemplate reactiveCqlTemplate(ReactiveSessionFactory reactiveCassandraSessionFactory) {
66+
return new ReactiveCqlTemplate(reactiveCassandraSessionFactory);
67+
}
68+
6169
@Bean
6270
@ConditionalOnMissingBean(ReactiveCassandraOperations.class)
63-
public ReactiveCassandraTemplate reactiveCassandraTemplate(ReactiveSession reactiveCassandraSession,
71+
public ReactiveCassandraTemplate reactiveCassandraTemplate(ReactiveCqlTemplate reactiveCqlTemplate,
6472
CassandraConverter converter) {
65-
return new ReactiveCassandraTemplate(reactiveCassandraSession, converter);
73+
return new ReactiveCassandraTemplate(reactiveCqlTemplate, converter);
6674
}
6775

6876
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,25 +19,23 @@
1919
import java.util.Collections;
2020

2121
import com.datastax.oss.driver.api.core.CqlSession;
22-
import org.junit.jupiter.api.AfterEach;
2322
import org.junit.jupiter.api.Test;
2423

24+
import org.springframework.boot.autoconfigure.AutoConfigurations;
2525
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
2626
import org.springframework.boot.autoconfigure.data.cassandra.city.City;
2727
import org.springframework.boot.autoconfigure.domain.EntityScan;
28-
import org.springframework.boot.test.util.TestPropertyValues;
28+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2929
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3030
import org.springframework.context.annotation.Bean;
3131
import org.springframework.context.annotation.Configuration;
3232
import org.springframework.core.convert.converter.Converter;
3333
import org.springframework.data.cassandra.core.CassandraTemplate;
3434
import org.springframework.data.cassandra.core.convert.CassandraConverter;
3535
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
36+
import org.springframework.data.cassandra.core.cql.CqlTemplate;
3637
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
3738
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
38-
import org.springframework.data.domain.ManagedTypes;
39-
import org.springframework.test.util.ReflectionTestUtils;
40-
import org.springframework.util.ObjectUtils;
4139

4240
import static org.assertj.core.api.Assertions.assertThat;
4341

@@ -50,74 +48,81 @@
5048
*/
5149
class CassandraDataAutoConfigurationTests {
5250

53-
private AnnotationConfigApplicationContext context;
51+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
52+
.withPropertyValues("spring.cassandra.keyspaceName=boot_test")
53+
.withUserConfiguration(CassandraMockConfiguration.class)
54+
.withConfiguration(
55+
AutoConfigurations.of(CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class));
5456

55-
@AfterEach
56-
void close() {
57-
if (this.context != null) {
58-
this.context.close();
59-
}
57+
@Test
58+
void cqlTemplateExists() {
59+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(CqlTemplate.class));
6060
}
6161

6262
@Test
6363
void templateExists() {
64-
load(CassandraMockConfiguration.class);
65-
assertThat(this.context.getBeanNamesForType(CassandraTemplate.class)).hasSize(1);
64+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(CassandraTemplate.class));
65+
}
66+
67+
@Test
68+
void templateUsesCqlTemplate() {
69+
this.contextRunner.run((context) -> {
70+
assertThat(context).hasSingleBean(CassandraTemplate.class);
71+
assertThat(context.getBean(CassandraTemplate.class).getCqlOperations())
72+
.isSameAs(context.getBean(CqlTemplate.class));
73+
});
6674
}
6775

6876
@Test
6977
void entityScanShouldSetManagedTypes() {
70-
load(EntityScanConfig.class);
71-
CassandraMappingContext mappingContext = this.context.getBean(CassandraMappingContext.class);
72-
ManagedTypes managedTypes = (ManagedTypes) ReflectionTestUtils.getField(mappingContext, "managedTypes");
73-
assertThat(managedTypes.toList()).containsOnly(City.class);
78+
this.contextRunner.withUserConfiguration(EntityScanConfig.class).run((context) -> {
79+
assertThat(context).hasSingleBean(CassandraMappingContext.class);
80+
CassandraMappingContext mappingContext = context.getBean(CassandraMappingContext.class);
81+
assertThat(mappingContext.getManagedTypes()).singleElement()
82+
.satisfies((typeInformation) -> assertThat(typeInformation.getType()).isEqualTo(City.class));
83+
});
7484
}
7585

7686
@Test
7787
void userTypeResolverShouldBeSet() {
78-
load();
79-
CassandraConverter cassandraConverter = this.context.getBean(CassandraConverter.class);
80-
assertThat(cassandraConverter).extracting("userTypeResolver").isInstanceOf(SimpleUserTypeResolver.class);
88+
this.contextRunner.run((context) -> {
89+
assertThat(context).hasSingleBean(CassandraConverter.class);
90+
assertThat(context.getBean(CassandraConverter.class)).extracting("userTypeResolver")
91+
.isInstanceOf(SimpleUserTypeResolver.class);
92+
});
8193
}
8294

8395
@Test
8496
void codecRegistryShouldBeSet() {
85-
load();
86-
CassandraConverter cassandraConverter = this.context.getBean(CassandraConverter.class);
87-
assertThat(cassandraConverter.getCodecRegistry())
88-
.isSameAs(this.context.getBean(CassandraMockConfiguration.class).codecRegistry);
97+
this.contextRunner.run((context) -> {
98+
assertThat(context).hasSingleBean(CassandraConverter.class);
99+
assertThat(context.getBean(CassandraConverter.class).getCodecRegistry())
100+
.isSameAs(context.getBean(CassandraMockConfiguration.class).codecRegistry);
101+
});
89102
}
90103

91104
@Test
92105
void defaultConversions() {
93-
load();
94-
CassandraTemplate template = this.context.getBean(CassandraTemplate.class);
95-
assertThat(template.getConverter().getConversionService().canConvert(Person.class, String.class)).isFalse();
106+
this.contextRunner.run((context) -> {
107+
CassandraTemplate template = context.getBean(CassandraTemplate.class);
108+
assertThat(template.getConverter().getConversionService().canConvert(Person.class, String.class)).isFalse();
109+
});
96110
}
97111

98112
@Test
99113
void customConversions() {
100-
load(CustomConversionConfig.class);
101-
CassandraTemplate template = this.context.getBean(CassandraTemplate.class);
102-
assertThat(template.getConverter().getConversionService().canConvert(Person.class, String.class)).isTrue();
114+
this.contextRunner.withUserConfiguration(CustomConversionConfig.class).run((context) -> {
115+
CassandraTemplate template = context.getBean(CassandraTemplate.class);
116+
assertThat(template.getConverter().getConversionService().canConvert(Person.class, String.class)).isTrue();
117+
});
103118
}
104119

105120
@Test
106121
void clusterDoesNotExist() {
107-
this.context = new AnnotationConfigApplicationContext(CassandraDataAutoConfiguration.class);
108-
assertThat(this.context.getBeansOfType(CqlSession.class)).isEmpty();
109-
}
110-
111-
void load(Class<?>... config) {
112-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
113-
TestPropertyValues.of("spring.cassandra.keyspaceName:boot_test").applyTo(ctx);
114-
if (!ObjectUtils.isEmpty(config)) {
115-
ctx.register(config);
122+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
123+
CassandraDataAutoConfiguration.class)) {
124+
assertThat(context.getBeansOfType(CqlSession.class)).isEmpty();
116125
}
117-
ctx.register(CassandraMockConfiguration.class, CassandraAutoConfiguration.class,
118-
CassandraDataAutoConfiguration.class);
119-
ctx.refresh();
120-
this.context = ctx;
121126
}
122127

123128
@Configuration(proxyBeanMethods = false)

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraReactiveDataAutoConfigurationTests.java

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,21 +16,19 @@
1616

1717
package org.springframework.boot.autoconfigure.data.cassandra;
1818

19-
import org.junit.jupiter.api.AfterEach;
2019
import org.junit.jupiter.api.Test;
2120

21+
import org.springframework.boot.autoconfigure.AutoConfigurations;
2222
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
2323
import org.springframework.boot.autoconfigure.data.cassandra.city.City;
2424
import org.springframework.boot.autoconfigure.domain.EntityScan;
25-
import org.springframework.boot.test.util.TestPropertyValues;
26-
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
25+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2726
import org.springframework.context.annotation.Configuration;
2827
import org.springframework.data.cassandra.core.ReactiveCassandraTemplate;
2928
import org.springframework.data.cassandra.core.convert.CassandraConverter;
29+
import org.springframework.data.cassandra.core.cql.ReactiveCqlTemplate;
3030
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
3131
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
32-
import org.springframework.data.domain.ManagedTypes;
33-
import org.springframework.test.util.ReflectionTestUtils;
3432

3533
import static org.assertj.core.api.Assertions.assertThat;
3634

@@ -43,50 +41,49 @@
4341
*/
4442
class CassandraReactiveDataAutoConfigurationTests {
4543

46-
private AnnotationConfigApplicationContext context;
44+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
45+
.withPropertyValues("spring.cassandra.keyspaceName=boot_test")
46+
.withUserConfiguration(CassandraMockConfiguration.class)
47+
.withConfiguration(AutoConfigurations.of(CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
48+
CassandraReactiveDataAutoConfiguration.class));
4749

48-
@AfterEach
49-
void close() {
50-
if (this.context != null) {
51-
this.context.close();
52-
}
50+
@Test
51+
void reactiveCqlTemplateExists() {
52+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ReactiveCqlTemplate.class));
5353
}
5454

5555
@Test
5656
void templateExists() {
57-
load("spring.cassandra.keyspaceName:boot_test");
58-
assertThat(this.context.getBeanNamesForType(ReactiveCassandraTemplate.class)).hasSize(1);
57+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ReactiveCassandraTemplate.class));
5958
}
6059

6160
@Test
62-
void entityScanShouldSetManagedTypes() {
63-
load(EntityScanConfig.class, "spring.cassandra.keyspaceName:boot_test");
64-
CassandraMappingContext mappingContext = this.context.getBean(CassandraMappingContext.class);
65-
ManagedTypes managedTypes = (ManagedTypes) ReflectionTestUtils.getField(mappingContext, "managedTypes");
66-
assertThat(managedTypes.toList()).containsOnly(City.class);
61+
void templateUsesReactiveCqlTemplate() {
62+
this.contextRunner.run((context) -> {
63+
assertThat(context).hasSingleBean(ReactiveCassandraTemplate.class);
64+
assertThat(context.getBean(ReactiveCassandraTemplate.class).getReactiveCqlOperations())
65+
.isSameAs(context.getBean(ReactiveCqlTemplate.class));
66+
});
6767
}
6868

6969
@Test
70-
void userTypeResolverShouldBeSet() {
71-
load("spring.cassandra.keyspaceName:boot_test");
72-
CassandraConverter cassandraConverter = this.context.getBean(CassandraConverter.class);
73-
assertThat(cassandraConverter).extracting("userTypeResolver").isInstanceOf(SimpleUserTypeResolver.class);
74-
}
75-
76-
private void load(String... environment) {
77-
load(null, environment);
70+
void entityScanShouldSetManagedTypes() {
71+
this.contextRunner.withUserConfiguration(EntityScanConfig.class).run((context) -> {
72+
assertThat(context).hasSingleBean(CassandraMappingContext.class);
73+
CassandraMappingContext mappingContext = context.getBean(CassandraMappingContext.class);
74+
assertThat(mappingContext.getManagedTypes()).singleElement()
75+
.satisfies((typeInformation) -> assertThat(typeInformation.getType()).isEqualTo(City.class));
76+
});
7877
}
7978

80-
private void load(Class<?> config, String... environment) {
81-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
82-
TestPropertyValues.of(environment).applyTo(ctx);
83-
if (config != null) {
84-
ctx.register(config);
85-
}
86-
ctx.register(CassandraMockConfiguration.class, CassandraAutoConfiguration.class,
87-
CassandraDataAutoConfiguration.class, CassandraReactiveDataAutoConfiguration.class);
88-
ctx.refresh();
89-
this.context = ctx;
79+
@Test
80+
void userTypeResolverShouldBeSet() {
81+
this.contextRunner.run((context) -> {
82+
assertThat(context).hasSingleBean(CassandraConverter.class);
83+
assertThat(context).hasSingleBean(CassandraConverter.class);
84+
assertThat(context.getBean(CassandraConverter.class)).extracting("userTypeResolver")
85+
.isInstanceOf(SimpleUserTypeResolver.class);
86+
});
9087
}
9188

9289
@Configuration(proxyBeanMethods = false)

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/data/nosql.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ There is a `spring-boot-starter-data-cassandra` starter for collecting the depen
440440
[[data.nosql.cassandra.connecting]]
441441
=== Connecting to Cassandra
442442

443-
You can inject an auto-configured javadoc:org.springframework.data.cassandra.core.CassandraTemplate[] or a Cassandra `CqlSession` instance as you would with any other Spring Bean.
443+
You can inject an auto-configured javadoc:org.springframework.data.cassandra.core.cql.CqlTemplate[], javadoc:org.springframework.data.cassandra.core.CassandraTemplate[], or a Cassandra `CqlSession` instance as you would with any other Spring Bean.
444444
The `spring.cassandra.*` properties can be used to customize the connection.
445445
Generally, you provide `keyspace-name` and `contact-points` as well the local datacenter name, as shown in the following example:
446446

0 commit comments

Comments
 (0)