Skip to content

Commit f997a61

Browse files
committed
Polishing.
Remove Jackson in favor of JsonHolder and PGobject usage within the domain model. Convert spaces to tabs. Reorder methods and classes. See #920 Original pull request: #1008.
1 parent d1e8e72 commit f997a61

File tree

1 file changed

+118
-202
lines changed

1 file changed

+118
-202
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
package org.springframework.data.jdbc.core.dialect;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.ObjectMapper;
3+
import static org.assertj.core.api.Assertions.*;
4+
55
import lombok.AllArgsConstructor;
66
import lombok.Data;
77
import lombok.NoArgsConstructor;
8-
import org.junit.jupiter.api.AfterAll;
9-
import org.junit.jupiter.api.BeforeAll;
8+
import lombok.Value;
9+
10+
import java.sql.SQLException;
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
13+
import java.util.List;
14+
import java.util.Optional;
15+
1016
import org.junit.jupiter.api.Test;
1117
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
1218
import org.junit.jupiter.api.extension.ExtendWith;
1319
import org.postgresql.util.PGobject;
20+
1421
import org.springframework.beans.factory.annotation.Autowired;
15-
import org.springframework.context.annotation.*;
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.context.annotation.ComponentScan;
24+
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.context.annotation.FilterType;
26+
import org.springframework.context.annotation.Import;
27+
import org.springframework.context.annotation.Profile;
1628
import org.springframework.core.convert.converter.Converter;
1729
import org.springframework.data.annotation.Id;
1830
import org.springframework.data.convert.CustomConversions;
19-
import org.springframework.data.convert.ReadingConverter;
20-
import org.springframework.data.convert.WritingConverter;
2131
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
2232
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
2333
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
@@ -30,210 +40,116 @@
3040
import org.springframework.test.context.junit.jupiter.SpringExtension;
3141
import org.springframework.transaction.annotation.Transactional;
3242

33-
import java.io.ByteArrayOutputStream;
34-
import java.io.PrintStream;
35-
import java.sql.SQLException;
36-
import java.util.ArrayList;
37-
import java.util.List;
38-
import java.util.Optional;
39-
40-
import static org.assertj.core.api.Assertions.assertThat;
41-
4243
/**
43-
* Tests for PostgreSQL Dialect.
44-
* Start this test with -Dspring.profiles.active=postgres
44+
* Integration tests for PostgreSQL Dialect. Start this test with {@code -Dspring.profiles.active=postgres}.
4545
*
4646
* @author Nikita Konev
47+
* @author Mark Paluch
4748
*/
4849
@EnabledIfSystemProperty(named = "spring.profiles.active", matches = "postgres")
4950
@ContextConfiguration
5051
@Transactional
5152
@ExtendWith(SpringExtension.class)
5253
public class PostgresDialectIntegrationTests {
5354

54-
private static final ByteArrayOutputStream capturedOutContent = new ByteArrayOutputStream();
55-
private static PrintStream previousOutput;
56-
57-
@Profile("postgres")
58-
@Configuration
59-
@Import(TestConfiguration.class)
60-
@EnableJdbcRepositories(considerNestedRepositories = true,
61-
includeFilters = @ComponentScan.Filter(value = CustomerRepository.class, type = FilterType.ASSIGNABLE_TYPE))
62-
static class Config {
63-
64-
private final ObjectMapper objectMapper = new ObjectMapper();
65-
66-
@Bean
67-
Class<?> testClass() {
68-
return PostgresDialectIntegrationTests.class;
69-
}
70-
71-
@WritingConverter
72-
static class PersonDataWritingConverter extends AbstractPostgresJsonWritingConverter<PersonData> {
73-
74-
public PersonDataWritingConverter(ObjectMapper objectMapper) {
75-
super(objectMapper, true);
76-
}
77-
}
78-
79-
@ReadingConverter
80-
static class PersonDataReadingConverter extends AbstractPostgresJsonReadingConverter<PersonData> {
81-
public PersonDataReadingConverter(ObjectMapper objectMapper) {
82-
super(objectMapper, PersonData.class);
83-
}
84-
}
85-
86-
@WritingConverter
87-
static class SessionDataWritingConverter extends AbstractPostgresJsonWritingConverter<SessionData> {
88-
public SessionDataWritingConverter(ObjectMapper objectMapper) {
89-
super(objectMapper, true);
90-
}
91-
}
92-
93-
@ReadingConverter
94-
static class SessionDataReadingConverter extends AbstractPostgresJsonReadingConverter<SessionData> {
95-
public SessionDataReadingConverter(ObjectMapper objectMapper) {
96-
super(objectMapper, SessionData.class);
97-
}
98-
}
99-
100-
private List<Object> storeConverters(Dialect dialect) {
101-
102-
List<Object> converters = new ArrayList<>();
103-
converters.addAll(dialect.getConverters());
104-
converters.addAll(JdbcCustomConversions.storeConverters());
105-
return converters;
106-
}
107-
108-
protected List<?> userConverters() {
109-
final List<Converter> list = new ArrayList<>();
110-
list.add(new PersonDataWritingConverter(objectMapper));
111-
list.add(new PersonDataReadingConverter(objectMapper));
112-
list.add(new SessionDataWritingConverter(objectMapper));
113-
list.add(new SessionDataReadingConverter(objectMapper));
114-
return list;
115-
}
116-
117-
@Primary
118-
@Bean
119-
CustomConversions jdbcCustomConversions(Dialect dialect) {
120-
SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
121-
122-
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)),
123-
userConverters());
124-
}
125-
126-
}
127-
128-
@BeforeAll
129-
public static void ba() {
130-
previousOutput = System.out;
131-
System.setOut(new PrintStream(capturedOutContent));
132-
}
133-
134-
@AfterAll
135-
public static void aa() {
136-
System.setOut(previousOutput);
137-
previousOutput = null;
138-
}
139-
140-
/**
141-
* An abstract class for building your own converter for PostgerSQL's JSON[b].
142-
*/
143-
static class AbstractPostgresJsonReadingConverter<T> implements Converter<PGobject, T> {
144-
private final ObjectMapper objectMapper;
145-
private final Class<T> valueType;
146-
147-
public AbstractPostgresJsonReadingConverter(ObjectMapper objectMapper, Class<T> valueType) {
148-
this.objectMapper = objectMapper;
149-
this.valueType = valueType;
150-
}
151-
152-
@Override
153-
public T convert(PGobject pgObject) {
154-
try {
155-
final String source = pgObject.getValue();
156-
return objectMapper.readValue(source, valueType);
157-
} catch (JsonProcessingException e) {
158-
throw new RuntimeException("Unable to deserialize to json " + pgObject, e);
159-
}
160-
}
161-
}
162-
163-
/**
164-
* An abstract class for building your own converter for PostgerSQL's JSON[b].
165-
*/
166-
static class AbstractPostgresJsonWritingConverter<T> implements Converter<T, PGobject> {
167-
private final ObjectMapper objectMapper;
168-
private final boolean jsonb;
169-
170-
public AbstractPostgresJsonWritingConverter(ObjectMapper objectMapper, boolean jsonb) {
171-
this.objectMapper = objectMapper;
172-
this.jsonb = jsonb;
173-
}
174-
175-
@Override
176-
public PGobject convert(T source) {
177-
try {
178-
final PGobject pGobject = new PGobject();
179-
pGobject.setType(jsonb ? "jsonb" : "json");
180-
pGobject.setValue(objectMapper.writeValueAsString(source));
181-
return pGobject;
182-
} catch (JsonProcessingException | SQLException e) {
183-
throw new RuntimeException("Unable to serialize to json " + source, e);
184-
}
185-
}
186-
}
187-
188-
@Data
189-
@AllArgsConstructor
190-
@Table("customers")
191-
public static class Customer {
192-
193-
@Id
194-
private Long id;
195-
private String name;
196-
private PersonData personData;
197-
private SessionData sessionData;
198-
}
199-
200-
@Data
201-
@NoArgsConstructor
202-
@AllArgsConstructor
203-
public static class PersonData {
204-
private int age;
205-
private String petName;
206-
}
207-
208-
@Data
209-
@NoArgsConstructor
210-
@AllArgsConstructor
211-
public static class SessionData {
212-
private String token;
213-
private Long ttl;
214-
}
215-
216-
interface CustomerRepository extends CrudRepository<Customer, Long> {
217-
218-
}
219-
220-
@Autowired
221-
CustomerRepository customerRepository;
222-
223-
@Test
224-
void testWarningShouldNotBeShown() {
225-
final Customer saved = customerRepository.save(new Customer(null, "Adam Smith", new PersonData(30, "Casper"), null));
226-
assertThat(saved.getId()).isNotZero();
227-
final Optional<Customer> byId = customerRepository.findById(saved.getId());
228-
assertThat(byId.isPresent()).isTrue();
229-
final Customer foundCustomer = byId.get();
230-
assertThat(foundCustomer.getName()).isEqualTo("Adam Smith");
231-
assertThat(foundCustomer.getPersonData()).isNotNull();
232-
assertThat(foundCustomer.getPersonData().getAge()).isEqualTo(30);
233-
assertThat(foundCustomer.getPersonData().getPetName()).isEqualTo("Casper");
234-
assertThat(foundCustomer.getSessionData()).isNull();
235-
236-
assertThat(capturedOutContent.toString()).doesNotContain("although it doesn't convert from a store-supported type");
237-
}
55+
@Autowired CustomerRepository customerRepository;
56+
57+
@Test // GH-920
58+
void shouldSaveAndLoadJson() throws SQLException {
59+
60+
PGobject sessionData = new PGobject();
61+
sessionData.setType("jsonb");
62+
sessionData.setValue("{\"hello\": \"json\"}");
63+
64+
Customer saved = customerRepository
65+
.save(new Customer(null, "Adam Smith", new JsonHolder("{\"hello\": \"world\"}"), sessionData));
66+
67+
Optional<Customer> loaded = customerRepository.findById(saved.getId());
68+
69+
assertThat(loaded).hasValueSatisfying(actual -> {
70+
71+
assertThat(actual.getPersonData().getContent()).isEqualTo("{\"hello\": \"world\"}");
72+
assertThat(actual.getSessionData().getValue()).isEqualTo("{\"hello\": \"json\"}");
73+
});
74+
}
75+
76+
@Profile("postgres")
77+
@Configuration
78+
@Import(TestConfiguration.class)
79+
@EnableJdbcRepositories(considerNestedRepositories = true,
80+
includeFilters = @ComponentScan.Filter(value = CustomerRepository.class, type = FilterType.ASSIGNABLE_TYPE))
81+
static class Config {
82+
83+
@Bean
84+
Class<?> testClass() {
85+
return PostgresDialectIntegrationTests.class;
86+
}
87+
88+
@Bean
89+
CustomConversions jdbcCustomConversions(Dialect dialect) {
90+
SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
91+
92+
return new JdbcCustomConversions(
93+
CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), userConverters());
94+
}
95+
96+
private List<Object> storeConverters(Dialect dialect) {
97+
98+
List<Object> converters = new ArrayList<>();
99+
converters.addAll(dialect.getConverters());
100+
converters.addAll(JdbcCustomConversions.storeConverters());
101+
return converters;
102+
}
103+
104+
private List<Object> userConverters() {
105+
return Arrays.asList(JsonHolderToPGobjectConverter.INSTANCE, PGobjectToJsonHolderConverter.INSTANCE);
106+
}
107+
}
108+
109+
enum JsonHolderToPGobjectConverter implements Converter<JsonHolder, PGobject> {
110+
111+
INSTANCE;
112+
113+
@Override
114+
public PGobject convert(JsonHolder source) {
115+
PGobject result = new PGobject();
116+
result.setType("json");
117+
try {
118+
result.setValue(source.getContent());
119+
} catch (SQLException e) {
120+
throw new RuntimeException(e);
121+
}
122+
return result;
123+
}
124+
}
125+
126+
enum PGobjectToJsonHolderConverter implements Converter<PGobject, JsonHolder> {
127+
128+
INSTANCE;
129+
130+
@Override
131+
public JsonHolder convert(PGobject source) {
132+
return new JsonHolder(source.getValue());
133+
}
134+
}
135+
136+
@Value
137+
@Table("customers")
138+
public static class Customer {
139+
140+
@Id Long id;
141+
String name;
142+
JsonHolder personData;
143+
PGobject sessionData;
144+
}
145+
146+
@Data
147+
@NoArgsConstructor
148+
@AllArgsConstructor
149+
public static class JsonHolder {
150+
String content;
151+
}
152+
153+
interface CustomerRepository extends CrudRepository<Customer, Long> {}
238154

239155
}

0 commit comments

Comments
 (0)