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 "<sqlmapper>" 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);
}
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/18490328