buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

spring mybatis upgrade to mybatisplus 实战小记

我司压箱底儿的税地系统,系统框架是spring,持久层是mybatis。最近,将Mybatisplus集成到系统中,以提高开发效率。

升级版本:

  • mybatis版本3.2.2,升级到3.5.16
  • Mybatisplus版本:3.5.3
  • mybatis-spring版本1.2.0,升级到3.0.0
  • pagehelper版本:5.3.1

【注】mybatis官方提供了Mybatis-Plus SpringMVC Demo:https://gitee.com/baomidou/mybatisplus-spring-mvc.git


本文整理升级过程中踩过的坑。

java.lang.IllegalArgumentException: invalid comparison: com.levy.enums.BillFormTypeEnum and java.lang.String

grep 'invalid comparison' /www/epaysch/tomcat-web-8180/logs/catalina.2024-10-17.out -c

sqlmapper文件 TInvoiceConfigChangeFlowMapper.xml 中有如下insert定义:

<insert id="saveFlow" parameterType="com.levy.entity.InvoiceConfigChangeFlow">
    <selectKey resultType="java.lang.Long" order="BEFORE" keyProperty="id">
        SELECT SEQ_INVOICE_CONFIG_CHANGE_FLOW.Nextval FROM dual
    </selectKey>
    insert into T_INVOICE_CONFIG_CHANGE_FLOW
    <trim prefix="(" suffix=")" suffixOverrides=",">
        ID,
        <if test="changeFormType != null and changeFormType!=''">
            CHANGE_FORM_TYPE,
        </if>
        ...
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
        #{id,jdbcType=DECIMAL},
        <if test="changeFormType != null and changeFormType != ''">
            #{changeFormType,jdbcType=VARCHAR},
        </if>
        ...
    </trim>
</insert>

其中,entity属性InvoiceConfigChangeFlow#changeFormType 是枚举类型。

@Data
public class InvoiceConfigChangeFlow implements Serializable {
    /**
     * 主键id
     */
    private Long id;

    /**
     * 发票形式类型,
     * BillFormTypeEnum:PAPER(纸票) ELECTRONIC(电票)
     */
    private BillFormTypeEnum changeFormType;
    。。。
}

注意sql语句中的 changeFormType != ''。 它将enum与String做比较。这在 mybatis-3.2.2 版本没有问题。而在升级后的版本 mybatis-3.5.16 却不兼容了。就是说,这个版本发现 将enum数据与字符串去比较,就抛出类型不匹配的异常:java.lang.IllegalArgumentException: invalid comparison: com.levy.enums.BillFormTypeEnum and java.lang.String

详细异常信息:

### Error updating database.  Cause: java.lang.IllegalArgumentException: invalid comparison: com.levy.enums.BillFormTypeEnum and java.lang.String
### The error may exist in URL [jar:file:/www/epaysch/tomcat-web-8180/webapps/BossMgr/WEB-INF/lib/tax-pay-ext-common-service-1.0-SNAPSHOT.jar!/mapper/InvoiceConfigChangeFlowMapper.xml]
### The error may involve com.levy.dao.InvoiceConfigChangeFlowDAO.saveFlow

解决办法自然是,去掉这段sql里的 changeFormType != ''。

那么,细究一下,为什么高版本的mybatis却不向下兼容这个“特性”了呢? 我认为,mybatis团队也是推崇开发者编写规范的代码。因此,我也不打算花时间研究让高版本的mybatis-3.5.16 来支持这一点。

Bean named 'xxx' is expected to be of type 'xxx' but was actually of type 'jdk.proxy2.$ProxyN'

继承了Mybatisplus.ServiceImpl的Manager类或Service类,当在这些子class中定义方法时,会抛异常:org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'xxx' is expected to be of type 'xxx' but was actually of type 'jdk.proxy2.$Proxy24'

例如: Bean named 'userManager' is expected to be of type 'com.baomidou.springmvc.service.system.UserManager' but was actually of type 'com.sun.proxy.$Proxy27'

解决办法:xml配置文件中设置proxy-target-class=true

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

Only one AsyncAnnotationBeanPostProcessor may exist within the context.

Configuration problem: Only one AsyncAnnotationBeanPostProcessor may exist within the context.

去配置文件中找 , 某个配置文件被引用了两次以上.移除后保留一个即可.如下即可产生上述问题

<import resource="classpath:testContext-currentproduct.xml" />
<import resource="classpath:testContext-currentproduct.xml" />
<import resource="classpath:testContext-elitecurrentproduct.xml" />

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.levy.dao.TBankBillFileDAO.listNotFinalStateByTimeRange

配置正确的mapperLocations。如<property name="mapperLocations" value="classpath*:mapper/*.xml"/>

MybatisSqlSessionFactoryBean#mapperLocations:

  /**
   * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration
   * at runtime.
   * <p>
   * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an MyBatis config file. This property being
   * based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.
   * "classpath*:sqlmap/*-mapper.xml".
   *
   * @param mapperLocations location of MyBatis mapper files
   */
  public void setMapperLocations(Resource... mapperLocations) {
      this.mapperLocations = mapperLocations;
  }

