Skip to content

Store the Java type in the column for further introspection #423

Closed
@djechelon

Description

@djechelon

This is a feature request impacting both SqlColumn<T> and the Mybatis generator.

I am working on a dynamic filter framework and I stumbled upon an obvious exception when working with dates.

I have the client sending a Short and I want to convert it (without too much hassle) in a java.time.Year.

@Data
public class SimpleFilter<T> {

    /**
     * Requires the property to be equal to the value
     */
    protected T eq;
    /**
     * Requires the property to be different from the value
     */
    protected T ne;
    /**
     * Requires the property to be any of the supplied values
     */
    protected T[] in;
    /**
     * Requires the property not to be any of the supplied values
     */
    protected T[] notIn;

    /**
     * Requires the property to be either not null or null.
     * The property can be used as:
     * <p>
     * - Unspecified: null check is ignored
     * - True: the property must be null
     * - False: the property must be not null
     */
    protected Boolean isNull;

}
  @Nullable
    @SuppressWarnings("unchecked")
    public <T> WhereApplier applyFilter(@Nullable WhereApplier whereApplier, SqlColumn<? super T> column, @Nullable SimpleFilter<T> propertyFilter) {
        if (propertyFilter == null)
            return whereApplier;

        if (propertyFilter.getEq() != null || (propertyFilter.getEq() instanceof String && isNotBlank((String) propertyFilter.getEq())))
            whereApplier = combineAnd(whereApplier, column, isEqualTo(propertyFilter.getEq()));
        if (propertyFilter.getNe() != null || (propertyFilter.getNe() instanceof String && isNotBlank((String) propertyFilter.getNe())))
            whereApplier = combineAnd(whereApplier, column, isNotEqualTo(propertyFilter.getEq()));
        if (propertyFilter.getIn() != null)
            whereApplier = combineAnd(whereApplier, column, isIn(propertyFilter.getIn()));
        if (propertyFilter.getNotIn() != null)
            whereApplier = combineAnd(whereApplier, column, isNotIn(propertyFilter.getNotIn()));
        if (propertyFilter.getIsNull() != null)
            whereApplier = combineAnd(whereApplier, column, propertyFilter.getIsNull() ? isNull() : isNotNull());

        if (propertyFilter instanceof SimpleComparableFilter)
            whereApplier = applyComparableFilter(whereApplier, (SqlColumn<? super Comparable<?>>) column, (SimpleComparableFilter) propertyFilter);


        return whereApplier;
    }

 /**
     * Static utility to combine WHERE predicates into AND expressions
     * <p>
     * Since there is a difference invoking method {@link org.mybatis.dynamic.sql.where.AbstractWhereDSL#where} vs
     * {@link org.mybatis.dynamic.sql.where.AbstractWhereDSL#and}, we must use <pre>where</pre> the first time, but
     * <pre>and</pre> the rest of the times.
     * <p>
     * This method checks if the argument is null
     * <p>
     * Trick: if you need additional overloads of <pre>where</pre>/<pre>and</pre> methods, you need to define additional
     * overloads of this method
     *
     * @param whereApplier
     * @param column
     * @param condition
     * @param <T>
     * @return
     */
    protected static <T> WhereApplier combineAnd(WhereApplier whereApplier, BindableColumn<T> column, VisitableCondition<T> condition) {
        if (whereApplier == null)
            return where -> where.where(column, condition);
        else return whereApplier.andThen(where -> where.and(column, condition));
    }

Basically I am cycling over the properties (each of type SimpleFilter<T>) and assign the value to the proper condition (equals, greater...).

The client sends me a number, such as 2021, so I would like, without much hassle, to convert it to a year.


java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.time.Year (java.lang.Integer and java.time.Year are in module java.base of loader 'bootstrap')
	at org.apache.ibatis.type.YearTypeHandler.setNonNullParameter(YearTypeHandler.java:28) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:73) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:87) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:94) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:88) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:62) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:89) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76) ~[mybatis-3.5.7.jar:3.5.7]
	at jdk.internal.reflect.GeneratedMethodAccessor141.invoke(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427) ~[mybatis-spring-2.0.6.jar:2.0.6]
	at com.sun.proxy.$Proxy126.selectOne(Unknown Source) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:160) ~[mybatis-spring-2.0.6.jar:2.0.6]
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145) ~[mybatis-3.5.7.jar:3.5.7]
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.7.jar:3.5.7]
	at com.sun.proxy.$Proxy178.count(Unknown Source) ~[na:na]
	at org.mybatis.dynamic.sql.util.mybatis3.MyBatis3Utils.countFrom(MyBatis3Utils.java:81) ~[mybatis-dynamic-sql-1.3.0.jar:1.3.0]
	at it.orbit.amlc.backend.data.mappers.VwPlanningAmlControlBaseMapper.count(VwPlanningAmlControlBaseMapper.java:106) ~[main/:na]

Request

To expose Class<T> getJavaType() in the SqlColumn<T> class. I will use such property to do some conversion, with the help of Spring's conversion service, before setting the dynamic value into the filter.

To amend the Mybatis generator to automatically add the Java type to the SqlColumn constructor in the helper class.

Porkaround

To do the conversion manually in a subclass YearFilter extends SimpleFilter<Year> as I already did with OffsetDateTime to LocalDateTime (whose conversion is simple)

Thanks for the attention

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions