Skip to content

Support Specifying javaType on SqlColumn, and Render Properly for MyBatis #386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/main/java/org/mybatis/dynamic/sql/BindableColumn.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand Down Expand Up @@ -51,4 +51,8 @@ default Optional<RenderingStrategy> renderingStrategy() {
default Object convertParameterType(T value) {
return value;
}

default Optional<Class<T>> javaType() {
return Optional.empty();
}
}
24 changes: 22 additions & 2 deletions src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand Down Expand Up @@ -36,6 +36,7 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
protected final RenderingStrategy renderingStrategy;
protected final ParameterTypeConverter<T, ?> parameterTypeConverter;
protected final BiFunction<TableAliasCalculator, SqlTable, Optional<String>> tableQualifierFunction;
protected final Class<T> javaType;

private SqlColumn(Builder<T> builder) {
name = Objects.requireNonNull(builder.name);
Expand All @@ -47,6 +48,7 @@ private SqlColumn(Builder<T> builder) {
renderingStrategy = builder.renderingStrategy;
parameterTypeConverter = builder.parameterTypeConverter;
tableQualifierFunction = Objects.requireNonNull(builder.tableQualifierFunction);
javaType = builder.javaType;
}

public String name() {
Expand All @@ -72,6 +74,11 @@ public Optional<String> typeHandler() {
return Optional.ofNullable(typeHandler);
}

@Override
public Optional<Class<T>> javaType() {
return Optional.ofNullable(javaType);
}

@Override
public Object convertParameterType(T value) {
return parameterTypeConverter == null ? value : parameterTypeConverter.convert(value);
Expand Down Expand Up @@ -158,6 +165,12 @@ public <S> SqlColumn<S> withParameterTypeConverter(ParameterTypeConverter<S, ?>
return b.withParameterTypeConverter(parameterTypeConverter).build();
}

@NotNull
public <S> SqlColumn<S> withJavaType(Class<S> javaType) {
Builder<S> b = copy();
return b.withJavaType(javaType).build();
}

/**
* This method helps us tell a bit of fiction to the Java compiler. Java, for better or worse,
* does not carry generic type information through chained methods. We want to enable method
Expand All @@ -178,7 +191,8 @@ private <S> Builder<S> copy() {
.withTypeHandler(this.typeHandler)
.withRenderingStrategy(this.renderingStrategy)
.withParameterTypeConverter((ParameterTypeConverter<S, ?>) this.parameterTypeConverter)
.withTableQualifierFunction(this.tableQualifierFunction);
.withTableQualifierFunction(this.tableQualifierFunction)
.withJavaType((Class<S>) this.javaType);
}

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

public Builder<T> withName(String name) {
this.name = name;
Expand Down Expand Up @@ -256,6 +271,11 @@ private Builder<T> withTableQualifierFunction(
return this;
}

public Builder<T> withJavaType(Class<T> javaType) {
this.javaType = javaType;
return this;
}

public SqlColumn<T> build() {
return new SqlColumn<>(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand Down Expand Up @@ -34,6 +34,7 @@ public String getFormattedJdbcPlaceholder(BindableColumn<?> column, String prefi
+ "." //$NON-NLS-1$
+ parameterName
+ renderJdbcType(column)
+ renderJavaType(column)
+ renderTypeHandler(column)
+ "}"; //$NON-NLS-1$
}
Expand All @@ -49,4 +50,10 @@ private String renderJdbcType(BindableColumn<?> column) {
.map(jt -> ",jdbcType=" + jt.getName()) //$NON-NLS-1$
.orElse(""); //$NON-NLS-1$
}

private String renderJavaType(BindableColumn<?> column) {
return column.javaType()
.map(jt -> ",javaType=" + jt.getName()) //$NON-NLS-1$
.orElse(""); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.mybatis.dynamic.sql.SqlColumn
import org.mybatis.dynamic.sql.SqlTable
import org.mybatis.dynamic.sql.render.RenderingStrategy
import java.sql.JDBCType
import kotlin.reflect.KClass

/**
* This function replaces the native functions in [@see SqlColumn} such as
Expand All @@ -31,7 +32,8 @@ fun <T : Any> SqlTable.column(
jdbcType: JDBCType? = null,
typeHandler: String? = null,
renderingStrategy: RenderingStrategy? = null,
parameterTypeConverter: ((T?) -> Any?)? = null
parameterTypeConverter: ((T?) -> Any?)? = null,
javaType: KClass<T>? = null
): SqlColumn<T> {
var column: SqlColumn<T> = if (jdbcType == null) {
column(name)
Expand All @@ -51,5 +53,9 @@ fun <T : Any> SqlTable.column(
column = column.withParameterTypeConverter(parameterTypeConverter)
}

if (javaType != null) {
column = column.withJavaType(javaType.java)
}

return column
}
6 changes: 5 additions & 1 deletion src/test/java/examples/simple/AddressDynamicSqlSupport.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand All @@ -26,12 +26,16 @@ public final class AddressDynamicSqlSupport {
public static final SqlColumn<String> streetAddress = address.streetAddress;
public static final SqlColumn<String> city = address.city;
public static final SqlColumn<String> state = address.state;
public static final SqlColumn<AddressRecord.AddressType> addressType = address.addressType;

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

public Address() {
super("Address");
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/examples/simple/AddressMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2016-2021 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
*
* http://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 examples.simple;

import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper;
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;

public interface AddressMapper extends CommonInsertMapper<AddressRecord>, CommonSelectMapper {
}
16 changes: 15 additions & 1 deletion src/test/java/examples/simple/AddressRecord.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand All @@ -20,6 +20,7 @@ public class AddressRecord {
private String streetAddress;
private String city;
private String state;
private AddressType addressType;

public Integer getId() {
return id;
Expand Down Expand Up @@ -52,4 +53,17 @@ public String getState() {
public void setState(String state) {
this.state = state;
}

public AddressType getAddressType() {
return addressType;
}

public void setAddressType(AddressType addressType) {
this.addressType = addressType;
}

public enum AddressType {
HOME,
BUSINESS
}
}
46 changes: 35 additions & 11 deletions src/test/java/examples/simple/PersonMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.junit.jupiter.api.Test;
import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter;
import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider;
import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.CountDSLCompleter;
import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
Expand All @@ -42,6 +43,7 @@
import java.util.Objects;
import java.util.Optional;

import static examples.simple.AddressDynamicSqlSupport.address;
import static examples.simple.PersonDynamicSqlSupport.addressId;
import static examples.simple.PersonDynamicSqlSupport.birthDate;
import static examples.simple.PersonDynamicSqlSupport.employed;
Expand All @@ -52,17 +54,7 @@
import static examples.simple.PersonDynamicSqlSupport.person;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mybatis.dynamic.sql.SqlBuilder.deleteFrom;
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
import static org.mybatis.dynamic.sql.SqlBuilder.isFalse;
import static org.mybatis.dynamic.sql.SqlBuilder.isGreaterThan;
import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
import static org.mybatis.dynamic.sql.SqlBuilder.isLike;
import static org.mybatis.dynamic.sql.SqlBuilder.isNotLike;
import static org.mybatis.dynamic.sql.SqlBuilder.isNull;
import static org.mybatis.dynamic.sql.SqlBuilder.isTrue;
import static org.mybatis.dynamic.sql.SqlBuilder.or;
import static org.mybatis.dynamic.sql.SqlBuilder.select;
import static org.mybatis.dynamic.sql.SqlBuilder.*;

class PersonMapperTest {

Expand All @@ -86,6 +78,7 @@ void setup() throws Exception {
Configuration config = new Configuration(environment);
config.addMapper(PersonMapper.class);
config.addMapper(PersonWithAddressMapper.class);
config.addMapper(AddressMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
}

Expand Down Expand Up @@ -650,6 +643,9 @@ void testJoinAllRows() {
assertThat(records.get(0).getAddress().getStreetAddress()).isEqualTo("123 Main Street");
assertThat(records.get(0).getAddress().getCity()).isEqualTo("Bedrock");
assertThat(records.get(0).getAddress().getState()).isEqualTo("IN");
assertThat(records.get(0).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.HOME);

assertThat(records.get(4).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.BUSINESS);
}
}

Expand Down Expand Up @@ -723,4 +719,32 @@ void testJoinCountWithSubcriteria() {
assertThat(count).isEqualTo(1);
}
}

@Test
void testWithEnumOrdinalTypeHandler() {
try (SqlSession session = sqlSessionFactory.openSession()) {
AddressMapper mapper = session.getMapper(AddressMapper.class);

GeneralInsertStatementProvider insertStatement = insertInto(address)
.set(address.id).toValue(4)
.set(address.streetAddress).toValue("987 Elm Street")
.set(address.city).toValue("Mayberry")
.set(address.state).toValue("NC")
.set(address.addressType).toValue(AddressRecord.AddressType.HOME)
.build()
.render(RenderingStrategies.MYBATIS3);

int rows = mapper.generalInsert(insertStatement);
assertThat(rows).isEqualTo(1);

SelectStatementProvider selectStatement = select(address.addressType)
.from(address)
.where(address.id, isEqualTo(4))
.build()
.render(RenderingStrategies.MYBATIS3);

Optional<Integer> type = mapper.selectOptionalInteger(selectStatement);
assertThat(type).hasValueSatisfying(i -> assertThat(i).isEqualTo(0));
}
}
}
9 changes: 6 additions & 3 deletions src/test/java/examples/simple/PersonWithAddressMapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2021 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.
Expand Down Expand Up @@ -33,6 +33,7 @@
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.dynamic.sql.BasicColumn;
import org.mybatis.dynamic.sql.SqlBuilder;
Expand Down Expand Up @@ -64,7 +65,9 @@ public interface PersonWithAddressMapper {
@Result(column="address_id", property="address.id", jdbcType=JdbcType.INTEGER),
@Result(column="street_address", property="address.streetAddress", jdbcType=JdbcType.VARCHAR),
@Result(column="city", property="address.city", jdbcType=JdbcType.VARCHAR),
@Result(column="state", property="address.state", jdbcType=JdbcType.CHAR)
@Result(column="state", property="address.state", jdbcType=JdbcType.CHAR),
@Result(column="address_type", property="address.addressType", jdbcType=JdbcType.INTEGER,
typeHandler = EnumOrdinalTypeHandler.class)
})
List<PersonWithAddress> selectMany(SelectStatementProvider selectStatement);

Expand All @@ -77,7 +80,7 @@ public interface PersonWithAddressMapper {

BasicColumn[] selectList =
BasicColumn.columnList(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, address.id,
address.streetAddress, address.city, address.state);
address.streetAddress, address.city, address.state, address.addressType);

default Optional<PersonWithAddress> selectOne(SelectDSLCompleter completer) {
QueryExpressionDSL<SelectModel> start = SqlBuilder.select(selectList).from(person)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ object AddressDynamicSqlSupport {
val streetAddress = address.streetAddress
val city = address.city
val state = address.state
val addressType = address.addressType

class Address : SqlTable("Address") {
val id = column<Int>(name = "address_id", jdbcType = JDBCType.INTEGER)
val streetAddress = column<String>(name = "street_address", jdbcType = JDBCType.VARCHAR)
val city = column<String>(name = "city", jdbcType = JDBCType.VARCHAR)
val state = column<String>(name = "state", jdbcType = JDBCType.VARCHAR)
val addressType = column(
name = "address_type",
jdbcType = JDBCType.INTEGER,
javaType = AddressType::class,
typeHandler = "org.apache.ibatis.type.EnumOrdinalTypeHandler"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2016-2021 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
*
* http://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 examples.kotlin.mybatis3.canonical

import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper

interface AddressMapper: CommonInsertMapper<AddressRecord>, CommonSelectMapper
Loading