尝试解决JPA懒加载异常问题(wildfly)

之前在JPA/Hibernate+JSF+CDI的项目中老是遇到懒加载异常问题,解决办法要不就是修改查询语句,要不就是改为EAGER。

最近百度了很久之后终于想到了一个还不错的办法,使用JPA的拦截器,不过这个解决方案需要使用Hibernate实现的时候才可用。


先看一下org.hibernate.collection.internal.AbstractPersistentCollection的代码,抛出懒加载异常的原因在这里:

               if ( session == null ) {
			if ( allowLoadOutsideTransaction ) {
				session = openTemporarySessionForLoading();
				isTempSession = true;
			}
			else {
				throwLazyInitializationException( "could not initialize proxy - no Session" );
			}
		}
		else if ( !session.isOpen() ) {
			if ( allowLoadOutsideTransaction ) {
				originalSession = session;
				session = openTemporarySessionForLoading();
				isTempSession = true;
			}
			else {
				throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
			}
		}
		else if ( !session.isConnected() ) {
			if ( allowLoadOutsideTransaction ) {
				originalSession = session;
				session = openTemporarySessionForLoading();
				isTempSession = true;
			}
			else {
				throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
			}
		}


所以,只需要在集合代理对象初始化之前,将Session对象传递给集合代理对象即可,所以,写一个JPA拦截器,使用@PostLoad注解拦截方法

public class LazyInitilazeCollectionListener {

	private static final Map<Class<?>,Set<Field>> PROXY_MAP= new HashMap<>();
	/**
	 * 1. 检查PROXY_MAP的键中是否包含o的Class对象,若包含,则取出相应的Set集合,即需要懒加载的成员
	 * 2. 若未包含o的Class对象,则检查o的Class对象的成员变量,看是否有成员变量加上了@OnetoMany,@ManyToMany注解,并且fetch值为FetchType.LAZY
	 *    若找到符合条件的变量(即懒加载对象),则将它们放入到Set中,然后放入PROXY_MAP中。
	 * 3. 若set为空,则表示没有需要进行懒加载的对象
	 * 4. 若set不为空,则遍历set,取出o的相应的变量的值(fieldValue),若该值(fieldValue)类型属于AbstractPersistentCollection,则说明此集合是懒加载的集合,需要对其进行再此代理处理。
	 * 5. 使用值(fieldValue)生成代理对象,将代理对象设置为o的相应的field的值,问题搞定。
	 * 在此后若是需要使用该懒加载集合的值时,调用该集合的方法即会调用代理对象的相应方法;代理方法会先检查该集合是否已经完全初始化,若是,则直接调用,若不是,则给其创造加载所需的环境(Session)
	 * @param o
	 */
	@PostLoad
	public void proxyCollection(Object o){
		Class<?> clazz = o.getClass();
		Set<Field> set = null;
		if(PROXY_MAP.containsKey(clazz)){
			set = PROXY_MAP.get(clazz);
		}else{
			//检查所有成员变量,是否有onetomany和manytomany注解并且申明为fetchType.LAZY
			for(Field field : clazz.getDeclaredFields()){
				if(field.isAnnotationPresent(OneToMany.class)){
					if(field.getAnnotation(OneToMany.class).fetch().equals(FetchType.LAZY)){
						if(set == null){
							set = new HashSet<>();
						}
						set.add(field);
					}
				}else if(field.isAnnotationPresent(ManyToMany.class)){
					if(field.getAnnotation(ManyToMany.class).fetch().equals(FetchType.LAZY)){
						if(set == null){
							set = new HashSet<>();
						}
						set.add(field);
					}
				}
			}
			synchronized (PROXY_MAP) {
				PROXY_MAP.put(clazz, set);
			}
		}
		//若是o中有需要代理的对象,此时set已经不为空了
		if(set == null){
			return;
		}
		
		for(Field field : set){
			field.setAccessible(true);
			try {
				Object fieldValue= field.get(o);
				if(fieldValue != null){
					if(fieldValue instanceof AbstractPersistentCollection){
						//生成代理对象
						Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{field.getType()},new Handler(fieldValue));
						field.set(o, obj);
					}
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			
		}
		
		
	}
	
	/**
	 * 到服务器上查找EntityManager对象,需要在persistence.xml文件中先配置<br/>
	 * <!-- 将EntityManagerFactory和EntityManager绑定到JNDI上 --><br/>
     *    <property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/myEntityManagerFactory" ><br/>
     *   <br/>
     *   <property name="jboss.entity.manager.jndi.name" value="java:/myEntityManager"><br/>
	 * @return
	 * @throws NamingException
	 */
	private static EntityManager lookupEntityManager() throws NamingException{
		Context ctx = new InitialContext();
		return (EntityManager)ctx.lookup("java:/myEntityManager");
	}
	
	class Handler implements InvocationHandler{
		private Object obj;
		
		public Handler(Object obj) {
			this.obj = obj;
		}
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			AbstractPersistentCollection collection = (AbstractPersistentCollection)obj;
			if(!collection.wasInitialized()){
				EntityManager em = lookupEntityManager();
				SessionImplementor session = em.unwrap(SessionImplementor.class);
				try {
					collection.setCurrentSession(session);
				} catch (Exception e) {
					//若是已关联了Session则会抛出异常,这里的异常只会发生在对象所处的Session还未关闭的情况下,这时是可以加载数据的,所以不需要任何处理
				}
			}
			return  method.invoke(obj, args);
		}
	}
}


我自己测试的时候是通过的,环境是Wildfly 10,服务器自带的Hibernate实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值