1.概念和准备

1.1 什么是 JdbcTemplate

  • Spring 框架对JDBC进行封装,使用 JdbcTemplate 方便实现对数据库操作

1.2 准备工作

  1. 引入依赖:

    • `spring-jdbc-xxx.jar``
    • ``spring-orm-5.2.6.xxx.jar`
    • spring-tx-5.2.6.xxx.jar
    • ``druid-xxx.jar`
    • mysql-connector-java-xxx.jar`
  2. 在 spring 配置文件配置数据库连接池:

<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${prop.driverClassName}"></property><property name="url" value="${prop.url}"></property><property name="username" value="${prop.username}"></property><property name="password" value="${prop.password}"></property>
</bean>
  1. 配置 JdbcTemplate 对象,注入 DataSource:
<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--注入 dataSource,因为JdbcTemplate类的有参构造方法中需要参数DataSource,而DataSource是在其父类的一个属性,所以这里是对其父类的一个set注入,不是说直接传入DataSource作为JdbcTemplate类的有参构造方法中的参数--><property name="dataSource" ref="dataSource"></property>
</bean>
  1. 创建 service 类和dao 类,在service注入dao对象以及在dao注入 JdbcTemplate 对象:
<!--注意开启组件扫描-->
<context:component-scan base-package="com.psj"></context:component-scan>
@Service
public class BookService {@Autowiredprivate BookDao bookDao;
}
@Repository
public class BookDaoImpl implements BookDao {@Autowiredprivate JdbcTemplate jdbcTemplate;
}

2.JdbcTemplate 操作数据库

  1. 对应数据库的表字段创建实体类:
public class Book {private String userId;private String username;private String usstatus;public String getUserId() {return userId;}// 剩余的set和get方法
  1. 编写 service 和 dao:
@Service
public class BookService {@Autowiredprivate BookDao bookDao;public void addBook(Book book){bookDao.add(book);}// 查询表记录数目public int findCount(){return bookDao.selectCount();}// 查询书本public Book findOne(String id){return bookDao.findBookInfo(id);}// 查询所有书本集合public List<Book> findAll(){return bookDao.findAllBook();}// 批量添加书本public void batchAdd(List<Object[]> batchArgs){bookDao.batchAddBook(batchArgs);}
}
@Repository
public class BookDaoImpl implements BookDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void add(Book book) {String sql = "insert into book values(?,?,?)";Object[] args = {book.getUserId(), book.getUsername(), book.getUsstatus()};int update = jdbcTemplate.update(sql, args);System.out.println(update);  // 表示成功添加的行数}@Overridepublic int selectCount() {String sql = "select count(*) from book";// 查询返回某个值// 第二个参数为返回的类型int count = jdbcTemplate.queryForObject(sql, Integer.class);return count;}@Overridepublic Book findBookInfo(String id) {String sql = "select * from book where user_id = ?";// 查询返回某个对象// 第二个参数为RowMapper,它是接口,针对返回的不同类型数据,使用这个接口里面完成类的数据封装Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);return book;}@Overridepublic List<Book> findAllBook() {String sql = "select * from book";List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));return bookList;}@Overridepublic void batchAddBook(List<Object[]> batchArgs) {String sql = "insert into book values(?,?,?)";// 将List数组中的每一组元素添加给占位符int[] update = jdbcTemplate.batchUpdate(sql, batchArgs);System.out.println(update);}
}
// 测试
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
BookService bookService = context.getBean("bookService", BookService.class);
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.事务操作

3.1 事务概念和特点

  • 事务是数据库操作最基本单元,逻辑上的一组操作(比如A转账给B100元,A要少100元,B同时要多100元)要么都成功,如果有一个失败所有操作都失败
  • 事务四个特性(ACID):
    • 原子性:要么都成功,要么都失败
    • 一致性:操作前和操作后的总量不变
    • 隔离性:多事务操作的时候它们之间不会产生影响
    • 持久性:事务提交后表中数据发生变化

3.2 异常场景

@Service
public class UserService {@Autowiredprivate UserDao userDao;public void accountMoney(){userDao.reduceMoney(); // psw少100int i = 1 / 0;  // 模拟异常userDao.addMoney(); // psj多100}
}
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void addMoney() {String sql = "update account set money=money+? where username=?";jdbcTemplate.update(sql, 100, "psj");  // psj多100}@Overridepublic void reduceMoney() {String sql = "update account set money=money-? where username=?";jdbcTemplate.update(sql, 100, "psw");  // psw少100}
}
// 正常执行userService.accountMoney()后数据库的变化是没有问题的,但是如果代码执行过程中出现异常。但此时出现除数异常,数据库中psw少100但是psj没有多100。需要使用事务进行解决

3.3 事务管理

