XhstormR


On a dark desert highway Cool wind in my hair


Spring Transaction

Updated on 2017-06-15

https://github.com/brettwooldridge/HikariCP

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/TransactionDefinition.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html

Concept

  • 事务的传播行为:Propagation
    • 作用:解决业务层中方法之间互相调用而产生的事务如何传递的问题。
    • 相同 事务中:REQUIRED
    • 不同 事务中:REQUIRES_NEW
    • 嵌套 事务中:NESTED
  • 事务的隔离级别:Isolation
  • 只读事务:readOnly
    • 作用:禁止 增删改 操作。
  • 发生异常 回滚事务:-Exception
  • 发生异常不回滚事务:+Exception

Configuration

build.gradle.kts

compile("org.springframework:spring-orm:+")
compile("org.springframework:spring-context:+")

compile("com.zaxxer:HikariCP:+")
compile("org.slf4j:slf4j-jdk14:+")
compile("org.postgresql:postgresql:+")

compile("org.hibernate:hibernate-core:+")
compile("org.hibernate:hibernate-hikaricp:+")
compile("org.hibernate:hibernate-infinispan:+")

hibernate.properties

hibernate.show_sql=true
hibernate.format_sql=false
hibernate.hbm2ddl.auto=update
hibernate.dialect=org.hibernate.dialect.PostgreSQL95Dialect
hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext

hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider
hibernate.hikari.dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
hibernate.hikari.dataSource.url=jdbc:postgresql://127.0.0.1:5432/postgres
hibernate.hikari.username=123
hibernate.hikari.password=123456
#hibernate.hikari.driverClassName=org.postgresql.Driver
#hibernate.hikari.jdbcUrl=jdbc:postgresql://127.0.0.1:5432/postgres

hibernate.cache.use_query_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.infinispan.InfinispanRegionFactory
hibernate.cache.infinispan.cfg=org/hibernate/cache/infinispan/builder/infinispan-configs-local.xml

hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <mapping class="entity.Account"/>
    </session-factory>
</hibernate-configuration>

编程式事务管理

基于 TransactionTemplate

entity

Account
package entity

import org.hibernate.annotations.Cache
import org.hibernate.annotations.CacheConcurrencyStrategy
import javax.persistence.*

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)     使用二级缓存
data class Account(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int? = null,
        var name: String? = null,
        var money: Int = 0
)

Hibernate 缓存策略:
一级缓存:强制开启,以 Session        为单位。
二级缓存:默认关闭,以 SessionFactory 为单位。

dao

AccountDao
package dao

interface AccountDao {
    fun outMoney(id: Int, money: Int)
    fun inMoney(id: Int, money: Int)
}
AccountDaoImpl
package dao

import entity.Account
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.orm.hibernate5.HibernateTemplate
import org.springframework.stereotype.Component

@Component
open class AccountDaoImpl : AccountDao {
    @Autowired
    private lateinit var hibernateTemplate: HibernateTemplate

    override fun outMoney(id: Int, money: Int) {
        val account = hibernateTemplate.get(Account::class.java, id).apply { this.money -= money }
        hibernateTemplate.update(account)
    }

    override fun inMoney(id: Int, money: Int) {
        val account = hibernateTemplate.get(Account::class.java, id).apply { this.money += money }
        hibernateTemplate.update(account)
    }
}

service

AccountService
package service

interface AccountService {
    fun transfer(out: Int, `in`: Int, money: Int)
}
AccountServiceImpl
package service

import dao.AccountDao
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.transaction.support.TransactionTemplate

@Component
open class AccountServiceImpl : AccountService {
    @Autowired
    private lateinit var accountDao: AccountDao
    @Autowired
    private lateinit var transactionTemplate: TransactionTemplate     注入事务管理模板

    override fun transfer(out: Int, `in`: Int, money: Int) {
        transactionTemplate.execute {     在同一个事务中执行
            accountDao.outMoney(out, money)
            accountDao.inMoney(`in`, money)
        }
    }
}

AppConfig

import org.hibernate.SessionFactory
import org.hibernate.boot.MetadataSources
import org.hibernate.boot.registry.StandardServiceRegistryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.orm.hibernate5.HibernateTemplate
import org.springframework.orm.hibernate5.HibernateTransactionManager
import org.springframework.transaction.support.TransactionTemplate

@Configuration
@ComponentScan(basePackages = arrayOf("dao", "service"))
open class AppConfig {
    @Bean
    open fun sessionFactory(): SessionFactory {
        val registry = StandardServiceRegistryBuilder().configure().build()
        return MetadataSources(registry).buildMetadata().buildSessionFactory()
    }

    @Bean
    open fun hibernateTemplate(sessionFactory: SessionFactory): HibernateTemplate {
        return HibernateTemplate(sessionFactory)
    }

    @Bean
    open fun transactionManager(sessionFactory: SessionFactory): HibernateTransactionManager {
        return HibernateTransactionManager(sessionFactory)
    }

    ----

    @Bean
    open fun transactionTemplate(transactionManager: HibernateTransactionManager): TransactionTemplate {
        return TransactionTemplate(transactionManager)     事务管理模板
    }
}

Main

import entity.Account
import org.hibernate.SessionFactory
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import service.AccountService

