声明式事务
JdbcTemplate
简介
Spring框架对JDBC进行了封装,使用JdbcTemplate方便实现对数据库操作
准备工作
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
</dependencies>
xml配置
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>

实现添加功能
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testInsert(){
String sql = "insert into t_user values(null,?,?,?,?,?)";
jdbcTemplate.update(sql,"root","123",23,"女","123@qq.com");
}
}
实现查询功能
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testInsert(){
String sql = "insert into t_user values(null,?,?,?,?,?)";
jdbcTemplate.update(sql,"root","123",23,"女","123@qq.com");
}
@Test
public void testGetUserById(){
String sql = "select * from t_user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);
System.out.println(user);
}
@Test
public void testGetAllUser(){
String sql = "select*from t_user";
List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
list.forEach(System.out::println);
}
@Test
public void testGetCount(){
String sql = "select count(*) from t_user";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("integer = " + integer);
}
编程式事务
事务功能的相关操作全部通过自己编写的代码来实现 编程式的实现方式存在缺陷:
- 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐
- 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用
声明式事务
既然事务控制的代码有规律可循,代码的基本结构是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装 封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作
- 好处1:提高开发效率
- 好处2:消除了冗余的代码
- 好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各方面的优化
所以,我们可以总结下面两个概念:
- 编程式:自己写代码实现功能
- 声明式:通过配置让框架实现功能
基于注解的声明式事务
准备工作
CREATE TABLE `t_book` (
`book_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`book_name` VARCHAR(20) DEFAULT NULL COMMENT '图书名称',
`price` INT(11) DEFAULT NULL COMMENT '价格',
`stock` INT(10) UNSIGNED DEFAULT NULL COMMENT '库存(无符号)',
PRIMARY KEY (`book_id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `t_book`(`book_id`,`book_name`,`price`,`stock`) VALUES (1,'斗破苍
穹',80,100),(2,'斗罗大陆',50,100);
CREATE TABLE `t_user` (
`user_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` VARCHAR(20) DEFAULT NULL COMMENT '用户名',
`balance` INT(10) UNSIGNED DEFAULT NULL COMMENT '余额(无符号)',
PRIMARY KEY (`user_id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `t_user`(`user_id`,`username`,`balance`) VALUES (1,'admin',50);
无事务功能实现
public void testBuyBook(){
bookController.buyBook(1,1);
上面在数据表中设置了无符号约束,所以是不能执行成功,但是这里没有添加事务,而这样就等于一个sql语句占一个事务,并且自动提交,所以只减少了库存,用户的余额并没有变化
实现事务功能
@Transactional
public class BookServiceImpl implements BookService {
服务层是事务处理层
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx-annotation.xml")
声明式事务的只读、超时、回滚策略
事务属性:只读
对于一个查询操作来说,如果我们把它设置为只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能针对查询操作来进行优化
@Override
@Transactional(
readOnly = true
)
public void buyBook(Integer userId, Integer bookId) {
如果对增删改操作设置只读会抛出下面异常 Caused by:java.sql.SQLException:Connection is read-only.Queries leading to data modification are not allowed
事务属性:超时
  
@Override
@Transactional(
timeout = 3
)
事务属性:回滚策略
 上面两个属性一般不设置,因为默认就是所有运行时异常自动回滚
@Override
@Transactional(
noRollbackForClassName = "java.lang.ArithmeticException"
)
public void buyBook(Integer userId, Integer bookId) {
事务隔离级别
    
@Transactional( isolation = Isolation.DEFAULT)
事务的传播行为
在两个方法都有事务时,默认会调用被调用方法的事务,如果买书和买多本书在一起调用,则是使用买多本书的事务,如果想要买书的事务单独使用,则需要通过属性propagation[传播性]来进行设置
@Transactional(
propagation = Propagation.REQUIRES_NEW)

XML实现声明式事务
需要加入Jar包
通过传递性传入aspects Jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
XML实现事务
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="buyBook" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="tx" pointcut="execution(* com.atguigu.spring.service.impl.*.*(..))"></aop:advisor>
</aop:config>

表示切入点对应的连接点的所有方法
<tx:method name="*"/>
|