我有一个小项目(大约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的东西都很标准(@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)
另外,我看不到引发异常的行,这很难过:( 你能帮助我吗?提前谢谢。
编辑:
@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());
}
}
答案 0 :(得分:2)
CLASSIC X-Y PROBLEM。
您正在尝试对@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
。
您有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应cascade
对Group
的所有操作表。 Hibernate应该从数据库中删除孤立的Group
对象。
@ManyToOne
&amp; @JoinColumn
表示此Group
对象具有指向User
对象的链接,并且它应该加入userId
表中名为User
的列周围。基本上将User
中的PK转换为Group
表的FK。
因此,如果您创建一个Group
个对象列表并转到User.setGroups(groupList)
然后调用userDao.save(user)
,Hibernate将通过级联在数据库中创建X Group
个记录。
同样,如果这没有意义,请告诉我。