JDBCTemplate

什么是 JdbcTemplate

Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作。

准备工作

1、创建数据库和表

  1. drop database if exists user_db;
  2. create database user_db default character set utf8 collate utf8_general_ci;
  3. use user_db;
  4. create table t_book(
  5. `book_id` bigint primary key auto_increment,
  6. `book_name` varchar(100) not null unique,
  7. `book_status` varchar(50) not null
  8. );

2、引入相关 jar 包
image.png
3、配置 spring xml 文件
(1)配置 druid 数据库连接池
(2)配置 JdbcTemplate 对象,注入 dataSource
(3)开启组件扫描

<?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:property-placeholder location="classpath:jdbc.properties" />

    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${prop.driverClass}"/>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.userName}"/>
        <property name="password" value="${prop.password}"/>
    </bean>

    <!-- JDBCTemplate对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 注入dataSource -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 组件扫描 -->
    <context:component-scan base-package="com"></context:component-scan>

</beans>

其中,外部文件是 jdbc.properties,如下:

prop.driverClass=com.mysql.cj.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
prop.userName=root
prop.password=abc123

插入、修改、删除操作

1、根据数据库表在 entity 包下创建实体 BooK 类,提供 get、set 方法

public class Book {
    private String bookId;
    private String bookName;
    private String status;
    ......
}

2、创建 dao 接口和实现类,定义方法,注入 JDBCTemplate 对象

public interface BookDao {
    // 添加的方法
    void add(Book book);

    // 修改的方法
    void update(Book book);

    // 删除的方法
    void delete(String id);
}
@Repository
public class BookDaoImpl implements BookDao{

    // 注入JDBCTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 添加的方法
    @Override
    public void add(Book book) {
        // 创建sql语句
        String sql = "insert into t_book values(?,?,?)";
        // 调用方法实现
        Object[] args = {book.getBookId(), book.getBookName(), book.getStatus()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }

    // 修改的方法
    @Override
    public void update(Book book) {
        String sql = "update t_book set book_name=?, book_status=? where book_id=?";
        Object[] args = {book.getBookName(), book.getStatus(), book.getBookId()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }

    // 删除的方法
    @Override
    public void delete(String id) {
        String sql = "delete from t_book where book_id=?";
        int update = jdbcTemplate.update(sql, id);
        System.out.println(update);
    }
}

3、创建 BookService 类,注入 dao,创建方法

@Service
public class BookService {
    // 注入dao
    @Autowired
    private BookDao bookDao;

    // 添加的方法
    public void addBook(Book book) {
        bookDao.add(book);
    }

    // 修改的方法
    public void updateBook(Book book) {
        bookDao.update(book);
    }

    // 删除的方法
    public void deleteBook(String id) {
        bookDao.delete(id);
    }
}

4、测试类

public class TestJDBCTemplate {
    // 测试添加
    @Test
    public void testAdd() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        Book book = new Book();
        book.setBookId("1001");
        book.setBookName("java从入门到精通");
        book.setStatus("已借出");
        bookService.addBook(book);
    }
    // 测试修改
    @Test
    public void testUpdate() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        Book book = new Book();
        book.setBookId("1001");
        book.setBookName("java从入门到放弃");
        book.setStatus("在架上");
        bookService.updateBook(book);
    }
    // 测试删除
    @Test
    public void testDelete() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.deleteBook("1001");
    }
}

效果:
image.png

查询操作

1、修改 dao 接口和实现类

// 查询表记录数
int selectCount();

// 查询返回对象
Book findBookInfo(String id);

// 查询返回集合
List<Book> findAllBook();
// 查询表记录数
@Override
public int selectCount() {
    String sql = "select count(*) from t_book";
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
    return count;
}

// 查询返回对象
@Override
public Book findBookInfo(String id) {
    String sql = "select * from t_book where book_id=?";
    Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
    return book;
}

// 查询返回集合
@Override
public List<Book> findAllBook() {
    String sql = "select * from t_book";
    List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
    return bookList;
}

2、修改service

// 查询表记录数
public int findCount() {
    return bookDao.selectCount();
}

// 查询返回对象
public Book findOne(String id) {
    return bookDao.findBookInfo(id);
}

// 查询返回集合
public List<Book> findAll() {
    return bookDao.findAllBook();
}

3、测试
其中,RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装。

@Test
public void testQuery1() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    int count = bookService.findCount();
    System.out.println(count);
}

@Test
public void testQuery2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    Book book = bookService.findOne("1001");
    System.out.println(book);
}

@Test
public void testQuery3() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    List<Book> all = bookService.findAll();
    System.out.println(all);
}

批量操作

1、修改 dao 接口和实现类

// 批量添加
void batchAddBook(List<Object[]> batchArgs);

// 批量修改
void batchUpdateBook(List<Object[]> batchArgs);

// 批量删除
void batchDeleteBook(List<Object[]> batchArgs);
// 批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
    String sql = "insert into t_book values(?,?,?)";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

// 批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
    String sql = "update t_book set book_name=?, book_status=? where book_id=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

// 批量删除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
    String sql = "delete from t_book where book_id=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

2、修改service

// 批量添加
public void batchAdd(List<Object[]> batchArgs) {
    bookDao.batchAddBook(batchArgs);
}

// 批量修改
public void batchUpdate(List<Object[]> batchArgs) {
    bookDao.batchUpdateBook(batchArgs);
}

