Property 'delegate' has no getter method 解决办法

我们使用json-lib包来用json转换hibernate持久化对象时候需要注意
如果持久化对象采用了延迟加载,或者持久化对象和另一持久化对象循环引用的时候,如何直接用
JSONArray jr = JSONArray.fromObject(Object);

这样的话 就会报错:java.lang.NoSuchMethodException: Property 'delegate' has no getter method

起初我被这个异常给迷惑了,以为就是简单的找不到 delegate变量的 getter方法。
实际上是由于采用了延迟加载或者循环引用引发的异常。
解决的办法如下:

新建两个类:HibernateJsonBeanProcessor和HibernateJsonBeanProcessorMatcher

HibernateJsonBeanProcessor代码如下:

import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.processors.*;

public class HibernateJsonBeanProcessor implements JsonBeanProcessor {
public JSONObject processBean(Object obj, JsonConfig jsonConfig) {
return new JSONObject();
}
}


HibernateJsonBeanProcessorMatcher代码如下:

import net.sf.json.processors.*;
import java.util.Set;
import org.apache.log4j.Logger;

public class HibernateJsonBeanProcessorMatcher extends JsonBeanProcessorMatcher {
private static Logger log = Logger
.getLogger(HibernateJsonBeanProcessorMatcher.class);

@Override
public Object getMatch(Class target, Set set) {

if (target.getName().contains("$$EnhancerByCGLIB$$")) {
log.warn("Found Lazy-References in Hibernate object "
+ target.getName());
return org.hibernate.proxy.HibernateProxy.class;
}
return DEFAULT.getMatch(target, set);
}
}


这两个类完成之后,我们用json转换持久化对象就变成了这样:

JsonConfig conf = new JsonConfig();
conf.registerJsonBeanProcessor(
org.hibernate.proxy.HibernateProxy.class,
new HibernateJsonBeanProcessor());
conf.setJsonBeanProcessorMatcher(new HibernateJsonBeanProcessorMatcher());
JSONArray jr = JSONArray.fromObject(device, conf);

这里这个device即为我们的持久化对象。

