保存收藏:org.hibernate。LazyInitializationException:没有延迟初始化一个集合,没有关闭会话或会话。

时间:2021-09-05 23:28:13

I have small project(about 15 tables). I also use Hibernate sessionFactory in my project, and when I try to save some collection, I get SEVERE: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed (stacktrace below)

我有一个小项目(大约15张桌子)。我还在我的项目中使用Hibernate sessionFactory,当我试图保存一些集合时,我变得很严重:org.hibernate。LazyInitializationException:没有延迟地初始化一个集合,没有会话或会话被关闭(下面的stacktrace)

I call saving method from XHTML page, but Eclipse IDE dont even catch breakpoint (this is very strange for me, cause another breakpoints catched normally).

我从XHTML页面调用save方法,但是Eclipse IDE甚至没有捕获断点(这对我来说很奇怪,因为另一个断点通常会被捕获)。

Also I use OpenSessionInViewFilter pattern to lazily get collection in view.

我还使用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>

Here's my bean's header

这是我的bean的头

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

    ...

I use Spring 3.2.5.RELEASE and Hibernate 4.1.0.Final, all the Hibernate stuff is quite standard (@Autowired, currentSession() and so on). I have @Transactional in my services.

我使用Spring 3.2.5。释放和Hibernate 4.1.0。最后,所有Hibernate都是相当标准的(@Autowired, currentSession()等等)。我的服务中有@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)

Also, I cant see the line where exception is thrown, this is very sad :( Can you help me? Thanks in advance.

另外,我看不出有例外的情况,这很令人伤心:(你能帮我吗?)提前谢谢。

EDIT:

编辑:

@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;
  }

}

And Group:

和组:

@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 个解决方案

#1


2  

CLASSIC X-Y PROBLEM.

典型的x - y问题。

Answer Given to answer question OP Asked:

You are trying to do something to the value of a collection outside of a @Transactional context. See the example below:

您试图在@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
    }
}

The exception is being thrown because Hibernate has not actually loaded your collection of Baz objects from the database. This is to save time where you don't actually need the objects. But Hibernate detects that you have tried to access the collection of Baz objects and so it goes to the database to actually try and get all the Baz objects. It can't do that because you are in your controller, outside of the @Transactional context, so it has no active session to use to retrieve your objects. The code needs to be redesigned as below:

正在抛出异常,因为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
    }
}

Does this all make sense? If not, let me know and I can expand on my explanation.

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

Edit 1: Things to note: Hibernate Session != HTTP Session, Hibernate Session != Application Context, Hibernate Session != Session Scoped Beans Session. A Hibernate session is a totally different concept, that revolves around the database and its interactions with the database.

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

Each Hibernate session is scoped around your @Transactional annotations. Each time you enter a @Transactional method, Hibernate creates a new session for you (using the SessionFactory and assuming you haven't specified a Propagation level). That's why your call to sessionFactory.getCurrentSession() succeeds and doesn't return null. When you exit the @Transactional method your database changes are automatically committed by Hibernate and the current Hibernate session is closed. Once the session is closed then Hibernate can't get your collection without opening another session by entering an annotated @Transactional method.

每个Hibernate会话都围绕着@Transactional注释。每次输入@Transactional方法时,Hibernate为您创建一个新会话(使用SessionFactory并假设您没有指定传播级别)。这就是为什么要调用sessionFactory.getCurrentSession()成功且不返回空值。当您退出@Transactional方法时,您的数据库更改是由Hibernate自动提交的,而当前Hibernate会话是关闭的。一旦会话关闭,那么Hibernate就不能通过输入带注释的@Transactional方法来获得您的集合,而不需要打开另一个会话。

The reason that in my example I used barDao.find(barId).getBaz() was that it is always better to get a fresh instance of the object that your collection is stored in before actually asking for the collection. That way you are sure that you are not going to suffer from phantom reads/writes etc. I believe that if you try to access the collection in a new session with a stale object, you get a StaleStateException.

在我的例子中,我使用了barDao.find(barId). getbaz()的原因是,在实际请求集合之前,最好得到一个对象的新实例。通过这种方式,您可以确定您不会受到幻读/写操作的影响。我相信,如果您试图在一个新的会话中使用一个陈旧的对象访问集合,您就会得到一个StaleStateException。

Actual Answer OP Wanted:

You have 2 objects, a User has a collection of Group's and you want to cascade operations on a User to a Group.

您有两个对象,一个用户拥有一个组的集合,您想要将一个用户的操作串级到一个组。

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;
}

The annotations do the following:

注释的作用如下:

OneToMany says that there is a collection of Group objects that have a link to this User object, Hibernate should cascade all operations to the Group table. Hibernate should remove orphaned Group objects from the database.

