Spring-Tx

Spring-Tx

1.JdbcTemplate

(1)简介

​ Spring提供的一个操作数据库的技术JdbcTemplate,是对Jdbc的封装。语法风格非常接近DBUtils。  JdbcTemplate可以直接操作数据库,加快效率,而且学这个JdbcTemplate也是为声明式事务做准备,毕竟要对数据库中的数据进行操纵!

(2)方法***

JdbcTemplate主要提供以下五类方法:

  • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
  • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
  • query方法及queryForXXX方法:用于执行查询相关语句;
  • call方法:用于执行存储过程、函数相关语句。

(3)案例

1.在src下面新建一个属性配置文件db.properties

我们通常将数据库的配置信息单独放到一个文件中,这样也为了方便后期维护

jdbc.user=root
jdbc.password=root123
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test

2.建立两个实体类User类 和 商品类Goods

(实现序列化Serializable接口)(实体类一般都会实现序列化接口 为了在网络上传输必须实现)

3.配置Spring配置文件applicationContext.xml

<context:component-scan base-package="cn/chenlei">context:component-scan>

	
	<context:property-placeholder location="classpath:db.properties"/>
	
	
	<bean id="dataSource"
		class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}">property>
		<property name="password" value="${jdbc.password}">property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}">property>
		<property name="driverClass" value="${jdbc.driverClass}">property>
	bean>
	
	
	<bean id="jdbcTemplate" 
		class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource">property>
	bean>

4.测试代码

public class MainTest {
	
	private ApplicationContext ac;
	private JdbcTemplate jdbcTemplate;
	
	{
		ac = new ClassPathXmlApplicationContext("beans.xml");
		jdbcTemplate =(JdbcTemplate) ac.getBean("jdbcTemplate");
	}
	
	@Test
	public void test1(){
		System.out.println(jdbcTemplate);
		ComboPooledDataSource bean = (ComboPooledDataSource) ac.getBean("dataSource");
		System.out.println(bean.getPassword());
	}
	
	//操作数据库完成数据更新
	@Test
	public void testUpdate(){
		String sql = "update user set u_name=? where u_id=?";
		jdbcTemplate.update(sql, "隔壁老王","3");
	}
	
	//批量执行,insert,update delete方法
	//需要调用batchUpdate方法
	@Test
	public void testBatchUpdate(){
		/*String sql = "insert into user (u_name,u_jifen) values(?,?)";
		//需要申明一个对象数组,List嵌套Object【】类型的数据
		//jdbcTemplate对象中,想要完成批量操作,用jdbcTemplate.batchUpdate();
		List list = new ArrayList<>();
		list.add(new Object[] {"陈大雷","300"});
		list.add(new Object[] {"陈二雷","800"});
		list.add(new Object[] {"陈三雷","900"});
		list.add(new Object[] {"陈小雷","400"});
		
		jdbcTemplate.batchUpdate(sql, list);*/
		
		//批量删除数据
		String sql = "delete from user where u_id=?";
		List<Object[]> list = new ArrayList<>();
		list.add(new Object[] {"9"});
		list.add(new Object[] {"10"});
		list.add(new Object[] {"11"});
		jdbcTemplate.batchUpdate(sql, list);
	}
	
	//单一查询,并且制定封装规则,用RomMapper
	@Test
	public void testSelectOne(){
		String sql = "select * from user where u_id=?";
		// 在JdbcTemplate对象中 ,想要查询数据 , 一定要指定数据的封装规则 。。
		//需要创建 一个RowMapper对象  。然后指定泛型。  接口 常用的实现类 。BeanPropertyRowMapper ...可以在构造器中指定,
		//你要把查询到的数据对应到那个类的属性当中 。
		RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);
		User user = jdbcTemplate.queryForObject(sql, mapper,"8");
		System.out.println(user);
	}

	/*public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
		DataSource bean = (DataSource) ac.getBean("dataSource");
		System.out.println(bean);
	}*/
	
	@Test
	public void testInteger(){
		Integer i1 = 1000;
		Integer i2 = 1000;
//		Integer i1 = 127;
//		Integer i2 = 127;
		System.out.println(i1.equals(i2));
	}

2.Spring的声明式事务控制

(一)简介

  • 编程式事务 :由程序员编程事务代码控制代码

  • 声明式事务 :代码已经由spring写好,只需要声明哪些方法需要进行事务控制和如何进行事务控制

      编程式事务管理将数据层提交事务的代码加入逻辑层,与Spring无侵入式编程思想的主思想冲突,实际开发过程中,往往采用声明式事务管理形式
    
      通过编程式事务管理的代码不难看出,在业务逻辑层对应的业务上添加某些代码即可完成整体事务管理的操作,SpringAOP的思想,将公共的代码加入后,即可完成对应的工作,这就是声明式事务管理的核心机制。
    

