从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集

Posted

技术标签:

【中文标题】从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集【英文标题】:how to lazy load collection when using spring-data-jpa, with hibernate, from an console application 【发布时间】:2013-02-03 00:40:23 【问题描述】:

我有一个小型控制台应用程序,我正在使用带有休眠功能的 spring-data-jpa。在独立控制台应用程序中,当使用 spring-data-jpa 及其存储库时,我真的不知道如何延迟初始化集合。这是我的一些代码:

@Entity
public class User 
...
    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="USER_ORDER_ID")
    private Set<Order> orders = new HashSet<Order>();
...

存储库:

public interface UserRepository extends PagingAndSortingRepository<User, Long> 

    public ArrayList<User> findByFirstNameIgnoreCase(String firstName);

服务实现:

@Service
@Repository
@Transactional
public class UserServiceImpl implements UserService 
    @Autowired
    private UserRepository userRepository;

public ArrayList<User> findByFirstNameIgnoreCase(String firstName) 
    ArrayList<User> users = new ArrayList<User>();
    users = userRepository.findByFirstNameIgnoreCase(firstName);
    return users;

我的主要方法:

...
user = userRepository.findByFirstNameIgnoreCase("john").get(0);
orders = user.getOrders();
for (Order order : orders) 
  LOGGER.info("getting orders: " + order.getId());
    

foreach 循环出现异常:

EVERE:未能延迟初始化角色集合:com.aki.util.User.orders,没有会话或会话已关闭 org.hibernate.LazyInitializationException: 延迟初始化角色集合失败:

请注意,从带有某种 OpenSessionInViewFilter 的 web 应用程序运行时,我没有这个问题。

【问题讨论】:

检查这个:***.com/questions/15359306/… 【参考方案1】:

一种解决方案是让User.orders 成为一个急切获取的集合

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Order> orders = new HashSet<Order>();

默认情况下,实体关联是延迟加载的。这意味着orders Set 实际上只是一个代理对象,在您调用它的方法之前不会被初始化。这很好,因为除非需要,否则不会加载关联的 Order 对象。但是,如果您尝试在正在运行的事务之外访问未初始化的集合,这可能会导致问题。

如果您知道在大多数情况下都需要用户的订单,那么急切地获取关联是有意义的。否则,您将必须确保集合在事务中被初始化/加载。您提到的OpenSessionInViewFilter 确保事务在请求处理期间保持打开状态,这就是您在 webapp 中没有此问题的原因。

如果您必须保持延迟加载,请尝试使用 Spring 的 TransactionTemplate 将代码包装在您的 main 方法中:

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() 
    @Override
    protected void doInTransactionWithoutResult(TransactionStatus status) 
    ...
    
);

【讨论】:

这不是我想要的。也许我不清楚我想做延迟初始化。我已经编辑了我的问题。 在这种情况下,请参阅我编辑的答案以使用 TransactionTemplate 我真的很讨厌使用 TransactionTemplate,但它确实有效。没有其他建议? @Transactional 注释方法与将其包装在TransactionTemplate大致相同,这基本上是打开/提交事务。如果您的 @Transactional 没有效果,可能有几个不同的原因。一个典型的例子是你把它放在一个不是接口方法实现的方法上。 (请记住,Spring AOP 默认使用 JDK 代理,这意味着它只能影响定义在接口上的方法。) 您上面的getSomeOrders() 方法无法初始化userOrders 集合的原因是因为user.getOrders() 只返回一个代理而不是真正的集合(这就是延迟加载的全部意义所在)。您必须调用代理上的方法才能对其进行初始化。一种常见的技术是调用size()。所以尝试添加userOrders.size(),它会起作用。【参考方案2】:

但是,我找到了一种方法。此方法在我的服务实现中:

public Set<Order> fetchUserOrders(Long userId) 
    User user = userRepository.findOne(userId);
    Hibernate.initialize(user.getOrders());
    Set<Order> orders = user.getOrders();
    return orders;

注意:这是@zagyi 在他的一个 cmets 中所建议的,但也要注意声明:Hibernate.initialize(user.getOrders()); 没有这个集合仍然不会被初始化,你会得到一个错误。

【讨论】:

请注意:Hibernate.initialize() 会将您的应用程序绑定到 Hibernate。如果你不在乎也没关系。如果您确实关心,请改用user.getOrders().size()。虽然这根本不会反映您的意图,但它不依赖于非 JPA 调用,并且您始终可以声明一个具有更具描述性名称的辅助方法,该方法接收一个集合并在其上调用 size()。跨度> 这行得通。没错,使用 user.getOrders().size 会好很多。并不是说我很快就会从休眠状态切换。我可以发誓我以前试过。虽然我不确定它来自服务层:)。无论如何,非常感谢您的帮助。 这是否意味着进入 for 循环以获取所有 usersWithOrders ?假设我在用户上拉 findAll,这意味着 usersWithOrders,我是否必须通过循环对每个用户执行 Hibernate.initialize?我知道 EAGER 会起作用,但如果是 Lazy,这是唯一的方法吗? 当它是 spring-data-jpa 和 hibernate 时,Hibernate.initialize 和 .getter().size() 都不起作用,我写这个只是为了节省其他人的时间...... @Tina 你修好了吗?【参考方案3】:

这对我有用:

@Component
public class Test 

    @Inject 
    OrderDAO orderDAO;

    @PersistenceUnit
    private EntityManagerFactory emf;

    @PostConstruct
    public void test()
        EntityManager em= emf.createEntityManager();
        for (Order o:orderDAO.findAll())
            o=em.find(o.getClass(),o.getId());
            System.out.println(o.getLazyField());
        
        em.close();
    

【讨论】:

以上是关于从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集的主要内容,如果未能解决你的问题,请参考以下文章

带有投影和限制的休眠条件查询问题

带有休眠和触发器的乐观锁定 - “奇怪”行为

带有子查询的休眠标准

休眠:加入带有额外列的表,从一侧删除子项

无法从应用引擎访问 Google 云功能(带有入口控制)

在 SQL Server 2008 中使用休眠