1

我正在尝试运行基于 Spring 的 CLI 应用程序,该应用程序使用 Spring Data CrudRepository 访问使用 c3p0 连接池在 MySQL5 (InnoDB) 数据库上使用 JPA 注释实现的基于 Hibernate4 的持久层。

我收到以下异常:

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 的新手。从我的角度来看,这是两个不同交易(一个 inUserServiceImpl::findByLogin另一个 in CategoryServiceImpl::deleteByUser)的问题。将实体更改User为对类别使用 Eager fetch 类型会有所帮助,但我想在此方法中使用延迟加载。

我仍然可以在服务层中使用延迟获取类型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;
    }
}
4

2 回答 2

1

我不认为您的问题与事务有关,您似乎已经正确设置了声明性事务边界(默认 Spring 事务传播是必需的——这意味着如果您调用用 装饰的嵌套方法@Transactional,则不会创建重叠事务)

您的问题似乎是categories由于在您获取它时未填充用户对象的属性(因为您将其设置为惰性)和/或休眠在会话关闭之前无法填充它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

On deleteByLogin,首先将用户对象合并到持久性上下文中,以便categories在您调用时可以延迟加载该属性getCategories()

@Override
@Transactional
public void deleteByUser(User user) {
    session.merge(user);
    repository.delete(user.getCategories());
}
于 2013-06-24T00:07:48.763 回答
1

运行此代码:

User user = userService.findByLogin(login);
categoryService.deleteByUser(user);

在这样的单个事务中:

@Transactional
public void deleteCategoriesByUser(String login) {
    User user = userService.findByLogin(login);
    categoryService.deleteByUser(user);
}

这将确保两个操作使用相同的 Hibernate 会话。

于 2013-06-23T21:29:39.643 回答