IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 03-JdbcTemplate与声明式事务 -> 正文阅读

[Java知识库]03-JdbcTemplate与声明式事务

笔记来源:尚硅谷Spring框架视频教程(spring5源码级讲解)

JdbcTemplate与声明式事务

1、JdbcTemplate

1.1、概述

前面我们已经学习了 Spring 中的Core Container核心部分和AOPAspects等面向切面编程部分,接下来就是Data Access/Integration即数据访问和集成部分

Spring 既可以单独使用,也可以集成其他框架,如HibernateMyBatis等。除此之外,其中对于JDBC也做了封装,即本章节的JdbcTemplate,用它可以比较方便地对数据库进行增删改查等操作

image.png

总结一下:

  • JdbcTemplate就是 Spring 框架对JDBC技术进行的二次封装模板,能够简化对数据库的操作

1.2、准备工作

步骤预览

  • 1)引入相关jar
  • 2)Spring 配置文件配置Druid连接池信息
  • 3)配置JdbcTemplate对象,注入dataSource
  • 4)创建 Service 和 Dao 类,在 Dao 类中注入JdbcTemplate对象

详细操作

  • 1)引入相关jar包(或依赖)
    • druid
    • mysql-connector-java
    • spring-jdbc
    • spring-orm
    • spring-tx

image.png

  • 2)Spring 配置文件配置Druid连接池信息
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${mysql.driverClassName}"/>
    <property name="url" value="${mysql.url}"/>
    <property name="username" value="${mysql.username}"/>
    <property name="password" value="${mysql.password}"/>
</bean>

沿用之前章节的Jdbc.properties配置信息,但稍作修改

mysql.driverClassName=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql:///book_db
mysql.username=root
mysql.password=root
  • 3)配置JdbcTemplate对象,注入dataSource
<!--配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--属性注入dataSource-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

为何使用属性注入?

JdbcTemplate虽然含有DataSource的有参构造,但其调用了setDataSource()方法

image.png

这个方法是在其父类中定义了的

image.png

  • 4)创建 Service 和 Dao 类,在 Dao 类中注入JdbcTemplate对象

Dao 类

public interface BookDao {
}
@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

Service 类

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}

别忘了开启注解扫描

<!--开启注解扫描-->
<context:component-scan base-package="com.vectorx.spring5.s15_jdbctemplate"/>

配置文件整体结构

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.vectorx.spring5.s15_jdbctemplate"/>

    <!--配置dataSource-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--属性注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

1.3、添加操作

步骤预览

  • 1)创建数据库中t_book表对应的实体对象
  • 2)编写 Service 和 Dao 代码,增加_添加图书_的功能逻辑
  • 3)代码测试

详细操作

  • 1)创建数据库中t_book表对应的实体对象

public class Book {
    private String bid;
    private String bname;
    private String bstatus;

    public String getBid() {
        return bid;
    }
    public void setBid(String bid) {
        this.bid = bid;
    }
    public String getBname() {
        return bname;
    }
    public void setBname(String bname) {
        this.bname = bname;
    }
    public String getBstatus() {
        return bstatus;
    }
    public void setBstatus(String bstatus) {
        this.bstatus = bstatus;
    }
}
  • 2)编写 Service 和 Dao 代码,增加_添加图书_的功能逻辑

Service 类:添加addBook()方法

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;

    public int addBook(Book book) {
        return bookDao.add(book);
    }
}

Dao 类:通过操作JdbcTemplate对象的update()方法可实现插入,其中两个参数分别是

  • 第一个参数sql:编写插入数据对应的sql语句,可使用通配符?做占位符
  • 第二个参数args:可变参数列表,设置占位符对应的参数值
public interface BookDao {
    int add(Book book);
}
@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Override
    public int add(Book book) {
        //操作JdbcTemplate对象,使用update方法进行添加操作
        String sql = "insert into t_book(bid,bname,bstatus) values(?,?,?)";
        Object[] args = {book.getBid(), book.getBname(), book.getBstatus()};
        return jdbcTemplate.update(sql, args);
    }
}
  • 3)代码测试