  • 编程式事务管理:会有很多冗余的代码
public void accountMoney() {try {// 1.开启事务// 2.业务操作userDao.reduceMoney(); // psw少100int i = 1 / 0;  // 模拟异常userDao.addMoney(); // psj多100// 3.没有发生异常就提交事务} catch (Exception e) {// 4.出现异常则事务回滚(回到操作之前的状态)}
}
  • 声明式事务管理:底层使用AOP

    • 基于注解:
    // 需要先引入名称空间
    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
    @Service
    // 可以加在类上(类中所有方法都添加事务),也可以加在方法上
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
    public class UserService {@Autowiredprivate UserDao userDao;public void accountMoney() {// 注意把try-catch删除,不删除不会执行回滚操作,直接被catchuserDao.reduceMoney(); // psw少100int i = 1 / 0;  // 模拟异常userDao.addMoney(); // psj多100}
    }
    // 此时再次执行accountMoney(),数据库中的两个人的money值都不会变化
    
    • 基于XML:
    <!--1.创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--2.配置通知-->
    <tx:advice id="txadvice"><!--配置事务参数--><tx:attributes><!--指定哪种规则的方法上面添加事务--><tx:method name="accountMoney" propagation="REQUIRED"/><!--<tx:method name="account*"/>-->  // 表示为account开头的方法添加事务</tx:attributes>
    </tx:advice>
    <!--3.配置切入点和切面-->
    <aop:config><!--配置切入点--><aop:pointcut id="pt" expression="execution(* com.psj.JDBCTemplate.service.UserService.*    (..))"/><!--配置切面--><aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
    
    • 完全注解:
    @Configuration  // 表示该类是配置类
    @ComponentScan(basePackages = "com.psj.JDBCTemplate")  // 开启组件扫描
    @EnableTransactionManagement  // 开启事务
    public class TxConfig {// 创建数据库连接池@Beanpublic DruidDataSource getDruidDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc?serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("xxxx");return dataSource;}@Bean// 创建JDBCTemplate对象public JdbcTemplate getJdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();// 注入dataSource:IOC容器中会自动去找DataSource类型的注入对象jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}@Beanpublic DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();// 注入dataSource:IOC容器中会自动去找DataSource类型的注入对象dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}
    }
    
    // 对应上述代码
    <!--开启组件扫描-->
    <context:component-scan base-package="com.psj.JDBCTemplate"></context:component-scan><!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${prop.driverClassName}"></property><property name="url" value="${prop.url}"></property><property name="username" value="${prop.username}"></property><property name="password" value="${prop.password}"></property>
    </bean>
    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--注入 dataSource,因为JdbcTemplate类中有参构造就是传入该参数--><property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    

tips:

  • 事务添加到Service层中(理论上可以加到三层中的任意一层)
  • Spring 事务管理是提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类,如下图中对Hibernate等都有实现类

  • @Transactional中的参数介绍:

    • propagation:事务传播行为(即多事务方法直接进行调用,则该过程事务是如何进行管理的),比如在已经加了事务注解的add方法中调用没加事务注解的update方法,事务该如何管理(传播行为有7种,以下两种最常见)

      • REQUIRED:默认值。如果add方法有事务,被调用的update方法会使用add方法的事务;如果add方法没有事务的话,update方法就使用自己的事务
      • REQUIRED_NEW:无论add方法是否有事务,update方法都创建新的事务

  • ioslation:事务隔离级别。事务的隔离性保证多事务操作(并发操作)之间不会产生影响,不考虑隔离性产生很多问题(三个读的问题)

    • 脏读:一个未提交事务读取到另一个未提交事务的数据(事务未提交就可以回滚)。假如psj开启事务A,psw开启事务B,此时psw将一条数据中的money由100改为200,此时psj读到的数据是200,但是如果psw开启事务回滚,数据又变回原来的100,和psj读到的数据不一致
    • 不可重复读:一个未提交事务读取到另一提交事务修改的数据。假如psj开启事务A,psw开启事务B,此时psw将一条数据中的money由100改为200,并且立刻提交事务。此时psj还没提交事务,但是却读到的数据变为200,正常情况应该要等psj提交完事务数据才会改变(为什么加不可重复读?假设psj原来读到100,可是在psw修改为200并提交事务后就再也读不到100这个值了)
    • 虚读(幻读):一个未提交事务读取到另一提交事务添加的数据,和不可重复读类似,就是psw添加数据并提交事务后,psj能读到该数据(相当于于psj第一查询有10条数据,等第二次查询就变成12条了)

  • timeout:超时时间。事务需要在一定时间内进行提交,如果不提交则进行回滚(默认值是 -1)

  • readOnly:是否只读。默认值 false,表示可以查询也可以添加修改删除操作

  • rollbackFor:回滚。设置出现哪些异常时进行事务回滚

  • noRollbackFor:不回滚。设置出现哪些异常时不进行事务回滚

  • 事务方法:对数据库数据进行变化的操作


Spring框架-JdbcTemplate相关推荐

  1. Spring框架 JdbcTemplate类 @Junit单元测试,可以让方法独立执行 如:@Test

    1 package cn.zmh.PingCe; 2 3 import org.junit.Test; 4 import org.springframework.jdbc.core.BeanPrope ...

  2. spring框架(五)之JdbcTemplate基本使用

    数据准备 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:/ ...

  3. Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发

    Spring JDBC     * Spring框架对JDBC的简单封装.提供了一个JDBCTemplate对象简化JDBC的开发     * 步骤:         1. 导入jar包        ...

  4. Spring框架(下)JdbcTemplate、声明式事务管理

    Spring框架(下)JdbcTemplate.声明式事务管理 (一)使用JdbcTemplate 1.概述 为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个 ...

  5. Spring框架针对dao层的jdbcTemplate操作crud之query查询数据操作

    查询目标是完成3个功能: (1)查询表,返回某一个值.例如查询表中记录的条数,返回一个int类型数据 (2)查询表,返回结果为某一个对象. (3)查询表,返回结果为某一个泛型的list集合. 一.查询 ...

  6. 使用Spring框架的好处

    转自:https://www.cnblogs.com/hoobey/p/6032506.html 在SSH框假中spring充当了管理容器的角色.我们都知道Hibernate用来做持久层,因为它将JD ...

  7. SSM-Spring-19:Spring中JdbcTemplate

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- Spring自带一个ORM持久化框架JdbcTemplate,他可以说是jdbc的加强版,但是对最细微的控制肯 ...

  8. 实现基于Spring框架应用的权限控制系统(转)

    为什么80%的码农都做不了架构师?>>>    前注:当我摸到了通过filter拦截权限,通过AOP拦截方法,通过权限控制菜单的时候,猛然发现这一切的一切已经是别人已经发明好的轮子. ...

  9. spring框架使用Quartz执行定时任务实例详解

    版权声明:本文为博主原创文章,如需转载,请标明出处. https://blog.csdn.net/alan_liuyue/article/details/80382324 Quartz简介 1.Qua ...

最新文章

  1. MOSES | 分子生成模型的基准平台
  2. 怎样开发一个 Node.js 命令行工具包
  3. codeforces 939C Convenient For Everybody 简直羞耻
  4. 物联网避坑 3 大指南!
  5. Hadoop tutorial - 3 Hello MapReduce- 2015-3-30
  6. UCHome风格模版 框架核心代码提取
  7. Hololens2 与Unity 远程连接调试程序和调试部署
  8. Google sheet 设置下拉列表
  9. 农民工看完都学会了!Android开发岗还不会这些问题,跳槽薪资翻倍
  10. C1 驾驶证考试科目二考试心得
  11. linux下puts和gets命令用法,puts()和gets()函数(示例代码)
  12. csv是什么意思中文_csv文件是什么意思
  13. VB不能加载MSCOMCTL.OCX所需文件
  14. python考研选什么专业好就业_人工智能考研专业就业怎么样 哪些院校开设人工智能专业...
  15. 有趣的灵魂是什么样的
  16. 【React Native】深入理解Native与RN通信原理
  17. Unity中的设备唯一码GAID、IDFA,用于广告跟踪和数据统计
  18. 2022年API安全研究报告
  19. Linux网络编程基础3:数据读写
  20. GMTSAR合成孔径雷达干涉测量InSAR数据处理、形变信息提取与分析等实践技术应用

热门文章

  1. android 瀑布流 的实现
  2. HTTP状态码(2xx,3xx,4xx,5xx)
  3. Go语言圣经 - 第3章 基础数据类型
  4. 英寸与毫米的换算依据
  5. unicode编码和utf-8编码的区别
  6. 量子计算机、康威扭结、奥数AI,这是2020年计算机、数学的重大突破
  7. 我一个普通程序员,光靠GitHub打赏就年入70万,
  8. 解读文献(五)------基于阻抗控制
  9. 618最强攻略揭秘:成为网易考拉的黑卡会员!
  10. 使枚举成功的USB设备成为WINUSB设备