简体   繁体   中英

MyBatis in Spring with Sql Builder and annotation

I am doing some tests with MyBatis in Spring and I decided to test two ways to make sql queries using annotations, but one of them is not working and I would like some help to understand the problem.

It's working:

@Select("<script> "
    + "SELECT * FROM user "
    + "<where>"
    + "<if test=\"username != null\"> OR username LIKE '%' #{username} '%' </if>"
    + "<if test=\"name != null\"> OR name LIKE '%' #{name} '%' </if>"
    + "<if test=\"email != null\"> OR email LIKE '%' #{email} '%' </if>"
    + "</where>"
    + " </script>")
public List<User> findByDynamicFilter(@Param("username") String username, 
        @Param("name") String name, @Param("email") String email);

The one that does not work:

Mapper

@SelectProvider(type = UserSqlBuilder.class, method = "buildFindByDynamicFilter")
public List<User> findByDynamicFilter(@Param("username") String username, 
        @Param("name") String name, @Param("email") String email);

Class SqlBuilder

public class UserSqlBuilder {

    public static String buildFindByDynamicFilter(final String username, final String name, final String email) {
        return new SQL() {
            {
                SELECT("*");
                FROM("user");
                if (name != null) {
                    OR().WHERE("name like #{name} || '%'");
                }
                if (username != null) {
                    OR().WHERE("username like #{username}");
                }
                if (email != null) {
                    OR().WHERE("email like #{email} || '%'");
                }
            }
        }.toString();
    }

}

Error:

2018-12-12 19:47:12.935 ERROR 7454 --- [nio-8080-exec-1] oaccC[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error invoking SqlProvider method (br.com.andre.springmvcuser.mapper.sqlbuilder.UserSqlBuilder.buildFindByDynamicFilter). Cause: org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [name, param3, param1, email, username, param2]] with root cause

org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [name, param3, param1, email, username, param2] at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:204) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.builder.annotation.ProviderSqlSource.extractProviderMethodArguments(ProviderSqlSource.java:156) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.builder.annotation.ProviderSqlSource.createSqlSource(ProviderSqlSource.java:120) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.builder.annotation.ProviderSqlSource.getBoundSql(ProviderSqlSource.java:102) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:292) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148) ~[mybatis-3.4.6.jar:3.4.6] at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.jav a:141) ~[mybatis-3.4.6.jar:3.4.6] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191] at sun.reflect .....

What are these parameters [name, param3, param1, email, username, param2]] ?

Does the way I do SqlBuilder need anything else?

The problem happens because mybatis cannot pass parameters from mapper method to the builder.

You either need to annotate the parameters in the builder:

public static String buildFindByDynamicFilter(
   final @Param("username") String username,
   final @Param("name") String name,
   final @Param("email") String email) {

or remove parameters that are not used from the builder method signature (in your case all of them can be removed as none of them is used in the builder):

public static String buildFindByDynamicFilter() {
    ...
} 

or include method parameter names into compiled classes and make sure that parameters in builder method are named the same way as parameters in the mapper method.

Mybatis java API that builds the query (functions like SELECT , FROM , WHERE etc) does not need parameters by itself. The purpose of the builder is to generate the sql query text. Parameters binding happens later when mybatis has retrieved the query text from the builder (or @Select annotation or xml mapper) and the query is executed using JDBC prepared statements.

[name, param3, param1, email, username, param2] are parameters of the mapper method. Every parameter is available under two keys, the first is the value of the @Param annotation on the mapper method argument and the second is just a sequential number of the argument prefixed with param (the index is zero based and the first one is skipped because the first parameter of the mapper method is implicit this ). Mybatis tries to show a reasonable error message with the list of the available parameters but in case when no method parameters information is available it is confusing.

In the builder if no @Param annotation is present reflection is used to get the method parameter. If method parameters information is not included with -parameters javac option then argument names are arg0 , arg1 and so on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM