首页 > 解决方案 > 如何将 Spring 事务管理与 Hibernate 集成?

问题描述

我一直在尝试使用 HibernateTransactionManager 来管理我的服务层中的事务,但它不起作用。用于创建 PlatformTransactionManager 的 Java 类配置:

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:hibernateConfig.properties")
public class HibernateConfig {

    @Value("${hibernate.dialect}")
    private String dialect;

    //Other hibernate properties 

    @Autowired
    private DataSource dataSource;

    private Properties hibernateProperties() {
        Properties hibernateProperties = new Properties();

        hibernateProperties.put("hibernate.dialect", dialect);
        //Other hibernate properties removed here for brevity 

        return hibernateProperties;
    }

    @Bean
    @DependsOn("dataSource")
    public SessionFactory sessionFactory() throws IOException {
        LocalSessionFactoryBean  sessionFactoryBean =
                new LocalSessionFactoryBean();

        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setPackagesToScan("com.ldp.vigilantBean.domain");
        sessionFactoryBean.setHibernateProperties(hibernateProperties());
        sessionFactoryBean.afterPropertiesSet();

        return sessionFactoryBean.getObject();
    }

    @Bean
    @DependsOn("sessionFactory")
    public PlatformTransactionManager platformTransactionManager() throws IOException {
        HibernateTransactionManager txManager = new HibernateTransactionManager();
        txManager.setSessionFactory(sessionFactory());
        txManager.afterPropertiesSet();

        return txManager;
    }
}

在这个方法调用的后面,有两次对持久层的调用,最后抛出了一个运行时异常。所以我希望回滚这两个对存储库的调用。

@Override
    @Transactional(rollbackFor = { RuntimeException.class })
    public boolean removeCartItem(Long cartItemId) {

        Cart cart = getCartOutOfContext();

        Optional<CartItem> optCartItemToRemove =
                cart.getCartItems()
                    .stream()
                    .filter(cartItem -> cartItem.getCartItemId().equals(cartItemId))
                    .findAny();

        if (optCartItemToRemove.isPresent()) {

            CartItem cartItemToRemove = optCartItemToRemove.get();

            //There is a bug with PersistentSet in Hibernate that makes
            //using .contains() and .remove() methods of Set interface unpredictable.
            //This is a workaround: reset the whole set.
            cart.setCartItems(
                cart.getCartItems().stream()
                                   .filter(cartItem -> !cartItem.equals(cartItemToRemove))
                                   .collect(Collectors.toSet())
            );

            Optional<Product> optProduct =
                    productRetrievalRepository.getProductById(cartItemToRemove.getProduct().getProductId());
            if (!optProduct.isPresent())
                throw new IllegalArgumentException("Specified product not found");
            Product productToRemove = optProduct.get();
            productToRemove.setUnitsInOrder(productToRemove.getUnitsInOrder() - cartItemToRemove.getQuantity());
            //First call
            productAlterRepository.updateProduct(productToRemove);

            //Second call
            cartRepository.updateCart(cart);
            if (true) throw new RuntimeException("An exception to check transactions");

            return true;
        } else
            return false;

    }

用于管理产品的存储库:

@Repository
class ProductAlterRepositoryImpl implements ProductAlterRepository {

    private SessionFactory sessionFactory;

    public ProductAlterRepositoryImpl(
            @Autowired
            SessionFactory sessionFactory) {

       this.sessionFactory = sessionFactory;
    }

    @Override
    public Optional<Product> updateProduct(Product product) {

        try (Session session = sessionFactory.openSession()) {

            session.getTransaction().begin();

            session.merge(product);

            session.getTransaction().commit();
        }

        return Optional.of(product);

    }
}

我不明白为什么在我的服务方法中抛出 RuntimException 之前所做的更改没有回滚:我使用相同的会话工厂来初始化平台事务管理器并通过我的存储库中的 Session 进行更改。另外,我的记录器中有这条线

*** LOG4J *** HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]

如果我是对的,当只使用一个资源(在我的例子中是 Hibernate 存储库)时,您不需要像 Atomikos 这样的全局事务提供程序。我认为假设有 3 个事务:一个外部(服务调用)和 2 个内部(存储库)。这个想法是,如果内部事务之一失败,它应该导致外部事务回滚,这意味着对存储库的所有两个调用都将被回滚。

标签: javaspringhibernatespring-transactions

解决方案


在 updateProduct(Product) 内部,除了服务级别的声明之外,您还再次打开了程序化事务。因此它将忽略 Spring 容器管理的事务管理器,并将单独使用它自己的。能否请您删除并重试。


推荐阅读