簡體   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

我正在嘗試運行基於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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM