从控制台应用程序使用带有hibernate的spring-data-jpa时如何进行延迟加载

aki*_*aki 31 hibernate spring-data-jpa

我有一个小的控制台应用程序,我使用spring-data-jpa和hibernate.在独立控制台应用程序中使用spring-data-jpa及其存储库时,我真的无法弄清楚如何延迟初始化集合.这是我的一些代码:

@Entity
public class User {
...
    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="USER_ORDER_ID")
    private Set<Order> orders = new HashSet<Order>();
...
}
Run Code Online (Sandbox Code Playgroud)

库:

public interface UserRepository extends PagingAndSortingRepository<User, Long> {

    public ArrayList<User> findByFirstNameIgnoreCase(String firstName);
}
Run Code Online (Sandbox Code Playgroud)

服务impl:

@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;
}
Run Code Online (Sandbox Code Playgroud)

我的主要方法:

...
user = userRepository.findByFirstNameIgnoreCase("john").get(0);
orders = user.getOrders();
for (Order order : orders) {
  LOGGER.info("getting orders: " + order.getId());
}    
Run Code Online (Sandbox Code Playgroud)

foreach循环获得异常:

EVERE:懒得初始化角色集合:com.aki.util.User.orders,没有关闭会话或会话org.hibernate.LazyInitializationException:懒得初始化角色集合:

请注意,从具有某种OpenSessionInViewFilter的webapp运行此问题时,我没有遇到此问题.

zag*_*gyi 13

一种解决方案可以是制作User.orders一个热切的收藏品

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

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

如果您知道在大多数情况下您将需要用户订单,那么切实地提取关联是有意义的.否则,您必须确保在事务中初始化/加载集合.在OpenSessionInViewFilter你提到确保了交易的请求处理过程中保持打开状态,这就是为什么你没有在YOUT的webapp这个问题.

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

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Override
    protected void doInTransactionWithoutResult(TransactionStatus status) {
    ...
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 这不是我要找的.也许我不清楚我想做懒惰的初始化.我已经编辑了我的问题. (4认同)
  • 上面的`getSomeOrders()`方法无法初始化`userOrders`集的原因是因为`user.getOrders()`只返回一个代理而不是一个真实集(这是延迟加载的整个点).您必须在代理上调用方法才能将其初始化.一种常见的技术是调用`size()`.所以尝试添加`userOrders.size()`它会起作用. (4认同)
  • 用`@ Transactional`注释方法的效果与将其包装在`TransactionTemplate`中的效果大致相同,后者实际上是在打开/提交事务。如果您的@Transactional没有任何作用,可能是由于多种原因。一种典型的情况是将其放在不是接口方法的实现的方法上。(请记住,Spring AOP默认使用JDK代理,这意味着它只能影响接口上定义的方法。) (3认同)

aki*_*aki 8

但是,我找到了一种方法.这个方法在我的服务实现中:

public Set<Order> fetchUserOrders(Long userId) {
    User user = userRepository.findOne(userId);
    Hibernate.initialize(user.getOrders());
    Set<Order> orders = user.getOrders();
    return orders;
}
Run Code Online (Sandbox Code Playgroud)

注意:这是因为@zagyi在他的一条评论中消化了,但也要注意语句:Hibernate.initialize(user.getOrders());没有这个,集合仍然不会被初始化,你会得到一个错误.

  • 只是一句话:'Hibernate.initialize()`将你的应用程序绑定到Hibernate.如果你不在乎,这没关系.如果你关心,请使用`user.getOrders().size()`代替.虽然这根本不会反映您的意图,但它不依赖于非JPA调用,并且您始终可以使用更具描述性的名称声明一个辅助方法,该方法接收一个集合并在其上调用`size()`. (8认同)
  • 当它是 spring-data-jpa 和 hibernate 时,Hibernate.initialize 和 .getter().size() 都不起作用,我写这个只是为了节省其他人的时间...... (2认同)