ApplicationContext context = new ClassPathXmlApplicationContext("bean13.xml");
BookService bookService = context.getBean("bookService", BookService.class);

//模拟新增图书
Book book = new Book();
book.setBid("1");
book.setBname("Spring JdbcTemplate");
book.setBstatus("1");
int result = bookService.addBook(book);
System.out.println(result);

测试结果

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
三月 06, 2022 10:25:49 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
1

刷新数据库中t_book表数据,核验是否插入成功

image.png

可以看到,表中成功新增了一条数据

1.4、修改和删除

修改、删除操作和添加操作代码逻辑基本一致

BookService 类:添加updateBook()deleteBook()方法

// 修改
public int updateBook(Book book) {
    return bookDao.update(book);
}
//删除
public int deleteBook(String id) {
    return bookDao.delete(id);
}

BookDao 类:添加update()delete()方法

// 修改
int update(Book book);
// 删除
int delete(String id);

BookDaoImpl 类:实现update()delete()方法

// 修改
@Override
public int update(Book book) {
    String sql = "update t_book set bname=?,bstatus=? where bid=?";
    Object[] args = {book.getBname(), book.getBstatus(), book.getBid()};
    return jdbcTemplate.update(sql, args);
}
// 删除
@Override
public int delete(String id) {
    String sql = "delete from t_book where bid=? ";
    return jdbcTemplate.update(sql, id);
}

测试修改

//修改图书信息
Book book = new Book();
book.setBid("1");
book.setBname("JdbcTemplate");
book.setBstatus("update");
int result2 = bookService.updateBook(book);
System.out.println(result2);

测试结果

image.png

测试删除

//删除图书
int result3 = bookService.deleteBook("1");
System.out.println(result3);

测试结果

image.png

1.5、查询操作

这里演示三种查询操作:

  • 1)查询返回某个值
  • 2)查询返回对象
  • 3)查询返回集合

为了演示效果,需要先在数据库的t_book表中添加两条数据

image.png

接着我们先将代码完成,最后再作进一步的分析说明

代码实现

BookService 类:添加findCount()findById()findAll()方法

// 查找返回一个值
public int findCount() {
    return bookDao.selectCount();
}
// 查找返回对象
public Book findById(String id) {
    return bookDao.selectById(id);
}
// 查找返回集合
public List<Book> findAll() {
    return bookDao.selectAll();
}

BookDao 类:添加selectCount()selectById()selectAll()方法

// 查找返回一个值
int selectCount();
// 查找返回对象
Book selectById(String id);
// 查找返回集合
List<Book> selectAll();

BookDaoImpl 类:实现selectCount()selectById()selectAll()方法

// 查找返回一个值
@Override
public int selectCount() {
    String sql = "select count(0) from t_book";
    return jdbcTemplate.queryForObject(sql, Integer.class);
}
// 查找返回对象
@Override
public Book selectById(String id) {
    String sql = "select * from t_book where bid=?";
    return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
}
// 查找返回集合
@Override
public List<Book> selectAll() {
    String sql = "select * from t_book where 1=1";
    return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
}

测试代码

int count = bookService.findCount();
System.out.println(count);
Book book = bookService.findById("1");
System.out.println(book);
List<Book> bookList = bookService.findAll();
System.out.println(bookList);

测试结果

2
Book{bid='1', bname='Spring', bstatus='add'}
[Book{bid='1', bname='Spring', bstatus='add'}, Book{bid='2', bname='SpringMVC', bstatus='add'}]

代码分析

上述代码逻辑中使用到了queryForObject()query()方法

jdbcTemplate.queryForObject(sql, Integer.class);
jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));

分别对应JdbcTemplate中的三个方法

public <T> T queryForObject(String sql, Class<T> requiredType);
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
public <T> List<T> query(String sql, RowMapper<T> rowMapper);

其中,有两个参数值得关注,一个是Class<T> requiredType,另一个是RowMapper<T> rowMapper

  • Class<T> requiredType:返回值的Class类型
  • RowMapper<T> rowMapper:是一个接口,返回不同类型数据,可以使用其实现类进行数据的封装。其实现类有很多,因为我们需要返回一个数据库实体对象,所以可以选择使用BeanPropertyRowMapper

