20201126 王维
学习总结
1 Spring 中的 JdbcTemplate
1.1 JdbcTemplate 概述
它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很 多的操作模板类。
操作关系型数据的: JdbcTemplate
HibernateTemplate
操作 nosql 数据库的: RedisTemplate
操作消息队列的: JmsTemplate
1.2 JdbcTemplate
对象的创建
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
public JdbcTemplate() {
}
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
setDataSource(dataSource);
setLazyInit(lazyInit);
afterPropertiesSet();
}
public void setDataSource(@Nullable DataSource dataSource) {
this.dataSource = dataSource;
}
}
除了默认构造函数之外,都需要提供一个数据源。既然有setter方法,使用依赖注入, 我们可以在配置文件中配置这些对象。
1.3 maven导入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
1.4 导入数据源jar包
<!-- 数据源 -->
<!-- C3P0 -->
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2.1</version>
</dependency>
<!-- dbcp -->
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- DRUID -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
C3P0 、DBCP和Druid都可以。要想使用这些数据源都需要导入对应的 jar 包。Druid自带一个查看SQL语句和连接的页面
1.5 xml文件中配置数据源
<!-- 配置DataSource对象 -->
<!-- C3P0 -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">
</property>
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/userinfo"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- DBCP -->
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/userinfo">
</property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- spring 内置数据源 -->
<bean id="datasource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/userinfo">
</property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- Druid内置数据源 -->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql://localhost:3306/userinfo">
</property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
1.6 导入将数据库连接信息的方式配置数据源
# 数据库驱动
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
user=root
pass=root
引入外部的属性文件
<!-- 引入外部属性文件 -->
<!-- 第一种方式 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"/>
</bean>
<!-- 第二种方式 -->
<context:property-placeholder location="classpath:db.properties"/>
配置数据源时引入导入的配置文件信息
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${pass}" />
</bean>
1.7 在 spring 配置文件中配置 JdbcTemplate
<!-- 配置一个数据库的操作模板:JdbcTemplate -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>
1.8 使用
最基本使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class AppTest
{
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void base(){
jdbcTemplate.execute("insert into tb_account(name,money)values('荒天
帝',500)");
}
}
增删改查
@Repository
public class AccountDaoImpl implements IAccountDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public void save(Account account) {
String sql = "insert into tb_account(name ,money) values(? , ?)";
jdbcTemplate.update( sql , account.getName() , account.getMoney());
System.out.println("增加成功");
}
@Override
public void update(Account account) {
String sql = "update tb_account set name = ? , money = ? where id = ?";
jdbcTemplate.update(sql , account.getName() , account.getMoney() , account.getId());
System.out.println("修改成功");
}
@Override
public void delete(Integer accountId) {
String sql = "delete from tb_account where id = ?";
jdbcTemplate.update(sql , accountId);
System.out.println("删除成功");
}
@Override
public Account findById(Integer accountId) {
String sql = "select id , name , money from tb_account where id = ? ";
Account account = jdbcTemplate.query(sql, new ResultSetExtractor<Account>() {
@Override
public Account extractData(ResultSet rs) throws SQLException, DataAccessException {
Account account = new Account();
if (rs.next()){
account.setId((Integer) rs.getObject("id"));
account.setName((String) rs.getObject("name"));
account.setMoney((double) rs.getFloat("money"));
}
return account;
}
} , accountId);
return account;
}
@Override
public List<Account> findAll() {
String sql = "select id , name , money from tb_account";
List<Account> list = jdbcTemplate.query(sql, new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId((Integer) rs.getObject("id"));
account.setName((String) rs.getObject("name"));
account.setMoney((double) rs.getFloat("money"));
return account;
}
});
return list;
}
}
查询返回一行一列操作
@Test
public void count() {
Integer total = jdbcTemplate.queryForObject("select count(*) from account where money > ? " , Integer.class, 500);
System.out.println(total);
}
2 Spring 中的事务控制
2.1 Spring 事务控制我们要明确的
第一:JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解 决方案。
第二:spring 框架为我们提供了一组事务控制的接口。这组接口是在 spring-tx5.0.2.RELEASE.jar 中。
第三:spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。
2.2 Spring 中事务控制的 API 介绍
2.2.1 PlatformTransactionManager
此接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
//获取事务状态信息
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
//提交事务
void commit(TransactionStatus status)
//回滚事务
void rollback(TransactionStatus status)
真正管理事务的对象 org.springframework.jdbc.datasource.DataSourceTransactionManager
使用 SpringJDBC 或 iBatis(Mybatis) 进行持久化数据时使用 org.springframework.orm.hibernate5.HibernateTransactionManager
使用 Hibernate 进行持久化数据时使用(需要导入spring-orm包)
2.2.2 TransactionDefinition
它是事务的定义信息对象,里面有如下方法:
//获取事务对象名称
String getName()
//获取事务隔离级别
int getIsolationLevel()
//获取事务传播行为
int getPropagationBehavior()
//获取事务超时时间
int getTimeout()
//获取事务是否只读
boolean isReadOnly()
2.2.2.1 事务的隔离级别
1、DEFAULT
默认隔离级别,每种数据库支持的事务隔离级别不一样,如果Spring配置事务时将isolation设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。顺便说一句,如果使用的MySQL,可以使用"select @@tx_isolation"来查看默认的事务隔离级别
2、READ_UNCOMMITTED
读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用
3、READ_COMMITED
读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读
4、REPEATABLE_READ
重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决
5、SERLALIZABLE
串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了
2.2.2.2 事务的传播行为
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一 般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW: 新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED 类似的操 作。
2.2.2.3 超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
2.2.2.4 是否是只读事务
读写型事务:增加、删除、修改开启事务
只读型事务:执行查询时,也会开启事务
- 只读事务内,不能增加、修改、删除内容,否则报Cannot execute statement in a READ ONLY transaction。
- 只读事务内,只能读取到执行时间点前的内容,期间修改的内容不能读取到。
- 只读事务作为ORM框架优化执行的一个暗号,比如放弃加锁,或者flush never。
2.2.3 TransactionStatus
此接口提供的是事务具体的运行状态,方法介绍如下:
//刷新事务
void flush()
//获取是否存在存储点
boolean hasSavepoint()
//获取事务是否完成
boolean isCompleted()
//获取事务是否为新的事务
boolean isNewTransaction()
//获取事务是否回滚
boolean isRollbackOnly()
//设置事务回滚
void setRollbackOnly()
2.3 基于 XML 的声明式事务控制(配置方式)
2.3.1 第一步:拷贝必要的 jar 包(pom.xml)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
spring-jdbc中会含有spring-tx5.0.2.RELEASE.jar 的依赖
2.3.2 第二步:创建 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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.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">
</beans>
2.3.3 第三步:配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" />
</bean>
2.3.4 第四步:配置事务的通知引用事务管理器
<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>
2.3.5 第五步:配置事务的属性
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<!--在 tx:advice 标签内部 配置事务的属性 -->
<tx:attributes>
<!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异
常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异
常时,事务回
滚。没有默认值,任何异常都回滚。
-->
<tx:method name="*" propagation="REQUIRED" read-only="false" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
2.3.6 第六步:配置 AOP 切入点表达式
<!-- 配置aop-->
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="pt1" expression="execution(* com.itlaobing.spring.service.impl.*.*(..))"/>
</aop:config>
2.3.7 第七步:配置切入点表达式和事务通知的对应关系
<!-- 配置aop-->
<aop:config>
<!-- 切入点 -->
<aop:pointcut id="pt1" expression="execution(* com.itlaobing.spring.service.impl.*.*(..))"/>
<!-- 在 aop:config 标签内部:建立事务的通知和切入点表达式的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1" />
</aop:config>基于注解的配置方式
2.4 基于注解的配置方式
2.4.1 在xml配置中开启对事务注解的支持
<!-- 开启 spring 对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager" />
2.4.2 配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" />
</bean>
2.4.3 在业务层使用@Transactional 注解
package com.itlaobing.spring.service.impl;
import com.itlaobing.spring.dao.IAccountDao;
import com.itlaobing.spring.dao.impl.AccountDaoImpl;
import com.itlaobing.spring.model.Account;
import com.itlaobing.spring.service.IAccountService;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Repository
@Transactional(propagation = Propagation.REQUIRED , readOnly = false)
public class AccountServiceImpl implements IAccountService {
@Resource
private AccountDaoImpl accountDao;
@Override
@Transactional(propagation = Propagation.SUPPORTS , readOnly = true)
public Account findAccountById(Integer id) {
return accountDao.findById(id);
}
@Override
public void transfer(Integer sourceId, Integer targetId, double money) {
Account source = accountDao.findById(sourceId);
Account target = accountDao.findById(targetId);
//修改两个账户的金额
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
accountDao.update(source);
// int i = 1/0;
accountDao.update(target);
}
}
该注解的属性和 xml 中的属性含义一致。该注解可以出现在接口上,类上和方法上。 他表示声明式事务管理,使用这个注解的类或者方法表示该类里面的所有方法或者这个方法的事务由spring处理。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持 ,支持继承
出现在方法上,表示方法有事务支持。
以上三个位置的优先级:方法 > 类 > 接口
2.4.4 不使用 xml 的配置方式
package com.itlaobing.spring.config;
import com.alibaba.druid.pool.DruidAbstractDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:db.properties")
@ComponentScan("com.itlaobing.spring")
//开启事务支持
@EnableTransactionManagement
public class SpringConfig {
@Value("${driver}")
private String driver;
@Value("${url}")
private String url;
@Value("${user}")
private String username;
@Value("${pass}")
private String password;
@Bean("dataSource")
public DataSource createDruidAbstractDataSource(){
DruidAbstractDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean("jdbcTemplate")
@Scope("prototype")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean("dataSourceTransactionManager")
@Scope("prototype")
public DataSourceTransactionManager createDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
3 Spring5 的新特性[了解]
心得体会
学习到今天,spring对业务层的操作基本结束,理解起来还算轻松,大部分都是按照规定使用,实现了对应的功能,后面学习完SpringMVC就算基本完成了对SSM框架的学习,然后就是框架整合与项目的编写,对于项目实操,想上手了。
近期评论