Spring5框架——JdbcTemplate&声明式事务控制

目录

JdbcTemplate

概述

JdbcTemplate开发步骤

操作数据库

事务管理

1. 事务概念

2. 搭建事务操作环境

3. Spring事务管理介绍

3.1 编程式事务控制三大对象

3.2 基于 XML 的声明式事务控制

3.3 基于注解的声明式事务控制

3.4 完全注解声明式事务管理

Spring5 新特性


JdbcTemplate

概述

        JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。

JdbcTemplate开发步骤

(1)引入相关 jar 包或写maven配置文件

<!--导入spring的jdbc坐标-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!--导入spring的tx坐标-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

 (2)在 spring 配置文件配置数据库连接池

<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
  <property name="url" value="jdbc:mysql:///user_db" />
  <property name="username" value="root" />
  <property name="password" value="root" />
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>

(3)配置 JdbcTemplate 对象,注入 DataSource

JdbcTemplate 对象是spring提供的,我们负责配置。在Spring容器内部将 数据源DataSource注入到JdbcTemplate模版对象中

<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <!--注入 dataSource-->
  <property name="dataSource" ref="dataSource"></property>
</bean>

(4)创建service类,创建dao类,在dao注入jdbcTemplate对象以便后续进行数据库操作,在service中注入dao对象

* 配置文件
<!-- 组件扫描 -->
<context:component-scan base-package="com.atguigu"></context:component-scan>

*创建类 

//Service
@Service
public class BookService {
 //注入 dao
 @Autowired
 private BookDao bookDao;
}

//Dao
@Repository
public class BookDaoImpl implements BookDao {
 //注入 JdbcTemplate
 @Autowired
 private JdbcTemplate jdbcTemplate;
}

操作数据库

创建表t_user,表结构如下

 添加

  1、对应数据库创建实体类
public class User {
    private Integer userId;
    private String username;
    private String ustatus;
    
    ... ...
}

 2、编写service和dao

 (1)在dao进行数据库添加操作
 (2)调用 JdbcTemplate 对象里面 update 方法实现添加操作
          update(String sql,Object... args)
                有两个参数 
                        第一个参数:sql 语句        
                        第二个参数:可变参数,设置 sql 语句值。预编译sql语句的占位符对应的值
@Repository
public class BookDaoImpl implements BookDao {
  //注入 JdbcTemplate
  @Autowired
  private JdbcTemplate jdbcTemplate;
  //添加的方法
  @Override
  public void add(User user) {
    //1 创建 sql 语句
    String sql = "insert into t_user values(?,?,?)";
    //2 调用方法实现
    Object[] args = {user.getUserId(), user.getUsername(), user.getUstatus()};
    int update = jdbcTemplate.update(sql,args);
    System.out.println(update);
  }
}
3、测试类
@Test
public void testJdbcTemplate() {
  ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
  UserService bookService = context.getBean("userService", UserService.class);
  User user = new User();
  user.setUserId("1");
  user.setUsername("java");
  user.setUstatus("a");
  userService.addUser(book);
}

修改和删除

1、修改
public void updateUser(User user) {
 String sql = "update t_user set username=?,ustatus=? where user_id=?";
 Object[] args = {user.getUsername(), user.getUstatus(),user.getUserId()};
 int update = jdbcTemplate.update(sql, args);
 System.out.println(update);
}
 
2、删除
@Override
public void delete(Integer id) {
 String sql = "delete from t_user where user_id=?";
 int update = jdbcTemplate.update(sql, id);
 System.out.println(update);
}

查询返回某个值

如查询表里面有多少条记录,返回是某个值
使用 JdbcTemplate 实现查询返回某个值代码
queryForObject(String sql,Class<T> requireType)
    有两个参数
          第一个参数:sql 语句
          第二个参数:返回值类型Class
//查询表记录数
@Override
public int selectCount() {
   String sql = "select count(*) from t_book";
   Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
   return count;
}

查询返回对象

1、场景:查询图书详情
2、JdbcTemplate 实现查询返回对象
      queryForObject(String sql,RowMapper<T> rowMapper,Object... args)
        有三个参数
        第一个参数:sql 语句
        第二个参数:RowMapper 是接口,针对返回不同的类型数据,使用这个接口里面的实现类 完成数据封装
        第三个参数:sql 语句中预定义值
//查询返回对象
@Override
public Book findBookInfo(String id) {
 String sql = "select * from t_book where user_id=?";
 //调用方法
 Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
 return book;
}

查询返回集合

1、场景:查询图书列表分页…
2、调用 JdbcTemplate 方法实现查询返回集合
    query(String sql,RowMapper<T> rowMapper,Object... args)
        有三个参数
        第一个参数:sql 语句
        第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类( BeanPropertyRowMappe r)完成数据封装
        第三个参数:sql 语句值