image.png

另外,queryForObject(String sql, RowMapper<T> rowMapper, Object... args)query(String sql, RowMapper<T> rowMapper)

区别在于

  • queryForObject返回一个对象
  • query返回一个集合

1.6、批量操作

JdbcTemplate中提供了batchUpdate()可供我们进行批量操作,如:批量添加、批量修改、批量删除等,代码实现上大同小异,我们对代码进行快速实现

代码实现

BookService 类:添加batchAddBook()batchUpdateBook()batchDelBook()方法

// 批量添加
public void batchAddBook(List<Object[]> bookList) {
    bookDao.batchAdd(bookList);
}
// 批量修改
public void batchUpdateBook(List<Object[]> bookList) {
    bookDao.batchUpdate(bookList);
}
// 批量删除
public void batchDelBook(List<Object[]> bookList) {
    bookDao.batchDel(bookList);
}

BookDao 类:添加batchAdd()batchUpdate()batchDel()方法

// 批量添加
void batchAdd(List<Object[]> bookList);
// 批量修改
void batchUpdate(List<Object[]> bookList);
// 批量删除
void batchDel(List<Object[]> bookList);

BookDaoImpl 类:实现batchAdd()batchUpdate()batchDel()方法

// 批量添加
@Override
public void batchAdd(List<Object[]> bookList) {
    String sql = "insert into t_book(bid,bname,bstatus) values(?,?,?)";
    extractBatch(sql, bookList);
}
// 批量修改
@Override
public void batchUpdate(List<Object[]> bookList) {
    String sql = "update t_book set bname=?,bstatus=? where bid=?";
    extractBatch(sql, bookList);
}
// 批量删除
@Override
public void batchDel(List<Object[]> bookList) {
    String sql = "delete from t_book where bid=? ";
    extractBatch(sql, bookList);
}
private void extractBatch(String sql, List<Object[]> bookList,) {
    int[] ints = jdbcTemplate.batchUpdate(sql, bookList);
    System.out.println(ints);
}

代码测试

测试批量添加

// 批量添加
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"3", "Java", "batchAdd"};
Object[] book2 = {"4", "Python", "batchAdd"};
Object[] book3 = {"5", "C#", "batchAdd"};
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
bookService.batchAddBook(bookList);

测试结果

image.png

测试批量修改

// 批量修改
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"Java++", "batchUpdate", "3"};
Object[] book2 = {"Python++", "batchUpdate", "4"};
Object[] book3 = {"C#++", "batchUpdate", "5"};
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
bookService.batchUpdateBook(bookList);

测试结果

image.png

测试批量删除

// 批量删除
List<Object[]> bookList = new ArrayList<>();
Object[] book1 = {"3"};
Object[] book2 = {"4"};
bookList.add(book1);
bookList.add(book2);
bookService.batchDelBook(bookList);

测试结果

image.png

可以看出,上述测试都完全符合我们的预期

小结

简单总结下JdbcTemplate操作数据库的各个方法

  • 添加、修改、删除操作:update()方法
  • 查询操作:queryForObject()query()方法,关注两个参数:
    • Class<T> requiredType:返回值的Class类型
    • RowMapper<T> rowMapper:接口,具体实现类BeanPropertyRowMapper,封装对象实体
  • 批量操作:batchUpdate()方法

2、事务

2.1、事务概念

  • 1)事务是数据库操作的最基本单元,是逻辑上的一组操作。这一组操作,要么都成功,要么都失败(只要有一个操作失败,所有操作都失败)
  • 2)典型场景:银行转账。Lucy 转账 100 元给 Mary,Lucy 少 100,Mary 多 100。转账过程中若出现任何问题,双方都不会多钱或少钱,转账就不会成功

