Spring @Transactional read-only propagation

后端 未结 4 498
栀梦
栀梦 2020-11-27 10:38

I\'m experimenting with using the command pattern to allow my web layer to work with Hibernate entities within the context of a single transaction (thus avoiding lazy loadin

4条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-27 11:29

    Calling readOnly=false from readOnly=true doesn't work since the previous transaction continues.

    In your example, the handle() method on your service layer is starting a new read-write transaction. If the handle method in turn calls service methods that annotated read-only, the read-only will take no effect as they will participate in the existing read-write transaction instead.

    If it is essential for those methods to be read-only, then you can annotate them with Propagation.REQUIRES_NEW, and they will then start a new read-only transaction rather than participate in the existing read-write transaction.

    Here is a worked example, CircuitStateRepository is a spring-data JPA repository.

    BeanS calls a transactional=read-only Bean1, which does a lookup and calls transactional=read-write Bean2 which saves a new object.

    • Bean1 starts a read-only tx.

    31 09:39:44.199 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''

    • Bean 2 pariticipates in it.

      31 09:39:44.230 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Participating in existing transaction

      Nothing is committed to the database.

    Now change Bean2 @Transactional annotation to add propagation=Propagation.REQUIRES_NEW

    • Bean1 starts a read-only tx.

      31 09:31:36.418 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''

    • Bean2 starts a new read-write tx

      31 09:31:36.449 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Suspending current transaction, creating new transaction with name [nz.co.vodafone.wcim.business.Bean2.createSomething]

    And the changes made by Bean2 are now committed to the database.

    Here's the example, tested with spring-data, hibernate and oracle.

    @Named
    public class BeanS {    
        @Inject
        Bean1 bean1;
    
        @Scheduled(fixedRate = 20000)
        public void runSomething() {
            bean1.startSomething();
        }
    }
    
    @Named
    @Transactional(readOnly = true)
    public class Bean1 {    
        Logger log = LoggerFactory.getLogger(Bean1.class);
    
        @Inject
        private CircuitStateRepository csr;
    
        @Inject
        private Bean2 bean2;
    
        public void startSomething() {    
            Iterable s = csr.findAll();
            CircuitState c = s.iterator().next();
            log.info("GOT CIRCUIT {}", c.getCircuitId());
            bean2.createSomething(c.getCircuitId());    
        }
    }
    
    @Named
    @Transactional(readOnly = false)
    public class Bean2 {    
        @Inject
        CircuitStateRepository csr;
    
        public void createSomething(String circuitId) {
            CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime());
    
            csr.save(c);
         }
    }
    

提交回复
热议问题