保存集合:org.hibernate.LazyInitializationException:懒得初始化集合,没有会话或会话被关闭

时间:2014-05-20 07:29:10

标签: java spring hibernate session

我有一个小项目(大约15个表)。我也在我的项目中使用Hibernate sessionFactory,当我尝试保存一些集合时,我得到了 SEVERE: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed(下面的堆栈跟踪)

我从XHTML页面调用了save方法,但Eclipse IDE甚至没有捕获断点(对我来说这很奇怪,导致另一个断点正常捕获)。

此外,我使用OpenSessionInViewFilter模式懒洋洋地获取视图中的集合。

<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>sessionFactoryBeanName</param-name>
        <param-value>sessionFactory</param-value>
    </init-param>
  </filter>

这是我的bean的标题

@ManagedBean(name = "userBean")
@ViewScoped
public class UserBean implements Serializable {

    ...

我使用Spring 3.2.5.RELEASE和Hibernate 4.1.0.Final,所有Hibernate的东西都很标准(@AutowiredcurrentSession()等等)。我的服务中有@Transactional

SEVERE: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393)
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:378)
    at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:381)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:128)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:314)
    at org.primefaces.component.selectmanymenu.SelectManyMenuRenderer.getConvertedValue(SelectManyMenuRenderer.java:37)
    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1046)
    at javax.faces.component.UIInput.validate(UIInput.java:976)
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1249)
    at javax.faces.component.UIInput.processValidators(UIInput.java:712)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at javax.faces.component.UIForm.processValidators(UIForm.java:253)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at org.primefaces.component.dialog.Dialog.processValidators(Dialog.java:368)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at org.primefaces.component.layout.Layout.processValidators(Layout.java:233)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1258)
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1195)
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:744)

另外,我看不到引发异常的行,这很难过:( 你能帮助我吗?提前谢谢。

编辑:

@Entity
@Table(name = "wbp_user")
public class User extends BaseEntity implements UserDetails {

  private static final long serialVersionUID = 197854700819034127L;
  private String username;
  private String firstname;
  private String lastname;
  private String password;
  private String adNickname;
  private boolean enabled;
  private Team team;
  private Group group;
  private boolean deleted;
  private Set<Group> groups = new HashSet<Group>();
  private Set<Role> roles = new HashSet<Role>();
  private Server server;
  private List<UserPenalty> userPenalties = new ArrayList<UserPenalty>();
  private List<UserProperty> userProperties = new ArrayList<UserProperty>();
  private Date createDate;
  private User createUser;
  private Date updateDate;
  private User updateUser;

  private GroupPropertyStatus status;
  private Date statusSetDate;

  @Column(name = "create_date")
  public Date getCreateDate() {
    return createDate;
  }

  public void setCreateDate(Date createDate) {
    this.createDate = createDate;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "create_user")
  public User getCreateUser() {
    return createUser;
  }

  public void setCreateUser(User createUser) {
    this.createUser = createUser;
  }

  @Column(name = "update_date")
  public Date getUpdateDate() {
    return updateDate;
  }

  public void setUpdateDate(Date updateDate) {
    this.updateDate = updateDate;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "update_user")
  public User getUpdateUser() {
    return updateUser;
  }

  public void setUpdateUser(User updateUser) {
    this.updateUser = updateUser;
  }

  @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY, mappedBy = "user")
  @Fetch(FetchMode.SUBSELECT)
  public List<UserPenalty> getUserPenalties() {
    return userPenalties;
  }

  public void setUserPenalties(List<UserPenalty> userPenalties) {
    this.userPenalties = userPenalties;
  }

  @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY, mappedBy = "user")
  @Fetch(FetchMode.SUBSELECT)
  @Where(clause = "enabled = 1")
  public List<UserProperty> getUserProperties() {
    return userProperties;
  }

  public void setUserProperties(List<UserProperty> userProperties) {
    this.userProperties = userProperties;
  }

  @Transient
  public Server getServer() {
    return server;
  }

  public void setServer(Server server) {
    this.server = server;
  }

  @Column(name = "username")
  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  @Column(name = "firstname")
  public String getFirstname() {
    return firstname;
  }

  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  @Column(name = "lastname")
  public String getLastname() {
    return lastname;
  }

  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  @Column(name = "password")
  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  @Column(name = "ad_nickname")
  public String getAdNickname() {
    return adNickname;
  }

  public void setAdNickname(String adNickname) {
    this.adNickname = adNickname;
  }

  @Column(name = "enabled")
  public boolean isEnabled() {
    return enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "team_id")
  public Team getTeam() {
    return team;
  }

  public void setTeam(Team team) {
    this.team = team;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "group_id")
  public Group getGroup() {
    return group;
  }

  public void setGroup(Group group) {
    this.group = group;
  }

  @Column(name = "deleted")
  public boolean isDeleted() {
    return deleted;
  }

  public void setDeleted(boolean deleted) {
    this.deleted = deleted;
  }

  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(name = "wbp_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "role_id"))
  public Set<Role> getRoles() {
    return roles;
  }

  public void setRoles(Set<Role> roles) {
    this.roles = roles;
  }

  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(name = "wbp_user_group", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "group_id"))
  @Where(clause = "enabled = 1")
  public Set<Group> getGroups() {
    return groups;
  }

  public void setGroups(Set<Group> groups) {
    this.groups = groups;
  }

  @Override
  public String toString() {
    return "id = " + id + ", username = " + username + ", group = " + group.getGroupName() + ", status = " + status;

  }

  @Transient
  public String getStatusDuration(){
    if(statusSetDate != null){
      Long value = new Date().getTime() - statusSetDate.getTime();
      return String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(value), TimeUnit.MILLISECONDS.toMinutes(value) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(value)), TimeUnit.MILLISECONDS.toSeconds(value) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(value)));
    }
    return null;
  }

  @Transient
  public Date getStatusSetDate() {
    return statusSetDate;
  }

  public void setStatusSetDate(Date statusSetDate) {
    this.statusSetDate = statusSetDate;
  }

  @Transient
  public GroupPropertyStatus getStatus() {
    return status;
  }

  public void setStatus(GroupPropertyStatus status) {
    this.status = status;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof User)) {
      return false;
    }
    final User obj = (User) o;

    return generateEquals(getId(), obj.getId());
  }

  @Override
  public int hashCode() {
    return generateHash(31, 42, getId());
  }

  @Transient
  public Set<GrantedAuthority> getAuthorities() {
    Set<GrantedAuthority> authorities = new LinkedHashSet<GrantedAuthority>();
    authorities.addAll(roles);
    return authorities;
  }

  @Transient
  public boolean isUserInRole(String roleName){
    for (Role role : getRoles()) {
      if(role.getRoleName().equals(roleName)){
        return true;
      }
    }
    return false;
  }

  @Transient
  public boolean isAccountNonExpired() {
    return true;
  }

  @Transient
  public boolean isAccountNonLocked() {
    return true;
  }

  @Transient
  public boolean isCredentialsNonExpired() {
    return true;
  }

}

和小组:

@Entity
@Table(name = "wbp_group")
public class Group extends BaseEntity {

  private static final long serialVersionUID = -2237526151218578392L;
  private String groupName;
  private String groupDescription;
  private Set<Queue> queues = new HashSet<Queue>();
  private List<GroupPenalty> groupPenalties = new ArrayList<GroupPenalty>();
  private List<GroupProperty> groupProperties = new ArrayList<GroupProperty>();
  private boolean deleted;
  private boolean enabled;
  private Date createDate;
  private User createUser;
  private Date updateDate;
  private User updateUser;

  @Column(name = "enabled")
  public boolean isEnabled() {
    return enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  @Column(name = "create_date")
  public Date getCreateDate() {
    return createDate;
  }

  public void setCreateDate(Date createDate) {
    this.createDate = createDate;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "create_user")
  public User getCreateUser() {
    return createUser;
  }

  public void setCreateUser(User createUser) {
    this.createUser = createUser;
  }

  @Column(name = "update_date")
  public Date getUpdateDate() {
    return updateDate;
  }

  public void setUpdateDate(Date updateDate) {
    this.updateDate = updateDate;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
  @JoinColumn(name = "update_user")
  public User getUpdateUser() {
    return updateUser;
  }

  public void setUpdateUser(User updateUser) {
    this.updateUser = updateUser;
  }

  @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, fetch = FetchType.LAZY, mappedBy = "group")
  @Fetch(FetchMode.SUBSELECT)
  @Where(clause = "enabled = 1")
  public List<GroupProperty> getGroupProperties() {
    return groupProperties;
  }

  public void setGroupProperties(List<GroupProperty> groupProperties) {
    this.groupProperties = groupProperties;
  }

  @Column(name = "group_name")
  public String getGroupName() {
    return groupName;
  }

  public void setGroupName(String groupName) {
    this.groupName = groupName;
  }

  @Column(name = "group_description")
  public String getGroupDescription() {
    return groupDescription;
  }

  public void setGroupDescription(String groupDescription) {
    this.groupDescription = groupDescription;
  }

  @Column(name = "deleted")
  public boolean isDeleted() {
    return deleted;
  }

  public void setDeleted(boolean deleted) {
    this.deleted = deleted;
  }

  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(name = "wbp_queue_group", joinColumns = {@JoinColumn(name = "group_id")}, inverseJoinColumns = @JoinColumn(name = "queue_id"))
  @Where(clause = "enabled = 1")
  public Set<Queue> getQueues() {
    return queues;
  }

  public void setQueues(Set<Queue> queues) {
    this.queues = queues;
  }

  @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, fetch = FetchType.LAZY, mappedBy = "group")
  @Fetch(FetchMode.SUBSELECT)
  public List<GroupPenalty> getGroupPenalties() {
    return groupPenalties;
  }

  public void setGroupPenalties(List<GroupPenalty> groupPenalties) {
    this.groupPenalties = groupPenalties;
  }

  @Override
  public String toString() {
    return "id = " + id + ", groupName = " + groupName;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof Group)) {
      return false;
    }
    final Group obj = (Group) o;

    return generateEquals(getId(), obj.getId());
  }

  @Override
  public int hashCode() {
    return generateHash(31, 42, getId());
  }

}

1 个答案:

答案 0 :(得分:2)

CLASSIC X-Y PROBLEM

答案给出回答问题OP问:

您正在尝试对@Transactional上下文之外的集合的值执行某些操作。请参阅以下示例:

@Service
public class FooServiceImpl implements FooService{
    @Autowired
    private BarDao barDao;

    @Transactional
    public Bar getBar(long barId){
        return barDao.find(barId);
    }
}

@Controller
public class Controller{
    @Autowired
    private FooService fooService;

    @RequestMapping(value="/home")
    public void accessCollectionWrong(){
        Bar bar = fooService.getBar(barId);
        //assume that bar contains a collection of Baz objects
        List<Baz> bazList = bar.getBazList(); <-- THIS THROWS THE EXCEPTION
    }
}

抛出异常是因为Hibernate实际上没有从数据库加载Baz个对象的集合。这是为了节省您实际不需要对象的时间。但是Hibernate检测到您已尝试访问Baz对象的集合,因此它会进入数据库以实际尝试获取所有Baz个对象。它不能这样做,因为您在@Transactional上下文之外的控制器中,因此它没有用于检索对象的活动会话。代码需要重新设计如下:

@Service
public class FooServiceImpl implements FooService{
    @Autowired
    private BarDao barDao;

    @Transactional
    public Bar getBar(long barId){
        return barDao.find(barId);
    }

    @Transactional
    public List<Baz> getBaz(long barId){
        return barDao.find(barId).getBaz();
    }
}

@Controller
public class Controller{
    @Autowired
    private FooService fooService;

    @RequestMapping(value="/home")
    public void accessCollectionWrong(){
        List<Baz> bazList = fooService.getBazList(barId); <-- THIS DOESN'T THROW THE EXCEPTION
    }
}

这一切都有意义吗?如果没有,请告诉我,我可以扩展我的解释。

编辑1:需要注意的事项:Hibernate Session!= HTTP会话,Hibernate会话!=应用程序上下文,Hibernate Session!= Session Scoped Beans Session。 Hibernate会话是一个完全不同的概念,它围绕着数据库及其与数据库的交互。

每个Hibernate会话都围绕着@Transactional注释。每次输入@Transactional方法时,Hibernate都会为您创建一个新会话(使用SessionFactory并假设您尚未指定Propagation级别。这就是为什么您对sessionFactory.getCurrentSession()的调用成功并且不会返回null的原因。退出@Transactional方法时,Hibernate会自动提交数据库更改,并关闭当前的Hibernate会话。会话关闭后,Hibernate无法通过输入带注释的@Transactional方法打开您的收藏而无需打开其他会话。

在我的示例中,我使用barDao.find(barId).getBaz()的原因是,在实际请求集合之前,最好先获取存储集合的对象的新实例。这样你就可以确定你不会受到幻像读/写等的影响。我相信如果你试图在一个带有陈旧对象的新会话中访问该集合,你会得到一个StaleStateException

实际答案OP通缉:

您有2个对象,User包含Group个集合,您希望将User上的操作级联到Group。< / p>

public class User{
    //Normal Id's and other fields
    @OneToMany(mappedBy="user" cascade=CascadeType.ALL, orphanRemoval=true)
    private List<Group> groups = new ArrayList<Group>();
}

public class Group{
    //Normal Id's and other fields.
    @ManyToOne
    @JoinColumn(name="userId")
    private User user;
}

注释执行以下操作:

OneToMany表示有一组Group个对象具有指向此User对象的链接,Hibernate应cascadeGroup的所有操作表。 Hibernate应该从数据库中删除孤立的Group对象。

@ManyToOne&amp; @JoinColumn表示此Group对象具有指向User对象的链接,并且它应该加入userId表中名为User的列周围。基本上将User中的PK转换为Group表的FK。

因此,如果您创建一个Group个对象列表并转到User.setGroups(groupList)然后调用userDao.save(user),Hibernate将通过级联在数据库中创建X Group个记录。

同样,如果这没有意义,请告诉我。