(二)具体案例

基于XML的声明式事务配置

1.配置beans.xml:


<context:component-scan base-package="cn/chenlei">context:component-scan>

	
	<context:property-placeholder location="classpath:db.properties"/>
	
	
	<bean id="dataSource"
		class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}">property>
		<property name="password" value="${jdbc.password}">property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}">property>
		<property name="driverClass" value="${jdbc.driverClass}">property>
	bean>
	

	<bean id="jdbcTemplate" 
		class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource">property>
	bean>

	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource">property>
	bean>
	
	
	<tx:annotation-driven transaction-manager="transactionManager"/>

2.把Dao和service都交给Spring管理 :

UserDao.java

@Repository
public class UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	// 查询用户积分的dao方法。
	public int getUserColumnValue(String userId) {
		String sql = "select u_jifen from user where u_id=?";
		int integral = jdbcTemplate.queryForObject(sql, Integer.class, userId);
		return integral;

	}
    省略以下方法
	// 查询商品积分
	// 用户积分的扣除方法。
	// 查询商品的库存返回库存数量。
	// 商品数量的修改方法。
}

UserServiceImpl.java

@Service("userServiceImpl")
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserDao userDao;
	
	// 配置事务,用注解的方法
	@Transactional
	@Override
	public void exchangeGood(String userId, String goodsId) {

		int userIntegral = userDao.getUserColumnValue(userId);
		int goodsIntegral = userDao.getGoodsColumnValue(goodsId);
		 System.out.println(userIntegral+"==="+goodsIntegral);
		if (goodsIntegral > userIntegral) {
			throw new UserAccountException("所需积分不足以兑换!!!");
		} else {
			// 否则,可以兑换。
			// 兑换流程,先扣除用户积分
			userDao.subtractUserIntegral(userId, goodsIntegral);
			int goodsNum = userDao.getGoodsNum(goodsId);
			if (goodsNum > 0) {
				userDao.subtractGoodsNum(goodsId);
				System.out.println("兑换成功");
			} else {
				throw new GoodsException("库存不足,兑换失败");
			}
		}
	}

	@Override
	public void exchangeGoods(String userId, List<String> goodsIds) {
		//批量兑换,在下面UserServicePlus实现
	}	
}

UserServicePlus.java

@Service("usp")
public class UserServicePlus implements UserService {

    //此处的userService最终其实是他的实现类UserServiceImpl
	@Resource(name="userServiceImpl")
	public UserService userService;
	
	@Override
	public void exchangeGood(String userId, String goodsId) {
	}
    
	@Transactional
	@Override
	public void exchangeGoods(String userId, List<String> goodsIds) {
		for(String sb:goodsIds){
			userService.exchangeGood(userId,sb);
		}
	}
}

(三)具体内容

1.DataSourceTransactionManager

​ spring为不同的持久化框架提供了不同的PlatformTransactionManager 实现,为Jdbc和iBatis提供了DataSourceTransactionManager的实现类

DataSourceTransactionManager 使用Connection进行事务的控制

开启事务:connection.setAutoCommit(false);

提交事务:connection.commit();

回滚事务:connection.rollback();
2.TransactionDefinition 事务定义信息

用来定义事务相关的属性,给事务管理器用该接口提供的方法

  • getIsolationLevel:隔离级别获取

  • getPropagationBehavior:传播行为获取

  • getTimeOut:获取超时时间

  • isReadOnly:是够只读(保存、更新、删除 对数据进行操作变为可读写。查询-设置为true只读不写,事务管理器会根据之歌返回值进行优化

    都可以在配置文件中配置

3.事务的隔离级别
  • READ_UNCOMMITED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读

    READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生(默认)

    DEFAULT 使用后端数据库默认的隔离级别(spring中的的选择项)

    REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。

    SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。

    4.脏读 不可重复读 幻读
  • 脏读:

    ​ 一个事务读取另一个事务改写单未提交的数据,如果这些数据回滚,则读到的数据是脏数据,无效的,这就是脏读

  • 不可重复读:

    ​ 在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

  • 幻读:

    ​ 一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

mysql默认的隔离级别REPEATABLE_READ

Oracle 默认隔离级别 READ_COMMITTED

4.事务的传播行为

事务的传播行为用于解决两个被事务管理的方法互相调用的问题

事务传播行为的7中类型

事务传播行为类型 说明

  • PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个

    PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
    PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
    PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
    PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
    PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
    PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行 只对DataSourceTransactionManager 起效

你可能感兴趣的:(框架)