#318 - Add support for Postgres JSON type.
We now pass-thru Postgres's JSON type that can be used in entities and simple queries.
This commit is contained in:
@@ -22,8 +22,20 @@ import org.springframework.util.ClassUtils;
|
||||
public class PostgresDialect extends org.springframework.data.relational.core.dialect.PostgresDialect
|
||||
implements R2dbcDialect {
|
||||
|
||||
private static final Set<Class<?>> SIMPLE_TYPES = new HashSet<>(
|
||||
Arrays.asList(UUID.class, URL.class, URI.class, InetAddress.class));
|
||||
private static final Set<Class<?>> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
|
||||
Set<Class<?>> simpleTypes = new HashSet<>(Arrays.asList(UUID.class, URL.class, URI.class, InetAddress.class));
|
||||
|
||||
if (ClassUtils.isPresent("io.r2dbc.postgresql.codec.Json", PostgresDialect.class.getClassLoader())) {
|
||||
|
||||
simpleTypes
|
||||
.add(ClassUtils.resolveClassName("io.r2dbc.postgresql.codec.Json", PostgresDialect.class.getClassLoader()));
|
||||
}
|
||||
|
||||
SIMPLE_TYPES = simpleTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.r2dbc.convert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.postgresql.codec.Json;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
||||
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
|
||||
/**
|
||||
* Postgres-specific unit tests for {@link MappingR2dbcConverter}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresMappingR2dbcConverterUnitTests {
|
||||
|
||||
RelationalMappingContext mappingContext = new R2dbcMappingContext();
|
||||
MappingR2dbcConverter converter = new MappingR2dbcConverter(mappingContext);
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
List<Object> converters = new ArrayList<>(PostgresDialect.INSTANCE.getConverters());
|
||||
converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS);
|
||||
CustomConversions.StoreConversions storeConversions = CustomConversions.StoreConversions
|
||||
.of(PostgresDialect.INSTANCE.getSimpleTypeHolder(), converters);
|
||||
|
||||
R2dbcCustomConversions customConversions = new R2dbcCustomConversions(storeConversions, Collections.emptyList());
|
||||
|
||||
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
|
||||
|
||||
converter = new MappingR2dbcConverter(mappingContext, customConversions);
|
||||
}
|
||||
|
||||
@Test // gh-318
|
||||
public void shouldPassThruJson() {
|
||||
|
||||
JsonPerson person = new JsonPerson(null, Json.of("{\"hello\":\"world\"}"));
|
||||
|
||||
OutboundRow row = new OutboundRow();
|
||||
converter.write(person, row);
|
||||
|
||||
assertThat(row).containsEntry(SqlIdentifier.unquoted("json_value"), SettableValue.from(person.jsonValue));
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
static class JsonPerson {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
Json jsonValue;
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,34 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.repository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.postgresql.codec.Json;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import lombok.AllArgsConstructor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@@ -47,9 +57,12 @@ public class PostgresR2dbcRepositoryIntegrationTests extends AbstractR2dbcReposi
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
|
||||
|
||||
@Autowired JsonPersonRepository jsonPersonRepository;
|
||||
|
||||
@Configuration
|
||||
@EnableR2dbcRepositories(considerNestedRepositories = true,
|
||||
includeFilters = @Filter(classes = PostgresLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
includeFilters = @Filter(classes = { PostgresLegoSetRepository.class, JsonPersonRepository.class },
|
||||
type = FilterType.ASSIGNABLE_TYPE))
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -93,4 +106,37 @@ public class PostgresR2dbcRepositoryIntegrationTests extends AbstractR2dbcReposi
|
||||
@Query("SELECT id FROM legoset")
|
||||
Flux<Integer> findAllIds();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSaveAndLoadJson() {
|
||||
|
||||
JdbcTemplate template = new JdbcTemplate(createDataSource());
|
||||
|
||||
template.execute("DROP TABLE IF EXISTS json_person");
|
||||
template.execute("CREATE TABLE json_person (\n" //
|
||||
+ " id SERIAL PRIMARY KEY,\n" //
|
||||
+ " json_value JSONB NOT NULL" //
|
||||
+ ");");
|
||||
|
||||
JsonPerson person = new JsonPerson(null, Json.of("{\"hello\": \"world\"}"));
|
||||
jsonPersonRepository.save(person).as(StepVerifier::create).expectNextCount(1).verifyComplete();
|
||||
|
||||
jsonPersonRepository.findAll().as(StepVerifier::create).consumeNextWith(actual -> {
|
||||
|
||||
assertThat(actual.jsonValue).isNotNull();
|
||||
assertThat(actual.jsonValue.asString()).isEqualTo("{\"hello\": \"world\"}");
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
static class JsonPerson {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
Json jsonValue;
|
||||
}
|
||||
|
||||
interface JsonPersonRepository extends ReactiveCrudRepository<JsonPerson, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user