2.2、事务四个特性(ACID)

  • 原子性(Atomicity):一个事务中的所有操作,要么都成功,要么都失败,整个过程不可分割
  • 一致性(Consistency):事务操作之前和操作之后,总量保持不变
  • 隔离性(Isolation):多事务操作时,相互之间不会产生影响
  • 持久性(Durability):事务最终提交后,数据库表中数据才会真正发生变化

2.3、搭建事务操作环境

我们知道 JavaEE 中的三层架构分为:表示层(web层)、业务逻辑层(service层)、数据访问层(dao层)

  • web层:与客户端进行交互
  • service层:处理业务逻辑
  • dao层:与数据库进行交互

因此,我们搭建操作环境也按照典型的三层架构来实现,不过目前现阶段我们只关注ServiceDao两层

image.png

我们以银行转账为例,因为整个转账操作包括两个操作:出账的操作和入账的操作

过程概览

  • 1)创建数据库表结构,添加几条记录
  • 2)创建ServiceDao类,完成对象创建和关系注入
  • 3)Dao中创建两个方法:出账的方法、入账的方法;Service中创建转账的方法

过程详解

**1)**创建数据库表结构,添加几条记录

# 建表语句
create table t_account
(
    id       varchar(20) not null,
    username varchar(50) null,
    amount   int         null,
    constraint transfer_record_pk
        primary key (id)
);
# 添加语句
INSERT INTO book_db.t_account (id, username, amount) VALUES ('1', 'Lucy', 1000);
INSERT INTO book_db.t_account (id, username, amount) VALUES ('2', 'Mary', 1000);

添加完成效果

image.png

**2)**创建ServiceDao类,完成对象创建和关系注入

Service中注入DaoDao中注入JdbcTemplateJdbcTemplate中注入DataSource

ServiceDao

public interface TransferRecordDao {
}
@Repository
public class TransferRecordDaoImpl implements TransferRecordDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
@Service
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;
}

Spring 配置文件

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="com.vectorx.spring5.s16_transaction"/>
    <!--配置dataSource-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--属性注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

3)Dao中创建两个方法:出账的方法、入账的方法;Service中创建转账的方法

  • Dao负责数据库操作,所以需要创建两个方法:出账的方法、入账的方法
public interface TransferRecordDao {
    void transferOut(int amount, String username);
    void transferIn(int amount, String username);
}
@Repository
public class TransferRecordDaoImpl implements TransferRecordDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void transferOut(int amount, String username) {
        String sql = "update t_account set amount=amount-? where username=?";
        Object[] args = {amount, username};
        jdbcTemplate.update(sql, args);
    }
    @Override
    public void transferIn(int amount, String username) {
        String sql = "update t_account set amount=amount+? where username=?";
        Object[] args = {amount, username};
        jdbcTemplate.update(sql, args);
    }
}
  • Service负责业务操作,所以需要创建一个方法,来调用Dao中两个方法
@Service
public class TransferRecordService {
    @Autowired
    private TransferRecordDao transferRecordDao;

    public void transferAccounts(int amount, String fromUser, String toUser) {
        transferRecordDao.transferOut(amount, fromUser);
        transferRecordDao.transferIn(amount, toUser);
    }
}

测试代码

ApplicationContext context = new ClassPathXmlApplicationContext("bean14.xml");
TransferRecordService transferRecordService = context.getBean("transferRecordService", TransferRecordService.class);
transferRecordService.transferAccounts(100, "Lucy", "Mary");

测试结果

image.png

可以发现,转账如期完成了。但真的没有一点问题么?

2.4、引入事务场景

我们模拟下在转账中途发生网络异常,修改TransferRecordService中转账方法

public void transferAccounts(int amount, String fromUser, String toUser) {
    transferRecordDao.transferOut(amount, fromUser);
    //模拟网络异常而导致操作中断
    int i = 10 / 0;
    transferRecordDao.transferIn(amount, toUser);
}

为了更清晰直观地看到数据的变化,我们还原数据表数据到最初状态

image.png

按照期望,转账应该失败,即双方账户不应该有任何变化。事实真的能够如我们所料么?

我们执行测试方法,如期抛出异常

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-22 20:22:50  更:2022-03-22 20:23:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:02:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码