jgcs123

导航

 

第三部分:Spring中的JDBC和事务

一、Spring中对的Jdbc封装

1、JdbcTemplate

1.1、概述

1.1.1、基本介绍

Spring对数据库的操作在jdbc上面做了基本的封装,让开发者在操作数据库时只需关注SQL语句和查询 结果处理器,即可完成功能(当然,只使用JdbcTemplate,还不能摆脱持久层实现类的编写)。

在配合spring的IoC功能,可以把DataSource注册到JdbcTemplate之中。同时利用spring基于 aop的事务即可完成简单的数据库CRUD操作。

JdbcTemplate的限定命名为org.springframework.jdbc.core.JdbcTemplate。要使用 JdbcTemlate需要导入spring-jdbc和spring-tx两个坐标。

 

1.1.2、源码

/**
* JdbcTemplate实现了JdbcOperations接口,操作方法都定义在此接口中
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
   /**
       * 使用默认构造函数构建JdbcTemplate
       */
   public JdbcTemplate() {
  }
   /**
       * 通过数据源构建JdbcTemplate
       */
   public JdbcTemplate(DataSource dataSource) {
       setDataSource(dataSource);
       afterPropertiesSet();
  }
   /**
       * 当使用默认构造函数构建时,提供了设置数据源的方法
       */
public void setDataSource(@Nullable DataSource dataSource) {
   this.dataSource = dataSource;
}
}

1.1.3、方法说明

execute方法: 可以用于执行任何SQL语句,一般用于执行DDL语句;

update方法及batchUpdate方法: update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语 句;

query方法及queryForXXX方法: 用于执行查询相关语句;

call方法: 用于执行存储过程、函数相关语句。

 

1.2、入门案例

1.2.1、导入坐标

<dependencies>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>5.1.6.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>5.1.6.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.45</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>5.1.6.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
</dependencies>

 

1.2.2、编写实体类

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
   private Integer id;
   private String name;
   private Double money;
   public Integer getId() {
       public void setId(Integer id) {
           this.id = id;
      }
       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;
      }
}

 