java.lang.NoClassDefFoundError: net/sf/jsqlparser/statement/select/SelectBody

java.lang.InstantiationError: net.sf.jsqlparser.statement.select.SelectItem

com.github.pagehelper:pagehelper:jar:5.3.1

com.github.jsqlparser:jsqlparser:jar:4.2

SelectItem类在jsqlparser-4.2.jar里。完整类名:net.sf.jsqlparser.statement.select.SelectItem


java.lang.IllegalStateException: Failed to load ApplicationContext

 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
 at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
 at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
 at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
 at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
 at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
 at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
 at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
 at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mybatisPlusInterceptor' defined in com.yft.mybatisplus.MybatisPlusConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor]: Factory method 'mybatisPlusInterceptor' threw exception; nested exception is java.lang.InstantiationError: net.sf.jsqlparser.statement.select.SelectItem
 at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)
 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)
 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
 at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:108)
 at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251)
 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
 ... 26 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor]: Factory method 'mybatisPlusInterceptor' threw exception; nested exception is java.lang.InstantiationError: net.sf.jsqlparser.statement.select.SelectItem
 at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
 at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
 ... 43 more
Caused by: java.lang.InstantiationError: net.sf.jsqlparser.statement.select.SelectItem
 at com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor.<clinit>(PaginationInnerInterceptor.java:69)
 at com.yft.mybatisplus.MybatisPlusConfig.mybatisPlusInterceptor(MybatisPlusConfig.java:27)
 at com.yft.mybatisplus.MybatisPlusConfig$$EnhancerBySpringCGLIB$$7cd6f94a.CGLIB$mybatisPlusInterceptor$0(<generated>)
 at com.yft.mybatisplus.MybatisPlusConfig$$EnhancerBySpringCGLIB$$7cd6f94a$$FastClassBySpringCGLIB$$1ebe875d.invoke(<generated>)
 at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
 at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:356)
 at com.yft.mybatisplus.MybatisPlusConfig$$EnhancerBySpringCGLIB$$7cd6f94a.mybatisPlusInterceptor(<generated>)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
 ... 44 more

完搭如下:

[INFO] +- com.baomidou:mybatis-plus:jar:3.5.3.1:compile
[INFO] |  \- com.baomidou:mybatis-plus-extension:jar:3.5.3.1:compile
[INFO] |     \- com.baomidou:mybatis-plus-core:jar:3.5.3.1:compile
[INFO] |        +- com.baomidou:mybatis-plus-annotation:jar:3.5.3.1:compile
[INFO] |        \- org.mybatis:mybatis:jar:3.5.16:compile
[INFO] +- org.mybatis:mybatis-spring:jar:3.0.0:compile
[INFO] +- com.github.pagehelper:pagehelper:jar:5.3.1:compile
[INFO] |  \- com.github.jsqlparser:jsqlparser:jar:4.2:compile

嗯,从上面dependency tree(依赖树)可以看到,pagehelper:jar:5.3.1 依赖了jsqlparser:jar:4.2。 见 pagehelper-5.3.1.pom

        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.2</version>
        </dependency>

经测:mybatis-plus-3.5.3.1 或者 mybatis-plus-3.5.3 均可。看来同一版本的小迭代在这一点是相同的。

而 mybatis-plus-3.5.7 则显式引入了 com.github.jsqlparser:jar:4.9。见 mybatis-plus-3.5.7.pom

    <dependency>
      <groupId>com.github.jsqlparser</groupId>
      <artifactId>jsqlparser</artifactId>
      <version>4.9</version>
      <scope>compile</scope>
    </dependency>

如果项目要使用 mybatis-plus-3.5.7,即使排除了jsqlparser,运行程序依然报上面错误:java.lang.InstantiationError: net.sf.jsqlparser.statement.select.SelectItem。待进一步研究。

来了!同时支持mybatis-plus原生分页 和 github.pageHelper分页

方式一:xml配置

<property name="plugins">
    <array>
        <bean class="com.github.pagehelper.PageInterceptor">
            <property name="properties">
                <value>
                    helperDialect=oracle
                </value>
            </property>
        </bean>
        <bean class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
            <property name="interceptors">
                <list>
                    <bean class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor">
                        <property name="maxLimit" value="2"></property>
                    </bean>
                </list>
            </property>
        </bean>
    </array>
</property>

方式二:xml配置结合@Bean

<property name="plugins" ref="mybatisPlusInterceptors">
@Bean
public List<Interceptor> mybatisPlusInterceptors() {

    MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
    mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor() {{
        setMaxLimit(2L);
    }});

    PageInterceptor pageInterceptor = new PageInterceptor();
    Properties properties = new Properties();
    properties.setProperty("helperDialect", "oracle");
    pageInterceptor.setProperties(properties);

    return Arrays.asList(mybatisPlusInterceptor, pageInterceptor);
}

posted on 2024-10-22 09:27  buguge  阅读(139)  评论(1编辑  收藏  举报