Craig Walls所著《Spring实战》一书里说,Spring的两大关键字:DI(依赖注入),AOP(面向切面编程)。在之前的两篇博客里,我们已经学习了spring的核心,DI和AOP。大头我们已经学完了,这篇博客主要是介绍剩下的一些知识点,如JdbcTemplate和事务管理等。
1 JdbcTemplate
- spring 提供用于操作JDBC工具类,类似:DBUtils。
- 依赖 连接池DataSource (数据源)
1.1环境搭建
创建表(我这里使用的是MySql)
USE test;
CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
PASSWORD VARCHAR(32)
);
INSERT INTO t_user(username,PASSWORD) VALUES('jack','1234');
INSERT INTO t_user(username,PASSWORD) VALUES('rose','5678');
导入Jar包
1.2使用API
public class TestApi {
public static void main(String[] args) {
//1 创建数据源(连接池) dbcp
BasicDataSource dataSource = new BasicDataSource();
// * 基本4项
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
//2 创建模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
//3 通过api操作
jdbcTemplate.update("insert into t_user(username,password) values(?,?);", "tom","998");
}
}
dao类:
public class UserDao {
//JDBC模板将有spring注入
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
public void update(User user){
String sql = "update t_user set username=?,password=? where id=?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
jdbcTemplate.update(sql,args);
}
}
1.3配置DBCP示例:
<?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:aop="http://www.springframework.org/schema/aop"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 创建数据源 -->
<bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 创建模板 ,需要注入数据源-->
<bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
<!-- 配置dao -->
<bean id="userDaoId" class="com.spring.dbcp.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplateId"></property>
</bean>
</beans>
1.4 C3P0配置示例
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/<span style="font-family:SimSun;">test</span>"></property>
<property name="user" value="root"></property>
<property name="password" value="1234"></property>
</bean>
1.5使用JdbcDaoSupport
为了简化dao类,每次注入JdbcTemplate,我们可以继承JdbcDaoSupport,替代set方法。
public class UserDao extends JdbcDaoSupport{
public void update(User user){
String sql = "update t_user set username=?,password=? where id=?";
Object[] args = {user.getUsername(),user.getPassword(),user.getId()};
this.getJdbcTemplate().update(sql,args);
}
}
<!-- 配置dao
* dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
-->
<bean id="userDaoId" class="com.spring.jdbcSupport.UserDao">
<property name="dataSource" ref="dataSourceId"></property>
</bean>
源码分析:
1.6配置Properties
properties文件:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/<span style="font-family:SimSun;">test</span>
jdbc.user=root
jdbc.password=1234
Spring配置文件
<!-- 加载配置文件
"classpath:"前缀表示 src下
在配置文件之后通过 ${key} 获得内容
-->
<context:property-placeholder location="classpath:com/spring/properties/jdbcInfo.properties"/>
<!-- 创建数据源 c3p0-->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
2.事务管理
2.1回顾事务
- 事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
- 特性:ACID
原子性:整体
一致性:完成
隔离性:并发
持久性:结果
-
隔离问题:
脏读:一个事务读到另一个事务没有提交的数据
不可重复读:一个事务读到另一个事务已提交的数据(update)
虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
-
隔离级别:
read uncommitted:读未提交。存在3个问题
read committed:读已提交。解决脏读,存在2个问题
repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
serializable :串行化。都解决,单事务。
- mysql 事务操作--简单,示例描述:
ABCD 一个事务
Connection conn = null;
try{
//1 获得连接
conn = ...;
//2 开启事务
conn.setAutoCommit(false);
A
B
C
D
//3 提交事务
conn.commit();
} catche(){
//4 回滚事务
conn.rollback();
}
- mysql 事务操作--Savepoint 示例描述:
需求:AB(必须),CD(可选)
Connection conn = null;
Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
//1 获得连接
conn = ...;
//2 开启事务
conn.setAutoCommit(false);
A
B
savepoint = conn.setSavepoint();
C
D
//3 提交事务
conn.commit();
} catche(){
if(savepoint != null){ //CD异常
// 回滚到CD之前
conn.rollback(savepoint);
// 提交AB
conn.commit();
} else{ //AB异常
// 回滚AB
conn.rollback();
}
}
2.2事务管理介绍
导入Jar包:
transaction --> tx
-
PlatformTransactionManager 平台事务管理器,spring要管理事务,必须使用事务管理器
进行事务配置时,必须配置事务管理器。
-
TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,
例如:隔离级别、是否只读、超时时间 等
进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
-
TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
spring底层根据状态进行相应操作。
2.2.1 PlatformTransactionManager事务管理器
-
导入jar包:需要时平台事务管理器的实现类
-
常见的事务管理器
DataSourceTransactionManager,jdbc开发时事务管理器,采用JdbcTemplate
HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate
- api详解
TransactionStatus getTransaction(TransactionDefinition definition),事务管理器 通过“事务详情”,获得“事务状态”,从而管理事务。
void commit(TransactionStatus status) 根据状态提交
void rollback(TransactionStatus status) 根据状态回滚
2.2.2 TransactionStatus(了解即可)
2.2.3 TransactionDefinition
- 传播行为:在两个业务之间如何共享事务。
PROPAGATION_REQUIRED , required , 必须 【默认值】
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将创建一个新的事务。
PROPAGATION_SUPPORTS ,supports ,支持
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将以非事务执行。
PROPAGATION_MANDATORY,mandatory ,强制
支持当前事务,A如果有事务,B将使用该事务。
如果A没有事务,B将抛异常。
PROPAGATION_REQUIRES_NEW , requires_new ,必须新的
如果A有事务,将A的事务挂起,B创建一个新的事务
如果A没有事务,B创建一个新的事务
PROPAGATION_NOT_SUPPORTED ,not_supported ,不支持
如果A有事务,将A的事务挂起,B将以非事务执行
如果A没有事务,B将以非事务执行
PROPAGATION_NEVER ,never,从不
如果A有事务,B将抛异常
如果A没有事务,B将以非事务执行
PROPAGATION_NESTED ,nested ,嵌套
A和B底层采用保存点机制,形成嵌套事务。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
案例;转账案例(晚些再贴上来)
学习到这里,Spring的知识点你已经基本掌握了,具体Spring和一些其他框架的整合,我会专门写一篇博客。这个系列的三篇Spring博客都很基础,而且是基于3.2的。然而Spring Boot,Spring家族中一个新的令人兴奋的项目。Spring致力于简化JAVA开发,而Spring Boot致力于让Spring本身更加简单!后期我应该会写关于SpringBoot的博客,有兴趣的朋友我们,我们可以共同学习,共同探讨。
现在,你仅仅是一个Spring的user距离真正的developer还有很长的距离。Spring的源码是你必须去深究的。就像《Spring实战》的最后作者说道,你的Spring之路才刚刚开始。