//查询返回集合
@Override
public List<Book> findAllBook() {
 String sql = "select * from t_book";
 //调用方法
 List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
 return bookList;
}

批量操作

1、批量操作:操作表里面多条记录
2、JdbcTemplate 实现批量添加操作
        batchUpdate(String sql,List<Object[]> batchArgs)
         有两个参数
                第一个参数:sql 语句
                第二个参数:List 集合,添加多条记录数据
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
 String sql = "insert into t_book values(?,?,?)";
 int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);//底层是遍历集合,将遍历得到的数组里面的值进行sql语句操作
 System.out.println(Arrays.toString(ints));
}

//批量添加测试
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3","java","a"};
Object[] o2 = {"4","c++","b"};
Object[] o3 = {"5","MySQL","c"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用批量添加
bookService.batchAdd(batchArgs);
3、JdbcTemplate 实现批量修改操作
//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
 String sql = "update t_book set username=?,ustatus=? where user_id=?";
 int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
 System.out.println(Arrays.toString(ints));
}
//批量修改
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"java0909","a3","3"};
Object[] o2 = {"c++1010","b4","4"};
Object[] o3 = {"MySQL1111","c5","5"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用方法实现批量修改
bookService.batchUpdate(batchArgs);
4、JdbcTemplate 实现批量删除操作
//批量删除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
 String sql = "delete from t_book where user_id=?";
 int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
 System.out.println(Arrays.toString(ints));
}
//批量删除
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3"};
Object[] o2 = {"4"};
batchArgs.add(o1);
batchArgs.add(o2);
//调用方法实现批量删除
bookService.batchDelete(batchArgs);

事务管理

1. 事务概念

1、什么事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账
                * lucy 转账 100 元 给 mary,* lucy 少 100,mary 多 100.过程中两个成功则都成功,有失败则都失败。

2、事务四个特性(ACID)
原子性(Atomicity: 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency): 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation): 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

2. 搭建事务操作环境

 1、创建数据库表,添加记录

2、创建 service,搭建 dao,完成对象创建和注入关系
(1)service 注入 dao,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource
@Service
public class UserService {
  //注入 dao
  @Autowired
  private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
  @Autowired
  private JdbcTemplate jdbcTemplate;
}

 3、在 dao 创建两个方法:多钱和少钱的方法,在 service 创建方法(转账的方法)

@Repository
public class UserDaoImpl implements UserDao {
  @Autowired
  private JdbcTemplate jdbcTemplate;
  //lucy 转账 100 给 mary
  //少钱
  @Override
  public void reduceMoney() {
    String sql = "update t_account set money=money-? where username=?";
    jdbcTemplate.update(sql,100,"lucy");
  }
  //多钱
  @Override
  public void addMoney() {
    String sql = "update t_account set money=money+? where username=?";
    jdbcTemplate.update(sql,100,"mary");
 }
}


@Service
public class UserService {
  //注入 dao
  @Autowired
  private UserDao userDao;

    //转账的方法
  public void accountMoney() {
    //lucy 少 100
    userDao.reduceMoney();
    //mary 多 100
    userDao.addMoney();
  }
}
4、上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,有问题

(1)上面问题如何解决呢?         * 使用事务进行解决
(2)事务操作过程
           try{开启事务        
                进行业务操作
                没有发生异常,提交事务
                }catch{
                出现异常,事务回滚
           }

3. Spring事务管理介绍

1、事务添加到JavaEE三层结构里面的Service 层(业务逻辑层)

2、在Spring进行事务管理操作
(1)有两种方式:编程式事务管理(如上通过代码实现,繁琐)和
声明式事务管理(通过配置实现,开发中使用)

3、声明式事务管理
(1)基于注解方式(使用)
(2)基于 xml 配置文件方式

4、声明式事务控制

        Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明 ,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作用
        事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可(Spring进行声明式事务管理,底层使用AOP原理实现,切点是业务代码,通知是事务控制)。

        在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

3.1 编程式事务控制三大对象

1. Spring事务管理器 PlatformTransactionManager

PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

 这个接口针对不同的框架提供不同的实现类。Dao层技术是jdbc或mybatis对应的实现类是DataSourceTransaactionManagerDao 层技术是hibernate时实现类是HibernateTransactionManager。(需要通过配置的方式告知spring使用的事务管理器类型)

2.  事务的信息定义对象 TransactionDefinition

TransactionDefinition 是事务的定义信息对象,里面有如下方法: 

(1) 事务隔离级别

        事务有特性称为隔离性,设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。mysql默认隔离级别可重复读
        有三个读问题:脏读、不可重复读、虚(幻)读
  • 脏读: 对于两个事务 T1, T2, T1读取了已经被T2更新但还没有被提交的字段。之后, 若T2 回滚, T1读取的内容就是临时且无效的。(问题一定要解决,读已提交数据隔离级别解决)
  • 不可重复读: 对于两个事务T1, T2, T1读取了一个字段, 然后T2更新了该字段。之后, T1再次读取同一个字段, 值就不同了。(可以接受,可重复读隔离级别解决)
  • 幻读: 对于两个事务T1, T2, T1从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。(可以接受。串行化隔离级别解决)

(2) 事务传播行为(propagation

事务方法:对数据库表数据进行变化的操作(增删改),查询不算
传播行为:解决多事务方法调用的过程中产生的影响。如在add方法中调update方法,在这个过程中事务是怎么管理的

分类:重点记前两个。传播行为默认是require

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

 MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
 REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
 NEVER:以非事务方式运行,如果当前存在事务,抛出异常
 NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作

传播行为需要通过配置告知spring框架

超时时间timeout:默认值是-1,没有超时限制。如果有,以秒为单位进行设置。事务需要在一定时间内进行提交,如果不提交需要进行回滚

是否只读readOnly:
(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询。建议查询时设置为只读
3、事务运行状态 TransactionStatus 
TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。

封装事务运行过程中的状态信息

3.2 基于 XML 的声明式事务控制

在 spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置事务AOP织入
<?xml version="1.0" encoding="UTF-8"?>
<!--在 spring 配置文件引入名称空间 tx-->
<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"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">

    <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>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--目标对象  内部的方法就是切点-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--配置平台事务管理器-->
    <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"/><!--以update开头的任意方法-->
            <tx:method name="*"/><!--任意方法-->
        </tx:attributes>
    </tx:advice>

    <!--配置事务的aop织入-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

</beans>

3.3 基于注解的声明式事务控制

1.自定义的bean用注解方式配置,非自定义的bean采用配置文件方式

 编写 AccoutDao

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private 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);
    }
}

编写 AccoutService

@Service("accountService")

public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
        public void transfer(String outMan, String inMan, double money) {
accountDao.out(outMan,money);
    int i = 1/0;
    accountDao.in(inMan,money);
    }
}

3. 编写 applicationContext.xml 配置文件

<!—之前省略datsSource、jdbcTemplate、平台事务管理器的配置-->
<!--组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--事务的注解驱动-->
<tx:annotation-driven/>

4、在 service 类上面(或者 service 类里面方法上面)添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面。可以加参数

(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务 

@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
    ......
}

3.4 完全注解声明式事务管理

1、创建配置类,使用配置类替代 xml 配置文件

@Configuration //配置类
@ComponentScan(basePackages = "com.atguigu") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
  //创建数据库连接池
  @Bean //表示方法返回的是对象,对象交给IOC管理,根据类型进行注入
  public DruidDataSource getDruidDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql:///user_db");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    return dataSource;
  }

 //创建 JdbcTemplate 对象
 @Bean 
 public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
   //到 ioc 容器中根据类型找到 dataSource
   JdbcTemplate jdbcTemplate = new JdbcTemplate();
   //注入 dataSource
   jdbcTemplate.setDataSource(dataSource);
   return jdbcTemplate;
 }

 //创建事务管理器
 @Bean
 public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
   DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
   transactionManager.setDataSource(dataSource);
   return transactionManager;
 }
}

Spring5 新特性

1、整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中删除
2、Spring 5.0 框架自带了通用的日志封装 。(通过日志可以看到程序运行过程,排查问题)
(1)Spring5 已经移除 Log4jConfigListener,官方建议使用Log4j2
(2)Spring5 框架整合 Log4j2
                第一步 引入 jar 包

                第二步 创建 log4j2.xml 配置文件(名字内容固定)

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

3、Spring5 框架核心容器支持@Nullable 注解

(1)@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
(2)注解用在方法上面,方法返回值可以为空

(3)注解使用在方法参数里面,方法参数可以为空

 (4)注解使用在属性上面,属性值可以为空

4、Spring5 核心容器支持函数式风格 GenericApplicationContext

@Test
public void testGenericApplicationContext() {
 //1 创建 GenericApplicationContext 对象
 GenericApplicationContext context = new GenericApplicationContext();
 //2 调用 context 的方法对象注册
 context.refresh();
 context.registerBean("user1",User.class,() -> new User());//通过lamaba表达式new的对象在spring中进行注册
 //3 获取在 spring 注册的对象
 // User user = (User)context.getBean("com.atguigu.spring5.test.User");
 User user = (User)context.getBean("user1");
 System.out.println(user);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值