Spring 声明式事务管理

本文内容纲要:

- 案例分析

- 实体类

- Spring配置信息

- 数据访问层

- 异常处理

- 逻辑业务层

- 那么要怎么办呢?

案例分析

  本案例是图书管理系统精简部分,在数据库中有3张表。分别保存图书库存、图书信息和用户信息。下面是建表SQL语句

1 DROP TABLE IF EXISTS store;

2 DROP TABLE IF EXISTS book ;

3 DROP TABLE IF EXISTS user;

4

5 -- 图书表

6 CREATE TABLE book(

7 sn VARCHAR(20) PRIMARY KEY , -- 图书编码

8 name VARCHAR(20) NOT NULL, -- 图书名称

9 price NUMERIC(9,2) NOT NULL -- 图书价格

10 );

11

12 -- 仓库表

13 CREATE TABLE store(

14 sn VARCHAR(20), -- 图书编码

15 stock INT(9) NOT NULL, -- 图书库存

16 CONSTRAINT fk_sn FOREIGN KEY (sn) REFERENCES book(sn)

17 );

18

19 -- 用户表

20 CREATE TABLE user(

21 id INT(11) PRIMARY KEY AUTO_INCREMENT, -- id

22 name VARCHAR(20) NOT NULL, -- 姓名

23 balance NUMERIC(9,2) NOT NULL DEFAULT 0 -- 余额

24 );

25

26 INSERT INTO book VALUES ('1001','Java从入门到精通',100);

27 INSERT INTO book VALUES ('1002','Spring从入门到精通',90);

28 INSERT INTO book VALUES ('1003','J2EE核心框架',80);

29

30 INSERT INTO store VALUES ('1001',50);

31 INSERT INTO store VALUES ('1002',20);

32 INSERT INTO store VALUES ('1003',10);

33

34 INSERT INTO user (name,balance) VALUES ('caoyc',150);

实体类

Book.java

1 package com.proc.bean;

2

3 public class Book {

4

5 private String sn;

6 private String name;

7 private Double price;

8

9 public String getSn() {

10 return sn;

11 }

12

13 public void setSn(String sn) {

14 this.sn = sn;

15 }

16

17 public String getName() {

18 return name;

19 }

20

21 public void setName(String name) {

22 this.name = name;

23 }

24

25 public Double getPrice() {

26 return price;

27 }

28

29 public void setPrice(Double price) {

30 this.price = price;

31 }

32

33 public String toString() {

34 return "Book [sn=" + sn + ", name=" + name + ", price=" + price + "]";

35 }

36

37 }

Store.java

1 package com.proc.bean;

2

3 /**仓库类*/

4 public class Store {

5

6 private String sn;

7 private Integer stock;

8 public String getSn() {

9 return sn;

10 }

11 public void setSn(String sn) {

12 this.sn = sn;

13 }

14 public Integer getStock() {

15 return stock;

16 }

17 public void setStock(Integer stock) {

18 this.stock = stock;

19 }

20 @Override

21 public String toString() {

22 return "Store [sn=" + sn + ", stock=" + stock + "]";

23 }

24

25

26 }

User.java

1 package com.proc.bean;

2

3 /**

4 * @author caoyc

5 *

6 */

7 public class User {

8

9 private Integer id;

10 private String name;

11 private Double balance;

12 public Integer getId() {

13 return id;

14 }

15 public void setId(Integer id) {

16 this.id = id;

17 }

18 public String getName() {

19 return name;

20 }

21 public void setName(String name) {

22 this.name = name;

23 }

24 public Double getBalance() {

25 return balance;

26 }

27 public void setBalance(Double balance) {

28 this.balance = balance;

29 }

30 @Override

31 public String toString() {

32 return "User [id=" + id + ", name=" + name + ", balance=" + balance

33 + "]";

34 }

35

36

37 }

Spring配置信息

使用db.properties记录数据库配置信息,这样便于后期维护

1 jdbc.user=root

2 jdbc.password=123456

3 jdbc.driverClass=com.mysql.jdbc.Driver

4 jdbc.jdbcUrl=jdbc\:mysql\:///test

配置applicationContext.xml信息

1     <!-- 配置自动扫描策略 -->

2 <context:component-scan base-package="com.proc"/>

3

4 <!-- 读取db.properties配置信息 -->

5 <context:property-placeholder location="classpath:db.properties"/>

6

7 <!-- 配置一个C3P0数据源 -->

8 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

9 <property name="user" value="${jdbc.user}"/>

10 <property name="password" value="${jdbc.password}"/>

11 <property name="driverClass" value="${jdbc.driverClass}"/>

12 <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>

13 </bean>

14

15 <!-- 配置一个JdbcTemplate,用来操作数据库 -->

16 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

17 <property name="dataSource" ref="dataSource"/>

