JDBCTemplate
什么是 JdbcTemplate
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作。
准备工作
1、创建数据库和表
drop database if exists user_db;create database user_db default character set utf8 collate utf8_general_ci;use user_db;create table t_book(`book_id` bigint primary key auto_increment,`book_name` varchar(100) not null unique,`book_status` varchar(50) not null);
2、引入相关 jar 包
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");
    }
}
效果:
查询操作
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
事务四个特性(ACID)
- 原子性
 - 一致性
 - 隔离性
 - 持久性
 
spring 事务管理介绍
1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2、Spring 进行事务管理操作有两种方式:
- 编程式事务管理
 - 声明式事务管理(推荐使用)底层使用 AOP 原理
 
3、声明式事务管理两种实现方式
- 基于注解方式(推荐使用)
 - 基于 xml 配置文件方式
 
4、 Spring 事务管理 API ,提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。
DataSourceTransactionManager适用于MyBatisHibernateTransactionManager适用于Hibernate

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
);
搭建 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** 有几个参数如下:
1、**propagation**:设置事务传播行为(默认值是**Propagation.REQUIRED**)

2、**isolation**:设置隔离等级(MySQL数据库默认是**Isolation.REPEATABLE_READ**)
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();
}
                    
