1.4 Spring之事务管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lwz45698752/article/details/88840148


事务概述

  • Spring生态圈满足各种类型应用开发
  • 事务管理属于Spring框架的数据访问部分

在这里插入图片描述

事务特点(原则)

在这里插入图片描述- 执行事务,要么全部成功,要么全部失败(事务操作的完整性)

  • 一致性(一边银行账户-1000,一边取得1000)
  • 永久性:取完钱后要回写到银行账户的数据库中,从而永久保存

Java事务

在这里插入图片描述在这里插入图片描述

  • JTA事务:该事务模式基于应用服务器或中间件(它们提供JTA事务的包装)
  • 容器事务:掌握编程范式

事务差异

在这里插入图片描述

  • JTA事务能跨数据库,因为其与JDBC事务后台实现机制不同
  • 容器事务局限于EJB(企业Javabean)相关操作

事务接口

在这里插入图片描述

  • 三个核心接口
  • 黄色的实现类不止这些,因为更多场景
  • 事务管理器:按照给定的事务规则(TransactionDefinition)进行提交,回滚操作,即其负责执行事务
  • TransactionDefinition定义事务
  • 根据事务状态值(TransactionStatus),控制事务
  • getTransaction得到事务状态对象

事务属性

在这里插入图片描述

  • 事务属性用于控制事务的精细化操作
  • Spring支持事务属性范围的定义,即通过TransactionDefinition接口来实现

数据读取类型

在这里插入图片描述

  • 脏读: 事务A修改数据,未提交,事务B访问并使用该数据(还没提交啊)
  • 不可重复读:控制事务使其不重复读,从而避免脏读,即两次读取的数据不同

在这里插入图片描述

  • 幻读:事务A想要修改某表的全部性别为男,事务B新插入一条数据,性别为女,事务A表示:怎么没有全部改完?

事务隔离级别

在这里插入图片描述- 事务隔离级别影响事务的读取的结果

  • 应用于事务并发时
  • Serializable级别:每一个操作都要通过事务底层控制——》最慢

事务传播行为

在这里插入图片描述

  • 理解为事务的嵌套

事务是否只读

在这里插入图片描述

  • Oracle事务设置为只读——》则数据库不启动回滚字段,不记录回滚日志,从而减少数据库操作,提高访问效率

事务超时

在这里插入图片描述在这里插入图片描述

  • 明确是Spring下的事务管理才会出现上述说明
  • 遇到检查型异常,则手动抛出运行期异常,那么也能回滚

Spring事务状态

在这里插入图片描述

  • 该接口能判定和控制事务状态

事务管理

  • Spring的事务管理是对Java事务管理的高度抽象和封装
  • 事务管理器方式:手动处理较多事务管理代码
  • 模板事务编程方式:封装好常用代码,使得coder能集中业务逻辑
  • JdbcTemplate类:具体操作类,实现数据操作的切入点

在这里插入图片描述在这里插入图片描述

基于事务管理器

TemplateUtils类

public class TemplateUtils {
	private final static  String dbDriver = "com.mysql.jdbc.Driver" ;
	private final static  String dbUrl = "jdbc:mysql://127.0.0.1:3306/test" ;
	private final static  String dbUser = "root";
	private final static  String dbPwd = "root";
	
	private static BasicDataSource dataSource ;
	//静态方式:创建连接数据源
	static {
	//创建DBCP简单数据源并初始化相关数据源属性
	//private void createSimpleDataSource(){
		dataSource = new   BasicDataSource() ;
		dataSource.setDriverClassName(dbDriver);
		dataSource.setUrl(dbUrl);
		dataSource.setUsername(dbUser);
		dataSource.setPassword(dbPwd);
		//指定数据库连接池初始连接数
		dataSource.setInitialSize(10);
		//设定同时向数据库申请的最大连接数
		dataSource.setMaxTotal(50);
		//设置连接池中保持的最少连接数量
		dataSource.setMinIdle(5);
	//}
	}
	public static TransactionTemplate getTransactionTemplate() {  
        PlatformTransactionManager txManager = new DataSourceTransactionManager(  
                dataSource);  
        return new TransactionTemplate(txManager);  
    }  
  