OK !经过这2步之后,程序运行正常!
<think>好的,用户现在遇到了一个错误:“There is no getter for property named 'delegate' in 'class com.sun.proxy.$Proxy167’”。我需要分析这个问题,找出可能的原因,并提供解决方案。 首先,这个错误通常出现在MyBatis或MyBatis Plus中,当尝试访问一个对象中不存在的属性时。用户提到的类名是com.sun.proxy.$Proxy167,这表明这是一个动态生成的代理类,可能是在使用MyBatis的拦截器或者AOP相关功能时创建的。 用户之前的问题是关于修改SQL中的sysdate为now(),所以可能他们在实现自定义拦截器时,使用了反射来修改BoundSql的sql字段。在之前的代码示例中,用户可能使用了反射来访问BoundSql的私有字段,比如通过getDeclaredField("sql")。但是,如果BoundSql实例实际上是一个代理对象,而不是原始的实现类,那么反射可能会失败,因为代理类可能没有该字段。 错误信息指出代理类没有名为'delegate'的getter方法。可能用户或他们在网上找到的示例代码试图通过访问代理对象的delegate属性来获取底层对象。例如,有些代码可能会用boundSql.getClass().getSuperclass()来获取原始类,或者尝试获取代理对象中的delegate字段来访问被包装的对象。但在这个情况下,代理类可能没有暴露这个属性,导致反射失败。 可能用户在他们的拦截器代码中使用了类似这样的代码: Field field = boundSql.getClass().getDeclaredField("sql"); 但实际boundSql是代理对象,没有sql字段,所以转而尝试访问delegate属性,但同样失败。 需要验证的是,BoundSql的实现类是否被代理,而代理类没有相应的字段。这时候可能需要通过不同的方式获取原始对象,比如通过反射访问代理对象的被代理实例,或者检查BoundSql的具体实现类。 解决方案可能包括: 1. 检查BoundSql的实际类型,如果是代理,则获取其被代理的对象。 2. 使用不同的方法获取sql字段,例如通过MetaObject工具类,这在MyBatis中更安全可靠。 3. 避免直接使用反射访问私有字段,转而使用MyBatis提供的工具方法,比如MetaObject来处理属性的访问。 另外,用户可能参考了网上的一些示例,这些示例直接修改BoundSql的sql字段,但在某些情况下,由于代理的存在,这种方法会失败。例如,MyBatis可能对某些对象进行了代理,导致反射无法直接访问字段。这时候,使用MetaObject可以更安全地处理这种情况,因为它能够处理代理对象,并访问其底层属性。 例如,使用MetaObject的forObject方法: MetaObject metaObject = SystemMetaObject.forObject(boundSql); metaObject.setValue("sql", modifiedSql); 这种方式不需要直接访问字段,而是通过MyBatis内部的机制来修改属性,避免了反射可能遇到的问题,特别是当对象被代理时。 总结用户的问题,他们的拦截器在处理BoundSql时,因为对象是代理类,导致反射获取字段失败。解决方法是改用MetaObject来设置sql的值,而不是直接通过反射操作字段。这样可以绕过代理的问题,更安全地修改SQL语句。</think>遇到错误 `There is no getter for property named 'delegate' in 'class com.sun.proxy.$Proxy167'`,通常是因为**反射操作试图访问代理对象中不存在的属性**。以下是问题分析和解决方案: --- ### **问题根源** 1. **动态代理机制**:MyBatis/MyBatis Plus 在某些场景下(如拦截器、AOP)会生成动态代理类(如 `$Proxy167`)。 2. **反射误操作**:尝试通过反射直接访问代理类中的 `delegate` 属性或字段,但代理类未暴露该属性。 3. **常见触发场景**:在自定义拦截器中,试图通过反射修改 `BoundSql` 的 `sql` 字段时,若 `BoundSql` 实例为代理对象,直接反射会失败。 --- ### **解决方案** #### **1. 使用 `MetaObject` 工具类(推荐)** 通过 MyBatis 提供的 `MetaObject` 安全操作对象属性,**无需直接反射**,且能穿透代理对象: ```java import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; // 替换反射代码: // Field field = boundSql.getClass().getDeclaredField("sql"); // field.setAccessible(true); // field.set(boundSql, modifiedSql); // 改用 MetaObject MetaObject metaObject = SystemMetaObject.forObject(boundSql); metaObject.setValue("sql", modifiedSql); ``` #### **2. 穿透代理对象(备选)** 若必须直接反射,需**获取原始被代理对象**后再操作: ```java // 获取原始对象(非代理对象) if (boundSql instanceof EnhancedInstance) { Object original = ((EnhancedInstance) boundSql).getOriginal(); Field field = original.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(original, modifiedSql); } ``` *注意:此方法依赖 MyBatis Plus 的 `EnhancedInstance` 接口,需确认版本兼容性。* --- ### **代码修正示例** 结合自定义拦截器的完整修改: ```java @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = invocation.getArgs()[1]; BoundSql boundSql = mappedStatement.getBoundSql(parameter); String originalSql = boundSql.getSql(); String modifiedSql = originalSql.replaceAll("(?i)sysdate", "now()"); // 使用 MetaObject 安全修改 SQL MetaObject metaObject = SystemMetaObject.forObject(boundSql); metaObject.setValue("sql", modifiedSql); return invocation.proceed(); } ``` --- ### **关键点** 1. **避免硬编码反射**:直接反射私有字段 (`sql`) 或假设代理对象结构(如 `delegate`)会导致兼容性问题。 2. **优先使用框架工具**:`MetaObject` 是 MyBatis 官方推荐的操作内部属性的方式,能自动处理代理对象。 3. **版本适配**:若使用 `EnhancedInstance`,需确保 MyBatis Plus 版本支持(通常 >= 3.4.0)。 --- ### **错误预防** - **单元测试**:拦截器修改后,需覆盖测试含 `sysdate` 的 SQL 场景。 - **日志监控**:在拦截器中添加日志输出,验证 SQL 替换是否生效: ```java System.out.println("Original SQL: " + originalSql); System.out.println("Modified SQL: " + modifiedSql); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值