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
适用于MyBatis
HibernateTransactionManager
适用于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();
}