简体   繁体   English

如何将 Spring 事务管理与 Hibernate 集成?

[英]How can I integrate Spring transaction management with Hibernate?

I've been trying to use HibernateTransactionManager to manage transactions in my service layer, but it doesn't work.我一直在尝试使用 HibernateTransactionManager 来管理我的服务层中的事务,但它不起作用。 Java class configuration for creating PlatformTransactionManager: Java class 用于创建 PlatformTransactionManager 的配置:

@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;
    }
}

Later in this method call there are two calls to the persistence layer and a Runtime Exception thrown in the end.在这个方法调用的后面,有两次对持久层的调用,最后抛出了一个运行时异常。 So I want these two calls to the repository to be rolled back.所以我希望回滚这两个对存储库的调用。

@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 for managing products:用于管理产品的存储库:

@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);

    }
}

I don't understand why the changes made prior to the RuntimException thrown in my service method are not rolled back: I use the same session factory to initialize Platform Transaction Manager and to make changes via Session in my repository.我不明白为什么在我的服务方法中抛出 RuntimException 之前所做的更改没有回滚:我使用相同的 session 工厂来初始化平台事务管理器并通过我的存储库中的 Session 进行更改。 Also, I've got this line in my logger另外,我的记录器中有这条线

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

If I'm right, when using only one resource (which is a Hibernate repository in my case), you don't need a Global Transaction Provider like Atomikos.如果我是对的,当只使用一个资源(在我的例子中是 Hibernate 存储库)时,您不需要像 Atomikos 这样的全局事务提供程序。 I think there suppose to be 3 transaction: one outer (the service call) and 2 inner (to repositories).我认为假设有 3 个事务:一个外部(服务调用)和 2 个内部(存储库)。 The idea is that if one of the inner transactions fails it should cause the outer transaction rollback meaning all two calls to repositories would be rolled back.这个想法是,如果内部事务之一失败,它应该导致外部事务回滚,这意味着对存储库的所有两个调用都将被回滚。

Inside updateProduct(Product) you have opened programmatic transaction again in addition to declarative at service level.在 updateProduct(Product) 内部,除了服务级别的声明之外,您还再次打开了程序化事务。 So it will ignore Spring container managed transaction manager and will use its own in isolation.因此它将忽略 Spring 容器管理的事务管理器,并将单独使用它自己的。 Can you please remove that and retry.能否请您删除并重试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM