Spring的JdbcTemplate是一个对JDBC的模板封装,它提供了一套JDBC的模板,能让我们写持久层代码时减少多余的代码,简化JDBC代码,使代码看起来更简洁。在介绍Spring的JdbcTemplate使用方法之前我们先来讨论一个问题,以下这是一段常见的往数据库写入数据的JDBC代码:
public int jdbcInsert(Student student) throws SQLException {
Connection connection = null;
try {
connection = dataSource.getConnection();
String sql = "INSERT INTO student(sname,age,sex,address) VALUES (?,?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, student.getName());
preparedStatement.setInt(2, student.getAge());
preparedStatement.setString(3, student.getSex());
preparedStatement.setString(4, student.getAddress());
return preparedStatement.executeUpdate();
} finally {
connection.close();
}
}
public int jdbcUpdate(Student student) throws SQLException {
Connection connection = null;
try {
connection = dataSource.getConnection();
String sql = "UPDATE student SET sname=?,age=?,sex=?,address=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, student.getName());
preparedStatement.setInt(2, student.getAge());
preparedStatement.setString(3, student.getSex());
preparedStatement.setString(4, student.getAddress());
return preparedStatement.executeUpdate();
} finally {
connection.close();
}
}
从如上的代码中,可以看到两个方法中基本99%的代码都是重复的,除了sql语句之外,都是重复的代码,重复的代码就是坏味道,会让我们的产生大量的冗余代码,不易于维护和修改,而且写起来还累。
所以Spring提供的JdbcTemplate正是用来解决这个问题的,其实Spring的JDBCTemplate有点像DBUtils,但是有时候还没有DBUitls好用。这里来学习一下使用Spring的JdbcTemplate来玩一下CRUD,毕竟JdbcTemplate在实际开发中一般不会使用,通常都是使用Mybatis、Hibernate等成熟、优秀的数据持久层框架,不过还是得知道Spring有一个这样的jdbc模板类
spring核心包4+2 jdbc模板包2个
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.2.9.RELEASEversion>
dependency>
@Test
public void test(){
//构建数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///test");
dataSource.setUsername("root");
dataSource.setPassword("root");
//获取对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//创建表
jdbcTemplate.execute("create table test01(name varchar(10) )");
}
目标:将数据源和jdbcTemplate都交给Spring来管理:
在applicationContext.xml中配置dataSource连接池和jdbcTemplate模版对象。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JdbcTempleTest {
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test(){
this.jdbcTemplate.execute("create table jdbctest05(name varchar(10))");
}
}
Apache commons-dbcp 需要导入dbcp包和 pool包
添加依赖
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource2"/>
bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JdbcTempleTest {
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test(){
this.jdbcTemplate.execute("create table jdbctest06(name varchar(10))");
}
}
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<bean id="dataSource3" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource3"/>
bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JdbcTempleTest {
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test(){
this.jdbcTemplate.execute("create table jdbctest07(name varchar(10))");
}
}
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.3version>
dependency>
<bean id="dataSource4" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="5"/>
<property name="minIdle" value="1"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource4"/>
bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JdbcTempleTest {
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test(){
this.jdbcTemplate.execute("create table jdbctest07(name varchar(10))");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F5402RTk-1664592948340)(assets/image-20211217000623540.png)]
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcurl=jdbc:mysql:///test?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource4" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.jdbcurl}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="5"/>
<property name="minIdle" value="1"/>
bean>
数据库准备
CREATE TABLE `t_book` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类
@Data
public class Book {
private Integer id;
private String name;
private Double price;
}
dao层
/**
* @Auther: yanqi
* @Desc: 继承 JdbcDaoSupport类,父类中已注入JdbcTemplate对象
*/
public class BookDao extends JdbcDaoSupport {
/**
* 查询所有
*/
public List<Book> queryAll(){
return super.getJdbcTemplate().query("select * from t_book", new BeanPropertyRowMapper<>(Book.class));
}
}
数据源
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource4" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.jdbcurl}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="bookDao" class="cn.yanqi.dao.BookDao">
<property name="dataSource" ref="dataSource4"/>
bean>
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcurl=jdbc:mysql:///test
jdbc.username=root
jdbc.password=root
/**
* @Auther: yanqi
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JdbcTemplateTest {
@Autowired
private BookDao bookDao;
@Test
public void test2(){
List<Book> books = this.bookDao.queryAll();
for(Book book : books){
System.out.println(book);
}
}
}
/**
* 添加
*/
public void addBook(Book book){
String sql = "insert into t_book(id,name,price) values(null,?,?)";
super.getJdbcTemplate().update(sql,book.getId(),book.getName(),book.getPrice());
}
/**
* 删除
*/
public void deleteBook(Integer id){
String sql = "delete from t_book where id = ?";
super.getJdbcTemplate().update(sql,id);
}
/**
* 修改
*/
public void updateBook(Book book){
String sql = "update t_book set name = ? where id = ?";
super.getJdbcTemplate().update(sql,book.getName(),book.getId());
}
CREATE TABLE `t_account` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`money` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
spring-transaction
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.6.4version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>runtimescope>
dependency>
dependencies>
public class AccountDao extends JdbcDaoSupport {
public void out(String outName , double money){
String sql = "update t_account set money = money - ? where name = ?";
this.getJdbcTemplate().update(sql,money,outName);
}
public void in(String inName , double money){
String sql = "update t_account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql,money,inName);
}
}
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void tranfar(String outName , String inName , double money){
this.accountDao.out(outName,money);
int a = 1/0;
this.accountDao.in(inName,money);
}
}
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///test
jdbc.username=root
jdbc.password=root
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource4" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="5"/>
<property name="minIdle" value="1"/>
bean>
<bean id="accountDao" class="cn.yanqi.dao.AccountDao">
<property name="dataSource" ref="dataSource4"/>
bean>
<bean id="accountService" class="cn.yanqi.service.AccountService">
<property name="accountDao" ref="accountDao"/>
bean>
beans>
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void tranfar() {
this.accountService.tranfar("jack","rose",100);
}
}
发现事务管理问题:没有事务的情况下,如果出现异常,则会转账不成功,数据异常。
扩展:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。
<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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource4"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transactionAccount"
isolation="DEFAULT"
propagation="REQUIRED"
timeout="-1"
rollback-for=""
no-rollback-for=""/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="bean(*Service)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
beans>
/**
* @Auther: yanqi
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void transactionAccount() {
this.accountService.transactionAccount("jack","rose",100.0);
}
}
遇到异常不回滚(相当于没有事务)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kb1d6f3J-1664592948342)(assets/image-20211217202707415.png)]
什么是事务的传播行为?
一个事务调用了另 一个事务,如果出现错误,两个事务是否属于同一个事务
有什么作用?
事务传播行为用于解决两个被事务管理的方法互相调用问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JlTaNfFY-1664592948344)(assets/image-20211217202839587.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J1A4nxQU-1664592948345)(assets/image-20211217202857824.png)]
PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。
事务默认传播行为 REQUIRED。最常用的。
PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
常用的事务传播行为:PROPAGATION_REQUIRES_NEW
PROPAGATION_NESTED
嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点
导入的依赖包跟xml方式一样
pring-transactional-ann
/**
* @Auther: yanqi
*/
@Service
// @Transactional //当前类都会被 事务管理
public class AccountService {
@Autowired
private AccountDao accountDao;
@Transactional //事务管理
public void transactionAccount(String outName, String inName , Double money){
this.accountDao.out(outName,money);
int a = 1/0;
this.accountDao.in(inName,money);
}
}
/**
* @Auther: yanqi
* @Desc: 先用jdbcTemplate完成对数据库的操作
*/
@Repository
public class AccountDao extends JdbcDaoSupport {
/**
* 随便写一个set方法,注入DataSource,使用父数据源
* @param dataSource
*/
@Autowired
public void setJdbcDaoSupport(DataSource dataSource){
super.setDataSource(dataSource);
}
/**
* 减钱
*/
public void out(String name , Double money){
String sql = "update t_account set money = money - ? where name = ?";
super.getJdbcTemplate().update(sql,money, name);
}
/**
* 加钱
*/
public void in(String name , Double money){
String sql = "update t_account set money = money + ? where name = ?";
super.getJdbcTemplate().update(sql,money, name);
}
}
<context:component-scan base-package="cn.yanqi"/>
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EpZrVRlh-1664592948346)(assets/image-20211217203126239.png)]
配置注解驱动事务管理的时候,tx:annotation-driven/默认会自动查找并加载名字为transactionManager的事务管理器Bean,因此,当事务管理器的bean的名字为transactionManager 时,可以省略transaction-manager的属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pNV522gW-1664592948347)(assets/image-20211217203218262.png)]
taSource">
- **配置事务的定义属性信息,在注解中直接配置**
[外链图片转存中...(img-EpZrVRlh-1664592948346)]
- **优化配置**
>配置注解驱动事务管理的时候, 默认会自动查找并加载名字为transactionManager的事务管理器Bean,因此,当事务管理器的bean的名字为transactionManager 时,可以省略transaction-manager的属性
[外链图片转存中...(img-pNV522gW-1664592948347)]