fun main(args: Array<String>) {
    System.setProperty("java.net.preferIPv4Stack", "true")
    val context = AnnotationConfigApplicationContext(AppConfig::class.java)

    context.getBean(SessionFactory::class.java).openSession().use {
        it.beginTransaction()
        it.save(Account(name = "张三", money = 1000))
        it.save(Account(name = "李四", money = 1000))
        it.save(Account(name = "王五", money = 1000))
        it.transaction.commit()
    }

    context.getBean(AccountService::class.java).transfer(1, 2, 400)

    context.destroy()
}

声明式事务管理

基于 TransactionProxyFactoryBean

service.AccountServiceImpl

package service

import dao.AccountDao
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

@Component
open class AccountServiceImpl : AccountService {
    @Autowired
    private lateinit var accountDao: AccountDao

    override fun transfer(out: Int, `in`: Int, money: Int) {
        accountDao.outMoney(out, money)
        accountDao.inMoney(`in`, money)
    }
}

AppConfig

import org.hibernate.SessionFactory
import org.hibernate.boot.MetadataSources
import org.hibernate.boot.registry.StandardServiceRegistryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.orm.hibernate5.HibernateTemplate
import org.springframework.orm.hibernate5.HibernateTransactionManager
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean
import service.AccountService
import java.util.*

@Configuration
@ComponentScan(basePackages = arrayOf("dao", "service"))
open class AppConfig {
    @Bean
    open fun sessionFactory(): SessionFactory {
        val registry = StandardServiceRegistryBuilder().configure().build()
        return MetadataSources(registry).buildMetadata().buildSessionFactory()
    }

    @Bean
    open fun hibernateTemplate(sessionFactory: SessionFactory): HibernateTemplate {
        return HibernateTemplate(sessionFactory)
    }

    @Bean
    open fun transactionManager(sessionFactory: SessionFactory): HibernateTransactionManager {
        return HibernateTransactionManager(sessionFactory)
    }

    ----

    @Bean
    open fun accountServiceProxy(accountService: AccountService, transactionManager: HibernateTransactionManager): TransactionProxyFactoryBean {
        return TransactionProxyFactoryBean().apply {     代理对象
            this.setTarget(accountService)     设置目标对象(增强对象)
            this.setTransactionManager(transactionManager)     设置事务管理器
            this.setTransactionAttributes(Properties().apply { this["*"] = "PROPAGATION_REQUIRED" })     设置事务属性
        }
    }
}

Main

import entity.Account
import org.hibernate.SessionFactory
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import service.AccountService

fun main(args: Array<String>) {
    System.setProperty("java.net.preferIPv4Stack", "true")
    val context = AnnotationConfigApplicationContext(AppConfig::class.java)

    context.getBean(SessionFactory::class.java).openSession().use {
        it.beginTransaction()
        it.save(Account(name = "张三", money = 1000))
        it.save(Account(name = "李四", money = 1000))
        it.save(Account(name = "王五", money = 1000))
        it.transaction.commit()
    }

    context.getBean("accountServiceProxy", AccountService::class.java).transfer(1, 2, 400)     使用代理对象

    context.destroy()
}

基于注解(推荐)

service.AccountServiceImpl

package service

import dao.AccountDao
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional

@Component
@Transactional     使用事务
open class AccountServiceImpl : AccountService {
    @Autowired
    private lateinit var accountDao: AccountDao

    override fun transfer(out: Int, `in`: Int, money: Int) {
        accountDao.outMoney(out, money)
        accountDao.inMoney(`in`, money)
    }
}

AppConfig

import org.hibernate.SessionFactory
import org.hibernate.boot.MetadataSources
import org.hibernate.boot.registry.StandardServiceRegistryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.orm.hibernate5.HibernateTemplate
import org.springframework.orm.hibernate5.HibernateTransactionManager
import org.springframework.transaction.annotation.EnableTransactionManagement

@Configuration
@EnableTransactionManagement     启用注解驱动的事务管理
@ComponentScan(basePackages = arrayOf("dao", "service"))
open class AppConfig {
    @Bean
    open fun sessionFactory(): SessionFactory {
        val registry = StandardServiceRegistryBuilder().configure().build()
        return MetadataSources(registry).buildMetadata().buildSessionFactory()
    }

    @Bean
    open fun hibernateTemplate(sessionFactory: SessionFactory): HibernateTemplate {
        return HibernateTemplate(sessionFactory)
    }

    @Bean
    open fun transactionManager(sessionFactory: SessionFactory): HibernateTransactionManager {
        return HibernateTransactionManager(sessionFactory)
    }
}

Main

import entity.Account
import org.hibernate.SessionFactory
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import service.AccountService

fun main(args: Array<String>) {
    System.setProperty("java.net.preferIPv4Stack", "true")
    val context = AnnotationConfigApplicationContext(AppConfig::class.java)

    context.getBean(SessionFactory::class.java).openSession().use {
        it.beginTransaction()
        it.save(Account(name = "张三", money = 1000))
        it.save(Account(name = "李四", money = 1000))
        it.save(Account(name = "王五", money = 1000))
        it.transaction.commit()
    }

    context.getBean(AccountService::class.java).transfer(1, 2, 400)

    context.destroy()
}
TOP