Skip to content

Commit 04f0a48

Browse files
authored
Added support for deserializing object property values based on value class (#510)
Add the getValue method in Type to deserialize JSON data from a ResultSet into an object of the specified class (clazz). ```java package com.example; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.querydsl.core.types.Path; import com.querydsl.sql.Configuration; import com.querydsl.sql.MySQLTemplates; import com.querydsl.sql.RelationalPathBase; import com.querydsl.sql.SQLTemplates; import com.querydsl.sql.mysql.MySQLQueryFactory; import com.querydsl.sql.types.Type; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.jdbc.datasource.DataSourceUtils; import javax.sql.DataSource; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Arrays; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; /** * ExampleTest class with QueryDSL configuration and JSON handling for DataSource properties. */ @SpringBootConfiguration @import(ExampleTest.QueryDSLConfiguration.class) @SpringBootTest @EnableAutoConfiguration class ExampleTest { /** * Interface representing a JSON entity. */ interface JsonEntity { } /** * JSON type handler using ObjectMapper to convert JSON strings to specified type objects. */ static class JSONType implements Type<JsonEntity> { private final ObjectMapper objectMapper; public JSONType() { objectMapper = new ObjectMapper(); objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } @OverRide public int[] getSQLTypes() { return new int[]{Types.BLOB, Types.VARBINARY, Types.VARCHAR, Types.BINARY}; } @OverRide public Class<JsonEntity> getReturnedClass() { return JsonEntity.class; } @OverRide public String getLiteral(JsonEntity value) { if (value == null) { return null; } try { return objectMapper.writeValueAsString(value); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } @OverRide public JsonEntity getValue(ResultSet rs, int startIndex) throws SQLException { try { return objectMapper.readValue(rs.getBytes(startIndex), JsonEntity.class); } catch (IOException e) { throw new RuntimeException(e); } } @OverRide public JsonEntity getValue(ResultSet rs, int startIndex, Class<JsonEntity> clazz) throws SQLException { try { return objectMapper.readValue(rs.getBytes(startIndex), clazz); } catch (IOException e) { throw new RuntimeException(e); } } @OverRide public void setValue(PreparedStatement st, int startIndex, JsonEntity value) throws SQLException { st.setObject(startIndex, getLiteral(value)); } } /** * DataSourceProperties class representing the properties of a data source. */ public static class DataSourceProperties implements JsonEntity { private String hostname; private int port; private String schema; private String username; private String password; private Map<String, Object> connectionProperties; public String getHostname() { return hostname; } public void setHostname(String hostname) { this.hostname = hostname; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Map<String, Object> getConnectionProperties() { return connectionProperties; } public void setConnectionProperties(Map<String, Object> connectionProperties) { this.connectionProperties = connectionProperties; } } /** * DataSourceEntity class representing a data source entity. * * <pre> * CREATE TABLE `data_source` ( * `id` int NOT NULL AUTO_INCREMENT, * `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, * `properties` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, * PRIMARY KEY (`id`) USING BTREE * ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; * * INSERT INTO `data_source` VALUES (1, 'mysql-localhost', '{\"hostname\": \"localhost\", \"port\": 3306, \"schema\": \"test\", \"username\": \"root\", \"password\": \"root\", \"connectionProperties\": {\"useSSL\": true, \"useCursorFetch\": true}}'); * </pre> */ public static class DataSourceEntity { private Integer id; private String name; private DataSourceProperties properties; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public DataSourceProperties getProperties() { return properties; } public void setProperties(DataSourceProperties properties) { this.properties = properties; } } /** * QDataSourceEntity class representing the QueryDSL path for the DataSourceEntity. */ public static class QDataSourceEntity extends RelationalPathBase<DataSourceEntity> { public QDataSourceEntity() { this(DataSourceEntity.class, "ds", null, "data_source"); } public QDataSourceEntity(Class<? extends DataSourceEntity> type, String variable, String schema, String table) { super(type, variable, schema, table); } @OverRide public List<Path<?>> getColumns() { return Arrays.asList( createNumber("id", Integer.class), createString("name"), createSimple("properties", DataSourceProperties.class) ); } } /** * QueryDSL configuration class. */ static class QueryDSLConfiguration { /** * SQLTemplates implementation for MySQL. */ @bean public SQLTemplates sqlTemplates() { return new MySQLTemplates(); } /** * MySQLQueryFactory - main entry point to build and execute SQL queries. */ @bean public MySQLQueryFactory queryFactory(DataSource dataSource) { Configuration configuration = new Configuration(new MySQLTemplates()); configuration.register(new JSONType()); return new MySQLQueryFactory(configuration, () -> DataSourceUtils.getConnection(dataSource)); } } @Autowired private MySQLQueryFactory queryFactory; @test public void test() { QDataSourceEntity qDataSource = new QDataSourceEntity(); List<DataSourceEntity> list = queryFactory.selectFrom(qDataSource).fetch(); DataSourceEntity dataSourceEntity = list.get(0); assertEquals("localhost", dataSourceEntity.getProperties().getHostname()); } } ```
2 parents ce16c49 + b4fb160 commit 04f0a48

File tree

30 files changed

+516
-596
lines changed

30 files changed

+516
-596
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ jobs:
140140
- run:
141141
name: 'Test'
142142
command: |
143-
./mvnw -ntp -B install -Pquickbuild -pl :querydsl-sql-spring,:querydsl-jpa-spring,:querydsl-kotlin-codegen,:querydsl-mongodb,:querydsl-r2dbc -am -T2
143+
./mvnw -ntp -B install -Pquickbuild -pl :querydsl-sql-spring,:querydsl-jpa-spring,:querydsl-kotlin-codegen,:querydsl-mongodb,:querydsl-r2dbc,:querydsl-sql-json -am -T2
144144
./mvnw -ntp -B verify -Pexamples -rf :querydsl-examples
145145
- save-test-results
146146
buildQuarkusExample:

querydsl-examples/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<packaging>pom</packaging>
1313
<name>Querydsl - Examples</name>
1414

15+
<properties>
16+
<main.java.version>17</main.java.version>
17+
</properties>
18+
1519
<modules>
1620
<module>querydsl-example-sql-guice</module>
1721
<module>querydsl-example-sql-spring</module>

querydsl-examples/querydsl-example-jpa-quarkus/src/main/java/com/querydsl/example/FruitResource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ public Response toResponse(Exception exception) {
101101
LOGGER.error("Failed to handle request", exception);
102102

103103
int code = 500;
104-
if (exception instanceof WebApplicationException) {
105-
code = ((WebApplicationException) exception).getResponse().getStatus();
104+
if (exception instanceof WebApplicationException applicationException) {
105+
code = applicationException.getResponse().getStatus();
106106
}
107107

108108
ObjectNode exceptionJson = objectMapper.createObjectNode();

querydsl-examples/querydsl-example-jpa-spring/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010
<artifactId>querydsl-example-jpa-spring</artifactId>
1111
<name>Querydsl example - JPA Spring</name>
1212

13-
<properties>
14-
<!-- to match spring JDK version -->
15-
<main.java.version>17</main.java.version>
16-
</properties>
17-
1813
<dependencyManagement>
1914
<dependencies>
2015
<dependency>

querydsl-examples/querydsl-example-jpa-spring/src/main/java/com/querydsl/example/Customer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public Customer(String firstName, String lastName) {
2424

2525
@Override
2626
public String toString() {
27-
return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
27+
return "Customer[id=%d, firstName='%s', lastName='%s']".formatted(id, firstName, lastName);
2828
}
2929

3030
public Long getId() {

querydsl-examples/querydsl-example-r2dbc-sql-codegen/pom.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@
1111
<artifactId>querydsl-example-r2dbc-sql-codegen</artifactId>
1212
<name>Querydsl example - R2DBC SQL Codegen</name>
1313

14-
<properties>
15-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16-
17-
<!-- to match spring JDK version -->
18-
<main.java.version>17</main.java.version>
19-
</properties>
20-
2114
<dependencyManagement>
2215
<dependencies>
2316
<dependency>
@@ -31,9 +24,7 @@
3124
</dependencyManagement>
3225

3326
<dependencies>
34-
3527
<!-- spring -->
36-
3728
<dependency>
3829
<groupId>org.springframework</groupId>
3930
<artifactId>spring-context</artifactId>

querydsl-examples/querydsl-example-sql-spring/pom.xml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@
1111
<artifactId>querydsl-example-sql-spring</artifactId>
1212
<name>Querydsl example - SQL Spring</name>
1313

14-
<properties>
15-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16-
17-
<!-- to match spring JDK version -->
18-
<main.java.version>17</main.java.version>
19-
</properties>
20-
2114
<dependencies>
22-
2315
<!-- spring -->
24-
2516
<dependency>
2617
<groupId>org.springframework</groupId>
2718
<artifactId>spring-context</artifactId>
@@ -51,6 +42,11 @@
5142
<artifactId>querydsl-sql</artifactId>
5243
<version>${project.version}</version>
5344
</dependency>
45+
<dependency>
46+
<groupId>io.github.openfeign.querydsl</groupId>
47+
<artifactId>querydsl-sql-json</artifactId>
48+
<version>${project.version}</version>
49+
</dependency>
5450
<dependency>
5551
<groupId>io.github.openfeign.querydsl</groupId>
5652
<artifactId>querydsl-sql-spring</artifactId>
@@ -90,6 +86,13 @@
9086
<customType>com.querydsl.sql.types.LocalDateTimeType</customType>
9187
<customType>com.querydsl.sql.types.LocalDateType</customType>
9288
</customTypes>
89+
<typeMappings>
90+
<typeMapping>
91+
<table>CUSTOMER_ADDRESS</table>
92+
<column>ADDRESS</column>
93+
<type>com.querydsl.example.dto.Address</type>
94+
</typeMapping>
95+
</typeMappings>
9396
<packageName>com.querydsl.example.sql</packageName>
9497
<targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
9598
</configuration>

querydsl-examples/querydsl-example-sql-spring/src/main/java/com/querydsl/example/config/JdbcConfiguration.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
import com.querydsl.sql.H2Templates;
44
import com.querydsl.sql.SQLQueryFactory;
5-
import com.querydsl.sql.SQLTemplates;
65
import com.querydsl.sql.spring.SpringConnectionProvider;
76
import com.querydsl.sql.spring.SpringExceptionTranslator;
87
import com.querydsl.sql.types.LocalDateTimeType;
98
import com.querydsl.sql.types.LocalDateType;
9+
import io.github.openfeign.querydsl.sql.json.types.JSONType;
1010
import javax.sql.DataSource;
1111
import org.springframework.beans.factory.annotation.Autowired;
1212
import org.springframework.context.annotation.Bean;
@@ -25,7 +25,7 @@ public class JdbcConfiguration {
2525

2626
@Bean
2727
public DataSource dataSource() {
28-
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
28+
var dataSource = new SimpleDriverDataSource();
2929
Class driver;
3030
try {
3131
driver = Class.forName(env.getRequiredProperty("jdbc.driver"));
@@ -46,17 +46,18 @@ public PlatformTransactionManager transactionManager() {
4646

4747
@Bean
4848
public com.querydsl.sql.Configuration querydslConfiguration() {
49-
SQLTemplates templates = H2Templates.builder().build();
50-
com.querydsl.sql.Configuration configuration = new com.querydsl.sql.Configuration(templates);
49+
var templates = H2Templates.builder().build();
50+
var configuration = new com.querydsl.sql.Configuration(templates);
5151
configuration.setExceptionTranslator(new SpringExceptionTranslator());
5252
configuration.register(new LocalDateTimeType());
5353
configuration.register(new LocalDateType());
54+
configuration.register(new JSONType());
5455
return configuration;
5556
}
5657

5758
@Bean
5859
public SQLQueryFactory queryFactory() {
59-
SpringConnectionProvider provider = new SpringConnectionProvider(dataSource());
60+
var provider = new SpringConnectionProvider(dataSource());
6061
return new SQLQueryFactory(querydslConfiguration(), provider);
6162
}
6263
}

querydsl-examples/querydsl-example-sql-spring/src/main/java/com/querydsl/example/dao/CustomerDaoImpl.java

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
package com.querydsl.example.dao;
22

33
import static com.querydsl.core.types.Projections.bean;
4-
import static com.querydsl.example.sql.QAddress.address;
54
import static com.querydsl.example.sql.QCustomer.customer;
65
import static com.querydsl.example.sql.QCustomerAddress.customerAddress;
76
import static com.querydsl.example.sql.QPerson.person;
87

98
import com.querydsl.core.group.GroupBy;
109
import com.querydsl.core.types.Predicate;
1110
import com.querydsl.core.types.QBean;
12-
import com.querydsl.example.dto.Address;
1311
import com.querydsl.example.dto.Customer;
1412
import com.querydsl.example.dto.CustomerAddress;
1513
import com.querydsl.example.dto.Person;
1614
import com.querydsl.sql.SQLQueryFactory;
17-
import com.querydsl.sql.dml.SQLInsertClause;
1815
import java.util.List;
1916
import org.springframework.beans.factory.annotation.Autowired;
2017
import org.springframework.transaction.annotation.Transactional;
@@ -30,7 +27,7 @@ public class CustomerDaoImpl implements CustomerDao {
3027
customerAddress.addressTypeCode,
3128
customerAddress.fromDate,
3229
customerAddress.toDate,
33-
bean(Address.class, address.all()).as("address"));
30+
customerAddress.address);
3431

3532
final QBean<Customer> customerBean =
3633
bean(
@@ -42,7 +39,7 @@ public class CustomerDaoImpl implements CustomerDao {
4239

4340
@Override
4441
public Customer findById(long id) {
45-
List<Customer> customers = findAll(customer.id.eq(id));
42+
var customers = findAll(customer.id.eq(id));
4643
return customers.isEmpty() ? null : customers.get(0);
4744
}
4845

@@ -52,14 +49,13 @@ public List<Customer> findAll(Predicate... where) {
5249
.from(customer)
5350
.leftJoin(customer.contactPersonFk, person)
5451
.leftJoin(customer._customer3Fk, customerAddress)
55-
.leftJoin(customerAddress.addressFk, address)
5652
.where(where)
5753
.transform(GroupBy.groupBy(customer.id).list(customerBean));
5854
}
5955

6056
@Override
6157
public Customer save(Customer c) {
62-
Long id = c.getId();
58+
var id = c.getId();
6359

6460
if (id == null) {
6561
id =
@@ -81,16 +77,11 @@ public Customer save(Customer c) {
8177
queryFactory.delete(customerAddress).where(customerAddress.customerId.eq(id)).execute();
8278
}
8379

84-
SQLInsertClause insert = queryFactory.insert(customerAddress);
80+
var insert = queryFactory.insert(customerAddress);
8581
for (CustomerAddress ca : c.getAddresses()) {
86-
if (ca.getAddress().getId() == null) {
87-
ca.getAddress()
88-
.setId(
89-
queryFactory.insert(address).populate(ca.getAddress()).executeWithKey(address.id));
90-
}
9182
insert
9283
.set(customerAddress.customerId, id)
93-
.set(customerAddress.addressId, ca.getAddress().getId())
84+
.set(customerAddress.address, ca.getAddress())
9485
.set(customerAddress.addressTypeCode, ca.getAddressTypeCode())
9586
.set(customerAddress.fromDate, ca.getFromDate())
9687
.set(customerAddress.toDate, ca.getToDate())

querydsl-examples/querydsl-example-sql-spring/src/main/java/com/querydsl/example/dto/Address.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package com.querydsl.example.dto;
22

3+
import io.github.openfeign.querydsl.sql.json.*;
34
import lombok.Data;
45

56
@Data
6-
public class Address {
7+
public class Address implements JsonEntity {
78

89
private Long id;
910

0 commit comments

Comments
 (0)