1.2.3、编写配置类

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}

 

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class JdbcConfig {
   @Value("${jdbc.driver}")
   private String driver;
   @Value("${jdbc.url}")
   private String url;
   @Value("${jdbc.username}")
   private String username;
   @Value("${jdbc.password}")
   private String password;
   @Bean
   public JdbcTemplate createJdbcTemplate(DataSource dataSource){
       @Bean
       public DataSource createDataSource(){
           DriverManagerDataSource dataSource = new
               DriverManagerDataSource(url,username,password);
           dataSource.setDriverClassName(driver);
           return dataSource;
      }
}

 

1.2.4、编写配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

 

1.2.5、测试方法

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
           @Autowired
           private JdbcTemplate jdbcTemplate;
           @Test
           public void testSave(){
           jdbcTemplate.update("insert into
           account(name,money)values(?,?)","ccc",1234f);
          }
           @Test
           public void testUpdate(){
           jdbcTemplate.update("update account set name=?,money=? where
           id=?","ccc",2345f,1);
          }
           @Test
           public void testDelete(){
           jdbcTemplate.update("delete from account where id = ?",1);
          }
           @Test
           public void testFindOne(){
           // List<Account> accounts = jdbcTemplate.query("select * from account
           where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
           Account account = jdbcTemplate.queryForObject("select * from account
           where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
           System.out.println(account);
           @Test
           public void testFindAll(){
           List<Account> accounts = jdbcTemplate.query("select * from account ",new
           BeanPropertyRowMapper<Account>(Account.class));
           for(Account account : accounts){
           System.out.println(account);
          }
          }
           @Test
           public void testFindCount(){
           Long count = jdbcTemplate.queryForObject("select count(*) from account
           where money > ?",Long.class,999d);
           System.out.println(count);
          }
           @Test
           public void testQueryForList(){
           // List<Map<String,Object>> list = jdbcTemplate.queryForList("select *
           from account where money > ?",999f);
           // for(Map<String,Object> map : list){
           // for(Map.Entry<String,Object> me : map.entrySet()) {
           // System.out.println(me.getKey()+","+me.getValue());
           // }
           // }
           List<Double> list = jdbcTemplate.queryForList("select money from
           account where money > ?", Double.class,999f);
           for(Double money : list){
           System.out.println(money);
          }
          }
           @Test
           public void testQueryForMap(){
           Map<String,Object> map = jdbcTemplate.queryForMap("select * from account
           where id = ?", 2);
           for(Map.Entry me:map.entrySet()){
           System.out.println(me.getKey()+","+me.getValue());
          }
          }
           @Test
           public void testQueryForRowSet(){
           SqlRowSet rowSet = jdbcTemplate.queryForRowSet("select * from account
           where money > ?",999d);
           System.out.println(rowSet);
           if(rowSet instanceof ResultSetWrappingSqlRowSet){
           while(rowSet.next()) {
           ResultSetWrappingSqlRowSet resultSetWrappingSqlRowSet =
          (ResultSetWrappingSqlRowSet) rowSet;
           String name = resultSetWrappingSqlRowSet.getString("name");
           System.out.println(name);
          }
          }
          }
}

 

 

1.3.、LobHandler和LobCreator

1.3.1、准备环境

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class Userinfo implements Serializable {
   private Integer id;
   private byte[] images;
   private String description;
   public Integer getId() {
       return id;
  }
   public void setId(Integer id) {
       this.id = id;
  }
   public byte[] getImages() {
       return images;
  }
   public void setImages(byte[] images) {
       this.images = images;
  }
   public String getDescription() {
       return description;
  }
   public void setDescription(String description) {
       this.description = description;
  }
   @Override
   public String toString() {
       return "Userinfo{" +
           "id=" + id +
           ", images=" + Arrays.toString(images) +
           ", description='" + description + '\'' +
           '}';
  }
}
/**
* 在JdbcConfig中加入如下代码
*/
@Bean
public LobHandler createLobHandler(){
return new DefaultLobHandler();
}

 

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private LobHandler lobHandler;
@Test
public void testWrite(){
try {
FileSystemResource res = new FileSystemResource
("C:\\Users\\zhy\\Desktop\\6.jpg");
byte[] mockImg = FileCopyUtils.copyToByteArray(res.getFile());
Userinfo userinfo = new Userinfo();
userinfo.setId(3);
userinfo.setImages(mockImg);
userinfo.setDescription("Spring对数据库的操作在jdbc上面做了基本的封装,让开
发者在操作数据库时只需关注SQL语句和查询结果处理器,即可完成功能(当然,只使用JdbcTemplate,还
不能摆脱持久层实现类的编写)。\t\n" +
"\t在配合spring的IoC功能,可以把DataSource注册到JdbcTemplate之
中。同时利用spring基于aop的事务即可完成简单的数据库CRUD操作。\n" +
" JdbcTemplate的限定命名为
org.springframework.jdbc.core.JdbcTemplate。要使用JdbcTemlate需要导入spring-jdbc和
spring-tx两个坐标。");
jdbcTemplate.execute("insert into
userinfo(id,image,description)values(?,?,?)", new
AbstractLobCreatingPreparedStatementCallback(lobHandler) {
@Override
protected void setValues(PreparedStatement ps, LobCreator
lobCreator) throws SQLException, DataAccessException {
ps.setInt(1,3);
lobCreator.setBlobAsBytes(ps, 2,userinfo.getImages() );
lobCreator.setClobAsString(ps,3,userinfo.getDescription());
}
});
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void testRead(){
// List list = jdbcTemplate.query("select * from userinfo where
id=?",new BeanPropertyRowMapper<Userinfo>(Userinfo.class),3);
Userinfo userinfo = jdbcTemplate.query(
"select * from userinfo where id=?",
new ResultSetExtractor<Userinfo>() {
@Override
public Userinfo extractData(ResultSet rs) throws
SQLException, DataAccessException {
Userinfo userinfo = null;
if(rs.next()){
int id = rs.getInt(1);
byte[] image = lobHandler.getBlobAsBytes(rs, 2);
String description =
lobHandler.getClobAsString(rs,3);
userinfo = new Userinfo();
userinfo.setId(id);
userinfo.setImages(image);
userinfo.setDescription(description);
}
return userinfo;
}
},3);
System.out.println(userinfo);
}
}

 

 

2、NamedParameterJdbcTemplate

2.1、概述

2.1.1、基本介绍

在经典的 JDBC 用法中, SQL 参数是用占位符 ? 表示,并且受到位置的限制. 定位参数的问题在于, 一旦参数的顺序发生变化, 就必须改变参数绑定.在 Spring JDBC 框架中, 绑定 SQL 参数的另一种选择 是使用具名参数(named parameter).

那么什么是具名参数? 具名参数: SQL 按名称(以冒号开头)而不是按位置进行指定. 具名参数更易于维护, 也提升了可读性. 具名 参数由框架类在运行时用占位符取代 具名参数只在 NamedParameterJdbcTemplate 中得到支持。NamedParameterJdbcTemplate可以使 用全部jdbcTemplate方法。

 

2.1.2、源码

/**
* 通过观察源码我们发现,NamedParameterJdbcTemplate里面封装了一个JdbcTemplate对象
* 只不过把它看成了接口类型JdbcOperations。
*/
public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
{
/** The JdbcTemplate we are wrapping. */
private final JdbcOperations classicJdbcTemplate;

public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource must not be null");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* 使用JdbcOperations 构建一个NamedParameterJdbcTemplate
*/
public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
Assert.notNull(classicJdbcTemplate, "JdbcTemplate must not be null");
this.classicJdbcTemplate = classicJdbcTemplate;
}
//其余代码略
}

2.2、入门案例

2.2.1、导入坐标

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

 

2.2.2、编写实体类

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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;
}
}

 

2.2.3、编写配置类

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}

 

 

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new
DriverManagerDataSource(url,username,password);
dataSource.setDriverClassName(driver);
return dataSource;
}
@Bean
public NamedParameterJdbcTemplate createNamedJdbcTemplate(JdbcTemplate
jdbcTemplate){
return new NamedParameterJdbcTemplate(jdbcTemplate);
}
}

 

2.2.4、编写配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

 

2.2.5、测试方法

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testFindMore(){
// List<Account> accounts = jdbcTemplate.query("select * from account
where id in (?,?)",new Object[]{1,2},new BeanPropertyRowMapper<Account>
(Account.class));
Map<String,List<Integer>> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
account where id in (:ids)",map,new BeanPropertyRowMapper<Account>
(Account.class));
System.out.println(accounts);
}
@Test
public void testNamedParameter(){
Account account = new Account();
account.setName("test");
account.setMoney(12345d);
BeanMap beanMap = BeanMap.create(account);
namedParameterJdbcTemplate.update("insert into
account(name,money)values(:name,:money)",beanMap);
}
}

 

3、JdbcTemplate的实现原理

3.1、自定义JdbcTemplate

3.1.1、创建工程并导入坐标

<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>

3.1.2、编写自定义JdbcTemplate

/**
* 自定义JdbcTemplate
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class JdbcTemplate {
//定义数据源
private DataSource dataSource;
//通过构造函数给数据源赋值
public JdbcTemplate(DataSource dataSource){
this.dataSource = dataSource;
}
//通过set方法给数据源赋值
}
/**
* 查询方法
* @param sql sql语句
* @param rsh 结果集处理器
* @param params sql语句的参数
* @return
*/
public Object query(String sql, ResultSetHandler rsh , Object... params){
//1.判断是否有数据源,没有数据源就直接抛异常
if(dataSource == null){
throw new NullPointerException("DataSource can not empty!");
}
//2.定义连接和处理对象
Connection connection = null;
PreparedStatement pstm = null;
ResultSet rs = null;
try {
//2.获取连接
connection = dataSource.getConnection();
//3.获取预处理对象
pstm = connection.prepareStatement(sql);
//4.获取参数元信息
ParameterMetaData pmd = pstm.getParameterMetaData();
//5.获取参数个数
int parameterCount = pmd.getParameterCount();
//6.验证参数
if(parameterCount > 0){
if(params == null ){
throw new NullPointerException("Parameter can not be null
!");
}
if(parameterCount != params.length){
throw new IllegalArgumentException("Incorrect parameter
count: expected "+String.valueOf(parameterCount)+", actual
"+String.valueOf(params.length));
}
//7.给参数赋值
for(int i=0;i<parameterCount;i++){
pstm.setObject((i+1),params[i]);
}
}
//8.验证通过,执行SQL语句
rs = pstm.executeQuery();
//9.处理结果集:策略模式
return rsh.handle(rs);
}catch (Exception e){
throw new RuntimeException(e);
}finally {
release(connection,pstm,rs);
}
}
/**
* 增删改操作
* @return
*/
public int update(String sql,Object... params){
//1.判断是否有数据源,没有数据源就直接抛异常
if(dataSource == null){
throw new NullPointerException("DataSource can not empty!");
}
//2.定义连接和处理对象
Connection connection = null;
PreparedStatement pstm = null;
try {
//2.获取连接
connection = dataSource.getConnection();
//3.获取预处理对象
pstm = connection.prepareStatement(sql);
//4.获取参数元信息
ParameterMetaData pmd = pstm.getParameterMetaData();
//5.获取参数个数
int parameterCount = pmd.getParameterCount();
//6.验证参数
if(parameterCount > 0){
if(params == null ){
throw new NullPointerException("Parameter can not be null
!");
}
if(parameterCount != params.length){
throw new IllegalArgumentException("Incorrect parameter
count: expected "+String.valueOf(parameterCount)+", actual
"+String.valueOf(params.length));
}
//7.给参数赋值
for(int i=0;i<parameterCount;i++){
pstm.setObject((i+1),params[i]);
}
}
//8.验证通过,执行SQL语句
return pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
release(connection,pstm,null);
}
}
private void release(Connection conn, PreparedStatement pstm, ResultSet rs){
if(rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(pstm != null){
try {
pstm.close();
}catch (Exception e){
}
if(conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}

 

3.2、自定义RowMapper

3.2.1、定义接口

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public interface ResultSetHandler<T> {
/**
* 处理结果集
* @param rs
* @return
*/
Object handle(ResultSet rs)throws Exception;
}

3.2.2、提供不同实现

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class BeanHandler<T> implements ResultSetHandler {
private Class<T> requiredType;
private BeanListHandler<T> beanListHandler;
/**
* 覆盖默认无参构造
* @param requriedType
*/
public BeanHandler(Class requriedType){
this.requiredType = requriedType;
}
public BeanHandler(BeanListHandler beanListHandler) {
this.beanListHandler = beanListHandler;
}
if(beanListHandler != null){
return beanListHandler.handle(rs).get(0);
}
//1.定义返回值
T bean = null;
//2.由于是查询一个,所以只需判断rs能往下走,不用while循环即可
if(rs.next()){
//3.实例化bean对象
bean = requiredType.newInstance();
//4.获取参数元信息
ResultSetMetaData rsmd = rs.getMetaData();
//5.取出参数个数
int columnCount = rsmd.getColumnCount();
//6.遍历参数个数
for(int i=0;i<columnCount;i++){
//7.取出列名称
String columnLabel = rsmd.getColumnLabel(i+1);
//8.取出列的值
Object value = rs.getObject(columnLabel);
//9.创建实体类的属性描述器,使用内省填充对象数据
PropertyDescriptor pd = new
PropertyDescriptor(columnLabel,requiredType);
//10.获取属性的写方法
Method method = pd.getWriteMethod();
//11.填充数据
method.invoke(bean,value);
}
}
//返回
return bean;
}
}

 

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class BeanListHandler<T> implements ResultSetHandler {
private Class<T> requiredType;
/**
* 覆盖默认无参构造
* @param requriedType
*/
public BeanListHandler(Class requriedType){
this.requiredType = requriedType;
}
@Override
public List<T> handle(ResultSet rs) throws Exception{
//1.定义返回值
List<T> list = new ArrayList();
T bean = null;
//3.实例化bean对象
bean = requiredType.newInstance();
//4.获取参数元信息
ResultSetMetaData rsmd = rs.getMetaData();
//5.取出参数个数
int columnCount = rsmd.getColumnCount();
//6.遍历参数个数
for(int i=0;i<columnCount;i++){
//7.取出列名称
String columnLabel = rsmd.getColumnLabel(i+1);
//8.取出列的值
Object value = rs.getObject(columnLabel);
//9.创建实体类的属性描述器,使用内省填充对象数据
PropertyDescriptor pd = new
PropertyDescriptor(columnLabel,requiredType);
//10.获取属性的写方法
Method method = pd.getWriteMethod();
//11.填充数据
method.invoke(bean,value);
}
//12.给list填充数据
list.add(bean);
}
//返回
return list;
}
}

 

3.3、测试

3.3.1、创建测试工程并导入坐标

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>tx02_designjdbctemplate</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

 

3.3.2、编写实体类

/**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public class Account implements Serializable{
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
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;
}
}

 

3.3.3、编写配置类

 

 

3.3.4、编写配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
jdbc.username=root
jdbc.password=1234

 

3.3.5、编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJdbcTemplateUseTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testSave(){
jdbcTemplate.update("insert into
account(name,money)values(?,?)","ccc",1234f);
}
@Test
public void testUpdate(){
jdbcTemplate.update("update account set name=?,money=? where
id=?","ccc",2345f,1);
}
@Test
public void testDelete(){
jdbcTemplate.update("delete from account where id = ?",1);
}
@Test
public void testFindOne(){
Account account = (Account)jdbcTemplate.query("select * from account
where id = ?",new BeanHandler(Account.class),1);
System.out.println(account);
}
@Test
public void testFindAll(){
List<Account> accounts = (List<Account>)jdbcTemplate.query("select *
from account ",new BeanListHandler<Account>(Account.class));
for(Account account : accounts){
System.out.println(account);
}
}
}

 

3.4、策略模式

 

 

二、Spring中的事务

1、API介绍

1.1、PlatformTransactionManager和它的实现类

1.1.1、作用

此接口是Spring的事务管理器核心接口。Spring本身并不支持事务实现,只是负责提供标准,应用底 层支持什么样的事务,需要提供具体实现类。此处也是策略模式的具体应用。在Spring框架中,也为我们内 置了一些具体策略,例如:DataSourceTransactionManager,HibernateTransactionManager, JpaTransactionManager,JtaTransactionManager等等。(JpaTransactionManager和 HibernateTransactionManager事务管理器在spring-orm包中)

 

1.1.2、类视图

1.1.3、方法说明

public interface PlatformTransactionManager {
/**
* 获取事务状态信息
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* 提交事务
*/
void commit(TransactionStatus status) throws TransactionException;


void rollback(TransactionStatus status) throws TransactionException;
}

 

1.2、TransactionDefinition

1.2.1、作用

此接口是Spring中事务可控属性的顶层接口,里面定义了事务的一些属性以及获取属性的方法。例如: 事务的传播行为,事务的隔离级别,事务的只读,事务的超时等等。通常情况下,我们在开发中都可以配置这 些属性,以求达到最佳效果。配置的方式支持xml和注解。

 

1.2.2、类视图

1.2.3、定义信息说明

public interface TransactionDefinition {
/**
* REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
一般的选择(默认值)
*/
int PROPAGATION_REQUIRED = 0;
/**
* SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
*/
int PROPAGATION_SUPPORTS = 1;
/**
* MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
*/
int PROPAGATION_MANDATORY = 2;
/**
* REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
/**
* NEVER:以非事务方式运行,如果当前存在事务,抛出异常
*/
int PROPAGATION_NEVER = 5;
/**
* NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的
操作。
*/
int PROPAGATION_NESTED = 6;
/**
* 事务的隔离级别默认值,当取值-1时,会采用下面的4个值其中一个。
* (不同数据库的默认隔离级别不一样)
*/
int ISOLATION_DEFAULT = -1;
/**
* 事务隔离级别为:读未提交
* 执行效率最高,但什么错误情况也无法避免
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* 事务隔离级别为:读已提交
* 可以防止脏读的发生,但是无法防住不可重复读和幻读的发生
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 事务隔离级别为:可重复读
* 可以防止脏读和不可重复读的发生,但是无法防住幻读的发生
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 事务隔离级别为:串行化
* 此时所有错误情况均可防住,但是由于事务变成了独占模式(排他模式),因此效率最低
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
/**
* 超时限制。默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
*/
int TIMEOUT_DEFAULT = -1;
/**
* 获取事务传播行为
*/
int getPropagationBehavior();
/**
* 获取事务隔离级别
/**
* 获取事务超时时间
*/
int getTimeout();
/**
* 获取事务是否只读
*/
boolean isReadOnly();
/**
* 获取事务名称
*/
@Nullable
String getName();
}

1.3、TransactionStatus

 

1.3.1、作用

此接口是事务运行状态表示的顶层接口,里面定义着获取事务运行状态的一些方法。

 

 

1.3.3、方法说明

public interface TransactionStatus extends SavepointManager, Flushable {
/**
* 是否一个新的事务
*/
/**
* 是否包含存储点
*/
boolean hasSavepoint();
/**
* 设置事务回滚
*/
void setRollbackOnly();
/**
* 是否是只回滚事务
*/
boolean isRollbackOnly();
/**
* 刷新事务
*/
@Override
void flush();
/**
* 事务是否已经完成(标识就是提交或者回滚了)
*/
boolean isCompleted();
}

 

2、入门案例

2.1、前置说明

1、Spring中事务控制的分类:

编程式事务:通过编码的方式,实现事务控制

声明式事务:通过配置的方式,实现事务控制。

本案例中,采用的是声明式事务,且注解驱动的方式配置。

2、案例的选择 本案例采用的是经典转账案例测试事务控制

2.2、环境搭建

2.2.1、导入坐标

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

2.2.2、编写基础代码

/**
* 账户的业务层接口
* @author 黑马程序员
* @Company http://www.itheima.com
*/
public interface AccountService {
/**
* 转账
* @param sourceName 转出账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
void transfer(String sourceName,String targetName,Double money);
}

 

 

posted on 2021-08-10 13:37  Dongdong98  阅读(100)  评论(0)    收藏  举报