Select Provider Mybatis Error: Parameter 'arg0' not found. Available parameters are [productAttributeDto, param1]

huangapple go评论69阅读模式
英文:

Select Provider Mybatis Error: Parameter 'arg0' not found. Available parameters are [productAttributeDto, param1]

问题

我在使用 Spring Boot 应用程序中集成 Mybatis。我以前使用 ProviderMethodResolver 生成 MySql 查询语句。我的应用程序支持 Mybatis 注解处理器和 XML 处理器。

为了实现这一点,我使用了以下 Mybatis 配置:

在 application.properties 文件中:

mybatis.mapper-locations=classpath*:/repository/**/*Repository.xml

还有一个 MybatisConfiguration.java 文件:

@Configuration
@EnableTransactionManagement
public class MybatisConfiguration {

    @Bean
    ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true);
                configuration.setCacheEnabled(true);
            }
        };
    }
}

通过上述配置,除了 @SelectProvider 的实现之外,所有内容(@Select, @Insert, @ResultMap)似乎都正常工作。

@SelectProvider 的实现如下:

VariantRepository.java

@Mapper
public interface VariantRepository {

    @SelectProvider(type = VariantSqlProvider.class, method = "getProductAttribute")
    VariantDto findByProductAttribute(@Param("productAttributeDto") ProductAttributeDto productAttributeDto);
}

我在这里使用了 org.apache.ibatis.annotations.Param

ProductAttributeDto.java

@Data
public class ProductAttributeDto {

    private Integer productId;

    Map<String, String> attributes;
}

VariantSqlProvider.class

public class VariantSqlProvider implements ProviderMethodResolver {

    @SuppressWarnings("unused")
    public static String getProductAttribute(final ProductAttributeDto productAttributeDto) {
        return new SQL() {{
            SELECT("*");
            FROM("ec_product_variant AS pv");
            if (Objects.nonNull(productAttributeDto.getAttributes())) {
                for (Entry<String, String> entry : productAttributeDto.getAttributes().entrySet()) {
                    if (Objects.nonNull(entry.getValue())) {
                        INNER_JOIN(
                                new StringBuilder("ec_attributes AS ")
                                        .append(entry.getKey())
                                        .append(" ON ")
                                        .append(entry.getKey()).append(".id = pv.").append(entry.getKey())
                                        .append(" AND ")
                                        .append(entry.getKey()).append(".value=#{productAttributeDto.attributes.").append(entry.getKey())
                                        .append("}").toString()
                        );
                    } else {
                        WHERE("pv." + entry.getKey() + " IS NULL");
                    }
                }
            }
            if (Objects.nonNull(productAttributeDto.getProductId())) {
                WHERE("pv.product_id = #{productAttributeDto.productId}");
            }
        }}.toString();
    }
}

当我调用 findByProductAttribute 方法时,出现如下错误:

org.apache.juli.logging.DirectJDKLog: 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 'public static java.lang.String com.ecommerce.app.repository.product.sqlprovider.VariantSqlProvider.getProductAttribute(com.ecommerce.app.model.product.ProductAttributeDto)' with specify parameter 'class org.apache.ibatis.binding.MapperMethod$ParamMap'.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [productAttributeDto, param1]] with root cause
org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [productAttributeDto, param1]
        at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
        at org.apache.ibatis.builder.annotation.ProviderSqlSource.extractProviderMethodArguments(ProviderSqlSource.java:223)

我期望生成的 SQL 查询语句是:

SELECT *
FROM ec_product_variant AS pv 
INNER JOIN ec_attributes AS color ON color.id = pv.color AND color.value=? 
WHERE (pv.size IS NULL AND pv.product_id = ?)

在这里,Mybatis 在寻找 arg0 而不是 productAttributeDto。有谁能帮助解决这个问题?我在这里做错了什么?提前感谢您的帮助。

英文:

I am using Mybatis with the spring boot application. Where I used to generate MySql query using ProviderMethodResolver. My application supports mybatis annotation processor and XML processor.

To achieve that I used this Mybatis configuration:

In appication.properties file

mybatis.mapper-locations=classpath*:/repository/**/*Repository.xml

and a MybatisConfiguration.java

@Configuration
@EnableTransactionManagement
public class MybatisConfiguration {
@Bean
ConfigurationCustomizer mybatisConfigurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(true);
}
};
}
}

With above configuration, everything(@Select, @Insert, @ResultMap) seems working fine except implementation of @SelectProvider.

SelectProvider Implemetation is

VariantRepository.java

@Mapper
public interface VariantRepository {
@SelectProvider(type = VariantSqlProvider.class, method = &quot;getProductAttribute&quot;)
VariantDto findByProductAttribute(@Param(&quot;productAttributeDto&quot;) ProductAttributeDto productAttributeDto);
}

> I am using org.apache.ibatis.annotations.Param

ProductAttributeDto.java

@Data
public class ProductAttributeDto {
private Integer productId;
Map&lt;String, String&gt; attributes;
}

