2.spring与AOP

本文深入解析了AOP(面向切面编程)的概念及其实现方式,包括动态代理技术如JDK Proxy和Cglib的使用,以及如何在Spring框架中通过全XML、半XML半注解和纯注解的方式进行AOP配置。同时,文章还详细介绍了AOP的专业术语,如目标对象、代理对象、连接点、切入点等。
1. AOP
1. AOP概念:面向切面编程。底层通过封装动态代理技术实现。
2.动态代理:在不修改源码的情况下,实现了方法的增强。
3.使用AOP技术,关注2点:
  1).手动创建增强方法; 2).通过配置文件,告诉spring在哪些方法上使用增强方法。
1.1 动态代理
AOP面向切面编程的底层实现,AOP封装两种动态代理:

(1) proxy的动态代理(基于接口,jdk提供)
	AccountService accountService = new AccountServiceImpl();
	//使用JDK的Proxy类,创建代理增强类
	AccountService proxy = (AccountService)Proxy.newProxyInstance(
        //参数一:和目标对象一样的类加载器
    	accountService.getClass().getClassLoader(),
        //参数二:和目标对象一样的接口--数组
        new Class[] {accountService.getClass()},
        //参数三:实现Invocationhandler接口的增强类(外部类、内部类、lambada实现)     
        new Invocationhandler( 
            	// 参数一:proxy 一般不要用,为代码自己调用的代理对象。
                 public Object invoke(Proxy proxy,Method method,object[] args){
                     if("add".equals(method.getName())){
                         try(流对象){
                             //1.方法执行前增强
                             method.invoke(accountService,args);//执行原方法
                             //2.方法执行后增强 
                         }catch{
                             //3.异常增强
                         }finally{
                             //最终增强 
                         }            
                     }else{
                         method.invoke(accountService,args);//执行原方法
                     }
                     //返回原方法执行后的返回值;或者返回加工后的返回值. 
                     return null; 
                 } 	
             )
       );
    proxy.add();  // proxy可以接受调用后的返回值。

