Skip to content

Commit 2cd7b27

Browse files
committed
Add GaussDB support for spring data jdbc
Signed-off-by: liubao68 <[email protected]>
1 parent 1f2e694 commit 2cd7b27

File tree

33 files changed

+1505
-6
lines changed

33 files changed

+1505
-6
lines changed

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<mysql-connector-java.version>8.0.33</mysql-connector-java.version>
4141
<postgresql.version>42.7.4</postgresql.version>
4242
<oracle.version>23.7.0.25.01</oracle.version>
43+
<gaussdb.version>506.0.0.b058</gaussdb.version>
4344

4445
<!-- R2DBC driver dependencies-->
4546
<r2dbc-postgresql.version>1.0.7.RELEASE</r2dbc-postgresql.version>

spring-data-jdbc/pom.xml

+38
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@
137137
<optional>true</optional>
138138
</dependency>
139139

140+
<dependency>
141+
<groupId>com.huaweicloud.gaussdb</groupId>
142+
<artifactId>gaussdbjdbc</artifactId>
143+
<version>${gaussdb.version}</version>
144+
<optional>true</optional>
145+
</dependency>
146+
140147
<dependency>
141148
<groupId>org.mariadb.jdbc</groupId>
142149
<artifactId>mariadb-java-client</artifactId>
@@ -318,6 +325,37 @@
318325
</plugins>
319326
</build>
320327
</profile>
328+
<profile>
329+
<id>gaussdb</id>
330+
<build>
331+
<plugins>
332+
<plugin>
333+
<groupId>org.apache.maven.plugins</groupId>
334+
<artifactId>maven-failsafe-plugin</artifactId>
335+
<executions>
336+
<execution>
337+
<id>gaussdb-test</id>
338+
<phase>integration-test</phase>
339+
<goals>
340+
<goal>integration-test</goal>
341+
</goals>
342+
<configuration>
343+
<includes>
344+
<include>**/*IntegrationTests.java</include>
345+
</includes>
346+
<excludes>
347+
<exclude/>
348+
</excludes>
349+
<systemPropertyVariables>
350+
<spring.profiles.active>gaussdb</spring.profiles.active>
351+
</systemPropertyVariables>
352+
</configuration>
353+
</execution>
354+
</executions>
355+
</plugin>
356+
</plugins>
357+
</build>
358+
</profile>
321359
<profile>
322360
<id>all-dbs</id>
323361
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2021-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.jdbc.core.dialect;
17+
18+
import java.util.Arrays;
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.List;
22+
import java.util.Set;
23+
import java.util.function.Consumer;
24+
25+
import org.springframework.data.relational.core.dialect.GaussDBDialect;
26+
import org.springframework.util.ClassUtils;
27+
28+
/**
29+
* JDBC specific GaussDBDialect Dialect.
30+
*
31+
* Notes: this file is token from JdbcPostgresDialect and add specific changes for GaussDB
32+
*
33+
* @author liubao
34+
*/
35+
public class JdbcGaussDBDialect extends GaussDBDialect {
36+
37+
public static final JdbcGaussDBDialect INSTANCE = new JdbcGaussDBDialect();
38+
39+
private static final Set<Class<?>> SIMPLE_TYPES;
40+
41+
static {
42+
43+
Set<Class<?>> simpleTypes = new HashSet<>(GaussDBDialect.INSTANCE.simpleTypes());
44+
List<String> simpleTypeNames = Arrays.asList( //
45+
"com.huawei.gaussdb.jdbc.util.PGobject", //
46+
"com.huawei.gaussdb.jdbc.geometric.PGpoint", //
47+
"com.huawei.gaussdb.jdbc.geometric.PGbox", //
48+
"com.huawei.gaussdb.jdbc.geometric.PGcircle", //
49+
"com.huawei.gaussdb.jdbc.geometric.PGline", //
50+
"com.huawei.gaussdb.jdbc.geometric.PGpath", //
51+
"com.huawei.gaussdb.jdbc.geometric.PGpolygon", //
52+
"com.huawei.gaussdb.jdbc.geometric.PGlseg" //
53+
);
54+
simpleTypeNames.forEach(name -> ifClassPresent(name, simpleTypes::add));
55+
SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
56+
}
57+
58+
@Override
59+
public Set<Class<?>> simpleTypes() {
60+
return SIMPLE_TYPES;
61+
}
62+
63+
/**
64+
* If the class is present on the class path, invoke the specified consumer {@code action} with the class object,
65+
* otherwise do nothing.
66+
*
67+
* @param action block to be executed if a value is present.
68+
*/
69+
private static void ifClassPresent(String className, Consumer<Class<?>> action) {
70+
if (ClassUtils.isPresent(className, GaussDBDialect.class.getClassLoader())) {
71+
action.accept(ClassUtils.resolveClassName(className, GaussDBDialect.class.getClassLoader()));
72+
}
73+
}
74+
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.core.io.support.SpringFactoriesLoader;
3030
import org.springframework.dao.NonTransientDataAccessException;
3131
import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect;
32+
import org.springframework.data.jdbc.core.dialect.JdbcGaussDBDialect;
3233
import org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect;
3334
import org.springframework.data.jdbc.core.dialect.JdbcPostgresDialect;
3435
import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect;
@@ -139,7 +140,9 @@ private static Dialect getDialect(Connection connection) throws SQLException {
139140
if (name.contains("oracle")) {
140141
return OracleDialect.INSTANCE;
141142
}
142-
143+
if (name.contains("gaussdb")) {
144+
return JdbcGaussDBDialect.INSTANCE;
145+
}
143146
LOG.info(String.format("Couldn't determine Dialect for \"%s\"", name));
144147
return null;
145148
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Copyright 2021-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.jdbc.core.dialect;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import java.sql.SQLException;
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.Objects;
25+
import java.util.Optional;
26+
27+
import org.junit.jupiter.api.Test;
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.ComponentScan;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.context.annotation.FilterType;
33+
import org.springframework.context.annotation.Import;
34+
import org.springframework.core.convert.converter.Converter;
35+
import org.springframework.data.annotation.Id;
36+
import org.springframework.data.convert.CustomConversions;
37+
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
38+
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
39+
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
40+
import org.springframework.data.jdbc.testing.DatabaseType;
41+
import org.springframework.data.jdbc.testing.EnabledOnDatabase;
42+
import org.springframework.data.jdbc.testing.IntegrationTest;
43+
import org.springframework.data.jdbc.testing.TestConfiguration;
44+
import org.springframework.data.mapping.model.SimpleTypeHolder;
45+
import org.springframework.data.relational.core.dialect.Dialect;
46+
import org.springframework.data.relational.core.mapping.Table;
47+
import org.springframework.data.repository.CrudRepository;
48+
49+
import com.huawei.gaussdb.jdbc.util.PGobject;
50+
51+
/**
52+
* Integration tests for GaussDB Dialect. Start this test with {@code -Dspring.profiles.active=gaussdb}.
53+
*
54+
* Notes: this file is token from PostgresDialectIntegrationTests and add specific changes for GaussDB
55+
*
56+
* @author liubao
57+
*/
58+
@IntegrationTest
59+
@EnabledOnDatabase(DatabaseType.GAUSSDB)
60+
public class GaussDBDialectIntegrationTests {
61+
62+
@Autowired CustomerRepository customerRepository;
63+
64+
@Test
65+
void shouldSaveAndLoadJson() throws SQLException {
66+
67+
PGobject sessionData = new PGobject();
68+
sessionData.setType("jsonb");
69+
sessionData.setValue("{\"hello\": \"json\"}");
70+
71+
Customer saved = customerRepository
72+
.save(new Customer(null, "Adam Smith", new JsonHolder("{\"hello\": \"world\"}"), sessionData));
73+
74+
Optional<Customer> loaded = customerRepository.findById(saved.getId());
75+
76+
assertThat(loaded).hasValueSatisfying(actual -> {
77+
78+
assertThat(actual.getPersonData().getContent()).isEqualTo("{\"hello\": \"world\"}");
79+
assertThat(actual.getSessionData().getValue()).isEqualTo("{\"hello\": \"json\"}");
80+
});
81+
}
82+
83+
@Configuration
84+
@Import(TestConfiguration.class)
85+
@EnableJdbcRepositories(considerNestedRepositories = true,
86+
includeFilters = @ComponentScan.Filter(value = CustomerRepository.class, type = FilterType.ASSIGNABLE_TYPE))
87+
static class Config {
88+
89+
@Bean
90+
CustomConversions jdbcCustomConversions(Dialect dialect) {
91+
SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
92+
93+
return new JdbcCustomConversions(
94+
CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), userConverters());
95+
}
96+
97+
private List<Object> storeConverters(Dialect dialect) {
98+
99+
List<Object> converters = new ArrayList<>();
100+
converters.addAll(dialect.getConverters());
101+
converters.addAll(JdbcCustomConversions.storeConverters());
102+
return converters;
103+
}
104+
105+
private List<Object> userConverters() {
106+
return Arrays.asList(JsonHolderToPGobjectConverter.INSTANCE, PGobjectToJsonHolderConverter.INSTANCE);
107+
}
108+
}
109+
110+
enum JsonHolderToPGobjectConverter implements Converter<JsonHolder, PGobject> {
111+
112+
INSTANCE;
113+
114+
@Override
115+
public PGobject convert(JsonHolder source) {
116+
PGobject result = new PGobject();
117+
result.setType("jsonb");
118+
try {
119+
result.setValue(source.getContent());
120+
} catch (SQLException e) {
121+
throw new RuntimeException(e);
122+
}
123+
return result;
124+
}
125+
}
126+
127+
enum PGobjectToJsonHolderConverter implements Converter<PGobject, JsonHolder> {
128+
129+
INSTANCE;
130+
131+
@Override
132+
public JsonHolder convert(PGobject source) {
133+
return new JsonHolder(source.getValue());
134+
}
135+
}
136+
137+
@Table("customers")
138+
public static final class Customer {
139+
140+
@Id private final Long id;
141+
private final String name;
142+
private final JsonHolder personData;
143+
private final PGobject sessionData;
144+
145+
public Customer(Long id, String name, JsonHolder personData, PGobject sessionData) {
146+
this.id = id;
147+
this.name = name;
148+
this.personData = personData;
149+
this.sessionData = sessionData;
150+
}
151+
152+
public Long getId() {
153+
return this.id;
154+
}
155+
156+
public String getName() {
157+
return this.name;
158+
}
159+
160+
public JsonHolder getPersonData() {
161+
return this.personData;
162+
}
163+
164+
public PGobject getSessionData() {
165+
return this.sessionData;
166+
}
167+
168+
public boolean equals(final Object o) {
169+
if (o == this)
170+
return true;
171+
if (!(o instanceof final Customer other))
172+
return false;
173+
final Object this$id = this.getId();
174+
final Object other$id = other.getId();
175+
if (!Objects.equals(this$id, other$id))
176+
return false;
177+
final Object this$name = this.getName();
178+
final Object other$name = other.getName();
179+
if (!Objects.equals(this$name, other$name))
180+
return false;
181+
final Object this$personData = this.getPersonData();
182+
final Object other$personData = other.getPersonData();
183+
if (!Objects.equals(this$personData, other$personData))
184+
return false;
185+
final Object this$sessionData = this.getSessionData();
186+
final Object other$sessionData = other.getSessionData();
187+
return Objects.equals(this$sessionData, other$sessionData);
188+
}
189+
190+
public int hashCode() {
191+
final int PRIME = 59;
192+
int result = 1;
193+
final Object $id = this.getId();
194+
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
195+
final Object $name = this.getName();
196+
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
197+
final Object $personData = this.getPersonData();
198+
result = result * PRIME + ($personData == null ? 43 : $personData.hashCode());
199+
final Object $sessionData = this.getSessionData();
200+
result = result * PRIME + ($sessionData == null ? 43 : $sessionData.hashCode());
201+
return result;
202+
}
203+
204+
public String toString() {
205+
return "PostgresDialectIntegrationTests.Customer(id=" + this.getId() + ", name=" + this.getName()
206+
+ ", personData=" + this.getPersonData() + ", sessionData=" + this.getSessionData() + ")";
207+
}
208+
}
209+
210+
public static class JsonHolder {
211+
String content;
212+
213+
public JsonHolder(String content) {
214+
this.content = content;
215+
}
216+
217+
public JsonHolder() {}
218+
219+
public String getContent() {
220+
return this.content;
221+
}
222+
223+
public void setContent(String content) {
224+
this.content = content;
225+
}
226+
}
227+
228+
interface CustomerRepository extends CrudRepository<Customer, Long> {}
229+
230+
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseType.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*/
2626
public enum DatabaseType {
2727

28-
DB2, HSQL, H2, MARIADB, MYSQL, ORACLE, POSTGRES, SQL_SERVER("mssql");
28+
DB2, HSQL, H2, MARIADB, MYSQL, ORACLE, POSTGRES, SQL_SERVER("mssql"), GAUSSDB;
2929

3030
private final String profile;
3131

0 commit comments

Comments
 (0)