VariantSqlProvider.class

  public class VariantSqlProvider implements ProviderMethodResolver {
@SuppressWarnings(&quot;unused&quot;)
public static String getProductAttribute(final ProductAttributeDto productAttributeDto) {
return new SQL() {{
SELECT(&quot;*&quot;);
FROM(&quot;ec_product_variant AS pv&quot;);
if (Objects.nonNull(productAttributeDto.getAttributes())) {
for (Entry&lt;String, String&gt; entry : productAttributeDto.getAttributes().entrySet()) {
if(Objects.nonNull(entry.getValue())) {
INNER_JOIN(
new StringBuilder(&quot;ec_attributes AS &quot;)
.append(entry.getKey())
.append(&quot; ON &quot;)
.append(entry.getKey()).append(&quot;.id = pv.&quot;).append(entry.getKey())
.append(&quot; AND &quot;)
.append(entry.getKey()).append(&quot;.value=#{productAttributeDto.attributes.&quot;).append(entry.getKey())
.append(&quot;}&quot;).toString()
);
} else {
WHERE(&quot;pv.&quot; + entry.getKey() + &quot; IS NULL&quot;);
}
}
}
if(Objects.nonNull(productAttributeDto.getProductId())) {
WHERE(&quot;pv.product_id = #{productAttributeDto.productId}&quot;);
}
}}.toString();
}
}

When I invoke the findByProductAttribute method, I get an error like this

org.apache.juli.logging.DirectJDKLog: 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 &#39;public static java.lang.String com.ecommerce.app.repository.product.sqlprovider.VariantSqlProvider.getProductAttribute(com.ecommerce.app.model.product.ProductAttributeDto)&#39; with specify parameter &#39;class org.apache.ibatis.binding.MapperMethod$ParamMap&#39;.  Cause: org.apache.ibatis.binding.BindingException: Parameter &#39;arg0&#39; not found. Available parameters are [productAttributeDto, param1]] with root cause
org.apache.ibatis.binding.BindingException: Parameter &#39;arg0&#39; not found. Available parameters are [productAttributeDto, param1]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
at org.apache.ibatis.builder.annotation.ProviderSqlSource.extractProviderMethodArguments(ProviderSqlSource.java:223)

The SQL query I am expecting to generate is:

SELECT *
FROM ec_product_variant AS pv 
INNER JOIN ec_attributes AS color ON color.id = pv.color AND color.value=? 
WHERE (pv.size IS NULL AND pv.product_id = ?)

> The query is based on the attributes key-value pair
> in productAttributeDto

Here mybatis is looking for arg0 instead of productAttributeDto.
Can anyone help on this. What am I doing wrong here? Thanks in advance.

答案1

得分: 3

错误是由于映射方法和提供方法之间的参数名称不匹配造成的。

  • 映射方法的参数名由@Param注解指定,即productAttributeDto
  • 提供方法中未指定参数名,因此使用默认名称arg0

这些名称必须匹配。

两种解决方案。

  1. 在提供方法参数上添加@Param注解。
  2. 使用启用了-parameters编译器选项构建应用程序。

解决方案1非常简单。

public static String getProductAttribute(
  @Param("productAttributeDto") final ProductAttributeDto productAttributeDto) {

解决方案2取决于您构建应用程序的方式。

如果您使用Maven,可能需要在pom.xml中配置maven-compiler-plugin。例如:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <compilerArgs>
          <arg>-parameters</arg>
        </compilerArgs>
      </configuration>
    </plugin>

在使用IDE时,您可能需要配置构建设置。

  • Eclipse:https://stackoverflow.com/a/38198699/
  • IntelliJ IDEA:https://stackoverflow.com/a/39232302/

这样,MyBatis可以从方法签名中检索参数名称,因此您不需要使用@Param注解(尽管如果您愿意,仍然可以使用@Param注解)。

结果,映射方法可以更简单...

@SelectProvider(type = VariantSqlProvider.class, method = "getProductAttribute")
VariantDto findByProductAttribute(ProductAttributeDto productAttributeDto);

...并且您问题中的提供方法应该可以正常工作。

英文:

The error is caused by the parameter name mismatch between the mapper method and the provider method.

  • The param name of the mapper method is specified by @Param annotation i.e. productAttributeDto.
  • The param name is not specified in the provider method, so the default name arg0 is used.

These names must match.


There are two solutions.

  1. Add @Param annotation on the provider method parameter.
  2. Build your application with -parameters compiler option enabled.

The solution 1 is pretty straightforward.

public static String getProductAttribute(
  @Param(&quot;productAttributeDto&quot;) final ProductAttributeDto productAttributeDto) {

The solution 2 depends on how you build your application.

If you use Maven, you may need to configure maven-compiler-plugin in the pom.xml. e.g.

&lt;build&gt;
  &lt;plugins&gt;
    &lt;plugin&gt;
      &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
      &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
      &lt;configuration&gt;
        &lt;compilerArgs&gt;
          &lt;arg&gt;-parameters&lt;/arg&gt;
        &lt;/compilerArgs&gt;
      &lt;/configuration&gt;
    &lt;/plugin&gt;

When using IDE, you might need to configure the build setting.

This way, MyBatis can retrieve the parameter name from the method signature, so you don't need to use @Param annotation (you can still use @Param if you want, though).

As a result, the mapper method can be simpler...

@SelectProvider(type = VariantSqlProvider.class, method = &quot;getProductAttribute&quot;)
VariantDto findByProductAttribute(ProductAttributeDto productAttributeDto);

...and the provider method in your question should work as-is.

huangapple
  • 本文由 发表于 2020年8月16日 00:52:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63428575.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定