Spring JDBC与事务管理
程序员文章站
2022-07-13 21:21:29
...
Spring JDBC与事务管理
Spring JDBC
- Spring JDBC是Spring框架用于处理关系型数据库的模块
- Spring JDBC对JDBC API进行封装,极大简化开发工作量
- JdbcTemplate是Spring JDBC核心类,提供数据CRUD方法
Spring JDBC的使用步骤
- Maven工程引入依赖spring-jdbc
- applicationContext.xml配置DataSource数据源
- 在Dao注入JdbcTemplate对象,实现数据CRUD
JdbcTemplate实现增删改查
Spring JDBC配置过程代码示例:
applicationContext.xml
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--JdbcTemplate提供CRUD的API--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao"> <!--为Dao注入JdbcTemplate对象--> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> </beans>
Employee.java
package com.imooc.spring.jdbc.entity; import java.util.Date; /** * @author Rex * @create 2021-01-05 14:44 */ public class Employee { private Integer eno; private String ename; private Float salary; private String dname; private Date hiredate; public Integer getEno() { return eno; } public void setEno(Integer eno) { this.eno = eno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public Float getSalary() { return salary; } public void setSalary(Float salary) { this.salary = salary; } public String getDname() { return dname; } public void setDname(String dname) { this.dname = dname; } public Date getHiredate() { return hiredate; } public void setHiredate(Date hiredate) { this.hiredate = hiredate; } @Override public String toString() { return "Employee{" + "eno=" + eno + ", ename='" + ename + '\'' + ", salary=" + salary + ", dname='" + dname + '\'' + ", hiredate=" + hiredate + '}'; } }
EmployeeDao.java
package com.imooc.spring.jdbc.dao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; /** * @author Rex * @create 2021-01-05 14:46 */ @Repository public class EmployeeDao { private JdbcTemplate jdbcTemplate; public Employee findById(Integer eno){ String sql = "select * from employee where eno = ?"; Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class)); return employee; } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
SpringApplication.java
package com.imooc.spring.jdbc; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author Rex * @create 2021-01-05 14:52 */ public class SpringApplication { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); EmployeeDao employeeDao = context.getBean("employeeDao", EmployeeDao.class); Employee employee = employeeDao.findById(3308); System.out.println(employee); } }
JDBC Template的数据查询代码示例:
EmployeeDao.java
public Employee findById(Integer eno){ String sql = "select * from employee where eno = ?"; //查询单条数据 Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class)); return employee; } public List<Employee> findByDname(String dname){ String sql = "select * from Employee where dname = ?"; //查询复合数据 List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class)); return list; } public List<Map<String, Object>> findMapByDname(String dname){ String sql = "select eno as empno, salary as s from employee where dname=?"; //将查询结果作为Map进行封装 List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname}); return maps; }
单条查询测试
@Test public void testFindById(){ Employee employee = employeeDao.findById(3308); System.out.println(employee); }
查询结果:
[Employee{eno=3308, ename='张三', salary=6000.0, dname='研发部', hiredate=2011-05-08 00:00:00.0}, Employee{eno=3420, ename='李四', salary=8700.0, dname='研发部', hiredate=2006-11-11 00:00:00.0}]
多条记录查询测试
@Test public void testFindByDname(){ List<Employee> list = employeeDao.findByDname("研发部"); System.out.println(list); }
查询结果:
Employee{eno=3308, ename='张三', salary=6000.0, dname='研发部', hiredate=2011-05-08 00:00:00.0}
将查询结果以Map作为封装测试
@Test public void testFindMapByDname(){ System.out.println(employeeDao.findMapByDname("研发部")); }
查询结果:
[{empno=3308, s=6000.0}, {empno=3420, s=8700.0}]
JdbcTemplate数据写入代码示例
数据新增:
public int insert(Employee employee){ String sql = "insert into employee(eno, ename, salary, dname, hiredate) values(?, ?, ?, ?, ?)"; //利用update方法实现数据写入操作 return jdbcTemplate.update(sql, new Object[]{employee.getEno(), employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate()}); }
测试代码:
@Test public void testInsert(){ Employee employee = new Employee(); employee.setEno(8888); employee.setEname("赵六"); employee.setSalary(6666f); employee.setDname("研发部"); employee.setHiredate(new Date()); int count = employeeDao.insert(employee); System.out.println("本次新增"+count+"条数据"); }
测试结果:
本次插入1条数据
数据修改:
public int update(Employee employee){ String sql = "update employee set ename = ?, salary = ?, dname = ?, hiredate = ? where eno = ?"; int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()}); return count; }
测试代码:
@Test public void testUpdate(){ Employee employee = employeeDao.findById(8888); employee.setSalary(employee.getSalary()+1000f); int count = employeeDao.update(employee); System.out.println("本次更新"+count+"条数据"); }
测试结果:
本次更新1条数据
数据删除:
public int delete(Integer eno){ String sql = "delete from employee where eno = ?"; return jdbcTemplate.update(sql, new Object[]{eno}); }
测试代码:
@Test public void testDelete(){ int count = employeeDao.delete(8888); System.out.println("本次删除"+count+"条数据"); }
测试结果:
本次删除1条数据
Spring编程式事务
什么是事务
- 事务以一种可靠的、一致的方式,访问和造作数据库的程序单元
- 说人话:要么把事情做完,要么什么都不做,不要做一半
- 事务依赖于数据库实现,MySQL通过事务区作为数据缓冲地带
编程式事务
- 编程式事务是指通过代码手动提交回滚事务的事务控制方法
- SpringJDBC通过TransactionManage事务管理器实现事务控制
- 事务管理器提供commit/rollback方法进行事务提交与回滚
代码示例:
ApplicationContext.xml
<bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService"> <property name="employeeDao" ref="employeeDao"></property> <property name="transactionManager" ref="transactionManager"></property> </bean> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
EmployeeService.java
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import java.util.Date; /** * @author Rex * @create 2021-01-05 16:31 */ public class EmployeeService { private EmployeeDao employeeDao; private DataSourceTransactionManager transactionManager; public void batchImport(){ //定义了事务默认的标准配置 TransactionDefinition definition = new DefaultTransactionDefinition(); //开始一个事务 TransactionStatus status = transactionManager.getTransaction(definition); try { for (int i = 0; i < 10; i++) { if ( i == 3) { throw new RuntimeException("意料之外的异常"); } Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("员工" + i); employee.setSalary(4000f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } // //提交事务 transactionManager.commit(status); }catch (RuntimeException e){ transactionManager.rollback(status); throw e; } } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public DataSourceTransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(DataSourceTransactionManager transactionManager) { this.transactionManager = transactionManager; } }
JdbcTemplateTestor.java
@Test public void testBatchImport(){ employeeService.batchImport(); System.out.println("批量导入成功"); }
测试结果:
... 16:55:40.411 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into employee(eno, ename, salary, dname, hiredate) values(?, ?, ?, ?, ?)] 16:55:40.490 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQLWarning ignored: SQL state '22007', error code '1292', message [Incorrect date value: '2021-01-05 16:55:40.405' for column 'hiredate' at row 1] 16:55:40.498 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update 16:55:40.498 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into employee(eno, ename, salary, dname, hiredate) values(?, ?, ?, ?, ?)] 16:55:40.502 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQLWarning ignored: SQL state '22007', error code '1292', message [Incorrect date value: '2021-01-05 16:55:40.498' for column 'hiredate' at row 1] 16:55:40.502 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update 16:55:40.503 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into employee(eno, ename, salary, dname, hiredate) values(?, ?, ?, ?, ?)] 16:55:40.507 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQLWarning ignored: SQL state '22007', error code '1292', message [Incorrect date value: '2021-01-05 16:55:40.502' for column 'hiredate' at row 1] 16:55:40.507 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback 16:55:40.507 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [[email protected]] 16:55:40.521 [main] DEBUG ... java.lang.RuntimeException: 意料之外的异常...
Spring声明式事务
声明式事务
- 声明式事务指在不修改源码情况下通过配置形式自动实现事务控制,声明式事务本质就是AOP环绕通知
- 当目标方法执行成功时,自动提交事务
- 当目标方法抛出运行时异常时,自动事务回滚
配置过程
- 配置TransactionManager事务管理器
- 配置事务通知与事务属性
- 为事务通知绑定PointCut切点
代码示例:
applicationContext.xml
<!--1.事务管理器,用于创建事务/提交/回滚--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚--> <tx:method name="batchImport" propagation="REQUIRED"/> <tx:method name="batch*" propagation="REQUIRED"></tx:method> <!--设置所有findXXX方法不需要使用事务--> <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/> <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--3.定义声明式事务的作用范围--> <aop:config> <aop:pointcut id="pointcut" expression="execution(public * com.imooc..*Service.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor> </aop:config>
EmployeeService.java
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import java.util.Date; public class EmployeeService { private EmployeeDao employeeDao; public void batchImport() { for (int i = 1; i <= 10; i++) { if(i == 3){ throw new RuntimeException("意料之外的异常"); } Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("员工" + i); employee.setSalary(4000f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } }
执行结果:
同上
事务传播行为
- 事务传播行为是指多个拥有事务的方法在嵌套调用时的事务控制方式
- XML:<tx:method name="…" propagation=“REQUIRED”>
- 注解:@Transactional(propagation=Propagation.REQUIRED)
事务传播行为七种类型
事务传播类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中。这是最常见的选择 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANADATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NOT_SUPPORT | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
Spring注解配置声明式事务
代码示例
applicationContext.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.imooc"/> <!--数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&usePublicKeyRetrieval=true"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--启用注解形式声明式事务--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
Employee.java
package com.imooc.spring.jdbc.entity; import java.util.Date; public class Employee { private Integer eno; private String ename; private Float salary; private String dname; private Date hiredate; public Integer getEno() { return eno; } public void setEno(Integer eno) { this.eno = eno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public Float getSalary() { return salary; } public void setSalary(Float salary) { this.salary = salary; } public String getDname() { return dname; } public void setDname(String dname) { this.dname = dname; } public Date getHiredate() { return hiredate; } public void setHiredate(Date hiredate) { this.hiredate = hiredate; } @Override public String toString() { return "Employee{" + "eno=" + eno + ", ename='" + ename + '\'' + ", salary=" + salary + ", dname='" + dname + '\'' + ", hiredate=" + hiredate + '}'; } }
EmployeeDao.java
package com.imooc.spring.jdbc.dao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.List; import java.util.Map; @Repository public class EmployeeDao { @Resource private JdbcTemplate jdbcTemplate; public Employee findById(Integer eno){ String sql = "select * from employee where eno = ?"; //查询单条数据 Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class)); return employee; } public List<Employee> findByDname(String dname){ String sql = "select * from employee where dname = ?"; //查询复合数据 List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class)); return list; } public List<Map<String, Object>> findMapByDname(String dname){ String sql = "select eno as empno , salary as s from employee where dname = ?"; //将查询结果作为Map进行封装 List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname}); return maps; } public void insert(Employee employee){ String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)"; //利用update方法实现数据写入操作 jdbcTemplate.update(sql,new Object[]{ employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate() }); } public int update(Employee employee){ String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?"; int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()}); return count; } public int delete(Integer eno){ String sql = "delete from employee where eno = ?"; return jdbcTemplate.update(sql, new Object[]{eno}); } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
BatchService.java
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Date; @Service @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public class BatchService { @Resource private EmployeeDao employeeDao; @Transactional(propagation = Propagation.REQUIRES_NEW) public void importJob1(){ for (int i = 1; i <= 10; i++) { Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("研发部员工" + i); employee.setSalary(4000f); employee.setDname("研发部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void importJob2(){ for (int i = 1; i <= 10; i++) { Employee employee = new Employee(); employee.setEno(9000 + i); employee.setEname("市场部员工" + i); employee.setSalary(4500f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } }
EmployeeService.java
package com.imooc.spring.jdbc.service; import com.imooc.spring.jdbc.dao.EmployeeDao; import com.imooc.spring.jdbc.entity.Employee; 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; import java.util.Date; @Service //声明式事务核心注解 //放在类上,将声明式事务配置应用于当前类所有方法,默认事务传播为REQUIRED @Transactional public class EmployeeService { @Resource private EmployeeDao employeeDao; @Resource private BatchService batchService; @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public Employee findById(Integer eno){ return employeeDao.findById(eno); } public void batchImport() { for (int i = 1; i <= 10; i++) { /*if(i==3){ throw new RuntimeException("意料之外的异常"); }*/ Employee employee = new Employee(); employee.setEno(8000 + i); employee.setEname("员工" + i); employee.setSalary(4000f); employee.setDname("市场部"); employee.setHiredate(new Date()); employeeDao.insert(employee); } } public void startImportJob(){ batchService.importJob1(); if(1==1){ throw new RuntimeException("意料之外的异常"); } batchService.importJob2(); System.out.println("批量导入成功"); } public EmployeeDao getEmployeeDao() { return employeeDao; } public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public BatchService getBatchService() { return batchService; } public void setBatchService(BatchService batchService) { this.batchService = batchService; } }
推荐阅读
-
Spring整合Struts2中拦截链与注解的使用
-
spring的事务管理
-
Spring Cloud EureKa Ribbon 服务注册发现与调用
-
Spring Boot(二)——项目热部署与程序发布
-
与JEE6/EJB3.1相比, Spring framework 丧失了几乎所有的优势
-
Spring Cloud 学习Consul服务注册与发现
-
【课程分享】基于plusgantt的项目管理系统实战开发(Spring3+JDBC
-
Spring Cloud Eureka 注册与发现操作步骤详解
-
dubbo与nginx都可以做负载均衡,然而哪个相对来说更优秀?为什么? springmvc+mybatisdubbo+zookeeperrestful redis分布式缓存spring mvc
-
dubbo与nginx都可以做负载均衡,然而哪个相对来说更优秀?为什么? springmvc+mybatisdubbo+zookeeperrestful redis分布式缓存spring mvc