Skip to content

Commit bb1314a

Browse files
authored
Merge pull request #386 from jeffgbutler/java-type-on-sqlcolumn
Support Specifying javaType on SqlColumn, and Render Properly for MyBatis
2 parents 3b5c51c + 7e207a3 commit bb1314a

File tree

17 files changed

+229
-49
lines changed

17 files changed

+229
-49
lines changed

src/main/java/org/mybatis/dynamic/sql/BindableColumn.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,4 +51,8 @@ default Optional<RenderingStrategy> renderingStrategy() {
5151
default Object convertParameterType(T value) {
5252
return value;
5353
}
54+
55+
default Optional<Class<T>> javaType() {
56+
return Optional.empty();
57+
}
5458
}

src/main/java/org/mybatis/dynamic/sql/SqlColumn.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
3636
protected final RenderingStrategy renderingStrategy;
3737
protected final ParameterTypeConverter<T, ?> parameterTypeConverter;
3838
protected final BiFunction<TableAliasCalculator, SqlTable, Optional<String>> tableQualifierFunction;
39+
protected final Class<T> javaType;
3940

4041
private SqlColumn(Builder<T> builder) {
4142
name = Objects.requireNonNull(builder.name);
@@ -47,6 +48,7 @@ private SqlColumn(Builder<T> builder) {
4748
renderingStrategy = builder.renderingStrategy;
4849
parameterTypeConverter = builder.parameterTypeConverter;
4950
tableQualifierFunction = Objects.requireNonNull(builder.tableQualifierFunction);
51+
javaType = builder.javaType;
5052
}
5153

5254
public String name() {
@@ -72,6 +74,11 @@ public Optional<String> typeHandler() {
7274
return Optional.ofNullable(typeHandler);
7375
}
7476

77+
@Override
78+
public Optional<Class<T>> javaType() {
79+
return Optional.ofNullable(javaType);
80+
}
81+
7582
@Override
7683
public Object convertParameterType(T value) {
7784
return parameterTypeConverter == null ? value : parameterTypeConverter.convert(value);
@@ -158,6 +165,12 @@ public <S> SqlColumn<S> withParameterTypeConverter(ParameterTypeConverter<S, ?>
158165
return b.withParameterTypeConverter(parameterTypeConverter).build();
159166
}
160167

168+
@NotNull
169+
public <S> SqlColumn<S> withJavaType(Class<S> javaType) {
170+
Builder<S> b = copy();
171+
return b.withJavaType(javaType).build();
172+
}
173+
161174
/**
162175
* This method helps us tell a bit of fiction to the Java compiler. Java, for better or worse,
163176
* does not carry generic type information through chained methods. We want to enable method
@@ -178,7 +191,8 @@ private <S> Builder<S> copy() {
178191
.withTypeHandler(this.typeHandler)
179192
.withRenderingStrategy(this.renderingStrategy)
180193
.withParameterTypeConverter((ParameterTypeConverter<S, ?>) this.parameterTypeConverter)
181-
.withTableQualifierFunction(this.tableQualifierFunction);
194+
.withTableQualifierFunction(this.tableQualifierFunction)
195+
.withJavaType((Class<S>) this.javaType);
182196
}
183197

184198
private String applyTableAlias(String tableAlias) {
@@ -209,6 +223,7 @@ public static class Builder<T> {
209223
protected ParameterTypeConverter<T, ?> parameterTypeConverter;
210224
protected BiFunction<TableAliasCalculator, SqlTable, Optional<String>> tableQualifierFunction =
211225
TableAliasCalculator::aliasForColumn;
226+
protected Class<T> javaType;
212227

213228
public Builder<T> withName(String name) {
214229
this.name = name;
@@ -256,6 +271,11 @@ private Builder<T> withTableQualifierFunction(
256271
return this;
257272
}
258273

274+
public Builder<T> withJavaType(Class<T> javaType) {
275+
this.javaType = javaType;
276+
return this;
277+
}
278+
259279
public SqlColumn<T> build() {
260280
return new SqlColumn<>(this);
261281
}

src/main/java/org/mybatis/dynamic/sql/render/MyBatis3RenderingStrategy.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ public String getFormattedJdbcPlaceholder(BindableColumn<?> column, String prefi
3434
+ "." //$NON-NLS-1$
3535
+ parameterName
3636
+ renderJdbcType(column)
37+
+ renderJavaType(column)
3738
+ renderTypeHandler(column)
3839
+ "}"; //$NON-NLS-1$
3940
}
@@ -49,4 +50,10 @@ private String renderJdbcType(BindableColumn<?> column) {
4950
.map(jt -> ",jdbcType=" + jt.getName()) //$NON-NLS-1$
5051
.orElse(""); //$NON-NLS-1$
5152
}
53+
54+
private String renderJavaType(BindableColumn<?> column) {
55+
return column.javaType()
56+
.map(jt -> ",javaType=" + jt.getName()) //$NON-NLS-1$
57+
.orElse(""); //$NON-NLS-1$
58+
}
5259
}

src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlTableExtensions.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.mybatis.dynamic.sql.SqlColumn
1919
import org.mybatis.dynamic.sql.SqlTable
2020
import org.mybatis.dynamic.sql.render.RenderingStrategy
2121
import java.sql.JDBCType
22+
import kotlin.reflect.KClass
2223

2324
/**
2425
* This function replaces the native functions in [@see SqlColumn} such as
@@ -31,7 +32,8 @@ fun <T : Any> SqlTable.column(
3132
jdbcType: JDBCType? = null,
3233
typeHandler: String? = null,
3334
renderingStrategy: RenderingStrategy? = null,
34-
parameterTypeConverter: ((T?) -> Any?)? = null
35+
parameterTypeConverter: ((T?) -> Any?)? = null,
36+
javaType: KClass<T>? = null
3537
): SqlColumn<T> {
3638
var column: SqlColumn<T> = if (jdbcType == null) {
3739
column(name)
@@ -51,5 +53,9 @@ fun <T : Any> SqlTable.column(
5153
column = column.withParameterTypeConverter(parameterTypeConverter)
5254
}
5355

56+
if (javaType != null) {
57+
column = column.withJavaType(javaType.java)
58+
}
59+
5460
return column
5561
}

src/test/java/examples/simple/AddressDynamicSqlSupport.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,12 +26,16 @@ public final class AddressDynamicSqlSupport {
2626
public static final SqlColumn<String> streetAddress = address.streetAddress;
2727
public static final SqlColumn<String> city = address.city;
2828
public static final SqlColumn<String> state = address.state;
29+
public static final SqlColumn<AddressRecord.AddressType> addressType = address.addressType;
2930

3031
public static final class Address extends SqlTable {
3132
public final SqlColumn<Integer> id = column("address_id", JDBCType.INTEGER);
3233
public final SqlColumn<String> streetAddress = column("street_address", JDBCType.VARCHAR);
3334
public final SqlColumn<String> city = column("city", JDBCType.VARCHAR);
3435
public final SqlColumn<String> state = column("state", JDBCType.VARCHAR);
36+
public final SqlColumn<AddressRecord.AddressType> addressType = column("address_type", JDBCType.INTEGER)
37+
.withTypeHandler("org.apache.ibatis.type.EnumOrdinalTypeHandler")
38+
.withJavaType(AddressRecord.AddressType.class);
3539

3640
public Address() {
3741
super("Address");
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2016-2021 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+
* http://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 examples.simple;
17+
18+
import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper;
19+
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
20+
21+
public interface AddressMapper extends CommonInsertMapper<AddressRecord>, CommonSelectMapper {
22+
}

src/test/java/examples/simple/AddressRecord.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ public class AddressRecord {
2020
private String streetAddress;
2121
private String city;
2222
private String state;
23+
private AddressType addressType;
2324

2425
public Integer getId() {
2526
return id;
@@ -52,4 +53,17 @@ public String getState() {
5253
public void setState(String state) {
5354
this.state = state;
5455
}
56+
57+
public AddressType getAddressType() {
58+
return addressType;
59+
}
60+
61+
public void setAddressType(AddressType addressType) {
62+
this.addressType = addressType;
63+
}
64+
65+
public enum AddressType {
66+
HOME,
67+
BUSINESS
68+
}
5569
}

src/test/java/examples/simple/PersonMapperTest.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.junit.jupiter.api.Test;
2828
import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter;
2929
import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider;
30+
import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider;
3031
import org.mybatis.dynamic.sql.render.RenderingStrategies;
3132
import org.mybatis.dynamic.sql.select.CountDSLCompleter;
3233
import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
@@ -42,6 +43,7 @@
4243
import java.util.Objects;
4344
import java.util.Optional;
4445

46+
import static examples.simple.AddressDynamicSqlSupport.address;
4547
import static examples.simple.PersonDynamicSqlSupport.addressId;
4648
import static examples.simple.PersonDynamicSqlSupport.birthDate;
4749
import static examples.simple.PersonDynamicSqlSupport.employed;
@@ -52,17 +54,7 @@
5254
import static examples.simple.PersonDynamicSqlSupport.person;
5355
import static org.assertj.core.api.Assertions.assertThat;
5456
import static org.junit.jupiter.api.Assertions.assertAll;
55-
import static org.mybatis.dynamic.sql.SqlBuilder.deleteFrom;
56-
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
57-
import static org.mybatis.dynamic.sql.SqlBuilder.isFalse;
58-
import static org.mybatis.dynamic.sql.SqlBuilder.isGreaterThan;
59-
import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
60-
import static org.mybatis.dynamic.sql.SqlBuilder.isLike;
61-
import static org.mybatis.dynamic.sql.SqlBuilder.isNotLike;
62-
import static org.mybatis.dynamic.sql.SqlBuilder.isNull;
63-
import static org.mybatis.dynamic.sql.SqlBuilder.isTrue;
64-
import static org.mybatis.dynamic.sql.SqlBuilder.or;
65-
import static org.mybatis.dynamic.sql.SqlBuilder.select;
57+
import static org.mybatis.dynamic.sql.SqlBuilder.*;
6658

6759
class PersonMapperTest {
6860

@@ -86,6 +78,7 @@ void setup() throws Exception {
8678
Configuration config = new Configuration(environment);
8779
config.addMapper(PersonMapper.class);
8880
config.addMapper(PersonWithAddressMapper.class);
81+
config.addMapper(AddressMapper.class);
8982
sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
9083
}
9184

@@ -650,6 +643,9 @@ void testJoinAllRows() {
650643
assertThat(records.get(0).getAddress().getStreetAddress()).isEqualTo("123 Main Street");
651644
assertThat(records.get(0).getAddress().getCity()).isEqualTo("Bedrock");
652645
assertThat(records.get(0).getAddress().getState()).isEqualTo("IN");
646+
assertThat(records.get(0).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.HOME);
647+
648+
assertThat(records.get(4).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.BUSINESS);
653649
}
654650
}
655651

@@ -723,4 +719,32 @@ void testJoinCountWithSubcriteria() {
723719
assertThat(count).isEqualTo(1);
724720
}
725721
}
722+
723+
@Test
724+
void testWithEnumOrdinalTypeHandler() {
725+
try (SqlSession session = sqlSessionFactory.openSession()) {
726+
AddressMapper mapper = session.getMapper(AddressMapper.class);
727+
728+
GeneralInsertStatementProvider insertStatement = insertInto(address)
729+
.set(address.id).toValue(4)
730+
.set(address.streetAddress).toValue("987 Elm Street")
731+
.set(address.city).toValue("Mayberry")
732+
.set(address.state).toValue("NC")
733+
.set(address.addressType).toValue(AddressRecord.AddressType.HOME)
734+
.build()
735+
.render(RenderingStrategies.MYBATIS3);
736+
737+
int rows = mapper.generalInsert(insertStatement);
738+
assertThat(rows).isEqualTo(1);
739+
740+
SelectStatementProvider selectStatement = select(address.addressType)
741+
.from(address)
742+
.where(address.id, isEqualTo(4))
743+
.build()
744+
.render(RenderingStrategies.MYBATIS3);
745+
746+
Optional<Integer> type = mapper.selectOptionalInteger(selectStatement);
747+
assertThat(type).hasValueSatisfying(i -> assertThat(i).isEqualTo(0));
748+
}
749+
}
726750
}

src/test/java/examples/simple/PersonWithAddressMapper.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
import org.apache.ibatis.annotations.ResultMap;
3434
import org.apache.ibatis.annotations.Results;
3535
import org.apache.ibatis.annotations.SelectProvider;
36+
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
3637
import org.apache.ibatis.type.JdbcType;
3738
import org.mybatis.dynamic.sql.BasicColumn;
3839
import org.mybatis.dynamic.sql.SqlBuilder;
@@ -64,7 +65,9 @@ public interface PersonWithAddressMapper {
6465
@Result(column="address_id", property="address.id", jdbcType=JdbcType.INTEGER),
6566
@Result(column="street_address", property="address.streetAddress", jdbcType=JdbcType.VARCHAR),
6667
@Result(column="city", property="address.city", jdbcType=JdbcType.VARCHAR),
67-
@Result(column="state", property="address.state", jdbcType=JdbcType.CHAR)
68+
@Result(column="state", property="address.state", jdbcType=JdbcType.CHAR),
69+
@Result(column="address_type", property="address.addressType", jdbcType=JdbcType.INTEGER,
70+
typeHandler = EnumOrdinalTypeHandler.class)
6871
})
6972
List<PersonWithAddress> selectMany(SelectStatementProvider selectStatement);
7073

@@ -77,7 +80,7 @@ public interface PersonWithAddressMapper {
7780

7881
BasicColumn[] selectList =
7982
BasicColumn.columnList(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, address.id,
80-
address.streetAddress, address.city, address.state);
83+
address.streetAddress, address.city, address.state, address.addressType);
8184

8285
default Optional<PersonWithAddress> selectOne(SelectDSLCompleter completer) {
8386
QueryExpressionDSL<SelectModel> start = SqlBuilder.select(selectList).from(person)

src/test/kotlin/examples/kotlin/mybatis3/canonical/AddressDynamicSqlSupport.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ object AddressDynamicSqlSupport {
2525
val streetAddress = address.streetAddress
2626
val city = address.city
2727
val state = address.state
28+
val addressType = address.addressType
2829

2930
class Address : SqlTable("Address") {
3031
val id = column<Int>(name = "address_id", jdbcType = JDBCType.INTEGER)
3132
val streetAddress = column<String>(name = "street_address", jdbcType = JDBCType.VARCHAR)
3233
val city = column<String>(name = "city", jdbcType = JDBCType.VARCHAR)
3334
val state = column<String>(name = "state", jdbcType = JDBCType.VARCHAR)
35+
val addressType = column(
36+
name = "address_type",
37+
jdbcType = JDBCType.INTEGER,
38+
javaType = AddressType::class,
39+
typeHandler = "org.apache.ibatis.type.EnumOrdinalTypeHandler"
40+
)
3441
}
3542
}

0 commit comments

Comments
 (0)