JDBCTemplate
Spring中为开发者提供了对JDBC进行操作的存取框架
所需的依赖
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
数据源配置
这里使用的是阿里的数据库连接池的包
<context:property-placeholder location="datasource.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${account}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
datasource.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/employees?serverTimezone=UTC
account = root
password = 123456
建立连接
public class MyJDBCTemplate {
public static void main(String[] args) throws SQLException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
String sql = "select * from employees";
RowMapper<Employee> employeeRowMapper = new BeanPropertyRowMapper<>(Employee.class);
List<Employee> employees = jdbcTemplate.query(sql,employeeRowMapper);
System.out.println(employees.size());
}
}
数据库表
Employee类
public class Employee {
private Integer emp_no;
private String first_name;
public Integer getEmp_no() {
return emp_no;
}
public void setEmp_no(Integer emp_no) {
this.emp_no = emp_no;
}
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
}
事务管理
编程式事务管理需要将事务管理代码嵌入业务代码中控制事务的提交和回滚。但这样会将事务管理代码与业务代码混在一起,也会造成代码的冗余
而Spring中的事务管理采用的是声明式事务管理,横向抽取吃事务管理代码,通过AOP实现声明式事务管理
具体实现通过增加一个事务管理器+注解的形式
操作步骤
1.配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.1开启注解驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
2.2配置文件方式
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--设置所有匹配的方法,然后设置传播级别和事务隔离-->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
3.@Transactional
被注解的方法/类内所发生的一切操作都会被认为式一次事务,如果出现指定异常报错就会事务回滚
关于事务回滚的触发条件可以参考这篇文章
https://blog.csdn.net/u011861874/article/details/81542767
事务的属性
- propagation:事务的传播级别
- isolation:事务的隔离级别
- timeout:回滚前可以等待的时间(秒)
- readonly:只读
- rollbackFor:指定触发回滚的异常类型
- noRollbackFor:指定不触发回滚的异常类型
- rollbackForClassName:指定触发回滚的异常类名
- noRollbackForClassName:指定不触发回滚的异常类名
1.propagation
Spring中的7个事务传播行为:
事务行为 | 说明 |
REQUIRED | 支持当前事务,假设当前没有事务。就新建一个事务 |
SUPPORTS | 支持当前事务,假设当前没有事务,就以非事务方式运行 |
MANDATORY | 支持当前事务,假设当前没有事务,就抛出异常 |
REQUIRES_NEW | 新建事务,假设当前存在事务。把当前事务挂起。两个事务的回滚不会影响对方事务的提交 |
NOT_SUPPORTED | 以非事务方式运行操作。假设当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式运行,假设当前存在事务,则抛出异常 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 REQUIRED类似的操作。 |
每个级别的说明我们可以参考https://blog.csdn.net/qq_38526573/article/details/87898161
2.隔离级别
- 读未提交
- 读已提交
- 可重复读
- 串行化
隔离级别 | 详述 |
读未提交 | A事务在执行期间可以读到B事务未提交但已修改的数据。会造成脏读(B事务一旦回滚,数据修改就是无效的,但是A已经开始用了) |
读已提交 | A事务在执行期间可以读到B事务修改的提交了的数据。会造成数据不一致即不可重复读(B事务一旦提交,A事务如果在B提交的先后进行过两次数据读取,会出现数据不一致性的问题) |
可重复读 | A事务在执行期间读到的数据不会发生变化。但会造成幻读(可重复读的原理是对数据记录进行上锁,其他数据无法操作数据,但是不影响其他事务提交新的数据记录,而事务A在执行过程中还是可以读取到事务B新增的记录,也就是我们所说的幻读。事务执行过程中,数据的记录条数不一致,像幻觉一样) |
串行化 | 独占式读取,当作单线程即可,安全级别最高,但是性能太低了,并发下基本属于瘫痪 |
3.readonly
指定当前事务操作为只读,通知数据库不需要加锁。但是并不意味着真的是只读,所以需要开发者自己把关。如果出现修改,但是我们声明为只读,就以为mysql不会上锁其他事务依旧可以修改我们读取的数据,就会造成脏读。
事务管理也是基于aop编程,但Spring中关于事务的切面都已写好封装完成,我们只需要配置