【问题标题】:Lazy loading one to many in spring春季懒加载一对多
【发布时间】:2015-08-13 18:58:50
【问题描述】:

我被这个问题困住了。基本上我在实体UserBook(用户可以有很多书)、两个事务存储库和事务服务之间有一对多的关系。我将 JPA 与休眠实现一起使用。

当我尝试访问延迟加载的书籍列表时,我得到NullPointerException,当我启用 SQL 日志时,我注意到没有调用 sql 查询。如果我改变为渴望它会按预期工作。

这是我认为与此问题相关的代码:

用户.java

@Entity
public class User {
...
   @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch =        FetchType.LAZY)
   @JsonIgnore
   private List<Book> rentedBooks;

Book.java

@Entity
public class Book {
...
    @ManyToOne(optional = true)
    @JoinColumn(name = "user_ID")
    @JsonProperty("user")
    private User user;

BookRepository.java

@Repository
@Transactional(Transactional.TxType.MANDATORY)
public class BookRepository {
    @PersistenceContext
    public EntityManager em;

UserRepository.java

@Repository
@Transactional(Transactional.TxType.MANDATORY)
public class UserRepository {
    @PersistenceContext
    EntityManager em;

BookService.java

@Service
@Transactional(Transactional.TxType.REQUIRED)
public class BookServices {
...
public void rentBook(String bookID, String fbID) {
        if (!userRepository.checkIfUserExists(fbID)) {
            User user = new User(fbID);
            userRepository.addUser(user);
        }
        User user = userRepository.findByUserByFacebookID(fbID);
        Book book = bookRepository.findBookByGoogleBookID(bookID);

        book.setStatus(Status.rented);
        book.setUser(user);
        bookRepository.updateBook(book);

        //user.getRentedBooks() is null
        user.getRentedBooks().add(book);
    }

我注意到如果用户不存在并且被创建 getRentedBooks 为 null,但如果它是之前创建的,getRentedBooks 不为 null 并且在延迟加载时按预期工作。

这是我的配置:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.polarcape" />
    <!--<property name = "hibernate.show_sql" value = "true" />-->
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
        <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

【问题讨论】:

    标签: java spring hibernate jpa transactions


    【解决方案1】:

    UserService 中初始化User 对象的List&lt;Book&gt;

    User user = userRepository.findByUserByFacebookID(fbID);
    Hibernate.initialize(user.getRentedBooks()); //Initialize the lazy-loaded collection
    Book book = bookRepository.findBookByGoogleBookID(bookID);
    

    关于Hibernate.initialize() 需求的快速介绍:(来自文档here

    如果一个 LazyInitializationException 将被 Hibernate 抛出 在范围之外访问未初始化的集合或代理 会话,即当实体拥有集合或拥有 对代理的引用处于分离状态。

    在您的情况下,List&lt;Book&gt; 是未初始化的集合

    【讨论】:

    • 我的错误,我忘了写我使用 JPA 和 hibernate 实现,Spring 不应该处理延迟加载集合的初始化吗?
    • @SarathChandra - getRentedBooks() 不应该做同样的事情,以防它在事务中被调用?
    • 我得到的例外是NullPointerException,而不是LazyInitializationException
    • @LeetzMKD:据我了解,user.getRentedBooks() 在新创建的User 的情况下返回null,因为List&lt;Book 属性它不是一个初始化的集合并且没有映射的书籍给给定的用户。但是,如果您初始化它,它会返回一个空列表,您可以愉快地向其中添加图书。
    【解决方案2】:

    在常见情况下,将book 与其user 关联后,您必须刷新 更改。然后您只需要刷新 user 实例。

    在您的情况下,您也可以这样做,但在程序上保持对象模型一致会更正确,您应该将 book 添加到 user.rentedBooks 集合中

    【讨论】:

    • 是的,这正是我正在做的事情,但正如我所说,我在访问书籍列表时得到了NullPointerException。我也尝试手动刷新对两个 repo 的更改,但仍然遇到相同的异常。
    • 如果您正在创建用户实例,您必须自己初始化集合(new ArrayList&lt;Book&gt;() 或类似的东西)什么是保持对象模型一致的一部分。
    猜你喜欢
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    • 2020-02-04
    • 1970-01-01
    • 2016-08-07
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多