OneToMany说,有一个组对象的集合,它们与这个用户对象有一个链接,Hibernate应该将所有操作串联到组表中。Hibernate应该从数据库中删除孤立的组对象。

@ManyToOne & @JoinColumn says that this Group object has a link to a User object and that it should join around the column called userId in the User table. Basically turns the PK in User into the FK for the Group table.

@ManyToOne & @JoinColumn说,这个组对象有一个指向用户对象的链接,它应该在用户表中连接名为userId的列。基本上将用户PK变成了组表的FK。

So if you create a List of Group objects and go User.setGroups(groupList) and then call userDao.save(user) Hibernate will create X Group records in the database by cascade.

因此,如果您创建了一个组对象和go用户. setgroups (groupList)的列表,然后调用userDao.save(用户)Hibernate将通过cascade在数据库中创建X组记录。

Again, if this doesn't make sense, tell me.

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

#1


2  

CLASSIC X-Y PROBLEM.

典型的x - y问题。

Answer Given to answer question OP Asked:

You are trying to do something to the value of a collection outside of a @Transactional context. See the example below:

您试图在@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
    }
}

The exception is being thrown because Hibernate has not actually loaded your collection of Baz objects from the database. This is to save time where you don't actually need the objects. But Hibernate detects that you have tried to access the collection of Baz objects and so it goes to the database to actually try and get all the Baz objects. It can't do that because you are in your controller, outside of the @Transactional context, so it has no active session to use to retrieve your objects. The code needs to be redesigned as below:

正在抛出异常,因为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
    }
}

Does this all make sense? If not, let me know and I can expand on my explanation.

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

Edit 1: Things to note: Hibernate Session != HTTP Session, Hibernate Session != Application Context, Hibernate Session != Session Scoped Beans Session. A Hibernate session is a totally different concept, that revolves around the database and its interactions with the database.

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

Each Hibernate session is scoped around your @Transactional annotations. Each time you enter a @Transactional method, Hibernate creates a new session for you (using the SessionFactory and assuming you haven't specified a Propagation level). That's why your call to sessionFactory.getCurrentSession() succeeds and doesn't return null. When you exit the @Transactional method your database changes are automatically committed by Hibernate and the current Hibernate session is closed. Once the session is closed then Hibernate can't get your collection without opening another session by entering an annotated @Transactional method.

每个Hibernate会话都围绕着@Transactional注释。每次输入@Transactional方法时,Hibernate为您创建一个新会话(使用SessionFactory并假设您没有指定传播级别)。这就是为什么要调用sessionFactory.getCurrentSession()成功且不返回空值。当您退出@Transactional方法时,您的数据库更改是由Hibernate自动提交的,而当前Hibernate会话是关闭的。一旦会话关闭,那么Hibernate就不能通过输入带注释的@Transactional方法来获得您的集合,而不需要打开另一个会话。

The reason that in my example I used barDao.find(barId).getBaz() was that it is always better to get a fresh instance of the object that your collection is stored in before actually asking for the collection. That way you are sure that you are not going to suffer from phantom reads/writes etc. I believe that if you try to access the collection in a new session with a stale object, you get a StaleStateException.

在我的例子中,我使用了barDao.find(barId). getbaz()的原因是,在实际请求集合之前,最好得到一个对象的新实例。通过这种方式,您可以确定您不会受到幻读/写操作的影响。我相信,如果您试图在一个新的会话中使用一个陈旧的对象访问集合,您就会得到一个StaleStateException。

Actual Answer OP Wanted:

You have 2 objects, a User has a collection of Group's and you want to cascade operations on a User to a Group.

您有两个对象,一个用户拥有一个组的集合,您想要将一个用户的操作串级到一个组。

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;
}

The annotations do the following:

注释的作用如下:

OneToMany says that there is a collection of Group objects that have a link to this User object, Hibernate should cascade all operations to the Group table. Hibernate should remove orphaned Group objects from the database.

OneToMany说,有一个组对象的集合,它们与这个用户对象有一个链接,Hibernate应该将所有操作串联到组表中。Hibernate应该从数据库中删除孤立的组对象。

@ManyToOne & @JoinColumn says that this Group object has a link to a User object and that it should join around the column called userId in the User table. Basically turns the PK in User into the FK for the Group table.

@ManyToOne & @JoinColumn说,这个组对象有一个指向用户对象的链接,它应该在用户表中连接名为userId的列。基本上将用户PK变成了组表的FK。

So if you create a List of Group objects and go User.setGroups(groupList) and then call userDao.save(user) Hibernate will create X Group records in the database by cascade.

因此,如果您创建了一个组对象和go用户. setgroups (groupList)的列表,然后调用userDao.save(用户)Hibernate将通过cascade在数据库中创建X组记录。

Again, if this doesn't make sense, tell me.

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