简体   繁体   English

Hibernate 4 + Spring Data CrudRepository,CLI应用程序:无法延迟初始化集合:无法初始化代理-没有会话

[英]Hibernate 4 + Spring Data CrudRepository, CLI application: failed to lazily initialize a collection: could not initialize proxy - no Session

I am trying to run Spring-based CLI application which uses Spring Data CrudRepository to access Hibernate4-based persistence layer implemented using JPA annotations over MySQL5 (InnoDB) database using c3p0 connection pool. 我正在尝试运行基于Spring的CLI应用程序,该应用程序使用Spring Data CrudRepository来访问基于Hibernate4的持久层,该持久层通过使用c3p0连接池的MySQL5(InnoDB)数据库通过JPA注释实现。

I receive the following exception: 我收到以下异常:

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

I'm newbie with Spring Data and Hibernate. 我是Spring Data和Hibernate的新手。 From my point of view it's an issue with two different transactions (one in UserServiceImpl::findByLogin and another in CategoryServiceImpl::deleteByUser ). 从我的角度来看,这是两个不同事务的问题(一个在UserServiceImpl::findByLogin ,另一个在CategoryServiceImpl::deleteByUser )。 Changing User entity to use Eager fetch type for Categories helps, but I want to use lazy loading in this method. User实体更改为对类别使用Eager访存类型会有所帮助,但是我想在此方法中使用延迟加载。

Can I still use lazy fetch type in UserServiceImpl::findByLogin and fetch dependent objects in service consumer later with CrudRepository and Spring-managed transactions in Service Layer? 我是否仍可以在UserServiceImpl::findByLogin使用延迟获取类型并稍后在服务层中使用CrudRepository和Spring管理的事务在服务使用者中获取依赖对象?

Excerpt from the application which causes an exception: 来自应用程序的摘录,它导致异常:

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

EDIT: I tried to use EntityManager::merge , but with no luck: 编辑:我试图使用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());
    }
}

Services (injected with @Autowired ): User service: 服务(注入@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);
    }
}

Category service: 分类服务:

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

Entities: User: 实体:用户:

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

Category: 类别:

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

Run this code: 运行此代码:

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

within a single transaction like so: 在单个交易中,如下所示:

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

which will make sure the same Hibernate session is used for both operations. 这将确保两个操作都使用相同的Hibernate会话。

I don't think your problem is to do with transaction, you seem to have setup your declarative transaction boundaries properly (default Spring transaction propagation is REQUIRED -- meaning if you call nested method decorated with @Transactional , no overlapping transaction is created) 我不认为您的问题与事务有关,您似乎已经正确设置了声明性事务边界(默认为Spring事务传播是必需的-这意味着,如果调用以@Transactional装饰的嵌套方法,则不会创建任何重叠的事务)

Your problem seem to be cause by categories property of the user object not populated when you're fetching it (because you set it to lazy) AND/OR hibernate unable to populate it before session is closed on deleteByLogin -- hence the object is already detached. 您的问题似乎是由于在获取用户对象时未填充用户对象的categories属性引起的(因为您将其设置为lazy)和/或休眠状态无法在deleteByLogin关闭会话之前填充它-因此该对象已经存在分离。

As fas as I can see there are two method to resolve this: 据我所知,有两种方法可以解决此问题:

1. Eagerly fetch the categories property of a user 1.认真获取用户的类别属性

Mark categories property as eagerly fetched: @OneToMany(fetch = FetchType.EAGER, ...) (WARNING: could eat massive amount of memory), or use LEFT JOIN FETCH syntax when querying for a user so its categories property is populated 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. Merge the detached user on deleteByLogin 2.将分离的用户合并到deleteByLogin

On deleteByLogin , merge the user object first into the persistence context so the categories property can be lazily loaded whenever you call getCategories() 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.

相关问题 休眠-无法延迟初始化角色集合:无法初始化代理-没有会话 - Hibernate - failed to lazily initialize a collection of role: could not initialize proxy - no Session Hibernate 无法延迟初始化角色集合 无法初始化代理 - 没有 Session - Hibernate failed to lazily initialize a collection of role could not initialize proxy - no Session Hibernate 延迟加载不适用于 Spring 启动 =&gt; 无法延迟初始化角色集合无法初始化代理 - 没有 Session - Hibernate Lazy loading not working with Spring Boot => failed to lazily initialize a collection of role could not initialize proxy - no Session Spring 数据无法延迟初始化角色集合,无法初始化代理 - 没有 Session - Spring Data failed to lazily initialize a collection of role, could not initialize proxy - no Session 无法延迟初始化角色[]的集合,无法初始化代理-没有会话 - failed to lazily initialize a collection of role [], could not initialize proxy - no Session 无法延迟初始化角色集合无法初始化代理-无会话 - Failed to lazily initialize a collection of role could not initialize proxy - no Session 面对未能延迟初始化集合,并且无法初始化代理-无会话 - facing failed to lazily initialize a collection and could not initialize proxy - no Session 无法延迟初始化角色集合:无法初始化代理-无会话 - failed to lazily initialize a collection of role : could not initialize proxy - no Session DefaultExceptionListener:未能延迟初始化角色集合无法初始化代理 - 否 Session - DefaultExceptionListener : failed to lazily initialize a collection of role could not initialize proxy - no Session Hibernate:LazyInitializationException:懒得初始化一个角色集合。 无法初始化代理 - 没有会话 - Hibernate: LazyInitializationException: failed to lazily initialize a collection of role. Could not initialize proxy - no Session
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM