Description
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