从控制台应用程序使用带有休眠功能的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时如何延迟加载收集的主要内容,如果未能解决你的问题,请参考以下文章