    public static JdbcTemplate getJdbcTemplate() {  
        return new JdbcTemplate(dataSource);  
    }  
  
    public static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {  
        return new NamedParameterJdbcTemplate(dataSource);  
    }  
  
    public static SimpleJdbcInsert getSimpleJdbcTemplate() {  
        return new SimpleJdbcInsert(dataSource);  
    }  
    
    /**
     * //获取事务管理器:TransactionManager
     * 根据需要,可以是如JDBC、Hibernate,这里定义JDBC事务管理其
     * @return DataSourceTransactionManager
     */
    public static DataSourceTransactionManager getDataSourceTransactionManager(){
    	 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    	 // 设置数据源:此事务数据源须和正式事务管理器的数据源一致
    	 dataSourceTransactionManager.setDataSource(dataSource);
    	 return dataSourceTransactionManager;
    }
}

事务管理类

public class TransManagerExample {

	public static void main(String[] args) {
		DataSourceTransactionManager dtm = TemplateUtils.getDataSourceTransactionManager();
		// 创建事务管理器属性对象——缺省的属性定义对象
		DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
		// 根据需要,设置事务管理器的相关属性
		transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
		// 获得事务状态对象
		TransactionStatus ts = dtm.getTransaction(transDef); 
		//基于当前事务管理器,获取操作数据库的JDBC模板对象
		JdbcTemplate jt = new JdbcTemplate(  dtm.getDataSource());  
	try{
		jt.update("update books set price=112.5,name='炎黄传奇'  where isbn='128-166-890-China' ");
		//其它数据操作如增删
		dtm.commit(ts); //如果不commit,则更新无效果
	} catch (Exception e) {  
        dtm.rollback(ts);  
        e.printStackTrace();  
    } 
		
	}

}
  • 关注编程范式

  • 通过工具类获取事务管理器

  • 操作接口自己写
    -工具类

    • 基于数据源(连接数据库的基本通道)获取事务管理器
    • 静态方式创建数据源并初始化相关属性
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED //静态变量方式提供传播行为属性

  • 基于事务管理器方式有较多非业务逻辑代码,且事务处理的相同部分(提交,回滚)可抽象化——》基于事务模板


基于事务模板的编程

public class ProTransExample {
	// 事务模板:第二种事务编程模式
	// private TransactionTemplate transactionTemplate ;

	// 数据持久化操作,不带返回值的方法
	public void addBook(Book book) {
		// 获取事务模板对象,通过工具类获取相应模板
		TransactionTemplate tt = TemplateUtils.getTransactionTemplate();
		// 可设置事务属性,如隔离级别、超时时间等,如:
		// tt.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
		tt.execute(new TransactionCallbackWithoutResult() {
			protected void doInTransactionWithoutResult(TransactionStatus s) {//后台会使用事务状态参数s
				try {
					// 数据库操作1
					// JdbcTemplate jdbcTemplate
					// =TemplateUtils.getJdbcTemplate();
					// jdbcTemplate.execute(sql);
					// 简单模板化新增数据
					SimpleJdbcInsert simpleInsert = TemplateUtils.getSimpleJdbcTemplate();
					simpleInsert.withTableName("books").usingColumns("isbn", "name", "price", "pubdate");
					Map<String, Object> parameters = new HashMap<String, Object>();
					parameters.put("isbn", book.getIsbn());
					parameters.put("name", book.getName());
					parameters.put("price", book.getPrice());
					parameters.put("pubdate", book.getPubdate());
					simpleInsert.execute(parameters);
					System.out.println("新增数据成功!");
					// 或者DAO数据操作模式:
					// BookDAO.save(book);
				} catch (Exception e) {
					s.setRollbackOnly();
					e.printStackTrace();
				}
			}
		});
	}

//带返回值的方法
	public Book findBookByIsbn(String isbn) {
		TransactionTemplate tt = TemplateUtils.getTransactionTemplate();
		Book book = null;
		@SuppressWarnings("unchecked")
		List<Map<String, Object>> books = (List<Map<String, Object>>) tt.execute(new TransactionCallback<Object>() {
			public Object doInTransaction(TransactionStatus arg0) {
				JdbcTemplate jdbcTemplate = TemplateUtils.getJdbcTemplate();
				return jdbcTemplate
						.queryForList("select isbn,name,price,pubdate from books where isbn ='" + isbn + "'");
			}
		});
		if (books != null) {// 封装获取的数据,转换为实体类,一些持久化框架会封装这些代码,主要通过DAO注解方式实现
			Map<String, Object> m = (Map) books.get(0);
			book = new Book();
			book.setIsbn(m.get("isbn").toString());
			book.setName(m.get("name").toString());
			book.setPrice((Float) m.get("price"));
			book.setPubdate((Date) m.get("pubdate"));
		}
		return book;
	}

	

}

  • 分为事务处理有无返回值,从而对应不同接口
  1. 获取事务模板
  2. 事务模板执行相应方法(execute方法传入匿名内部类,匿名内部类定义具体事务操作)

总结

在这里插入图片描述

  • Spring事务管理是基于数据库事务管理之上——》要有数据源
  • 除了JdbcTemplate还有其他可用类,如各种模板类
  • 编程式事务管理不常用,主要体会后台运行机制

声明式事务管理

在这里插入图片描述

  • 实现原理:基于AOP机制,对方法前后进行拦截
  • 方法前,开启事务,方法后,根据方法执行情况,提交或回滚
  • 声明式事务管理使得我们只关注业务逻辑,其他事务层管理交给spring容器
  • 声明式业务管理不需要掺杂事务管理代码(XML方式来实现)

  • 前三种类型过时
  • 拦截器即事务的底层支持,新版不需要手动配置拦截器事务了,直接使用声明式拦截器模式:tx拦截器
  • 全注释:注解方式

实现方式

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    http://www.springframework.org/schema/tx     
    http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- 引入数据库连接属性配置文件 -->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:database.properties" />
    </bean>
	<!-- 配置数据源 -->  
	<!-- #DBCP数据库连接池配置属性详细内容可参考官网描述:
       #http://commons.apache.org/proper/commons-dbcp/configuration.html 
       -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="${initialSize}"></property>     //引用属性文件的参数
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="${maxIdle}"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="${minIdle}"></property>
    </bean>

    <!-- jdbc事务管理器 -->  
    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource" />	
    </bean>  

    <!-- 2、注释模式事务:启动使用注解实现声明式事务管理的支持   -->
    <tx:annotation-driven transaction-manager="txManager" />
    <!-- 要创建的事务服务对象 -->
    <bean id="bookService" class="com.mooc.service.BookServiceImpl">
        <property name="dataSource" ref="dataSource"/> //数据源属性引用
    </bean>
    <!-- 1、通过事务通知的(AOP)模式实现事务管理
    事务通知-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--事务语义定义... -->
        <tx:attributes>
            <!-- 以get开头的所有方法都为只读事务 -->
            <tx:method name="find*"  read-only="true"/>
            <!-- 其它方法使用默认事务设置 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!-- 确保上述事务通知对定义在BookService接口中的方法都起作用,
           即对每个方法都开启一个对应的事务 -->
    <aop:config>
    //定义切点,具体事务拦截的方法定义见上面
    //类或其子类的相应方法进行事务拦截
        <aop:pointcut id="bookServiceOperation" expression="execution(* com.mooc.service.BookService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="bookServiceOperation"/>
        //引用前面事务通知——》从而对方法切入事务(执行相应方法,会自动进行事务处理)
    </aop:config>


</beans>  
  • XML文件的头文件处作用:配置规范文件,引入对应命名空间,进行标准化XML的配置

  • 引入外部属性文件(包含数据库连接参数)来配置数据库

  • 配置事务管理器——》该事务管理器基于JDBC事务——》使用JDBC管理器(一定要有数据源)

  • 关注tx拦截器属性配置

  • ApplicationContext ctx = new ClassPathXmlApplicationContext("/springContext.xml");//创建spring的IOC容器

  1. 引入配置文件(含数据源参数),然后进行数据源配置(也可以用连接池配置)
  2. 事务管理器配置(引用了数据源)
  3. 启动注解扫描
  4. 配置事务管理的bean
  5. 配置事务通知,如tx:advice
  6. AOP配置:切点和切面(引用了事务通知)

