【问题标题】:Delete from Lazy fetch List - Hibernate从延迟获取列表中删除 - 休眠
【发布时间】:2014-01-25 07:49:54
【问题描述】:

我在尝试从对象的惰性提取列表中删除时遇到问题。我有一个用户对象和一个事件对象。用户可以参加一个事件,这存储在两个对象中。

用户对象:

@Entity
@Table(name = "User")
public class User implements Serializable {

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "attendingUsers")
    private List<Event> attendingEvents = new ArrayList<Event>();
}

事件对象:

@Entity
@Table(name = "Event")
public class Event implements Serializable {

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "Event_User",
        joinColumns = @JoinColumn(name = "event_id", nullable = false, updatable = false), 
        inverseJoinColumns = @JoinColumn(name = "user_id", nullable = false, updatable = false))
    List<User> attendingUsers = new ArrayList<User>();
}

当然,这些列表有 Setter 和 Getter。现在我想实现以用户身份无人参与这些事件的可能性。因此我尝试了以下方法:

public void unattend(Event e, User u){
    List<User> attendingUsers = getAttendingUsers(e.getId());
    List<Event> attendingEventsForUser = userService.getEvents(u.getId());
    if(attendingUsers.contains(u)){
        e.getAttendingUsers().clear();
        u.getAttendingEvents().clear();
        attendingUsers.remove(u);
        attendingEventsForUser.remove(e);
        e.getAttendingUsers().addAll(attendingUsers);
        u.getAttendingEvents().addAll(attendingEventsForUser);
        update(e);
        userDAO.update(u);
    }
}

我得到的异常如下:

07.01.2014 15:52:20 org.apache.catalina.core.StandardWrapperValve 调用 SCHWERWIEGEND:Servlet.service() 用于 servlet [appServlet] 在上下文中与路径 [/tripitude] 抛出异常[请求处理失败;嵌套异常是 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ac.tuwien.ase08.tripitude.entity.Event.attendingUsers, could not initialize proxy - no Session] 根本原因 org.hibernate.LazyInitializationException:无法延迟初始化角色集合:ac.tuwien.ase08.tripitude.entity.Event.attendingUsers,无法初始化代理 - 没有会话 在 org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566) 在 org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186) 在 org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545) 在 org.hibernate.collection.internal.PersistentBag.clear(PersistentBag.java:381) 在 ac.tuwien.ase08.tripitude.service.EventService.unattend(EventService.java:76) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597) 在 org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 在 org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 在 $Proxy64.unattend(Unknown Source)

虽然我正在使用方法来确保执行延迟提取:

public List<User> getAttendingUsers(Long id){
    Query query = sessionFactory.getCurrentSession().createQuery(
            "SELECT u FROM User AS u LEFT JOIN u.attendingEvents AS ae WHERE ae.id = :id");
    query.setParameter("id", id);

    List<User> l = (List<User>)query.list();

    return l;
}

还有一个:

    @Override
public List<Event> getEvents(Long id) {
    Query query = sessionFactory.getCurrentSession().createQuery(
            "SELECT e FROM Event AS e LEFT JOIN e.attendingUsers AS au WHERE au.id = :id");
    query.setParameter("id", id);

    List<Event> l = (List<Event>)query.list();

    return l;
}

我正在考虑使用查询删除,但我又想不出正确的代码:/

非常感谢您的帮助,谢谢!

编辑:我刚刚意识到我一直在调用.getAttendingUsers().getAttendingEvents(),而我还没有真正初始化这两个。所以现在我跳过了那部分并将我的代码更新为:

public void unattend(Event e, User u){
    Query query = sessionFactory.getCurrentSession().createQuery(
            "SELECT u FROM User AS u LEFT JOIN FETCH u.attendingEvents AS ae WHERE ae.id = :id");
    query.setParameter("id", e.getId());
    List<User> attendingUsers = (List<User>)query.list();

    Query query2 = sessionFactory.getCurrentSession().createQuery(
            "SELECT e FROM Event AS e LEFT JOIN FETCH e.attendingUsers AS au WHERE au.id = :id");
    query2.setParameter("id", u.getId());

    List<Event> attendingEventsForUser = (List<Event>)query2.list();

    if(attendingUsers.contains(u)){
        attendingUsers.remove(u);
        attendingEventsForUser.remove(e);
        e.setAttendingUsers(attendingUsers);
        u.setAttendingEvents(attendingEventsForUser);
        update(e);
        userDAO.update(u);
    }
}

我得到一个新的例外:

07.01.2014 16:51:34 org.apache.catalina.core.StandardWrapperValve 调用 SCHWERWIEGEND:Servlet.service() 用于路径 [/tripitude] 的上下文中的 servlet [appServlet] 引发异常 [请求处理失败;嵌套异常是 org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话相关联:[ac.tuwien.ase08.tripitude.entity.Event#3]] 根本原因 org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联:[ac.tuwien.ase08.tripitude.entity.Event#3] 在 org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:697) 在 org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:296) 在 org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:241) 在 org.hibernate.event.internal.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:55) 在 org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90) 在 org.hibernate.internal.SessionImpl.fireUpdate(SessionImpl.java:786) 在 org.hibernate.internal.SessionImpl.update(SessionImpl.java:778) 在 org.hibernate.internal.SessionImpl.update(SessionImpl.java:774) 在 ac.tuwien.ase08.tripitude.dao.HibernateDAO.update(HibernateDAO.java:43) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597) 在 org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 在 org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 在 $Proxy63.update(未知来源) 在 ac.tuwien.ase08.tripitude.service.EventService.update(EventService.java:41) 在 ac.tuwien.ase08.tripitude.service.EventService.unattend(EventService.java:84)

【问题讨论】:

  • unattend() 是否在事务的上下文中被调用(它似乎没有被注释 @Transactional)? EventService 类是否注解为 @Transactional
  • unattend() 是在负责事件的 RestController 中调用的(该调用来自 android 应用程序的 post 请求)。 EventService 使用 @Transactional(propagation= Propagation.REQUIRED, readOnly=false) 进行注释,而方法本身没有以任何方式进行注释。

标签: java spring hibernate


【解决方案1】:

您正在尝试访问事务之外的惰性集合。您可以使用 @Transactional 注释相关方法或使用 fetch joins:

SELECT e FROM Event AS e LEFT JOIN FETCH e.attendingUsers ...

通常最好在事务中工作,因为您仅在需要时才获取相关实体。

编辑:Exception 可能源自“无人参与”方法中的if 子句,您可以在其中访问列表:e.getAttendingUsers()。我会将其从控制器移到事务服务中,例如 EventService

【讨论】:

  • 我尝试了 fetch 连接,但没有任何区别 - 仍然是同样的错误。 @Transactional 也没有帮助(我忘了提到 EventService@Transactional 注释。
  • 我明白了 - 提取连接是不够的,因为您可以在多个地方访问集合。请查看编辑。
  • 是的,谢谢,我意识到了同样的事情!但是,我不能简单地说setAttendingEvents(attendingEventsForUser),因为这会导致新的异常(请参阅我的编辑)。
  • 对我来说似乎是一个合并问题,我会看看它afaik
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多