18 </bean>

  这里我们使用自动扫描策略,使用的是C3P0连接池。同时使用了Spring内置的jdbc封装类JdbcTemplate

数据访问层

Java代码:BookDao.java

1 package com.proc.dao;

2

3 import org.springframework.beans.factory.annotation.Autowired;

4 import org.springframework.jdbc.core.BeanPropertyRowMapper;

5 import org.springframework.jdbc.core.JdbcTemplate;

6 import org.springframework.jdbc.core.RowMapper;

7 import org.springframework.stereotype.Repository;

8

9 import com.proc.bean.Book;

10

11 /**图书Dao*/

12 @Repository

13 public class BookDao {

14

15 @Autowired

16 private JdbcTemplate jdbcTemplate;

17

18 /**通过图书编号获取图书信息*/

19 public Book get(String sn){

20

21 String sql="SELECT * FROM book WHERE sn=?";

22 RowMapper<Book> rowMapper=new BeanPropertyRowMapper<Book>(Book.class);

23 Book book=jdbcTemplate.queryForObject(sql, rowMapper,sn);

24 return book;

25 }

26 }

StoreDao

1 package com.proc.dao;

2

3 import org.springframework.beans.factory.annotation.Autowired;

4 import org.springframework.jdbc.core.BeanPropertyRowMapper;

5 import org.springframework.jdbc.core.JdbcTemplate;

6 import org.springframework.jdbc.core.RowMapper;

7 import org.springframework.stereotype.Repository;

8

9 import com.proc.bean.Store;

10

11 /**图书仓库Dao*/

12 @Repository

13 public class StoreDao {

14

15 @Autowired

16 private JdbcTemplate jdbcTemplate;

17

18 /**通过图书编号获取图书库存信息*/

19 public Store get(String sn){

20 String sql="SELECT * FROM store WHERE sn=?";

21 RowMapper<Store> rowMapper=new BeanPropertyRowMapper<Store>(Store.class);

22 Store store=jdbcTemplate.queryForObject(sql, rowMapper,sn);

23 return store;

24 }

25

26 /**通过图书编号,修改图书库存 库存=当前库存-1*/

27 public void update(String sn){

28 String sql="UPDATE store SET stock=stock-1 WHERE sn=?";

29 jdbcTemplate.update(sql, sn);

30 }

31 }

UserDao.java

1 package com.proc.dao;

2

3 import org.springframework.beans.factory.annotation.Autowired;

4 import org.springframework.jdbc.core.BeanPropertyRowMapper;

5 import org.springframework.jdbc.core.JdbcTemplate;

6 import org.springframework.jdbc.core.RowMapper;

7 import org.springframework.stereotype.Repository;

8

9 import com.proc.bean.User;

10

11 /**用户Dao*/

12 @Repository

13 public class UserDao {

14

15 @Autowired

16 private JdbcTemplate jdbcTemplate;

17

18 /**通过用户ID获取用户信息*/

19 public User get(Integer id){

20 String sql="SELECT * FROM user WHERE id=?";

21 RowMapper<User> rowMapper=new BeanPropertyRowMapper<User>(User.class);

22 User user=jdbcTemplate.queryForObject(sql, rowMapper,id);

23 return user;

24 }

25

26 /**修改用户余额*/

27 public void update(Integer id,Double price){

28 String sql="UPDATE user SET balance=balance-? WHERE id=?";

29 jdbcTemplate.update(sql, new Object[]{price,id});

30 }

31 }

  

  这里为每个Dao都注入了一个JdbcTemplate的实例,可以使用JDBC方式操作数据库

异常处理

  考虑到有可能会出现用户余额或图书库存不足的情况,这里我们自定义了两个异常

1、库存不足异常类:

1 package com.proc.exception;

2

3 public class BookStockException extends RuntimeException{

4 public BookStockException(String msg) {

5 super(msg);

6 }

7 }

2、余额不足异常类

1 package com.proc.exception;

2

3 public class UserBalanceException extends RuntimeException {

4 public UserBalanceException(String msg) {

5 super(msg);

6 }

7 }

逻辑业务层

1、定义一个接口

1 package com.proc.service;

2

3 public interface BookShopService {

4

5 /**

6 * 购买图书

7 * @param userId 购买用户ID

8 * @param sn 图书编号

9 */

10 void purchase(Integer userId,String sn);

11 }

2、定义一个BookShopService借口实现类

1 package com.proc.service;

2

3 import org.springframework.beans.factory.annotation.Autowired;

4 import org.springframework.stereotype.Service;

5 import org.springframework.transaction.annotation.Transactional;

6

7 import com.proc.bean.Book;

8 import com.proc.bean.Store;

9 import com.proc.bean.User;

10 import com.proc.dao.BookDao;

11 import com.proc.dao.StoreDao;

12 import com.proc.dao.UserDao;

