1 Spring JdbcTemplate基本使用
1.1 JdbcTemplate概述
JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。
spring框架为我们提供了很多操作模板类。列如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
1.2 JdbcTemplate开发的基本步骤
① 导入spring-jdbc和spring-tx坐标
② 创建数据库表和实体类
③ 创建JdbcTemplate对象
④ 执行数据库操作
代码实现过程,我们先来明确两个问题。
- JdbcTemplate是否需要数据源?如果需要如何设置数据源?
创建JdbcTemplate对象,然后设置数据源,因此需要提前创建数据源,同时设置好数据库相关信息。
- JdbcTemplate调用哪个方法完成插入操作?
调用JdbcTemplate的update方法传入sql语句和参数完成插入操作
① 导入spring-jdbc和spring-tx坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
② 创建数据库表和实体类
public class Account {
private String name;
private double money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
③ 创建JdbcTemplate对象
//创建数据源对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("root");
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源对象 知道数据库在哪
jdbcTemplate.setDataSource(dataSource);
④ 执行数据库操作
//执行操作
int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000);
System.out.println(row); //受影响的行数
1.4 JdbcTemplate XML配置
将JdbcTemplate的创建权交给Spring,将数据源DataSource的创建权也交给spring,在spring容器内部将数据源DataSource注入到JdbcTemplate模板对象中,而这些都可以通过Spring的配置来完成。
- Spring如何配置bean及如何通过配置完成注入?
- 先配置数据源,然后配置JdbcTemplate,然后将数据源注入到JdbcTemplate中。
- 通过Spring容器获得JdbcTemplate对象,调用JdbcTemplate对象的update方法,传入sql语句及参数完成插入数据的操作。
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
- 抽取jdbc.properties文件
- 将数据库的连接信息配置抽取成外部的properties配置文件
- Spring读取外部的properties属性配置文件
<!--加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
1.5 JdbcTemplate基本操作-CRUD
注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
JdbcTemplate执行增删改都是使用update方法。
- 更新
@Test
public void testUpdate(){
jdbcTemplate.update("update account set money=? where name=?",10000,"tom");
}
- 删除
@Test
public void testDelete(){
jdbcTemplate.update("delete from account where name=?","tom");
}
-
查询
要将结果集封装成javaBean我们需要传入一个参数BeanPropertyRowMapper,并且传入要封装成的JavaBean的字节码。
Query方法表示将结果集封装成javaBean的集合,queryForObject方法表示将结果集封装成单个javaBean对象或者封装结合查询的结果,比如统计查询。
@Test
public void testQueryCount(){
Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
}
@Test
public void testQueryOne(){
Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "tom");
System.out.println(account);
}
@Test
public void testQueryAll(){
List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(accountList);
}
2 Spring的事务控制
2.1 编程式事务控制
-
PlatformTransactionManager 平台事务管理器
PlatformTransactionManager接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法。
PlatformTransactionManager是接口类型,不同的Dao层技术有不同的实现类,列如:dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager,当Dao层技术是Hibernate时:org.springframework.orm.hibernateTransactionManager -
TransactionDefinition 事务定义器
TransactionDefinition是事务的定义信息对象,里面有如下方法:
① 事务隔离级别
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚度。
- ISOLATION_DEFAULT
- ISOLATION_READ_UNCOMMITTED
- ISOLATION_READ_COMMITTED
- ISOLATION_REPEATABLE_READ
-
ISOLATION_SERIALIZABLE
② 事务传播行为
-
TransactionStatus 事务状态
TransactionStatus接口提供的是事务具体的运行状态,方法介绍如下。
2.2 基于XML的声明式事务控制
-
什么是声明式事务控制
spring的声明式事务控制顾名思义就是
。这里所说的声明,就是指在配置文件中声明,用在spring配置文件中声明式的处理事务来代替代码式的处理事务。采用声明的方式来处理事务
- 声明式事务处理的作用
- 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理师是系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可。
-
在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。
注意:Spring声明式事务控制底层就是AOP
- 转账业务环境搭建
账户实体类
public class Account {
private String name;
private double money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
dao层逻辑
public interface AccountDao {
public void out(String outMan,double money);
public void in(String inMan,double money);
}
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void out(String outMan, double money) {
jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
}
public void in(String inMan, double money) {
jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
}
}
service层业务逻辑
public interface AccountService {
public void transfer(String outMan,String inMan,double money);
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
//int i = 1/0;
accountDao.in(inMan,money);
}
}
测试,tom向lucy转账500元。
public class AccountController {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = app.getBean(AccountService.class);
accountService.transfer("tom","lucy",500);
}
}
当在service层的转账业务出现了异常,则accountDao.out(outMan,money)事务执行完,然而accountDao.in(inMan,money)事务由于int i = 1/0异常而终止,因此导致两个事务不同步的问题。因此,需要声明式事务控制来管理此类事务,我们使用XML配置的方式来控制。
- 声明式事务控制明确事项:
- 谁是切点? ----> 需要被管理事务
- 谁是通知? ----->
- 配置切面?
-
声明式事务控制的实现
① 引入tx命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
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="dataSource"/>
</bean>
<!--通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--设置事务的属性信息的-->
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
<tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
<tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
<tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
③ 配置事务AOP织入
<!--配置事务的aop织入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
- 切点方法的事务参数的配置 其中,tx:method代表切点方法的事务参数的配置,列如:
name:切点方法名称
isolation:事务的隔离级别
propagation:事务的传播行为
timeout:超时时间
read-only:是否只读
2.3 基于注解的声明式事务控制
在业务代码中加上以下这个注解,可以在类上也可以定义在方法上:
@Transactional
需要在spring的xml配置文件中开启事务注解驱动,注入事务管理器
<!--开启事务的注解驱动-->
<tx:annotation-driven transaction-manager="transationManager">
总结:
① 使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,列如隔离级别、传播行为等。
② 注解使用在类上,那么该类的所有方法都是要同一套注解参数配置。
③ 使用在方法上,不同的方法可以使用不同的事务参数配置。
④ xml配置文件中要开启事务注解驱动
<tx:annotation-driven transaction-manager="transationManager">