`
yiyanwan77
  • 浏览: 182921 次
  • 性别: Icon_minigender_1
  • 来自: 威海
社区版块
存档分类
最新评论

aop解决flex blazeds+jpa(hibernate)延迟加载抛出session关闭异常的问题

阅读更多
  • 问题简单描述

项目中后台使用hibernate实现的jpa作为持久层,当blazeds解析返回数据包含未加载项时,触发了延迟加载动作,而此时事物已经结束,于是抛出session关闭异常。

  • 解决思路

后台通过jpa返回的数据结果其实为动态代理创建的该类型的子类,调用这些对象的get方法时,动态代理子类的包装壳判断是否加载了数据(通过查看hibernate源码,可以得知有个initialized属性标记),如果没有,则查询该项。因此,只要避免转换时触发加载动作就可以解决该问题,简单想来有以下几种方案:

  1. aop切到最外的service层,得到返回结果,递归遍历属性,判断是否加载了属性(基本属性不存在这个问题)-注意,即使属性未加载,该属性值并不为空,而是一个代理对象),如果没有加载则想办法使其返回空(考虑过使用动态代理的方式,未能解决)
  2. 还是用aop,得到返回结果后,创建新的实例返回,依然要判断是否加载,如果已加载则返回真实值 (因为得到的对象为动态代理类,在对象为集合的时候获取实际类有点困难,我暂未能获知该途径,若有知情人,还望不吝赐教)
  3. 上面两种方案都需要增加递归遍历过程,对性能时有影响的,特别时第二种,所有属性都要重新创建,开销比较大。因此最佳方案应该时直接修改blazeds的源码,返回结果处理时,再用上述第一种方案的思路。
  • 实现

因为项目时间上的压力,考虑从第一种方案入手,在实际解决的过程中仍然遇到了重重困难,无数次断点调试后发现了了如下规律:如果返回值为集合,则获取到的类名为PersistenceSet(实际上并非所有集合都是用Set代理 ),而如果为单个对象则类名中含有_$$_javasis..等内容(该规则与动态代理的实现有关),后来查看hibernate源码,发现代理其实为PersistentCollection或HibernateProxy接口的实现类,大部分问题都解决了,但是最后遇到子项加载父项时,如果直接置空属性,则将更新(此时session已经关闭,但实际上更新确成功了,这个现象一直没弄明白 ),尝试用动态代理解决,没有成功。最后,迫于项目时间压力,暂时再该期使用了第二种实现方式:

 

org.hibernate.collection.*

PersistentSet extends AbstractPersistentCollection implements java.util.Set

AbstractPersistentCollection implements Serializable, PersistentCollection

PersistentArrayHolder extends AbstractPersistentCollection

PersistentBag extends AbstractPersistentCollection implements List

PersistentElementHolder extends AbstractPersistentCollection

PersistentIdentifierBag extends AbstractPersistentCollection implements List

PersistentIndexedElementHolder extends AbstractPersistentCollection

PersistentList extends AbstractPersistentCollection implements List

PersistentListElementHolder extends PersistentIndexedElementHolder

PersistentMap extends AbstractPersistentCollection implements Map

PersistentMapElementHolder extends PersistentIndexedElementHolder

PersistentSet extends AbstractPersistentCollection implements java.util.Set

PersistentSortedMap extends PersistentMap implements SortedMap

PersistentSortedSet extends PersistentSet implements SortedSet

 

 

LazyAspect.java

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.proxy.HibernateProxy;


/**
 * 解决使用延迟加载配置,且service层返回时该项未加载时,blazeds抛出session关闭的问题
 * 
 * @author 王逸群
 * @date 2011-10-8
 */
public class LazyAspect {

	private static Logger log = Logger.getLogger(LazyAspect.class);

	public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
		try {
			Object retVal = pjp.proceed();
			Object safeObject = safeCopy(retVal);
			return safeObject;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}

	}

	/**
	 * 不触发延迟加载的深度拷贝
	 * 
	 * @param obj
	 *            拷贝对象
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws InstantiationException
	 */
	public Object safeCopy(Object obj) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException,
			InstantiationException {
		return _safeCopy(obj, new HashMap<Object, Object>());
	}

	/**
	 * 不触发延迟加载的深度拷贝
	 * 
	 * @param obj
	 *            拷贝对象
	 * @param copiedMap
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws InstantiationException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Object _safeCopy(Object obj, Map<Object, Object> copiedMap)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException, InstantiationException {

		// 基本类型不可能为代理,直接返回
		if (isPrimitive(obj)) {
			return obj;
		}

		// 如果已经解析,直接从map中获取
		if (copiedMap.containsKey(obj)) {
			return copiedMap.get(obj);
		}

		Class clazz = obj.getClass();
		

		// Collection对象
		if (obj instanceof Collection) {
			Collection $collection = null;

			// PersistentCollection 代理
			if (obj instanceof PersistentCollection) {
				PersistentCollection $persistentCollection = (PersistentCollection) obj;

				// 已经加载字段
				if ($persistentCollection.wasInitialized()) {
					if ($persistentCollection instanceof Set) {
						//暂无法获取集合实际类
						$collection = new HashSet();
					} else if ($persistentCollection instanceof List) {
						//暂无法获取集合实际类
						$collection = new ArrayList();
					}
				}

				// 未加载
				else {
					copiedMap.put(obj, null);
					return null;
				}
			}

			// 非PersistentCollection代理集合
			else {
				$collection = (Collection) obj.getClass().newInstance();
			}
			Iterator itr = ((Collection) obj).iterator();
			if (itr != null)
				while (itr.hasNext()) {
					$collection.add(_safeCopy(itr.next(), copiedMap));
				}
			copiedMap.put(obj, $collection);
			return $collection;
		}

		// Map
		else if (obj instanceof Map) {
			Map $map = null;
			Map map = (Map) obj;

			// 代理
			if (obj instanceof PersistentCollection) {
				PersistentCollection $persistentCollection = (PersistentCollection) obj;
				// 已加载
				if ($persistentCollection.wasInitialized()) {
					//暂无法获取集合实际类
					$map = new HashMap();
				}
				// 未加载
				else {
					copiedMap.put(obj, null);
					return null;
				}
			}
			// 非代理对象
			else {
				$map = (Map) obj.getClass().newInstance();
			}

			for (Object ele : map.keySet()) {
				$map.put(ele, _safeCopy(map.get(ele), copiedMap));
			}
			copiedMap.put(obj, $map);
			return $map;
		}

		// 数组
		else if (clazz.isArray()) {
			int len = Array.getLength(obj);

			if (len > 0) {

				String arrClassName=clazz.getName();
				String className=arrClassName.substring(arrClassName.indexOf("L")+1,arrClassName.length()-1);
				
				// 注意,这里创建的实例非Array
				Object $arr = Array.newInstance(Class.forName(className), len);
				 
				for (int i = 0; i < len; i++) {
					Array.set($arr, i, _safeCopy(Array.get(obj, i), copiedMap));
				}
				copiedMap.put(obj, $arr);
				return $arr;
			} else {
				copiedMap.put(obj, null);
				return null;
			}

		}
		// 代理
		if (obj instanceof HibernateProxy) {
			HibernateProxy proxy = (HibernateProxy) obj;
			// 已加载
			if (!proxy.getHibernateLazyInitializer().isUninitialized()) {
				Object $obj = _safeCopy(proxy.getHibernateLazyInitializer()
						.getImplementation(), copiedMap);
				copiedMap.put(obj, $obj);
				return $obj;
			}
			// 未加载
			else {
				copiedMap.put(obj, null);
				return null;
			}
		}

		// 普通类
		else {

			Object $obj = obj.getClass().newInstance();
			copiedMap.put(obj, $obj);

			// 解析属性
			Field[] fields = obj.getClass().getDeclaredFields();
			for (Field field : fields) {
				String subFieldName = field.getName();
				Object fieldValue = null;
				
				// 类中可能有不可读属性(指非标准setter,getter访问方式),比如public static
				if(PropertyUtils.isReadable(obj, subFieldName)){
					fieldValue = PropertyUtils.getProperty(obj, subFieldName);
				}
				// 属性值为空或者无法读取的不处理
				if (fieldValue != null) {
					PropertyUtils.setProperty($obj, subFieldName,
							_safeCopy(fieldValue, copiedMap));
				}
			}

			return $obj;
		}
	}

	/**
	 * 是否为基本类型,注意,与jdk api中的isPrimitive规则不同
	 * 
	 * @param obj
	 * @return
	 */
	public boolean isPrimitive(Object obj) {
		if (obj == null) {
			return true;
		}
		Class clazz = obj.getClass();
		if (clazz.isPrimitive()) {
			return true;
		}
		String className = clazz.getName();
		return className.indexOf("java") == 0 && (!(obj instanceof Collection))
				&& (!(obj instanceof Map)) && (!clazz.isArray());
	}
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics