Spring AOP记录器方面的LazyInitializationException异常
我用Spring、JSF JPA和Hibernate构建了一个应用程序。Spring AOP记录器方面的LazyInitializationException异常,spring,jsf,jpa,Spring,Jsf,Jpa,我用Spring、JSF JPA和Hibernate构建了一个应用程序。 我有一个方面可以“监视”应用程序服务层中的每个更新*、创建*、删除*方法。此特性记录方法params,将apply toString应用于每个param,并将它们记录在数据库中。问题是,我在jsf中使用域对象,当我尝试更新*某些东西时,当toString()应用于方法param时,我会得到一个LazyInitializationException异常。 一种解决方案是从toString中删除所有表示其他对象的参数,但这样操
我有一个方面可以“监视”应用程序服务层中的每个更新*、创建*、删除*方法。此特性记录方法params,将apply toString应用于每个param,并将它们记录在数据库中。问题是,我在jsf中使用域对象,当我尝试更新*某些东西时,当toString()应用于方法param时,我会得到一个LazyInitializationException异常。
一种解决方案是从toString中删除所有表示其他对象的参数,但这样操作就没有意义了,因为我不记录我感兴趣的细节。
即,我有一个名为Price的实体,它有一个依赖项PriceList:
public class Price extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "price")
private Double price;
//bi-directional many-to-one association to TelCoPriceList
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "price_list_id")
private PriceList priceList;
.....................
}
价格被用于AddPriceForm.xhtml,这是jsf表单。JSF AddPriceMB引用了执行实际更新的PriceService。PriceService由以下方面“监控”:
@Aspect
@Named("auditLogAspectImpl")
public class AuditLogAspectImpl implements AuditLogAspect {
@Inject
private UserService userService;
@Inject
private AuditLogService auditLogService;
@Override
@AfterReturning(pointcut = "execution(* com.videanuadrian.core.impl.services..*.save*(..)) or execution(* com.videanuadrian.core.impl.services..*.update*(..)) or execution(* com.videanuadrian.core.impl.services..*.delete*(..))", returning = "retVal")
public boolean afterLogEvent(JoinPoint joinPoint,Object retVal) {
String className = joinPoint.getTarget().getClass().getName();
String methondName = joinPoint.getSignature().getName();
...................................................................
StringBuffer logMessage = new StringBuffer();
Object[] args = joinPoint.getArgs();
//for login action we only get the username and hash the password
if (methondName.compareTo("login") == 0){
logMessage.append(args[0]);
}else {
int argsLen = args.length;
//else log all the parameters
for (int i = 0; i < argsLen; i++) {
// this is where the exception occurs !!!!!!!!!!!!!!!!!!!!!!!!!!!
logMessage.append(args[i]).append(",");
}
if (argsLen > 0) {
logMessage.deleteCharAt(logMessage.length() - 1);
}
}
//some save/update methods return Boolean
Boolean status = false;
if (retVal instanceof Boolean){
status = (Boolean) retVal;
}
//some return the ID of the object inserted,and if these methods return an integer the status is true, if they return null, the status si false
if (retVal instanceof Integer){
if (retVal!=null)
status = true;
}
auditLogService.addAuditLogEvent(uid, className+"."+methondName, status,logMessage.toString());
return true;
}
@方面
@命名(“auditLogAspectImpl”)
公共类AuditLogAspectImpl实现AuditLogAspect{
@注入
私人用户服务;
@注入
私有AuditLogService AuditLogService;
@凌驾
@返回(pointcut=“execution(*com.videanuadrian.core.impl.services..*.save*(..)或执行(*com.videanuadrian.core.impl.services..*.update*(..)或执行(*com.videanuadrian.core.impl.services..*.delete*(..)后,返回=“retVal”)
公共布尔afterLogEvent(JoinPoint、JoinPoint、Object retVal){
字符串className=joinPoint.getTarget().getClass().getName();
字符串methondName=joinPoint.getSignature().getName();
...................................................................
StringBuffer logMessage=新的StringBuffer();
对象[]args=joinPoint.getArgs();
//对于登录操作,我们只获取用户名和散列密码
如果(methondName.compareTo(“登录”)==0{
logMessage.append(args[0]);
}否则{
int argsLen=args.length;
//否则,记录所有参数
对于(int i=0;i0){
logMessage.deleteCharAt(logMessage.length()-1);
}
}
//某些保存/更新方法返回布尔值
布尔状态=假;
if(返回布尔值的实例){
status=(布尔)retVal;
}
//有些方法返回插入对象的ID,如果这些方法返回一个整数,则状态为true,如果返回null,则状态为false
if(retVal instanceof Integer){
如果(retVal!=null)
状态=真;
}
addAuditLogEvent(uid,className+““+methodName,status,logMessage.toString());
返回true;
}
在以前的版本中,我没有遇到这个问题,因为我使用了DTO对象,DTO和域对象之间的转换是在服务层执行的。错误很明显,因为与我执行这个toString()操作的时间相比,我的会话早就关闭了。
你知道不使用openSessionInView或扩展持久性上下文如何实现这一点吗?或者我的方法不太好……不要使用AOP,而是为这类任务实现Hibernate事件侦听器。另请检查Hibernate Envers项目。好的,谢谢你的提示。我以前使用过Envers,但我正在寻找通用解决方案我明白。如果你真的想去AOP的方式,那么你也可以考虑不使用实体作为服务方法参数。你可以实现特殊的传输对象(例如,对于<代码>价格>代码>实体实现<代码> PrimeUpDebug < /代码>对象)。。这样您就不会有延迟初始化的问题。缺点是应用程序中的对象数量(以及这些对象之间所需的映射逻辑)增加了很多。:)一个月前,我做了这样的设置,每个DomainObject都由一个具有完全相同字段的DTO对象备份,并且在视图中我使用了该DTO。谢谢您的提示。