Skip to content

Commit c357e5b

Browse files
mp911deschauder
authored andcommitted
#30 - Add custom conversion support.
We now support custom conversions via R2dbcCustomConversions. Custom conversions introduces simple types that depend on the used dialect. Custom conversions and simple types are held in RelationalConverter and MappingContext. Simple types and conversions are used by DatabaseClient and repository support to properly apply registered converters and support native types such as array-columns. Related tickets: #22, #26. Original pull request: #31.
1 parent 06c2a3a commit c357e5b

14 files changed

+334
-40
lines changed

src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@
1717

1818
import io.r2dbc.spi.ConnectionFactory;
1919

20+
import java.util.Collections;
2021
import java.util.Optional;
2122

2223
import org.springframework.context.annotation.Bean;
2324
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.core.convert.converter.Converter;
26+
import org.springframework.data.convert.CustomConversions;
27+
import org.springframework.data.convert.CustomConversions.StoreConversions;
2428
import org.springframework.data.r2dbc.dialect.Database;
2529
import org.springframework.data.r2dbc.dialect.Dialect;
2630
import org.springframework.data.r2dbc.function.DatabaseClient;
2731
import org.springframework.data.r2dbc.function.DefaultReactiveDataAccessStrategy;
2832
import org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy;
33+
import org.springframework.data.r2dbc.function.convert.R2dbcCustomConversions;
2934
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
3035
import org.springframework.data.r2dbc.support.SqlErrorCodeR2dbcExceptionTranslator;
3136
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
@@ -95,33 +100,59 @@ public DatabaseClient databaseClient(ReactiveDataAccessStrategy dataAccessStrate
95100
* Register a {@link RelationalMappingContext} and apply an optional {@link NamingStrategy}.
96101
*
97102
* @param namingStrategy optional {@link NamingStrategy}. Use {@link NamingStrategy#INSTANCE} as fallback.
103+
* @param r2dbcCustomConversions customized R2DBC conversions.
98104
* @return must not be {@literal null}.
99105
* @throws IllegalArgumentException if any of the required args is {@literal null}.
100106
*/
101107
@Bean
102-
public RelationalMappingContext r2dbcMappingContext(Optional<NamingStrategy> namingStrategy) {
108+
public RelationalMappingContext r2dbcMappingContext(Optional<NamingStrategy> namingStrategy,
109+
R2dbcCustomConversions r2dbcCustomConversions) {
103110

104111
Assert.notNull(namingStrategy, "NamingStrategy must not be null!");
105112

106-
return new RelationalMappingContext(namingStrategy.orElse(NamingStrategy.INSTANCE));
113+
RelationalMappingContext relationalMappingContext = new RelationalMappingContext(
114+
namingStrategy.orElse(NamingStrategy.INSTANCE));
115+
relationalMappingContext.setSimpleTypeHolder(r2dbcCustomConversions.getSimpleTypeHolder());
116+
117+
return relationalMappingContext;
107118
}
108119

109120
/**
110121
* Creates a {@link ReactiveDataAccessStrategy} using the configured {@link #r2dbcMappingContext(Optional)
111122
* RelationalMappingContext}.
112123
*
113124
* @param mappingContext the configured {@link RelationalMappingContext}.
125+
* @param r2dbcCustomConversions customized R2DBC conversions.
114126
* @return must not be {@literal null}.
115-
* @see #r2dbcMappingContext(Optional)
127+
* @see #r2dbcMappingContext(Optional, R2dbcCustomConversions)
116128
* @see #getDialect(ConnectionFactory)
117129
* @throws IllegalArgumentException if any of the {@literal mappingContext} is {@literal null}.
118130
*/
119131
@Bean
120-
public ReactiveDataAccessStrategy reactiveDataAccessStrategy(RelationalMappingContext mappingContext) {
132+
public ReactiveDataAccessStrategy reactiveDataAccessStrategy(RelationalMappingContext mappingContext,
133+
R2dbcCustomConversions r2dbcCustomConversions) {
121134

122135
Assert.notNull(mappingContext, "MappingContext must not be null!");
123-
return new DefaultReactiveDataAccessStrategy(getDialect(connectionFactory()),
124-
new BasicRelationalConverter(mappingContext));
136+
137+
BasicRelationalConverter converter = new BasicRelationalConverter(mappingContext, r2dbcCustomConversions);
138+
139+
return new DefaultReactiveDataAccessStrategy(getDialect(connectionFactory()), converter);
140+
}
141+
142+
/**
143+
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
144+
* {@link CustomConversions} will be registered with the {@link BasicRelationalConverter} and
145+
* {@link #r2dbcMappingContext(Optional, R2dbcCustomConversions)}. Returns an empty {@link R2dbcCustomConversions}
146+
* instance by default.
147+
*
148+
* @return must not be {@literal null}.
149+
*/
150+
@Bean
151+
public R2dbcCustomConversions r2dbcCustomConversions() {
152+
153+
Dialect dialect = getDialect(connectionFactory());
154+
StoreConversions storeConversions = StoreConversions.of(dialect.getSimpleTypeHolder());
155+
return new R2dbcCustomConversions(storeConversions, Collections.emptyList());
125156
}
126157

127158
/**

src/main/java/org/springframework/data/r2dbc/dialect/Dialect.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package org.springframework.data.r2dbc.dialect;
22

3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.HashSet;
6+
7+
import org.springframework.data.mapping.model.SimpleTypeHolder;
8+
39
/**
410
* Represents a dialect that is implemented by a particular database.
511
*
@@ -26,10 +32,41 @@ public interface Dialect {
2632
@Deprecated
2733
String generatedKeysClause();
2834

35+
/**
36+
* Return a collection of types that are natively supported by this database/driver. Defaults to
37+
* {@link Collections#emptySet()}.
38+
*
39+
* @return a collection of types that are natively supported by this database/driver. Defaults to
40+
* {@link Collections#emptySet()}.
41+
*/
42+
default Collection<? extends Class<?>> getSimpleTypes() {
43+
return Collections.emptySet();
44+
}
45+
46+
/**
47+
* Return the {@link SimpleTypeHolder} for this dialect.
48+
*
49+
* @return the {@link SimpleTypeHolder} for this dialect.
50+
* @see #getSimpleTypes()
51+
*/
52+
default SimpleTypeHolder getSimpleTypeHolder() {
53+
return new SimpleTypeHolder(new HashSet<>(getSimpleTypes()), true);
54+
}
55+
2956
/**
3057
* Return the {@link LimitClause} used by this dialect.
3158
*
3259
* @return the {@link LimitClause} used by this dialect.
3360
*/
3461
LimitClause limit();
62+
63+
/**
64+
* Returns {@literal true} whether this dialect supports array-typed column. Collection-typed columns can map their
65+
* content to native array types.
66+
*
67+
* @return {@literal true} whether this dialect supports array-typed columns.
68+
*/
69+
default boolean supportsArrayColumns() {
70+
return false;
71+
}
3572
}

src/main/java/org/springframework/data/r2dbc/dialect/PostgresDialect.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
package org.springframework.data.r2dbc.dialect;
22

3+
import java.net.InetAddress;
4+
import java.net.URI;
5+
import java.net.URL;
6+
import java.util.Arrays;
7+
import java.util.Collection;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Set;
11+
import java.util.UUID;
12+
313
/**
414
* An SQL dialect for Postgres.
515
*
616
* @author Mark Paluch
717
*/
818
public class PostgresDialect implements Dialect {
919

20+
private static final Set<Class<?>> SIMPLE_TYPES = new HashSet<>(
21+
Arrays.asList(List.class, Collection.class, String[].class, UUID.class, URL.class, URI.class, InetAddress.class));
22+
1023
/**
1124
* Singleton instance.
1225
*/
@@ -62,6 +75,15 @@ public String generatedKeysClause() {
6275
return "RETURNING *";
6376
}
6477

78+
/*
79+
* (non-Javadoc)
80+
* @see org.springframework.data.r2dbc.dialect.Dialect#getSimpleTypesKeys()
81+
*/
82+
@Override
83+
public Collection<? extends Class<?>> getSimpleTypes() {
84+
return SIMPLE_TYPES;
85+
}
86+
6587
/*
6688
* (non-Javadoc)
6789
* @see org.springframework.data.r2dbc.dialect.Dialect#limit()
@@ -70,4 +92,13 @@ public String generatedKeysClause() {
7092
public LimitClause limit() {
7193
return LIMIT_CLAUSE;
7294
}
95+
96+
/*
97+
* (non-Javadoc)
98+
* @see org.springframework.data.r2dbc.dialect.Dialect#supportsArrayColumns()
99+
*/
100+
@Override
101+
public boolean supportsArrayColumns() {
102+
return true;
103+
}
73104
}

src/main/java/org/springframework/data/r2dbc/dialect/SqlServerDialect.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package org.springframework.data.r2dbc.dialect;
22

3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
import java.util.UUID;
8+
39
/**
410
* An SQL dialect for Microsoft SQL Server.
511
*
612
* @author Mark Paluch
713
*/
814
public class SqlServerDialect implements Dialect {
915

16+
private static final Set<Class<?>> SIMPLE_TYPES = new HashSet<>(Collections.singletonList(UUID.class));
17+
1018
/**
1119
* Singleton instance.
1220
*/
@@ -63,6 +71,15 @@ public String generatedKeysClause() {
6371
return "select SCOPE_IDENTITY() AS GENERATED_KEYS";
6472
}
6573

74+
/*
75+
* (non-Javadoc)
76+
* @see org.springframework.data.r2dbc.dialect.Dialect#getSimpleTypesKeys()
77+
*/
78+
@Override
79+
public Collection<? extends Class<?>> getSimpleTypes() {
80+
return SIMPLE_TYPES;
81+
}
82+
6683
/*
6784
* (non-Javadoc)
6885
* @see org.springframework.data.r2dbc.dialect.Dialect#limit()

0 commit comments

Comments
 (0)