Spring DAO
- DAO(Data Access Object)数据访问对象
- Spring DAO为整合JDBC提供了封装,简化了DAO组件的编写
- Spring DAO提供了AOP模式的事务处理
- Spring DAO提供了统一的异常处理层次,它包装的异常类型
DataAccessException
继承自RuntimeException
无须显式的捕获
Spring DAO所做的工作
- 对JDBC的操作进行封装,简化了JDBC的操作
- 对JDBC的事务进行了AOP模式的封装,简化了事物的管理和操作
- 对数据库访问中的异常,由原来的检查异常,封装成了继承
RunTimeException
的非检查异常DataAccessException
JDBCTemplate
这个类负责加载驱动、获取连接、获取执行环境和释放资源
我们只需要关心sql的编写和数据的处理
如何获取JDBCTemplate
最直接的是继承JDBCDAOSupport
这个类
如果不继承JDBCDAOSupport
则需要自己注入模版
使用继承的方式
- 准备一张表放入数据
- 写一个产品的接口,接口中定义一个查询产品数据的方法
- 先导入
ioc
,aop
,dao
对应的jar包,写一个接口的实现类继承JDBCDAOSupport
并实现接口 - 使用模板和模板对应的方法完成数据量的查询
- 测试dao实现类,看是否能获取到对应的数据
以继承JDBCDAOSupport的方式查询数据
- 建立一张银行账户表 并且建立对应的序列,最后使用序列作为主键值插入几条数据
- 建立一个项目拷贝
ioc
,aop
,连接池
,数据库驱动包
以及Spring容器对应的配置文件
,开启组件扫描和注入dataSource
- 写一个接口定义一个查询数据量的方法
- 写一个接口的实现类,继承
JDBCDAOSupport
,加标注@Repository
并且注入dataSource
,这个dataSource
要定义在xml中并且传递给父类
使用模板完成数据查询 - 写一个测试类测试功能
根据银行卡号查询一个银行账户
- 建立表和序列以及插入数据
- 根据表设计实体类
- 在接口加一个根据卡号查询账户的方法
- 在实现类中使用模板来完成功能(使用模板完成功能时,需要用到RowMapper的实现类)
- 测试
根据卡号和密码查询一个银行账户
- 建立表和序列以及插入数据
- 根据表设计实体类
- 在接口加一个根据卡号和密码查询账户的方法
- 在实现类中使用模板来完成功能(使用模板完成功能时,需要用到RowMapper的实现类)
- 测试
查询对象列表
给银行账户表增加数据
- 接口中增加一个方法,传入一个银行账户对象进来,然后把这些数据传入数据库
- 实现类中实现这个方法
- 测试
Spring整合JDBC
JdbcTemplate提供了以下的主要方法
queryForInt()
queryForObject()
query()
update()
execute()
总结JDBCTemplate的方法
queryForInt
查询数据的数据量
queryForObject
查询数据的数据量,也可以查询单个对象(使用到RowMapper)
query
查询数据列表(也会用到RowMapper)
update
用来完成增删改查
execute
一般用来执行DDL(建表、删表、改表),用得比较少,但是有相关的业务还是得会!
不继承JDBCDAOSupport的方式
就需要我们自己注入JDBCTemplate
使用模板和不使用模板的区别
-
继承JDBCDAOSupport
与不继承JDBCDAOSupport
-
注入的方式不同,例如:
使用模板
@Autowired public BankAccountDaoImp(DataSource dataSource) { super.setDataSource(dataSource); }
不使用模板
@Override public int getBankAccountCount() { String sql = "select count(*) from BANK_ACCOUNT_2018_11"; return jdbcTemplate.queryForObject(sql, Integer.class); }
Spring中的事务
把多个sql(dml,增删改)操作看成一个逻辑整体,这些语句要求一起成功或者一起失败
特性
- 原子性(事务中的语句是一个整体不可再分)
- 一致性(事务中的语句状态要保持一致)
- 隔离性(隔离级别:脏读、幻读、不可重复读)
详细解释隔离级别
脏读:一个事务读取到了另外一个事务没有提交的数据
不可重复读:一个事务在开始时读取了一份数据,在事务的操作过程中,另外一个事务对你读取的那份数据进行了修改(改动),并进行了提交,当第一个事务再次读取数据时,发现数据发生了改变。
幻读:一个事务在事务开始时对所有的数据进行了统计 在统计过程中数据发生了增加数据 再次统计所有数据时,数据改变了
为了解决三大读问题,引入四个隔离级别
读未提交 读提交 可重复读 序列化
1 2 3 4
持久性
Spring中事务的实现
- 编程式事务(使用大量Java代码完成事务)
- 声明式事务(基于AOP编程)
Spring中声明式事务的实现
Spring声明式事务的优势
通过Spring的配置,对事务管理而不是在源代码进行直接编码,解除了事务管理和代码的耦合,可以随时从Spring容器中进行删除
声明式事务的实现步骤
- 开启组件扫描和声明式事务
<!--开启组件扫描-->
<context:component-scan base-package="com.dao"/>
<!--开启声明式事务 transaction-manager就是事务管理器的意思,proxy-target-class如果是false代表jdk的代理机制(必须要求你的这个类实现接口),如果是true代表使用cglib的动态代理-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
- 配置事务管理器和数据源
<!--数据源配置文件-->
<util:properties id="db" location="classpath:db.properties"/>
<!--数据源对象-->
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="#{db.driverClassName}"/>
<property name="url">
<value>#{db.url}</value>
</property>
<property name="username" value="#{db.username}"/>
<property name="password" value="#{db.password}"/>
</bean>
<!--创建事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"></property>
</bean>
- 在需要事务管理的方法或者类上加
@Transaction
标注
注意:实际上事务管理器可以不动任何源代码!
@Transactional
注解标记有以下属性,在使用时可以根据需要做特殊设定。
propagation:设置事务传播
isolation:设置事务隔离级别
readOnly:设置为只读,还是可读写
rollbackFor:设置遇到哪些异常必须回滚
,针对检查异常
noRollbackFor:设置遇到哪些异常不回滚
,针对运行异常
Oracle只支持isolation的两个级别:读提交和幻读(序列化)
任何的RuntimeException将触发事务回滚,但是任何CheckedException将不触发事务回滚
只有service有资格加事务标注
二次修订版
文件说明
- Spring-jdbc.xml -> 配置文件
Spring dao
Spring dao所做的工作
- 对JDBC的操作进行封装,简化了JDBC的操作
- 对JDBC的事务进行了AOP模式的封装,简化了事物的管理和操作
- 对数据库访问中的异常,由原来的检查异常,封装成了继承RunTimeException的非检查异常DataAccessException
JDBCTemplate
这个类负责加载驱动
、获取连接
、获取执行环境
、释放资源
我们就只需要关心sql语句的编写和数据的处理
如何获取JDBCTemplate
- 最直接的是继承JDBCDAOSupport这个类
- 如果不继承JDBCDAOSupport则需要自己注入模版
使用继承的方式得到模板类
- 准备一张表放入数据
- 写一个产品的接口,接口中定义一个查询产品数据的方法
- 先导入ioc,aop,dao对应的jar包,写一个接口的实现类继承JDBCDAOSupport并实现接口
- 使用模板和模板对应的方法完成数据量的查询
- 测试dao实现类,看是否能获取到对应的数据
在3注入一个dataSource
测试得到xx_user表的数据
表里的数据如下:
id | uname | upass | uemail |
0 | admin | 123456 | 2301927217@qq.com |
1 | test | 123456 | wlmno1@vip.qq.com |
UserDao接口
package com.dao;
/**
* @author biuaxia
*/
public interface UserDao {
/**
* @return 表中的所有用户数量
*/
int getUserCount();
}
UserDaoImp实现类
package com.dao;
import com.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* Class Describe:
*
* @author biuaxia
* @date 2018/11/13
* @time 19:39
*/
@Repository("userdao")
public class UserDaoImp extends JdbcDaoSupport implements UserDao {
/**
* 注入一个dataSource
*/
/**
* 不能直接使用private DataSource dataSource注入
* 因为JdbcDaoSupport中的dataSource为final,不能通过set注入
* 我们通过自定义方法名进行注入
* 方法如果不改名就会找MyDataSource这个东西,不指定名字会比较危险
*/
@Resource(name = "dataSource")
public void setMyDataSource(DataSource dataSource) {
//把注入的dataSource传递给父类
super.setDataSource(dataSource);
}
@Override
public int getUserCount() {
//利用模板调用对应的api完成查询
String sql = "select count(*) from XX_USER";
/*return super.getJdbcTemplate().queryForInt(sql);*///此方法已过时
return super.getJdbcTemplate().queryForObject(sql, Integer.class);
}
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("spring-jdbc.xml");
UserDao dao = app.getBean("userdao", UserDao.class);
System.out.println(dao.getUserCount());
}
}
spring-jdbc.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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.dao"/>
<!--引入外部db.properties-->
<util:properties id="db" location="classpath:db.properties"></util:properties>
<!--配置dataSource-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="#{db.driverClassName}"></property>
<property name="url" value="#{db.url}"></property>
<property name="username" value="#{db.username}"></property>
<property name="password" value="#{db.password}"></property>
</bean>
</beans>
db.properties
driverClassName=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
username=system
password=123456
运行UserDaoImp的主方法即可
2
Process finished with exit code 0
查询银行账户表中的数据量
- 建立一张银行账户表 并且建立对应的序列,最后使用序列作为主键值插入几条数据
- 建立一个项目拷贝
ioc
,aop
,连接池
,数据库驱动包
以及Spring容器对应的配置文件
,开启组件扫描和注入dataSource
- 写一个接口定义一个查询数据量的方法
- 写一个接口的实现类,继承
JDBCDAOSupport
,加标注@Repository
并且注入dataSource
,这个dataSource
要定义在xml中并且传递给父类(要自定义set方法) - 使用模板完成数据查询使用模板完成数据查询,写一个测试类测试功能