13 import com.proc.exception.BookStockException;

14 import com.proc.exception.UserBalanceException;

15

16 @Service("bookShopService")

17 public class BookShopServiceJdbcImps implements BookShopService{

18

19 @Autowired

20 private UserDao userDao;

21 @Autowired

22 private BookDao bookDao;

23 @Autowired

24 private StoreDao storeDao;

25

26

27 /**购买图书方法*/

28 public void purchase(Integer userId, String sn) {

29

30 //1:查收出图库存信息

31 Store store= storeDao.get(sn);

32 if(store.getStock()<=0){

33 throw new BookStockException("图书库存不足:"+store);

34 }

35

36 //2:查询图书信息

37 Book book=bookDao.get(sn);

38

39

40 //3:查询用户信息

41 User user=userDao.get(userId);

42 if(user.getBalance()<book.getPrice()){

43 throw new UserBalanceException("用户余额不足:"+user);

44 }

45

46 //4:修改库存

47 storeDao.update(sn);

48

49 //5:修改余额

50 userDao.update(userId, book.getPrice());

51 }

52

53 }

  

测试代码:

1 package com.proc.test;

2

3 import org.junit.Test;

4 import org.springframework.context.ApplicationContext;

5 import org.springframework.context.support.ClassPathXmlApplicationContext;

6

7 import com.proc.service.BookShopService;

8

9 public class TestShopBook {

10

11 private ApplicationContext ctx;

12 private BookShopService bookShopService;

13 {

14 ctx=new ClassPathXmlApplicationContext("applicationContext.xml");

15 bookShopService=(BookShopService) ctx.getBean("bookShopService");

16 }

17

18 @Test

19 public void purchase(){

20

21 bookShopService.purchase(1, "1001");

22 }

23 }

第一次执行:

可以成功的看到数据是符合要求的

第二次执行:

**  **我们看到会抛出异常。由于余额50元以不够买价格为100元的1001编号书籍。所有抛出异常。我们看看数据库中结果怎么样

  看起来数据是正确的。由于余额不足,那么购买不成功,所有库存和金额都不会变好。那是不是使用了事务呢?

  答案是:没有。这里没有使用事务。只是因为我们先判断了图书库和用户余额是否足够,然后再执行的修改信息。如果要测试代码。我们将我们逻辑业务层代码中第4步放到第2步前面执行

1 //1:查收出图库存信息

2 Store store= storeDao.get(sn);

3 if(store.getStock()<=0){

4 throw new BookStockException("图书库存不足:"+store);

5 }

6

7 //4:修改库存

8 storeDao.update(sn);

9

10 //2:查询图书信息

11 Book book=bookDao.get(sn);

12

13

14 //3:查询用户信息

15 User user=userDao.get(userId);

16 if(user.getBalance()<book.getPrice()){

17 throw new UserBalanceException("用户余额不足:"+user);

18 }

再次执行代码:

  虽然在此时还是或抛出余额不足的异常。但是库存却改变了。余额没有改变。所有不满足事务的要求。

那么要怎么办呢?

1、在spring配置文件中配置事务管理器

1 <!-- 配置事务管理器 -->

2 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

3 <property name="dataSource" ref="dataSource"></property>

4 </bean>

5

6 <!-- 使得事务注解生效 -->

7 <tx:annotation-driven transaction-manager="transactionManager"/>

  事务管理器需要注入一个DataSource接口类型的数据源

2、在需要使用事务管理的方法前加上@Transactional注解

1        @Transactional

2 /**购买图书方法*/

3 public void purchase(Integer userId, String sn) {

4

5 //1:查收出图库存信息

6 Store store= storeDao.get(sn);

7 if(store.getStock()<=0){

8 throw new BookStockException("图书库存不足:"+store);

9 }

10

11 //4:修改库存

12 storeDao.update(sn);

13

14 //2:查询图书信息

15 Book book=bookDao.get(sn);

16

17

18 //3:查询用户信息

19 User user=userDao.get(userId);

20 if(user.getBalance()<book.getPrice()){

21 throw new UserBalanceException("用户余额不足:"+user);

22 }

23

24 //5:修改余额

25 userDao.update(userId, book.getPrice());

26 }

再次执行代码:

  虽然还是抛出了异常。但是库存和余额都没有发生变化。这里证明是使用了事务

【总结】:基于声明式的事务就是上面用的这种方法

第一步:在spring配置中配置事务管理器

第二步:在需要使用事务的方法前面加上@Transactional注解

本文内容总结:案例分析,实体类,Spring配置信息,数据访问层,异常处理,逻辑业务层,那么要怎么办呢?,

原文链接:https://www.cnblogs.com/caoyc/p/5632198.html

以上是 Spring 声明式事务管理 的全部内容, 来源链接: utcz.com/z/362724.html

回到顶部