// 批量删除
public void batchDelete(List<Object[]> batchArgs) {
    bookDao.batchDeleteBook(batchArgs);
}

3、测试

@Test
public void testAddBatch() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"1003", "java", "已借出"};
    Object[] o2 = {"1004", "c++", "在架上"};
    Object[] o3 = {"1005", "python", "在架上"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    // 调用批量添加方法
    bookService.batchAdd(batchArgs);
}

@Test
public void testUpdateBatch() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"go语言", "已借出","1003",};
    Object[] o2 = {"c语言", "在架上", "1004"};
    Object[] o3 = {"python", "已借出", "1005"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    batchArgs.add(o3);
    // 调用批量添加方法
    bookService.batchUpdate(batchArgs);
}

@Test
public void testDelteBatch() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    BookService bookService = context.getBean("bookService", BookService.class);
    List<Object[]> batchArgs = new ArrayList<>();
    Object[] o1 = {"1003",};
    Object[] o2 = {"1004"};
    batchArgs.add(o1);
    batchArgs.add(o2);
    // 调用批量添加方法
    bookService.batchDelete(batchArgs);
}

事务

什么是事务

( 1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账,比如:lucy 转账 100 元 给 mary,lucy 少 100, mary 多 100
image.png

事务四个特性(ACID)

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

spring 事务管理介绍

1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

2、Spring 进行事务管理操作有两种方式:

  • 编程式事务管理
  • 声明式事务管理(推荐使用)底层使用 AOP 原理

3、声明式事务管理两种实现方式

  • 基于注解方式(推荐使用)
  • 基于 xml 配置文件方式

4、 Spring 事务管理 API ,提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。

  • DataSourceTransactionManager 适用于MyBatis
  • HibernateTransactionManager 适用于 Hibernate

image.png

Spring 声明式事务管理(基于注解)

Spring xml 配置事务管理器

其余配置和前面的 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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${prop.driverClass}"/>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.userName}"/>
        <property name="password" value="${prop.password}"/>
    </bean>

    <!-- JDBCTemplate对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 注入dataSource -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 组件扫描 -->
    <context:component-scan base-package="com"></context:component-scan>

    <!-- 创建事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 开启事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

</beans>

创建数据表

create table t_account(
    `id` varchar(20) primary key,
    `username` varchar(50) not null,
    `money` int
);

image.png

搭建 Dao

其它准备工作和 JDBCTemplate 里相同。

public interface UserDao {
    // 多钱的方法
    public void addMoney();
    // 少钱的方法
    public void reduceMoney();
}
@Repository
public class UserDaoImpl implements UserDao{

    // 注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney() {
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql, 100, "mary");
    }

    @Override
    public void reduceMoney() {
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql, 100, "lucy");
    }
}

创建 Service 添加事务注解

@Service
@Transactional(timeout = -1, isolation = Isolation.REPEATABLE_READ,
        propagation = Propagation.REQUIRED, readOnly = false)
public class UserService {

    // 注入Dao
    @Autowired
    private UserDao userDao;

    // 转账的方法
    public void accountMoney() {
        // lucy少100
        userDao.reduceMoney();
        // 模拟异常
        int i = 12 / 0;
        // mary少100
        userDao.addMoney();
    }
}

测试:

@Test
public void test1() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}

声明式事务管理注解参数介绍

**@Transactional**,这个注解添加到类上面,也可以添加方法上面:

  • 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
  • 如果把这个注解添加方法上面,为这个方法添加事务

**@Transactional** 有几个参数如下:
image.png

1、**propagation**:设置事务传播行为(默认值是**Propagation.REQUIRED**
图7.jpg事务传播行为.jpg

2、**isolation**:设置隔离等级(MySQL数据库默认是**Isolation.REPEATABLE_READ**
事务隔离级别.jpg

3、**timeout**:设置超时时间(默认值是-1)
事务需要在一定时间内进行提交,如果不提交会进行回滚,设置时间以秒为单位进行计算。

4、**readOnly**:设置是否只读(默认值是false)

  • false:表示可以查询,也可以添加修改删除
  • true:表示只能查询

5、**rollbackFor**:设置出现哪些异常进行事务回滚

6、**noRollbackFor**:设置出现哪些异常不进行事务回滚

Spring 声明式完全注解管理事务

前述注解开发,仍然使用到了 xml 配置文件,下面演示不依托 xml 文件进行事务管理:

创建配置类替代 xml 配置文件

@Configuration // 配置类
@ComponentScan(basePackages = "com") // 组件扫描
@EnableTransactionManagement // 开启事务
public class TxConfig {

    // 创建Druid数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/user_db?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("abc123");
        return druidDataSource;
    }

    // 创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        // 到IOC容器中根据类型找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    // 创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

其余代码不变,UserService 保持事务注解,测试方法:

@Test
public void test3() {
    ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}

Spring 声明式事务管理(基于xml)

配置 spring xml 文件

主要有三步:

  • 配置事务管理器
  • 配置通知
  • 配置切入点和切面 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

      <tx:attributes>
          <!-- 指定哪种方法上面添加事务 -->
          <tx:method name="accountMoney" propagation="REQUIRED"/>
      </tx:attributes>
    

      <!-- 配置切入点 -->
      <aop:pointcut id="pt" expression="execution(* com.service.UserService.*(..))"/>
      <!-- 配置切面 -->
      <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    

去掉 UserService 类上面的事务注释,然后测试
```java
@Test
public void test2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}