“一个人最好的状态:梦想藏在心里,行动落于腿脚。”
目录
1、前言
上一篇我们已经简单学习了Spring框架的Aop模块,这一篇我们主要介绍Spring框架的DAO模块对JDBC的支持和事务的相关内容
2、Dao模块
原生的JDBC的代码
public static void main(String[] args) {
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db20", "root", "root");
//获取传输器
Statement statement = connection.createStatement();
//执行Sql语句
ResultSet resultSet = statement.executeQuery("SELECT * from Student");
//处理结果集
while (resultSet.next()){
String id = resultSet.getString("id");
String name = resultSet.getString("name");
String birth = resultSet.getString("birth");
String sex = resultSet.getString("sex");
System.out.println(id +":"+name+":"+birth+":"+sex);
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
2.1 JdbcTemplate
Spring提供的一种操作数据库的方法,封装了JDBC
使用Spring的注入功能,可以直接将DataSource注入到JdbcTemplate中
接下来我们开始快速入门:
-
在pom.xml文件中导入所需要的的jar包(之前第一篇文章已经导入过)
-
创建DataSource,创建JdbcTemplate
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///yonghedb?serverTimezone=GMT&characterEncoding=UTF-8"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
-
编辑EmpDao接口
public interface IEmp {
void save();
}
-
编辑EmpDao实现类
@Component
public class EmpDao implements IEmp{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save() {
String sql = "INSERT into emp(name,job,salary) VALUES('桔子','程序员',123456789);";
jdbcTemplate.update(sql);
}
}
-
测试代码
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpDao empDao = (EmpDao) context.getBean("empDao");
empDao.save();
}
}
-
输出结果:
结果显示,成功的存入了一条数据
JdbcTemplate查询
-
我们使用JdbcTemplate查询会发现有很多重载了query()的方法
一般的我们通常会使用queryForMap()这个方法,那么只能封装一行数据,如果封装多行的话,就会报错
又因为返回的值是Map集合,还需要我们自己去转换成自己需要的类型
-
一般我们用下面这个方法,自定义每行封装成什么类型
-
编辑EmpDao接口
public interface IEmp {
void query(int id);
}
-
编辑EmpDao实现类、
@Component
public class EmpDao implements IEmp{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void query(int id) {
String sql = "SELECT * from emp where id = ?";
RowMapper<Emp> rowMapper = new RowMapper<Emp>(){
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
emp.setId(rs.getInt("id"));
emp.setName(rs.getString("name"));
emp.setJob(rs.getString("job"));
emp.setSalary(rs.getInt("salary"));
return emp;
}
};
List<Emp> query = jdbcTemplate.query(sql,rowMapper,id);
System.out.println(query);
}
}
-
测试代码:
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpDao empDao = (EmpDao) context.getBean("empDao");
empDao.query(30);
}
}
-
输出结果
2.2 事务
事务(Transaction)是一个业务,一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性
2.3 事务特性
-
原子性(Atomicaty): 一个事务中的多个操作要么都成功要么都失败
-
一致性(Consistency): 一个事务执行之前和执行之后都保持一个状态
-
隔离性(Isolation): 事务和事务之间是相互隔离的,互不影响
-
持久性(Durability): 事务一旦提交,数据要永久保存
2.4 事务控制
事务控制分为两种:
-
编程式事务控制
-
声明式事物控制
2.5 编程式事务控制
自己手动通过编码方式实现事务,这就叫做编程式事务控制
2.6 声明式事务控制
由Spring框架提供,基于Spring的Aop实现,可以将具体的业务逻辑和事务控制进行解耦,只需要根据步骤进行配置,这就叫做声明式事务控制
从具体配置实现上,Spring提供了两种方式:
-
XML方式实现声明式事物控制
-
注解方式实现声明式事物控制
第一种:XML方式实现事务控制
-
搭建配置环境
上面我们在介绍Spring的Dao模块的时候,JdbcTemplate快速入门的时候,我们的代码是没有事务控制的
也就是说假如我在EmpService的save方法中调用两次empDao.save时,假如在中间出现异常,那么也会有一条数据保存在数据库中
@Service
public class EmpService {
@Autowired
private EmpDao empDao;
public void save(){
//没有事务控制,即使报错了,也会插入一条数据
empDao.save();
int i = 1 / 0;
empDao.save();
}
}
测试代码:
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpService empService = (EmpService) context.getBean("empService");
empService.save();
}
}
输出结果:
但是数据库中还是存入了一条数据
现在我们开始添加事务控制,看看会发生什么?
-
在配置文件中添加事务的管理器类
<!--1:配置事物的管理器类-->
<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引用数据库连接池-->
<property name="dataSource" ref="dataSource"/>
</bean>
-
配置事务管理类如何管理事务
<!--2.配置如何管理事务-->
<tx:advice id="txAdvice" transaction-manager="txManage">
<!--配置事务的属性-->
<tx:attributes>
<!--所有的方法,并不是只读-->
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
-
配置要拦截哪些方法
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.cn.service.EmpService.save())"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
-
再次测试
public class test1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpService empService = (EmpService) context.getBean("empService");
empService.save();
}
}
-
输出结果
控制台打印出了异常,但是数据库中没有添加数据,事务控制成功
第二种:注解方式实现事务控制
-
在配置文件中添加事务的管理器类(和XML的配置方式第一步一样)
<!--1:配置事物的管理器类-->
<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引用数据库连接池-->
<property name="dataSource" ref="dataSource"/>
</bean>
-
开启注解的方式实现事务
<!--2: 开启以注解的方式实现-->
<tx:annotation-driven transaction-manager="txManage"></tx:annotation-driven>
-
最后在需要事务控制的方法上添加@Transactional这个注解就可以(类上添加的话就是控制整个类的事务)
@Service
public class EmpService {
@Autowired
private EmpDao empDao;
@Transactional//事务管理
public void save(){
empDao.save();
int i = 1 / 0;
empDao.save();
}
}
-
再次测试代码(同上,这里不再添加)
-
输出结果
控制台依旧打印出了异常,但是数据库中没有添加数据,事务控制成功 。
总结:代码中的@Transactional注解,可以用来描述类和方法,告诉Spring框 架要在哪里进行事务控制,具体说明如下:
-
当@Transactional注解应用在类上时表示类中的所有方法进行事务控制,并且一般用于事务共性的定义。
-
当@Transactional注解应用在方法上时,表示只有此方法进行事务控制,假如类和方法上都有这个注解,则方法上的注解一般用于事务特性的定义。
事务属性
第一种我们在配置事务管理器类如何管理事务时,就是在指定事务的属性
第二种我们可以在@Transactional注解后写事务的属性
下面表格是列出的常见的事务属性:
常见事务属性 | |
---|---|
timout | 事务的超时时间,规定时间内未执行完,事务不提交 |
read-only | 只读事务,默认值为fase,表示只能进行查询,不能进行修改等操作 |
rollbock-for | 指定目标方法出现检查时异常时回滚 |
no-rollbock-for | 指定目标方法出现检查时异常时抛出异常不回滚 |
isolation | 事务的隔离级别,默认值为DEFAULT |
propagation | 事务的传播行为,常用的是: 1:REQUIRED 表示如果当前方法有事务了,加入当前事务 2:REQUIRED NEW 表示如果当前方法有事务了,当前方法事务挂起,始终开启一个新的事务,直到新的事务执行完,当前方法的事务才开始 |
总结:
Spring中事务管理大家以后会用到很多,声明式事务是Spring最核心,最常用的功能。
由于Spring通过IOC和AOP的功能非常透明的实现了声明式事务的功能,对于一般的开发者来说暂时无需了解它的内部细节,能够熟练的进行配置即可
但是如果有时间的话,后期还是可以继续了解深挖一下,毕竟一名程序员的目标还是很明确的。