Skip to content

Add Ability to configure a mapped Java property in an SqlColumn #969

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 6 commits into from
Aug 11, 2025
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Runtime behavior changes:
are not supported by the library out of the box. The statement renderers now call methods `renderCondition` and
`renderLeftColumn` that you can override to implement any rendering you need. In addition, we've made `filter` and
`map` support optional if you implement custom conditions
- Added support for configuring a Java property name to be associated with an `SqlColumn`. This property name can be
used with the record based insert methods to reduce the boilerplate code for mapping columns to Java properties.

## Release 1.5.2 - June 3, 2024

Expand Down
20 changes: 19 additions & 1 deletion src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
protected final ParameterTypeConverter<T, ?> parameterTypeConverter;
protected final @Nullable String tableQualifier;
protected final @Nullable Class<T> javaType;
protected final @Nullable String javaProperty;

private SqlColumn(Builder<T> builder) {
name = Objects.requireNonNull(builder.name);
Expand All @@ -49,6 +50,7 @@ private SqlColumn(Builder<T> builder) {
parameterTypeConverter = Objects.requireNonNull(builder.parameterTypeConverter);
tableQualifier = builder.tableQualifier;
javaType = builder.javaType;
javaProperty = builder.javaProperty;
}

public String name() {
Expand Down Expand Up @@ -79,6 +81,10 @@ public Optional<Class<T>> javaType() {
return Optional.ofNullable(javaType);
}

public Optional<String> javaProperty() {
return Optional.ofNullable(javaProperty);
}

@Override
public @Nullable Object convertParameterType(@Nullable T value) {
return value == null ? null : parameterTypeConverter.convert(value);
Expand Down Expand Up @@ -164,6 +170,11 @@ public <S> SqlColumn<S> withJavaType(Class<S> javaType) {
return b.withJavaType(javaType).build();
}

public <S> SqlColumn<S> withJavaProperty(String javaProperty) {
Builder<S> b = copy();
return b.withJavaProperty(javaProperty).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 @@ -185,7 +196,8 @@ private <S> Builder<S> copy() {
.withRenderingStrategy(this.renderingStrategy)
.withParameterTypeConverter((ParameterTypeConverter<S, ?>) this.parameterTypeConverter)
.withTableQualifier(this.tableQualifier)
.withJavaType((Class<S>) this.javaType);
.withJavaType((Class<S>) this.javaType)
.withJavaProperty(this.javaProperty);
}

public static <T> SqlColumn<T> of(String name, SqlTable table) {
Expand All @@ -212,6 +224,7 @@ public static class Builder<T> {
protected ParameterTypeConverter<T, ?> parameterTypeConverter = v -> v;
protected @Nullable String tableQualifier;
protected @Nullable Class<T> javaType;
protected @Nullable String javaProperty;

public Builder<T> withName(String name) {
this.name = name;
Expand Down Expand Up @@ -263,6 +276,11 @@ public Builder<T> withJavaType(@Nullable Class<T> javaType) {
return this;
}

public Builder<T> withJavaProperty(@Nullable String javaProperty) {
this.javaProperty = javaProperty;
return this;
}

public SqlColumn<T> build() {
return new SqlColumn<>(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
import org.mybatis.dynamic.sql.util.Buildable;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.MappedColumnMapping;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
import org.mybatis.dynamic.sql.util.RowMapping;
Expand All @@ -48,6 +49,11 @@ public <F> ColumnMappingFinisher<F> map(SqlColumn<F> column) {
return new ColumnMappingFinisher<>(column);
}

public <F> BatchInsertDSL<T> withMappedColumn(SqlColumn<F> column) {
columnMappings.add(MappedColumnMapping.of(column));
return this;
}

@Override
public BatchInsertModel<T> build() {
return BatchInsertModel.withRecords(records)
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
import org.mybatis.dynamic.sql.util.Buildable;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.MappedColumnMapping;
import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping;
Expand All @@ -49,6 +51,16 @@ public <F> ColumnMappingFinisher<F> map(SqlColumn<F> column) {
return new ColumnMappingFinisher<>(column);
}

public <F> InsertDSL<T> withMappedColumn(SqlColumn<F> column) {
columnMappings.add(MappedColumnMapping.of(column));
return this;
}

public <F> InsertDSL<T> withMappedColumnWhenPresent(SqlColumn<F> column, Supplier<?> valueSupplier) {
columnMappings.add(MappedColumnWhenPresentMapping.of(column, valueSupplier));
return this;
}

@Override
public InsertModel<T> build() {
return InsertModel.withRow(row)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
import org.mybatis.dynamic.sql.util.Buildable;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.MappedColumnMapping;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
import org.mybatis.dynamic.sql.util.RowMapping;
Expand All @@ -46,6 +47,11 @@ public <F> ColumnMappingFinisher<F> map(SqlColumn<F> column) {
return new ColumnMappingFinisher<>(column);
}

public <F> MultiRowInsertDSL<T> withMappedColumn(SqlColumn<F> column) {
columnMappings.add(MappedColumnMapping.of(column));
return this;
}

@Override
public MultiRowInsertModel<T> build() {
return MultiRowInsertModel.withRecords(records)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore;

import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.SqlTable;
import org.mybatis.dynamic.sql.exception.InvalidSqlException;
import org.mybatis.dynamic.sql.util.Messages;

public class InsertRenderingUtilities {
private InsertRenderingUtilities() {}
Expand All @@ -33,4 +36,10 @@ public static String calculateInsertStatement(SqlTable table, FieldAndValueColle
public static String calculateInsertStatementStart(SqlTable table) {
return "insert into " + table.tableName(); //$NON-NLS-1$
}

public static String getMappedPropertyName(SqlColumn<?> column) {
return column.javaProperty().orElseThrow(() ->
new InvalidSqlException(Messages
.getString("ERROR.50", column.name()))); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.render.RenderingStrategy;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.MappedColumnMapping;
import org.mybatis.dynamic.sql.util.MultiRowInsertMappingVisitor;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
Expand Down Expand Up @@ -69,6 +70,16 @@ public FieldAndValueAndParameters visit(RowMapping mapping) {
.build();
}

@Override
public FieldAndValueAndParameters visit(MappedColumnMapping mapping) {
return FieldAndValueAndParameters.withFieldName(mapping.columnName())
.withValuePhrase(calculateJdbcPlaceholder(
mapping.column(),
InsertRenderingUtilities.getMappedPropertyName(mapping.column()))
)
.build();
}

private String calculateJdbcPlaceholder(SqlColumn<?> column) {
return column.renderingStrategy().orElse(renderingStrategy).getRecordBasedInsertBinding(column, prefix);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import org.mybatis.dynamic.sql.render.RenderingStrategy;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.InsertMappingVisitor;
import org.mybatis.dynamic.sql.util.MappedColumnMapping;
import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping;
Expand Down Expand Up @@ -80,6 +82,25 @@ public Optional<FieldAndValueAndParameters> visit(RowMapping mapping) {
.buildOptional();
}

@Override
public Optional<FieldAndValueAndParameters> visit(MappedColumnMapping mapping) {
return FieldAndValueAndParameters.withFieldName(mapping.columnName())
.withValuePhrase(calculateJdbcPlaceholder(
mapping.column(),
InsertRenderingUtilities.getMappedPropertyName(mapping.column()))
)
.buildOptional();
}

@Override
public Optional<FieldAndValueAndParameters> visit(MappedColumnWhenPresentMapping mapping) {
if (mapping.shouldRender()) {
return visit((MappedColumnMapping) mapping);
} else {
return Optional.empty();
}
}

private String calculateJdbcPlaceholder(SqlColumn<?> column) {
return column.renderingStrategy().orElse(renderingStrategy)
.getRecordBasedInsertBinding(column, "row"); //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ public interface ColumnMappingVisitor<R> {
R visit(ColumnToColumnMapping mapping);

R visit(RowMapping mapping);

R visit(MappedColumnMapping mapping);

R visit(MappedColumnWhenPresentMapping mapping);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,14 @@ public final R visit(ColumnToColumnMapping columnMapping) {
public final R visit(RowMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_14));
}

@Override
public R visit(MappedColumnMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_16));
}

@Override
public R visit(MappedColumnWhenPresentMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_17));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ public enum InternalError {
INTERNAL_ERROR_12(12),
INTERNAL_ERROR_13(13),
INTERNAL_ERROR_14(14),
INTERNAL_ERROR_15(15);
INTERNAL_ERROR_15(15),
INTERNAL_ERROR_16(16),
INTERNAL_ERROR_17(17),
INTERNAL_ERROR_18(18),
INTERNAL_ERROR_19(19),
INTERNAL_ERROR_20(20);

private final int number;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2016-2025 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
*
* https://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 org.mybatis.dynamic.sql.util;

import org.mybatis.dynamic.sql.SqlColumn;

public class MappedColumnMapping extends AbstractColumnMapping {

protected MappedColumnMapping(SqlColumn<?> column) {
super(column);
}

@Override
public <R> R accept(ColumnMappingVisitor<R> visitor) {
return visitor.visit(this);
}

public static MappedColumnMapping of(SqlColumn<?> column) {
return new MappedColumnMapping(column);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2016-2025 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
*
* https://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 org.mybatis.dynamic.sql.util;

import java.util.Objects;
import java.util.function.Supplier;

import org.mybatis.dynamic.sql.SqlColumn;

public class MappedColumnWhenPresentMapping extends MappedColumnMapping {
private final Supplier<?> valueSupplier;

private MappedColumnWhenPresentMapping(SqlColumn<?> column, Supplier<?> valueSupplier) {
super(column);
this.valueSupplier = Objects.requireNonNull(valueSupplier);
}

public boolean shouldRender() {
return valueSupplier.get() != null;
}

@Override
public <R> R accept(ColumnMappingVisitor<R> visitor) {
return visitor.visit(this);
}

public static MappedColumnWhenPresentMapping of(SqlColumn<?> column, Supplier<?> valueSupplier) {
return new MappedColumnWhenPresentMapping(column, valueSupplier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ public abstract class MultiRowInsertMappingVisitor<R> extends InsertMappingVisit
public final R visit(PropertyWhenPresentMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_12));
}

@Override
public R visit(MappedColumnWhenPresentMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_18));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@ public final R visit(PropertyWhenPresentMapping mapping) {
public final R visit(RowMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_15));
}

@Override
public R visit(MappedColumnMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_19));
}

@Override
public R visit(MappedColumnWhenPresentMapping mapping) {
throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_20));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.mybatis.dynamic.sql.insert.BatchInsertDSL
import org.mybatis.dynamic.sql.insert.BatchInsertModel
import org.mybatis.dynamic.sql.util.AbstractColumnMapping
import org.mybatis.dynamic.sql.util.Buildable
import org.mybatis.dynamic.sql.util.MappedColumnMapping

typealias KotlinBatchInsertCompleter<T> = KotlinBatchInsertBuilder<T>.() -> Unit

Expand All @@ -37,6 +38,10 @@ class KotlinBatchInsertBuilder<T : Any> (private val rows: Collection<T>): Build
columnMappings.add(it)
}

fun <C : Any> withMappedColumn(column: SqlColumn<C>) {
columnMappings.add(MappedColumnMapping.of(column))
}

override fun build(): BatchInsertModel<T> {
assertNotNull(table, "ERROR.23") //$NON-NLS-1$
return with(BatchInsertDSL.Builder<T>()) {
Expand Down
Loading