(2) Enhancer的动态代理(基于父类 cglib提供)
    1.导入Cglib的包; 2.目标类不能被final关键字修饰
    AccountService accountService = new AccountServiceImpl();
	//Cglib的工具类Enhancer,创建代理增强类
    AccountService proxy = (AccountService)Enhancer.create(
    	//参数一:目标类的字节码文件
    	accountService.getClass(),
    	//参数二:增强类
    	new MethodInterceptor(
      	//参数三:methodProxy 用不到,方法的代理对象,内部自己调用
   		public object intercept(
            Object Proxy,Method method,object[] arg,MethodProxy methodProxy){
             if("add".equals(method.getName())){
                 try(流对象){
                     //1.方法执行前增强
                     method.invoke(accountService,args);//执行原方法
                     //2.方法执行后增强 
                 }catch{
                     //3.异常增强
                 }finally{
                     //4.最终增强 
                 }            
             }else{
                 method.invoke(accountService,args); //执行原方法
             }
          	return null; 
      } 
  );
  enhancer.add();

  基于接口的Proxy动态代理,与目标类是兄弟关系;
  基于父类的Enhancer动态代理,与目标类是父子关系,目标类为父类;
2. 基于全XML的AOP配置
实现步骤:
1.创建Maven工程,导入坐标。(IOC容器坐标、AOP坐标)
2.创建创建增强类,创建增强方法。创建被增强类,被增强方法。(不加注解,全XML的bean配置)               
3.创建spring配置文件:配置通知、切入点。

  // 1.配置被增强类
  <bean id="AccountService" class="cn.itcast.service.impl.AccountServiceImpl">
  // 2.配置增强类
  <bean id="logger" class="cn.itcast.untils.Logger">
  // 3.AOP配置实现
  <aop:config>
  	<aop:pointcut id="pt" expresition="excution( * cn.itcast..save(..))"/> 
  	<aop:aspect ref="logger"> 
  		//前置通知
  	 	<aop:before method="before" pointcut-ref="pt"></aop:before>
  	 	//后置通知
  	 	<aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
  	 	//异常通知
  	 	<aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
  	 	//最终通知
  	 	<aop:after method="after" pointcut-ref="pt"></aop:after>
  	 	//注意: 最终通知和后置通知,是按照配置的上下顺序执行的,想要固定顺序,可使用环绕通知。
  	 	//      环绕通知不能跟上边通知一起使用,环绕通知固定执行顺序。
  	 	//环绕通知
  	 	<aop:around method="around" pointcut-ref="pt"></aop:around>
     </aop:aspect>
  </aop:config>

环绕通知的增强方法:
    public void around(ProceedingJoinPoint joinPoint){
        try {
            // 前置
            System.out.println("前置通知(之前增强)");
            // 原方法调用
            joinPoint.proceed(); //inboke
            // 后置
            System.out.println("后置通知(之后增强)");
        }catch (Throwable throwable){
            // 异常
            System.out.println("异常通知(异常增强)");
        }finally {
            // 最终
            System.out.println("最终通知(最终增强)");
        }
    }
      
      
表达式语法:excution( * cn.itcast..AccountImpl.save(..))
      最前边的*: 代表任意的返回值,public 省略没写。
      .. 可以代替包名,代表任意包以及其子包
      .. 可以代表参数,代表任意参数
      *  可以代表包,代表任意包;可以代表类,任意类;代表方法名,任意方法名。
3. 基于半XML半注解的AOP配置
实现步骤:
1.创建Maven工程,导入坐标。(IOC容器坐标、AOP坐标)
2.创建增强类,创建增强方法。创建被增强类,被增强方法。(加注解,添加注解扫描context:component实现) 
3.创建spring配置文件,配置IOC容器扫描,AOP开启注解。
<context:component-scan base-package="cn.itcast"></context:component-scan >
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>  //开启aop注解。
    
切面类注解(增强类):
类上:@Component(“id”) @Ascept

切面类方法上:
//抽取切入点表达式:1.创建一个无参数、无返回值的方法;2.使用@Pointcut("表达式");3增强方法调用抽取方法。
@pointcut("execution(* cn.itcast..save(..))")
Public void pointcut(){} 
@Before("pointcut()")  //前置通知 
@After-returning("pointcut()") //后置通知
@After-throwing("pointcut()")  //异常通知
@after("pointcut()")  //最终通知

@Around("pointcut()") //环绕通知
               
4. 基于纯注解的AOP配置
实现步骤:
与3实现步骤几乎相同,只是想spring配置文件,换做配置类即可。测试的时候,加载配置类。

3.创建配置类SpringConfig类,添加注解实现配置文件中,以下配置。
<context:component-scan base-package="cn.itcast"></context:component-scan >
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>  //开启aop注解使用
@Configuration //实现配置类
@ComponentScan //实现注解扫描
@EnableAspectJAutoProxy //开启AOP注解使用    
public class SpringConfig{}  

测试时,注解修改:
@Runwith(SpringJunit4ClassRunner.calss)
@ContextConfigration(classes=SpringConfig.class)
//@ContextConfigration(locations="classpath:application.xml")    
public class SpringJunit{ }
4.AOP专业术语–面试题
目标对象--Target:   要增强的对象
代理对象--Proxy :   增强后的代理对象
连接点--Joinpoint:  目标对象的所有方法
切入点--Pointcut:   目标对象中要被增强的方法(在增强类中配置)
通知--Advice:       自己写的增强方法

前置通知:在目标方法之前进行的增强方法
后置通知:在目标方法之后进行的增强方法
异常通知:在目标方法异常之时的增强方法
最终通知:在目标方法增强之后,一定会执行的方法

环绕通知:能够代替上面4Aspect(切面):  切入点+通知=切面 --> 被增强方法+增强方法==切面
Weaving(织入): 将切入点集成到切面的这一个过程,我们称为织入 -->目标方法+增强方法的合成过程==织入过程
ion*****#*#*****qlSession*****#*#*****[mybatis-spring-2.1.2.jar:2.1.2] ... 144 more 2025-10-21 11:06:16 [WebContainer : [yWX*235][]-D200116823-169-T:DFT-6ea2f057-adb8-4258-84df-d8f553022590] ERROR ExceptionHandler:243 - Exception when request uri:/services/workorderFullsetService/getWorkorderFullsetListByPage/page/30/1 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'locationCodeList' not found. Available parameters are [condition, param1] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:97) ~[mybatis-spring-2.1.2.jar:2.1.2] at org.mybatis.spring.SqlSession*****#*#*****Session*****#*#*****qlSession*****#*#*****[mybatis-spring-2.1.2.jar:2.1.2] at com.sun.proxy.$Proxy249.selectList(Unknown Source) ~[?:?] at org.mybatis.spring.SqlSession*****#*#*****SqlSession*****#*#*****[mybatis-spring-2.1.2.jar:2.1.2] at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147) ~[mybatis-3.5.13.jar:3.5.13] at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80) ~[mybatis-3.5.13.jar:3.5.13] at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:142) ~[mybatis-3.5.13.jar:3.5.13] at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.13.jar:3.5.13] at com.sun.proxy.$Proxy357.getWorkorderFullsetList(Unknown Source) ~[?:?] at com.huawei.it.tgmes.tmas.wavejuxin.service.impl.WorkorderFullsetServiceImpl.getWorkorderFullsetListByPage(WorkorderFullsetServiceImpl.java:152) ~[classes/:?] at com.huawei.it.tgmes.tmas.wavejuxin.service.impl.WorkorderFullsetServiceImpl$$FastClassBySpringCGLIB$$44870253.invoke(<generated>) ~[classes/:?] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:792) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:58) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:58) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.39-h2.jar:5.3.39-h2] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.39-h2.jar:5.3.39-h2] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at com.huawei.it.jalor5.security.service.impl.internal.SecurityInterceptor.invoke(SecurityInterceptor.java:117) ~[jalor-auth*****#*#*****LEASE.jar:6.6.8.0.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at com.huawei.it.jalor5.security.service.impl.internal.SecurityInterceptor.invoke(SecurityInterceptor.java:117) ~[jalor-auth*****#*#*****LEASE.jar:6.6.8.0.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:707) ~[spring-aop-5.3.39-h2.jar:5.3.39-h2] at com.huawei.it.tgmes.tmas.wavejuxin.service.impl.WorkorderFullsetServiceImpl$$EnhancerBySpringCGLIB$$c90edfe4.getWorkorderFullsetListByPage(<generated>) ~[classes/:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_332] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_332] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_332] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_332] at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[cxf-core-3.5.11.jar:3.5.11] at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[cxf-core-3.5.11.jar:3.5.11] at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[cxf-rt-frontend-jaxrs-3.5.11.jar:3.5.11] at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[cxf-rt-frontend-jaxrs-3.5.11.jar:3.5.11] at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[cxf-core-3.5
最新发布
10-22
<think>我们注意到新的异常堆栈信息显示: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'locationCodeList' not found. Available parameters are [condition, param1] 这个错误表示在Mapper方法中,尝试使用一个名为'locationCodeList'的参数,但是MyBatis在参数列表中找不到这个参数。可用的参数是[condition, param1]。 原因分析: 1. 在Mapper接口的方法定义中,可能没有使用@Param注解来明确指定参数名称,导致MyBatis无法识别参数名。 2. 在XML映射文件中,引用了错误的参数名。 解决方案: 1. 检查Mapper接口方法的参数,如果只有一个参数,MyBatis默认会使用该参数本身(不需要指定名字),但如果有多个参数,则必须使用@Param注解。 2. 在XML中引用参数时,需要确保使用的参数名Mapper接口方法中@Param注解指定的名称一致。 例如,假设Mapper接口方法如下: List<Workorder> getWorkorderFullsetListByPage(@Param("condition") WorkorderCondition condition, @Param("locationCodeList") List<String> locationCodeList); 那么在XML中,应该使用`#{condition.property}`和`#{locationCodeList}`来引用这两个参数。 如果方法定义是: List<Workorder> getWorkorderFullsetListByPage(WorkorderCondition condition, List<String> locationCodeList); 由于没有使用@Param注解,MyBatis会默认使用参数索引(param1, param2)来引用参数。因此,在XML中应该使用`#{param1.property}`和`#{param2}`。 但是,从错误信息来看,可用的参数是[condition, param1],这说明: - 有一个参数被命名为'condition'(可能是通过@Param注解指定的) - 另一个参数可能是按索引访问(param1),但实际上这里有两个参数,第二个参数应该可以用param2访问?为什么这里只有param1? 这里可能需要更仔细的分析。实际上,错误信息显示可用的参数只有两个:condition和param1。这很奇怪,因为通常每个参数都会有一个名字(如果有@Param)或者按索引(param1, param2,...)。 可能的情况是: - 方法只有一个参数,名字叫做condition(通过@Param指定),但我们在XML中却引用了locationCodeList,所以找不到。 因此,我们需要检查Mapper接口方法的定义。 假设实际方法定义是: List<Workorder> getWorkorderFullsetListByPage(@Param("condition") WorkorderCondition condition); 但是在XML中却使用了`locationCodeList`,那么就会报错。 或者,方法有两个参数,但第二个参数没有用@Param注解,那么它应该被叫做param2(索引从1开始,第一个是param1,第二个是param2)。但是错误信息中只有condition和param1,没有param2。所以可能方法只有一个参数,即condition。 所以,请检查: 1. Mapper接口方法参数列表。 2. XML中引用参数的地方。 修正建议: 情况1:如果方法只有一个参数(condition),但XML中使用了locationCodeList,则修正XML,使用condition中的属性,例如`condition.locationCodeList`(如果condition对象中包含locationCodeList属性)或者移除对locationCodeList的引用。 情况2:如果方法确实有两个参数,那么需要为第二个参数添加@Param注解: List<Workorder> getWorkorderFullsetListByPage(@Param("condition") WorkorderCondition condition, @Param("locationCodeList") List<String> locationCodeList); 然后在XML中,使用`#{locationCodeList}`引用第二个参数。 情况3:如果方法有两个参数但没有使用@Param,那么在XML中应该使用: #{param1.property} 和 #{param2} 但是错误信息中可用的参数是[condition, param1],这表明第一个参数被命名为condition(可能是通过@Param),第二个参数没有被命名(或者被命名为param1?这不太可能,因为param1已经被占用了)。实际上,当使用@Param命名一个参数后,另一个参数如果没有命名,那么它应该按索引命名(比如第二个参数是param2)。所以这里可能是MyBatis的一个内部表示。 因此,为了清晰起见,强烈建议为每个参数都使用@Param注解。 总结步骤: 1. 确认Mapper接口方法的参数个数和名称。 2. 为每个参数添加@Param注解,指定明确的名称(如果还没有的话)。 3. 在XML映射文件中,确保引用的参数名称@Param注解中指定的名称一致。 示例修正代码: Mapper接口: public interface WorkorderMapper { List<Workorder> getWorkorderFullsetListByPage( @Param("condition") WorkorderCondition condition, @Param("locationCodeList") List<String> locationCodeList); } XML映射文件: <select id="getWorkorderFullsetListByPage" resultType="Workorder"> SELECT * FROM work_order WHERE location_code IN <foreach item="item" collection="locationCodeList" open="(" separator="," close=")"> #{item} </foreach> ...其他条件(使用condition中的属性)... </select> 注意:foreach中的collection属性必须指定为@Param注解的名称,即"locationCodeList"。 这样修正后,参数'locationCodeList'就能被正确识别了。 §§相关问题§§ 1. 在MyBatis中,当方法有多个参数但没有使用@Param注解时,默认的参数名是如何生成的? 2. 如何避免在MyBatis的XML映射文件中引用参数时出现参数名错误的问题? 3. 使用@Param注解对MyBatis的性能是否有影响?为什么? 4. 如果Mapper接口方法的参数是一个Map,那么在XML中应该如何引用其中的值? 5. 在MyBatis的动态SQL中,如何判断一个集合参数是否为空且长度大于0? </think>### MyBatis参数绑定异常分析解决方法 #### 核心问题诊断 错误信息明确指出: ``` Parameter 'locationCodeList' not found. Available parameters are [condition, param1] ``` 这表明MyBatis在解析SQL参数时无法找到名为`locationCodeList`的参数,系统可识别参数只有`condition`和`param1`。 #### 常见原因分析 1. **参数命名问题** - 接口方法参数未使用`@Param`注解明确命名 - XML中引用的参数名方法参数名不一致 2. **参数传递方式错误** - 使用Map或对象包装参数时属性名错误 - 基本类型参数未使用`@Param` 3. **动态SQL处理问题** - `<foreach>`等标签的collection属性值错误 - 参数作用域问题(嵌套结构内参数不可见) #### 完整解决方案 **1. 修正Mapper接口方法** 添加`@Param`注解明确参数名: ```java // 修改前 List<Workorder> getWorkorderFullsetListByPage( WorkorderCondition condition, List<String> locationCodeList); // 修改后 List<Workorder> getWorkorderFullsetListByPage( @Param("condition") WorkorderCondition condition, @Param("locationCodeList") List<String> locationCodeList); ``` **2. 修正XML映射文件** 确保动态SQL引用正确的参数名: ```xml <!-- 修改前 --> <foreach collection="locationCodeList" item="code" separator=","> #{code} </foreach> <!-- 修改后 --> <foreach collection="locationCodeList" item="code" open="(" close=")" separator=","> #{code} </foreach> ``` **3. 参数集合类型特殊处理** 若参数是复杂对象内的集合: ```xml <if test="condition.locationCodes != null"> <foreach collection="condition.locationCodes" item="code"> #{code} </foreach> </if> ``` **4. 参数存在性检查** 添加安全的空值检查: ```xml <if test="locationCodeList != null and !locationCodeList.isEmpty()"> AND location_code IN <foreach collection="locationCodeList" item="code" open="(" close=")" separator=","> #{code} </foreach> </if> ``` #### 验证步骤 1. **编译检查**:确保接口和XML修改后重新编译 2. **单元测试**: ```java @Test void testLocationCodeParameter() { List<String> codes = Arrays.asList("LOC001", "LOC002"); mapper.getWorkorderFullsetListByPage(new Condition(), codes); // 应正常运行 } ``` 3. **SQL日志验证**: 期望输出SQL片段: ```sql AND location_code IN ('LOC001','LOC002') ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值