[英]Hibernate - failed to lazily initialize a collection of role: could not initialize proxy - no Session
[英]Hibernate 4 + Spring Data CrudRepository, CLI application: failed to lazily initialize a collection: could not initialize proxy - no Session
我正在嘗試運行基於Spring的CLI應用程序,該應用程序使用Spring Data CrudRepository來訪問基於Hibernate4的持久層,該持久層通過使用c3p0連接池的MySQL5(InnoDB)數據庫通過JPA注釋實現。
我收到以下異常:
Exception in thread "main" java.lang.RuntimeException: org.springframework.orm.hibernate3.HibernateSystemException: failed to lazily initialize a collection of role: <package>.entity.User.categories, could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: <package>.entity.User.categories, could not initialize proxy - no Session
我是Spring Data和Hibernate的新手。 從我的角度來看,這是兩個不同事務的問題(一個在UserServiceImpl::findByLogin
,另一個在CategoryServiceImpl::deleteByUser
)。 將User
實體更改為對類別使用Eager訪存類型會有所幫助,但是我想在此方法中使用延遲加載。
我是否仍可以在UserServiceImpl::findByLogin
使用延遲獲取類型並稍后在服務層中使用CrudRepository
和Spring管理的事務在服務使用者中獲取依賴對象?
來自應用程序的摘錄,它導致異常:
User user = userService.findByLogin(login);
categoryService.deleteByUser(user);
編輯:我試圖使用EntityManager::merge
,但沒有運氣:
@Service
@Repository
@Transactional
public class CategoryServiceImpl implements CategoryService, InitializingBean {
@Autowired
private CategoryRepository repository;
@Autowired
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
@Override
public void afterPropertiesSet() throws Exception {
entityManager = entityManagerFactory.createEntityManager();
}
@Override
@Transactional(readOnly = true)
public Category findById(Long categoryId) {
return repository.findOne(categoryId);
}
@Override
@Transactional
public Category save(Category category) {
return repository.save(category);
}
@Override
@Transactional
public void delete(Category category) {
repository.delete(category);
}
@Override
@Transactional
public void deleteByUser(User user) {
entityManager.merge(user);
repository.delete(user.getCategories());
}
}
服務(注入@Autowired
):用戶服務:
package <package>.service.jpa;
import com.google.common.collect.Lists;
import <package>.entity.User;
import <package>.repository.UserRepository;
import <package>.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Repository
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository repository;
@Override
@Transactional(readOnly = true)
public List<User> findAll() {
return Lists.newArrayList(repository.findAll());
}
@Override
@Transactional(readOnly = true)
public User findById(Long userId) {
return repository.findOne(userId);
}
@Override
@Transactional(readOnly = true)
public User findByLogin(String login) {
return repository.findByLogin(login);
}
@Override
@Transactional
public User save(User user) {
return repository.save(user);
}
@Override
@Transactional
public void delete(User user) {
repository.delete(user);
}
}
分類服務:
package <package>.service.jpa;
import <package>.entity.Category;
import <package>.entity.User;
import <package>.repository.CategoryRepository;
import <package>.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Repository
@Transactional
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryRepository repository;
@Override
@Transactional(readOnly = true)
public Category findById(Long categoryId) {
return repository.findOne(categoryId);
}
@Override
@Transactional
public Category save(Category category) {
return repository.save(category);
}
@Override
@Transactional
public void delete(Category category) {
repository.delete(category);
}
@Override
@Transactional
public void deleteByUser(User user) {
repository.delete(user.getCategories());
}
}
實體:用戶:
package <package>.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table
public class User {
private Long userId;
private int version;
private String login;
private Set<Category> categories = new HashSet<Category>();
private Set<CategoryFeed> categoryFeeds = new HashSet<CategoryFeed>();
@Id
@GeneratedValue
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Column
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
public void addCategory(Category category) {
categories.add(category);
}
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<CategoryFeed> getCategoryFeeds() {
return categoryFeeds;
}
public void setCategoryFeeds(Set<CategoryFeed> categoryFeeds) {
this.categoryFeeds = categoryFeeds;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
if (login != null ? !login.equals(user.login) : user.login != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return login != null ? login.hashCode() : 0;
}
}
類別:
package <package>.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"userId", "title"}))
public class Category {
public static final String ROOT_CATEGORY_TITLE = "";
private Long categoryId;
private int version;
private User user;
private String title = ROOT_CATEGORY_TITLE;
private Set<CategoryFeed> feeds = new HashSet<CategoryFeed>();
@Id
@GeneratedValue
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@ManyToOne
@JoinColumn(name = "userId")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@ManyToMany(mappedBy = "categories", cascade = CascadeType.ALL)
public Set<CategoryFeed> getFeeds() {
return feeds;
}
public void setFeeds(Set<CategoryFeed> feeds) {
this.feeds = feeds;
}
public void addFeed(CategoryFeed feed) {
this.feeds.add(feed);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Category)) {
return false;
}
Category category = (Category) o;
if (title != null ? !title.equals(category.title) : category.title != null) {
return false;
}
if (user != null ? !user.equals(category.user) : category.user != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = user != null ? user.hashCode() : 0;
result = 31 * result + (title != null ? title.hashCode() : 0);
return result;
}
}
運行此代碼:
User user = userService.findByLogin(login);
categoryService.deleteByUser(user);
在單個交易中,如下所示:
@Transactional
public void deleteCategoriesByUser(String login) {
User user = userService.findByLogin(login);
categoryService.deleteByUser(user);
}
這將確保兩個操作都使用相同的Hibernate會話。
我不認為您的問題與事務有關,您似乎已經正確設置了聲明性事務邊界(默認為Spring事務傳播是必需的-這意味着,如果調用以@Transactional
裝飾的嵌套方法,則不會創建任何重疊的事務)
您的問題似乎是由於在獲取用戶對象時未填充用戶對象的categories
屬性引起的(因為您將其設置為lazy)和/或休眠狀態無法在deleteByLogin
關閉會話之前填充它-因此該對象已經存在分離。
據我所知,有兩種方法可以解決此問題:
1.認真獲取用戶的類別屬性
將categories
屬性標記為急切獲取: @OneToMany(fetch = FetchType.EAGER, ...)
(警告:可能會消耗大量內存),或者在查詢用戶時使用LEFT JOIN FETCH
語法,以便填充其categories
屬性
select distinct u from User as u left join fetch u.categories where u.login = :login
2.將分離的用戶合並到deleteByLogin
在deleteByLogin
,首先合並用戶對象為持久化上下文這樣categories
屬性可以延遲加載,只要你撥打getCategories()
@Override
@Transactional
public void deleteByUser(User user) {
session.merge(user);
repository.delete(user.getCategories());
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.