一、jdbcTemplate
1.1 添加依赖
<!-- Spring 在执行持久化层操作与持久化层技术进行整合过程中需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>6.1.14</version>
</dependency>
<!--数据库-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
<!--数据源,选一个就行-->
<!--阿里巴巴数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.23</version>
</dependency>
<!--c3p0数据源-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.10.1</version>
</dependency>
1.2 创建jdbc.properties
db.xxx是可以自己取的,用的时候根据这个键调用
db.user=root
db.password=密码
db.url=jdbc:mysql://localhost:3306/库名?serverTimezone=UTC
db.driver=com.mysql.cj.jdbc.Driver
1.3 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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源,pom导入的哪个就写哪个-->
<!--c3p0-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${db.user}"/>
<property name="password" value="${db.password}"/>
<property name="jdbcUrl" value="${db.url}"/>
<property name="driverClass" value="${db.driver}"/>
</bean>
<!--阿里巴巴-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
<property name="url" value="${db.url}"/>
<property name="driverClassName" value="${db.driver}"/>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--装配数据源-->
<property name="dataSource" ref="druidDataSource"/>
</bean>
</beans>
1.4 数据表
根据表创建实体类并提供get、set方法
1.5 实现CURD
增删改的代码都是一致的
@SpringJUnitConfig(locations = "classpath:jdbc.xml")
public class JdbcTemplateTest {
@Resource
private JdbcTemplate jdbcTemplate;
/*增删改操作*/
@Test
public void insert(){
//1.编写sql语句
String sql = "insert into user(username,money) values(?,?)";
//2.调用jdbcTemplate方法,传入相关参数
int i = jdbcTemplate.update(sql, "王五", 20);
System.out.println(i);
}
@Test
public void update(){
//1.编写sql语句
String sql = "update user set money=? where username=?";
//2.调用jdbcTemplate方法,传入相关参数
int i = jdbcTemplate.update(sql, 400,"王五");
System.out.println(i);
}
@Test
public void del(){
//1.编写sql语句
String sql = "delete from user where id=?";
//2.调用jdbcTemplate方法,传入相关参数
int i = jdbcTemplate.update(sql, 3);
System.out.println(i);
}
}
单条数据查询
@Test
public void selOne(){
//1.编写sql语句
String sql = "select id,username,money from user where id=?";
//2.调用jdbcTemplate方法,传入相关参数
//方法一 手动封装
User u = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setMoney(rs.getInt("money"));
return user;
}, 2);
System.out.println(u);
}
@Test
public void selOne(){
//1.编写sql语句
String sql = "select id,username,money from user where id=?";
//2.调用jdbcTemplate方法,传入相关参数
//方法二
User u = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 2);
System.out.println(u);
}
全查
@Test
public void selAll(){
//1.编写sql语句
String sql = "select id,username,money from user";
//2.调用jdbcTemplate方法,传入相关参数
//2.1手动封装
/*List<User> list = jdbcTemplate.query(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setMoney(rs.getInt("money"));
return user;
});*/
//2.2使用BeanPropertyRowMapper
List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
System.out.println(list);
}
聚合函数COUNT()
//查询返回单个值
@Test
public void selValue(){
//1.编写sql语句
String sql = "select count(*) from user";
//2.调用jdbcTemplate方法,传入相关参数
int i = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(i);
}
二、声明式事务的概念
要么都成功,有一个失败全部都失败。
事务的特性:ACID,原子性,一致性,隔离性,持久性
三、基于注解的声明式事务
加一个数据表,编写实体类,提供get、色图、等方法
在1.3中的核心配置文件中开启组件扫描
<context:component-scan base-package="com.hait.tx"/>
按以下目录准备项目
流程:controller调用service,service调用dao,所以在controller曾注入service,在service曾注入dao
实现用户买书功能
@Controller
public class LibController {
@Autowired
private LibService libService;
//买书 传入用户id和书id
public void buyBook(int uid,int lid){
libService.buyBook(uid,lid);
}
}
public interface LibService {
void buyBook(int uid, int lid);
}
@Service
public class LibServiceImpl implements LibService {
@Autowired
private LibDao libDao;
@Override
public void buyBook(int uid, int lid) {
//1.查询图书价格
double price = libDao.selectPrice(lid);
//2.图书库存-1
libDao.updateCount(lid);
//3.用户余额-图书价格
libDao.updateMoney(uid,price);
}
}
public interface LibDao {
//查询价格
double selectPrice(int lid);
//更新库存
void updateCount(int lid);
//更新用户余额
void updateMoney(int uid,double price);
}
@Repository
public class LibDaoImpl implements LibDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public double selectPrice(int lid) {
return jdbcTemplate.queryForObject("select price from lib where id=?", Double.class, lid);
}
@Override
public void updateCount(int lid) {
jdbcTemplate.update("update lib set count=count-1 where id=?",lid);
}
@Override
public void updateMoney(int uid,double price) {
jdbcTemplate.update("update user set money=money-? where id=?",price,uid);
}
}
测试:张三买一本数学书(还没加事务,那么如果买书张三余额不够的话,库存已经-1,张三的余额不变,这肯定是不符合逻辑的)
@SpringJUnitConfig(locations = "classpath:jdbc.xml")
public class test {
@Autowired
private LibController libController;
@Test
public void buyBookTest(){
libController.buyBook(1,1);
}
}
添加事务:在配置文件中开启事务
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
<!--开启事务-->
<!--1.事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!--2.开启注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional注解加在类上则会影响类中所有方法,加在方法上只会影响该方法,在LibServiceImpl类上加上该注解
测试:用户余额比要买的书的价格少
控制台报错的意思是,价格无符号,如果执行完方法就变负号了
余额和书本数量均未发生变化,说明事务回滚成功
事务的属性
只读
@Transactional(readOnly = true)
超时
@Transactional(timeout = 3)//单位是秒
//TODO 模拟超时
try {
TimeUnit.SECONDS.sleep(5);//延时执行5秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
回滚策略
设置哪些类进行回滚
rollbackFor:需要设置一个Class类型的对象
rollbackForClassName:需要设置一个字符串类型的全类名
设置哪些类不进行回滚
noRollbackFor:需要设置一个Class类型的对象
noRollbackForClassName:需要设置一个字符串类型的全类名
假设买书的方法中出现了除数为0的异常,让它不进行回滚:
@Transactional(noRollbackFor = ArithmeticException.class)
数据库操作成功
隔离级别
mysql默认的隔离级别是: @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别 @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交 @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交 @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读 @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
传播行为
REQUIRED:如果调用方法有事务,就加入,如果没有就新建自己独立的事务
REQUIRES_NEW:不管调用方法是否有事务,被调用方法都新建事务,都是独立的事务
Propagation.NESTED:如果当前存在事务,则在该事务中嵌套一个新事务如果没有事务,则与Propagation.REQUIRED一样
Propagation.SUPPORTS:如果当前存在事务,则加入该事务,否则以非事务方式执行
Propagation.NOT SUPPORTED:以非事务方式执行,如果当前存在事务挂起该事务
Propagation.MANDATORY:必须在一个已有的事务中执行,否则抛出异常
Propagation.NEVER:必须在没有事务的情况下执行,否则抛出异常