Oracle_Day06(JDBC对事物的处理,JDBC工具类库—Apache Common-DBUtils,项目中数据访问层的处理)
JDBC对事物的处理
数据准备:
CREATE TABLE account(
id number(10),
name varchar2(20),
balance number(10,2)
)
--插入数据
insert into account values(1,'jack',1000.00);
insert into account values(2,'rose',1000.00);
--查询
SELECT * FROM account;
主要API
与Connection相关的方法:
操作步骤:
- 获取连接
- 开启事务
- 获取PreparedStatement对象
- 执行sql
- 正常情况下 提交事务
- 出现异常 回滚事务
- 释放资源
// 使用事务 完成转账操作
@Test
public void testTx() {
//1 获取连接
Connection conn = JDBCUtils.getConnection();
PreparedStatement ps = null;
try {
// 开启事务 将事务的提交设置为手动模式
conn.setAutoCommit(false);
// 2 获取PreparedStatement
String sql1 = "update account set balance = balance - ? where id=?";
ps = conn.prepareStatement(sql1);
//3 绑定参数
ps.setDouble(1,200);
ps.setInt(2,1);
ps.executeUpdate();
// 出现异常
System.out.println(1/0);
String sql2 = "update account set balance = balance + ? where id=?";
ps = conn.prepareStatement(sql2);
ps.setDouble(1,200);
ps.setInt(2,2);
ps.executeUpdate();
//提交事务
conn.commit();
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
try {
//回滚事务
conn.rollback();
} catch (SQLException se){
se.printStackTrace();
}
System.out.println("转账失败");
}finally {
JDBCUtils.close(ps,conn);
}
数据库连接池技术
解决传统开发中数据库频繁连接释放的问题。
数据库连接池的思想:
就是为数据库建立一个“缓冲池”,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接的时候,从缓冲池汇总并取出一个来使用,当使用完毕之后,再将连接归还到缓冲池。
数据库连接池的作用:负责数据库连接的分配、管理、释放等工作。他允许程序重复使用一个现有的连接,而不是重新建立一个。
数据库连接池在初始化的时候就创建一定数量的连接放到连接池中,这些连接是由连接池的最小连接数来决定,无论连接是否被使用,连接都将保证在连接池中存在这最小数量的连接。当连接不满足需求的时候,他会再次创建一定数量的连接,创建的连接的上限将由最大连接数来决定。当程序的请求的连接数超过了连接池所能提供的最大连接数的时候,此时这些请求将被加入到排队队列中等待空闲连接。
数据库连接池优点:
- 资源的重用。
- 更快的系统响应。
- 新的资源分配手段
- 统一的连接管理,避免了数据库连接的泄露。
常见的数据库连接池:
JDBC连接池使用的是javax.sql , Interface DataSource 。
接口的常见实现:
- DBCP 是Apache提供的数据库连接池。
- C3P0 是一个开源组织提供的一个数据库连接池。速度相对DBCP要慢一些。Hibernate的默认使用技术。
- Druid 是由阿里提供的一个数据库连接池,集DBCP和C3P0的优点与一身
DataSource通常被称为数据源。他包含了数据库连接池的创建和管理两部分工作。
使用中,我们将使用DataSource来取代DriverManager获取数据库连接Connection 从而提高数据库的访问速度
当使用数据库连接池获取到的连接在调用Connection的close方法的时候 ,不在是直接将连接关闭,而是将连接归还到连接池。
连接池的使用:
C3P0的使用步骤:
- 导包:
- 配置连接池
- 创建数据源对象
- 获取连接
//使用C3P0数据库连接池获取连接
@Test
public void getConnectionByC3p0() throws PropertyVetoException, SQLException {
// 获取数据源
ComboPooledDataSource ds = new ComboPooledDataSource();
// 设置连接属性
ds.setDriverClass("oracle.jdbc.driver.OracleDriver");
ds.setJdbcUrl("jdbc:oracle:thin:@192.168.25.129:1521:orcl");
ds.setUser("scott");
ds.setPassword("tiger");
// 获取连接
for(int i = 0 ; i < 5;i++){
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
使用C3P0的配置文件来获取连接
//使用C3P0的配置文件来配置数据库连接池
@Test
public void getConnection2() throws SQLException {
ComboPooledDataSource ds = new ComboPooledDataSource();
for(int i = 0 ; i < 5;i++){
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
配置文件 默认的文件名c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<!-- 超时时间 毫秒-->
<property name="checkoutTimeout">30000</property>
<!-- 初始化连接数-->
<property name="initialPoolSize">10</property>
<!-- 最大空闲数-->
<property name="maxIdleTime">30</property>
<!-- 最大连接数-->
<property name="maxPoolSize">100</property>
<!-- 最小连接数-->
<property name="minPoolSize">10</property>
<!-- 每个连接随支持 最大的statement对象-->
<property name="maxStatements">200</property>
<!-- 当链接数不足时,每次向数据库申请的连接数-->
<property name="acquireIncrement">5</property>
<!--链接属性的配置 链接属性的name值是固定的 必须参考官方文档来写-->
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="jdbcUrl">jdbc:oracle:thin:@192.168.25.129:1521:orcl</property>
<property name="user">scott</property>
<property name="password">tiger</property>
</default-config>
</c3p0-config>
DBCP:
导包
@Test
public void getConnectionByDBCP() throws SQLException {
//创建 数据源
BasicDataSource ds = new BasicDataSource();
// 设置连接属性
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUrl("jdbc:oracle:thin:@192.168.25.129:1521:orcl");
ds.setUsername("scott");
ds.setPassword("tiger");
// 配置连接池
ds.setInitialSize(5);
for(int i = 0 ; i < 5;i++){
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
使用配置文件
http://commons.apache.org/proper/commons-dbcp/configuration.html
@Test
public void getConnectionByDBCP2() throws Exception {
Properties ps = new Properties();
ps.load(DataSourceDemo.class.getClassLoader().getResourceAsStream("dbcp.properties"));
BasicDataSource bds = BasicDataSourceFactory.createDataSource(ps);
for(int i = 0 ; i < 5;i++){
Connection conn = bds.getConnection();
System.out.println(conn);
}
}
配置文件 dbcp.properties
driverClassName= oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.25.129:1521:orcl
username=scott
password=tiger
initialSize=10
maxTotal=20
maxIdle=10
minIdle=5
Druid:
国内开发者最受欢迎的数据库连接池
导包:
@Test
public void getConnectionByDruid() throws Exception {
Properties ps = new Properties();
ps.load(DataSourceDemo.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(ps);
for(int i = 0 ; i < 5;i++){
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
配置文件 druid.properties
url=jdbc:oracle:thin:@192.168.25.129:1521:orcl
driverClassName=oracle.jdbc.driver.OracleDriver
username=scott
password=tiger
initialSize=10
maxActive=20
maxWait=1000
Jdbc工具类的重构:
public class JDBCUtils2 {
// 数据源
private static DataSource ds;
// 初始化数据源 使用druid数据源
static{
try {
Properties ps = new Properties();
ps.load(JDBCUtils2.class.getClassLoader().getResourceAsStream("druid.properties"));
ds= DruidDataSourceFactory.createDataSource(ps);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection(){
Connection conn = null;
try {
conn = ds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn;
}
//释放资源
public static void close(ResultSet resultSet , Statement stmt , Connection conn){
if(resultSet !=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();// 归还连接
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//释放资源
public static void close( Statement stmt , Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
// 获取数据源的方法
public static DataSource getDataSource(){
return ds;
}
}
测试:
@Test
public void jdbcUtilsTest(){
for(int i = 0 ; i < 5;i++){
Connection conn = JDBCUtils2.getConnection();
System.out.println(conn);
}
}
JDBC工具类库—Apache Common-DBUtils
特点:
- Small - you should be able to understand the whole package in a short amount of time.
- Transparent - DbUtils doesn’t do any magic behind the scenes. You give it a query, it executes it and cleans up for you.
- Fast - You don’t need to create a million temporary objects to work with DbUtils.
DBUtils是一个开源JDBC工具类库。对JDBC的操作进行了封装。简化了JDBC编码。
常见API:
QueryRunner 负责执行查询或者更新
ResultSetHandler 负责结果集的处理
DbUtils 操作的工具类
DbUtils:
提供了关闭数据库连接、装载驱动等工作。
QueryRunner:
封装了SQL查询、更新等相关的数据库操作,简化了操作的步骤,提供ResultSetHanddler 来处理结果集。
ResultSetHanddler:
实现类
DBUtils的使用:
public class DBUtisDemo {
// 测试插入数据
@Test
public void insertTest() throws SQLException {
// 创建QueryRunner对象
QueryRunner qr = new QueryRunner();
String sql = "insert into student(id,name,gender,birthday) values(?,?,?,?)";
Connection conn = JDBCUtils2.getConnection();
int row = qr.update(conn,sql,6,"太白金星",1,java.sql.Date.valueOf("2021-01-01"));
System.out.println("插入了"+row+"条记录");
DbUtils.close(conn);
}
//测试更新
@Test
public void updateTest() throws SQLException {
// 创建QueryRunner对象
QueryRunner qr = new QueryRunner();
String sql = "update student set name=? where id=?";
Connection conn = JDBCUtils2.getConnection();
int row = qr.update(conn,sql,"太上老君",6);
System.out.println("更新了"+row+"条记录");
DbUtils.close(conn);
}
@Test
public void deleteTest() throws SQLException {
// 创建QueryRunner对象
QueryRunner qr = new QueryRunner();
String sql = "delete from student where id=?";
Connection conn = JDBCUtils2.getConnection();
int row = qr.update(conn,sql,6);
System.out.println("删除了"+row+"条记录");
DbUtils.close(conn);
}
}
DBUtils执行查询:
// 查询一个对象
@Test
public void selectObjTest() throws SQLException {
// 获取数据源
DataSource ds = JDBCUtils2.getDataSource();
// 利用数据源构建QueryRunner对象
QueryRunner qr = new QueryRunner(ds);
String sql = "Select * from student where id = ?";
// BeanHandler 来处理结果集 可以直接得到我们想要的对象 要求使用时列名需要和javabean的 属性名一致
Student stu = qr.query(sql,new BeanHandler<>(Student.class),1);
System.out.println(stu);
}
// 查询一个对象 列名和属性名不一致 可以使用查询的时候的字段的别名来映射我们的javabean的属性
@Test
public void selectObjTest2() throws SQLException {
// 获取数据源
DataSource ds = JDBCUtils2.getDataSource();
// 利用数据源构建QueryRunner对象
QueryRunner qr = new QueryRunner(ds);
String sql = "Select id, name stuName,gender,birthday from student where id = ?";
// BeanHandler 来处理结果集 可以直接得到我们想要的对象 要求使用时列名需要和javabean的 属性名一致
Student stu = qr.query(sql,new BeanHandler<>(Student.class),1);
System.out.println(stu);
}
// 查询多条记录 将查询到的结果封装成一个集合
@Test
public void selelctListTest() throws SQLException {
// 获取数据源
DataSource ds = JDBCUtils2.getDataSource();
// 利用数据源构建QueryRunner对象
QueryRunner qr = new QueryRunner(ds);
String sql = "Select id, name stuName,gender,birthday from student ";
// BeanHandler 来处理结果集 可以直接得到我们想要的对象 要求使用时列名需要和javabean的 属性名一致
List<Student> stuList = qr.query(sql,new BeanListHandler<>(Student.class));
System.out.println(stuList);
}
// 自定义结果集处理器
@Test
public void selelctListTest2() throws SQLException {
// 获取数据源
DataSource ds = JDBCUtils2.getDataSource();
// 利用数据源构建QueryRunner对象
QueryRunner qr = new QueryRunner(ds);
String sql = "Select id, name,gender,birthday from student ";
// BeanHandler 来处理结果集 可以直接得到我们想要的对象 要求使用时列名需要和javabean的 属性名一致
List<Student> stuList = qr.query(sql, new ResultSetHandler<List<Student>>() {
@Override
public List<Student> handle(ResultSet resultSet) throws SQLException {
List<Student> studentList = new ArrayList<>();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int gender = resultSet.getInt("gender");
Date biretday = resultSet.getDate("birthday");
Student student = new Student(id,name,gender,biretday);
studentList.add(student);
}
return studentList;
}
});
System.out.println(stuList);
}
// 查询单个值 记录数 最大值 最小值 平均值 和
@Test
public void selectValueTest() throws SQLException {
DataSource ds = JDBCUtils2.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String countSql = "select count(id) from student";
BigDecimal count = qr.query(countSql,new ScalarHandler<BigDecimal>());
System.out.println(count.intValue());
// 获取年龄最小的
String ageSql = "select max(birthday) from student";
Date birthday = qr.query(ageSql,new ScalarHandler<Date>());
System.out.println(birthday);
}
事务的处理:
// 事务处理
@Test
public void txTest(){
Connection conn = JDBCUtils2.getConnection();
QueryRunner qr = new QueryRunner();
try {
conn.setAutoCommit(false);
String sql1 = "update account set balance=balance - ? where id=?";
qr.update(conn,sql1,100,1);
System.out.println(1/0);
String sql2 = "update account set balance=balance + ? where id=?";
qr.update(conn,sql2,100,2);
conn.commit();
System.out.println("转账成功");
} catch ( Exception throwables) {
throwables.printStackTrace();
try {
DbUtils.rollbackAndClose(conn);
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("转账失败");
}finally {
try {
DbUtils.close(conn);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
项目中数据访问层的处理
一般都将数据访问层定义为DAO data access Object 将包括对数据的CRUD操作
Dao接口:
public interface IStudentDao {
void insert(Student stduent);
void update(Student stduent);
void delete(int id);
Student select(int id);
List<Student> selectAll();
List<Student> pageList(int currentPage, int pages);
}
BaseDao:
public class BaseDao {
// 对增删改的封装
public int update(String sql,Object...objs){
DataSource ds = JDBCUtils2.getDataSource();
QueryRunner qr = new QueryRunner(ds);
int row = 0 ;
try {
row = qr.update(sql,objs);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return row;
}
// 查询一个对象
public <T> T getObject(String sql,Class<? extends T> clazz ,Object ... objs){
DataSource ds = JDBCUtils2.getDataSource();
QueryRunner qr = new QueryRunner(ds);
T t = null;
try {
t = qr.query(sql,new BeanHandler<>(clazz),objs);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return t;
}
// 查询结果为一个集合 支持分页查询
public <T> List<T> getObjectList(String sql ,Class<? extends T> clazz,Object... objs){
DataSource ds = JDBCUtils2.getDataSource();
QueryRunner qr = new QueryRunner(ds);
List<T> list = new ArrayList<>();
try {
list = qr.query(sql,new BeanListHandler<T>(clazz),objs);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return list;
}
}
Dao实现:
public class StudentDaoImpl extends BaseDao implements IStudentDao {
@Override
public void insert(Student stduent) {
String sql = "insert into";
update(sql,stduent);
}
@Override
public void update(Student stduent) {
String sql= "";
update(sql,stduent);
}
@Override
public void delete(int id) {
String sql ="";
update(sql ,id);
}
@Override
public Student select(int id) {
String sql= "";
return getObject(sql,Student.class,id);
}
@Override
public List<Student> selectAll() {
String sql ="";
return getObjectList(sql,Student.class) ;
}
@Override
/*
(currentPage -1 ) * pages 0 5
(currentPage * pages 5 10
*/
public List<Student> pageList(int currentPage, int pages) {
String sql = "select * from (select rownum r, student.* from student ) temp where temp.r >? and temp.r <= ?";
List<Student> stuList = getObjectList(sql,Student.class,(currentPage -1 ) * pages,currentPage * pages);
return stuList;
}
}
测试:
public class DaoTest {
@Test
public void pageListTest(){
IStudentDao dao = new StudentDaoImpl();
List<Student> list = dao.pageList(2,3);
System.out.println(list);
}
}