注解方式

@Transactional
public class XbeanServiceImpl  implements XbeanService{
    @Override
    public Xbean getXbean(int id) {
        Xbean xb = new Xbean() ;
        xb.setName("业务Bean的ID="+id);
        xb.setName("Bean默认名称");
        return xb ;
    }

    @Override
    public void insertXbean(Xbean xb) {
        throw new UnsupportedOperationException();
    }
}
  • 要开启注解扫描: <tx:annotation-driven transaction-manager=“txManager” />
  • 可以单独注解某一方法,对其进行事务管理,而不是注解整个类
  • 通过注解可以定义事务属性

在这里插入图片描述

实例

图书实体类

public class Book {
    private String isbn;
    private String name;
    private float price;
    private Date pubdate;

    public Book() {
    }

    public Book(String isbn, String name, float price, Date pubdate) {
        super();
        this.isbn = isbn;
        this.name = name;
        this.price = price;
        this.pubdate = pubdate;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public Date getPubdate() {
        return pubdate;
    }

    public void setPubdate(Date pubdate) {
        this.pubdate = pubdate;
    }

    @Override
    public String toString() {
        return "Book{" +
                "isbn='" + isbn + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", pubdate=" + pubdate +
                '}';
    }
}

操作实体类接口

ublic interface BookService {
    /**
     * 向数据库新增加书籍信息
     * @param book 新增的书籍对象
     */
    void insertBook(Book book);

    /**
     *  更新数据库里的对应书籍信息
     * @param book 待更新的书籍对象
     */
    void updateBook(Book book);

    /**
     * 根据唯一编号查找对应的书籍
     * @param isbn 书籍的唯一编号
     * @return 返回查找的书籍对象
     */
    Book findBookByIsbn(String isbn);
}

接口实现类


/**
 * POJO服务类
 */
public class BookServiceImpl extends JdbcDaoSupport implements BookService {

    //r若不继承JdbcDaoSupport,则可按下面方式进行数据源的注入
    /*private JdbcTemplate jdbcTemplate;
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }*/
        @Override
    public void insertBook(Book book) {
            String sql= "insert books(isbn,name,price,pubdate) values"
                    +"('"+book.getIsbn()+"','"+book.getName()+"',"+book.getPrice()+",'"+book.getPubdate()+"')" ;
        this.getJdbcTemplate().update(sql);
    }

    @Override
    public void updateBook(Book book) {
        String sql = "update books set name='"+book.getName()+"' where isbn='"+book.getIsbn()+"' ";
        this.getJdbcTemplate().update(sql);
    }

    @Override
    public Book findBookByIsbn(String isbn) {
        String sql =   "select isbn,name,price,pubdate from books where isbn ='" + isbn + "'" ;
       List<Book> books =  this.getJdbcTemplate().query(sql,new RowMapper<Book>() {
            public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
                Book bk  = new Book();
                bk.setIsbn(rs.getString("isbn"));
                bk.setName(rs.getString("name"));
                bk.setPrice(rs.getFloat("price"));
                bk.setPubdate(rs.getDate("pubdate"));
                return bk;
            }
        });
        return books!=null?books.get(0):null ;
    }
}

  • 接口实现类要有数据源(和数据库交互),且最好继承帮助类
  • 帮助类提供了getJdbcTemplate()方法,本质还是基于JDBC模板类进行操作
  • 不继承帮助类,则要定义自己的模板,如上图注释处

编程式与声明式事务管理辨析

在这里插入图片描述
在这里插入图片描述

事务管理器选择

在这里插入图片描述

  • 编程式能手动控制事务边界和属性
  • 两者底层都是基于AOP机制
  • 声明式的事务管理由容器控制,不需要用户
  • 设置事务名称后可以为关联操作提供相应支持
  • 跨数据库,分布式的事务管理使用JTA事务管理器

总结

  • Spring封装传统的事务管理机制

猜你喜欢

转载自blog.csdn.net/lwz45698752/article/details/88840148
1.4