Skip to content

Commit bda4611

Browse files
committed
added matching strategy option
1 parent 11e258c commit bda4611

File tree

4 files changed

+151
-11
lines changed

4 files changed

+151
-11
lines changed

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import com.clickhouse.client.api.internal.SettingsConverter;
3030
import com.clickhouse.client.api.internal.TableSchemaParser;
3131
import com.clickhouse.client.api.internal.ValidationUtils;
32+
import com.clickhouse.client.api.metadata.ColumnToMethodMatchingStrategy;
33+
import com.clickhouse.client.api.metadata.DefaultColumnToMethodMatchingStrategy;
3234
import com.clickhouse.client.api.metadata.TableSchema;
3335
import com.clickhouse.client.api.metrics.ClientMetrics;
3436
import com.clickhouse.client.api.metrics.OperationMetrics;
@@ -146,8 +148,10 @@ public class Client implements AutoCloseable {
146148
private Map<String, TableSchema> tableSchemaCache = new ConcurrentHashMap<>();
147149
private Map<String, Boolean> tableSchemaHasDefaults = new ConcurrentHashMap<>();
148150

151+
private final ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy;
152+
149153
private Client(Set<String> endpoints, Map<String,String> configuration, boolean useNewImplementation,
150-
ExecutorService sharedOperationExecutor) {
154+
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy) {
151155
this.endpoints = endpoints;
152156
this.configuration = configuration;
153157
this.endpoints.forEach(endpoint -> {
@@ -170,6 +174,7 @@ private Client(Set<String> endpoints, Map<String,String> configuration, boolean
170174
this.oldClient = ClientV1AdaptorHelper.createClient(configuration);
171175
LOG.info("Using old http client implementation");
172176
}
177+
this.columnToMethodMatchingStrategy = columnToMethodMatchingStrategy;
173178
}
174179

175180
/**
@@ -211,6 +216,7 @@ public static class Builder {
211216
private boolean useNewImplementation = true;
212217

213218
private ExecutorService sharedOperationExecutor = null;
219+
private ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy;
214220

215221
public Builder() {
216222
this.endpoints = new HashSet<>();
@@ -846,6 +852,18 @@ public Builder serverSetting(String name, Collection<String> values) {
846852
return this;
847853
}
848854

855+
/**
856+
* Sets column to method matching strategy. It is used while registering POJO serializers and deserializers.
857+
* Default is {@link DefaultColumnToMethodMatchingStrategy}.
858+
*
859+
* @param strategy - matching strategy
860+
* @return same instance of the builder
861+
*/
862+
public Builder columnToMethodMatchingStrategy(ColumnToMethodMatchingStrategy strategy) {
863+
this.columnToMethodMatchingStrategy = strategy;
864+
return this;
865+
}
866+
849867
public Client build() {
850868
setDefaults();
851869

@@ -891,7 +909,7 @@ public Client build() {
891909
throw new IllegalArgumentException("Nor server timezone nor specific timezone is set");
892910
}
893911

894-
return new Client(this.endpoints, this.configuration, this.useNewImplementation, this.sharedOperationExecutor);
912+
return new Client(this.endpoints, this.configuration, this.useNewImplementation, this.sharedOperationExecutor, this.columnToMethodMatchingStrategy);
895913
}
896914

897915
private static final int DEFAULT_NETWORK_BUFFER_SIZE = 300_000;
@@ -963,6 +981,10 @@ private void setDefaults() {
963981
if (!configuration.containsKey("client_allow_binary_reader_to_reuse_buffers")) {
964982
allowBinaryReaderToReuseBuffers(false);
965983
}
984+
985+
if (columnToMethodMatchingStrategy == null) {
986+
columnToMethodMatchingStrategy = DefaultColumnToMethodMatchingStrategy.INSTANCE;
987+
}
966988
}
967989
}
968990

@@ -1018,19 +1040,17 @@ public synchronized void register(Class<?> clazz, TableSchema schema) {
10181040
}
10191041
tableSchemaCache.put(schemaKey, schema);
10201042

1043+
ColumnToMethodMatchingStrategy matchingStrategy = columnToMethodMatchingStrategy;
1044+
10211045
//Create a new POJOSerializer with static .serialize(object, columns) methods
10221046
Map<String, Method> classGetters = new HashMap<>();
10231047
Map<String, Method> classSetters = new HashMap<>();
10241048
for (Method method : clazz.getMethods()) {//Clean up the method names
1025-
String methodName = method.getName();
1026-
if (methodName.startsWith("get") || methodName.startsWith("has")) {
1027-
methodName = methodName.substring(3).toLowerCase();
1028-
classGetters.put(methodName, method);
1029-
} else if (methodName.startsWith("is")) {
1030-
methodName = methodName.substring(2).toLowerCase();
1049+
if (matchingStrategy.isGetter(method.getName())) {
1050+
String methodName = matchingStrategy.normalizeMethodName(method.getName());
10311051
classGetters.put(methodName, method);
1032-
} else if (methodName.startsWith("set")) {
1033-
methodName = methodName.substring(3).toLowerCase();
1052+
} else if (matchingStrategy.isSetter(method.getName())) {
1053+
String methodName = matchingStrategy.normalizeMethodName(method.getName());
10341054
classSetters.put(methodName, method);
10351055
}
10361056
}
@@ -1040,7 +1060,7 @@ public synchronized void register(Class<?> clazz, TableSchema schema) {
10401060
boolean defaultsSupport = schema.hasDefaults();
10411061
tableSchemaHasDefaults.put(schemaKey, defaultsSupport);
10421062
for (ClickHouseColumn column : schema.getColumns()) {
1043-
String propertyName = column.getColumnName().toLowerCase().replace("_", "").replace(".", "");
1063+
String propertyName = columnToMethodMatchingStrategy.normalizeColumnName(column.getColumnName());
10441064
Method getterMethod = classGetters.get(propertyName);
10451065
if (getterMethod != null) {
10461066
schemaSerializers.put(column.getColumnName(), (obj, stream) -> {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.clickhouse.client.api.metadata;
2+
3+
4+
/**
5+
* Strategy to match column names to method names.
6+
*/
7+
public interface ColumnToMethodMatchingStrategy {
8+
9+
/**
10+
* Normalizes method name to match column name.
11+
* @param methodName original method name
12+
* @return normalized method name
13+
*/
14+
String normalizeMethodName(String methodName);
15+
16+
/**
17+
* Checks if the method is a setter.
18+
* @param methodName original (not normalized) method name
19+
* @return true if the method is a setter
20+
*/
21+
boolean isSetter(String methodName);
22+
23+
/**
24+
* Checks if the method is a getter.
25+
* @param methodName original (not normalized) method name
26+
* @return true if the method is a getter
27+
*/
28+
boolean isGetter(String methodName);
29+
30+
/**
31+
* Normalizes column name to match method name.
32+
* @param columnName original column name
33+
* @return normalized column name
34+
*/
35+
String normalizeColumnName(String columnName);
36+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.clickhouse.client.api.metadata;
2+
3+
4+
import java.util.regex.Pattern;
5+
6+
/**
7+
* Default implementation of {@link ColumnToMethodMatchingStrategy} takes the following rules:
8+
* <ul>
9+
* <li>Method name is normalized by removing prefixes like "get", "set", "is", "has".</li>
10+
* <li>Column name is normalized by removing special characters like "-", "_", ".".</li>
11+
* <li>Normalized method name and column name are compared case-insensitively.</li>
12+
* </ul>
13+
*
14+
*
15+
*/
16+
public class DefaultColumnToMethodMatchingStrategy implements ColumnToMethodMatchingStrategy {
17+
18+
public static final DefaultColumnToMethodMatchingStrategy INSTANCE = new DefaultColumnToMethodMatchingStrategy();
19+
20+
private final Pattern getterPattern;
21+
private final Pattern setterPattern;
22+
23+
private final Pattern methodReplacePattern;
24+
25+
private final Pattern columnReplacePattern;
26+
27+
28+
public DefaultColumnToMethodMatchingStrategy() {
29+
this("^(get|is|has).+", "^(set).+", "^(get|set|is|has)|_", "[-_.]");
30+
}
31+
32+
public DefaultColumnToMethodMatchingStrategy(String getterPatternRegEx, String setterPaternRegEx, String methodReplacePatternRegEx, String columnReplacePatternRegEx) {
33+
this.getterPattern = Pattern.compile(getterPatternRegEx);
34+
this.setterPattern = Pattern.compile(setterPaternRegEx);
35+
this.methodReplacePattern = Pattern.compile(methodReplacePatternRegEx);
36+
this.columnReplacePattern = Pattern.compile(columnReplacePatternRegEx);
37+
}
38+
39+
@Override
40+
public String normalizeMethodName(String methodName) {
41+
return methodReplacePattern.matcher(methodName).replaceAll("").toLowerCase();
42+
}
43+
44+
@Override
45+
public boolean isSetter(String methodName) {
46+
return setterPattern.matcher(methodName).matches();
47+
}
48+
49+
@Override
50+
public boolean isGetter(String methodName) {
51+
return getterPattern.matcher(methodName).matches();
52+
}
53+
54+
@Override
55+
public String normalizeColumnName(String columnName) {
56+
return columnReplacePattern.matcher(columnName).replaceAll("").toLowerCase();
57+
}
58+
}

client-v2/src/test/java/com/clickhouse/client/metadata/MetadataTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import com.clickhouse.client.ClickHouseRequest;
1010
import com.clickhouse.client.ClickHouseResponse;
1111
import com.clickhouse.client.api.Client;
12+
import com.clickhouse.client.api.metadata.DefaultColumnToMethodMatchingStrategy;
1213
import com.clickhouse.client.api.metadata.TableSchema;
1314
import com.clickhouse.data.ClickHouseColumn;
1415
import com.clickhouse.data.ClickHouseRecord;
1516
import org.testng.Assert;
1617
import org.testng.annotations.BeforeMethod;
18+
import org.testng.annotations.DataProvider;
1719
import org.testng.annotations.Test;
1820

1921
import java.util.Iterator;
@@ -82,4 +84,28 @@ private void prepareDataSet(String tableName) {
8284
Assert.fail("Failed to prepare data set", e);
8385
}
8486
}
87+
88+
@Test(groups = {"integration"}, dataProvider = "testMatchingNormalizationData")
89+
public void testDefaultColumnToMethodMatchingStrategy(String methodName, String columnName) {
90+
methodName = DefaultColumnToMethodMatchingStrategy.INSTANCE.normalizeMethodName(methodName);
91+
columnName = DefaultColumnToMethodMatchingStrategy.INSTANCE.normalizeColumnName(columnName);
92+
Assert.assertEquals(methodName, columnName, "Method name: " + methodName + " Column name: " + columnName);
93+
}
94+
95+
@DataProvider(name = "testMatchingNormalizationData")
96+
public Object[][] testMatchingNormalizationData() {
97+
return new Object[][]{
98+
{"getLastName", "LastName"},
99+
{"getLastName", "last_name"},
100+
{"getLastName", "last.name"},
101+
{"setLastName", "last.name"},
102+
{"isLastUpdate", "last_update"},
103+
{"hasMore", "more"},
104+
{"getFIRST_NAME", "first_name"},
105+
{"setUPDATED_ON", "updated.ON"},
106+
{"getNUM_OF_TRIES", "num_of_tries"},
107+
{"gethas_more", "has_more"},
108+
109+
};
110+
}
85111
}

0 commit comments

Comments
 (0)