javaWeb_事务&数据库连接池&DBUtils

事务

transaction 事务指的是一组操作,里面包含许多个单一的逻辑,只要有一个逻辑没有执行成功,那么都算失败,所有的数据回到最初的状态(回滚)

*事务存在的意义

为了确保许多逻辑必须成功,例子,银行转账。

使用命令行演示事务

1 关闭自动提交功能
javaWeb_事务&数据库连接池&DBUtils_第1张图片
2演示事务
javaWeb_事务&数据库连接池&DBUtils_第2张图片
总结:

开启事务:
start transaction
提交事务或者回滚事务
commit ; 提交事务,数据将会写道磁盘上的数据库
rollback 数据回滚,回到最初的状态

使用代码方式实现

贴一个配置的时候遇到的问题:
Unknown initial character set index ‘255’ received from server. Initial client character set can be

package test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.junit.jupiter.api.Test;

import uitl.JDBCUtil;

public class testDemo {
	Connection conn = null;
	PreparedStatement ps = null;
	ResultSet rs = null;

	@Test
	public void testTransaction() {
		try {
			conn = JDBCUtil.getConn();
			conn.setAutoCommit(false);
			String sql = "update account set money = money -? where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();

//		rs = ps.executeQuery();
//			while (rs.next()) {
//				String name = rs.getString("name");
//				int money = rs.getInt("money");
//				System.out.println(name + " -- " + money);
//
//			}
			
			int a = 10 / 0;
			ps.setInt(1, -100);
			ps.setInt(2, 2);
			ps.executeUpdate();
			// 成功就提交
			conn.commit();

		} catch (Exception e) {
			try {
				// TODO: handle exception
				//中间出现了问题,所以需要回滚
				e.printStackTrace();
				conn.rollback();
			} catch (Exception e2) {
				// TODO: handle exception
			}
		} finally {
			JDBCUtil.release(conn, ps, rs);
		}
	}
}

代码里面的事务,主要是针对链接来的,通过
1 conn.setAutoCommit(false)来设置自动关闭提交的设置
2 提交事务conn.commit();
3 回滚事务conn.rollback();

事务的特性

ACID

  • 原子性:

指的是事务中包含的逻辑不可分割

  • 一致性

事务执行前后,数据的完整性保持一致

  • 隔离性

指的是事务在执行期间,不应该受到其他事务的影响

  • 持久性

指的是 事务执行成功,那么数据应该持久保存到磁盘上。

事务的安全隐患

不考虑隔离级别设置,那么会出现以下问题

脏读 不可重读读 幻读

脏读 : 一个事务读取到了另一个事务还未提交的数据
javaWeb_事务&数据库连接池&DBUtils_第3张图片
步骤1 设置A窗口的隔离级别,读未提交
步骤2 两个窗口都开启事务javaWeb_事务&数据库连接池&DBUtils_第4张图片
读未提交容易造成脏读

事务总结

需要掌握的

1 在代码里面会使用事务
conn.setAutoCommit(false);
conn.cimmit();
conn.rollback();
2 事务只是针对链接对象,如果再开一个链接对象,那么是要默认的提交
3 事务是会自动提交的

需要了解的

安全隐患

读
	 脏读
	 	一个事务读取到了另一个事务未提交的数据
	 不可重复度
	 	一个事务读取到了另一个事务已提交的数据,造成两次前后查询结果不一致
	 幻读
	 	一个事务读取到了另一个事务insert 的数据,造成前后查询结果不一致
写
	丢失更新

隔离级别

读未提交

引发问题:脏读

读已提交

解决: 脏读, 引发: 不可重复读

可重复读

解决:脏读 不可重复读 未解决 : 幻读

可串行化

解决:脏读 不可重复读 幻读

mysql 默认的隔离级别是 可重复读
oracle 的默认隔离级别是 读已提交

丢失更新:两类

javaWeb_事务&数据库连接池&DBUtils_第5张图片
两种方法解决:1 悲观锁 2 乐观锁
1 悲观锁

可以在查询的时候,加入for update

select * from account for update;// 数据库的锁机制,排他锁

javaWeb_事务&数据库连接池&DBUtils_第6张图片
2 乐观锁

要求程序员自己控制,需要在存值的时候额外的加上一个字段
version:0
javaWeb_事务&数据库连接池&DBUtils_第7张图片

程序员自己控制

数据库的连接池

一开始在内存中开辟一块空间(集合),一开始往池子里面防止多个链接对象,后面需要链接的话,直接从赤字里面去,不要去自己创造链接了。使用完毕,记得要归还链接,确保连接对象能够循环利用

javaWeb_事务&数据库连接池&DBUtils_第8张图片

数据库链接池的创建

package uitl;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

//这是一个数据库连接池,一开始往池子里面放10个链接
//1 开始创建10 个链接
//2 来的程序通过getconnection 获取链接
//3 用完以后,使用addBack归还链接
//4 扩容
//sun 公司发布的连接池规范里面的名字是datasource 的接口
public class MyDataSource implements DataSource {
	// 该连接池对外公布的获取链接的方法
	List list = new ArrayList();

	public MyDataSource() {
		for (int i = 0; i < 10; i++) {
			Connection conn = JDBCUtil.getConn();
			list.add(conn);
		}
	}

	@Override
	public Connection getConnection() throws SQLException {
		if (list.isEmpty()) {
			for (int i = 0; i < 5; i++) {
				Connection conn = JDBCUtil.getConn();
				list.add(conn);
			}
		}
		// remove 是移除第一个

		Connection conn = list.remove(0);
		return conn;
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {

		// TODO Auto-generated method stub
		return null;
	}

	public void addBack(Connection conn) {
		list.add(conn);
	}

	// ------------------------------
	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public  T unwrap(Class iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isWrapperFor(Class iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		// TODO Auto-generated method stub

	}

	@Override
	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

}

问题:

1 sun 公司给出的一套规范,但是我们自己加了一套方法addback方法把connection 返回回来所以需要额外的记一套方法
2 单例
3 无法面向接口编程

UserDao dao =new UserDaoImpl();
dao.insert();
dataSource dataSource = new MyDataSource();

因为接口里面没有顶底addBack方法
4 怎么解决 以addBack 为切入点

解決自定义数据库连接池出现的问题

由于多了一个addBack方法,所以使用这个连接池的地方,需要额外记住这个方法, 还不能面向接口编程

planA 修改接口中的close 方法,原来connection 的close方法是真的关闭链接。
打算修改close 方法,以后再调用close,并不是真的关闭,而是归还链接对象

如何扩展一个方法?

不是我们需要的方法逻辑,需要修改自己的逻辑
1 直接改源码,但是实现不了
2 继承,必须得知道这个接口的具体实现是谁,没法做
3 使用装饰者模式
4 动态代理

装饰者模式详解

总结
优点

装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者模式可以提供比继承更多的灵活性。
通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

缺点

这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

DBUTILS

增删改

//dbutils 只是帮我们简化了CRUD 的代码, 但是连接的创建以及获取工作。 不在他的考虑范围
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

//增加
//queryRunner.update("insert into account values (null , ? , ? )", "aa" ,1000);
	
//删除
//queryRunner.update("delete from account where id = ?", 5);
	
//更新
//queryRunner.update("update account set money = ? where id = ?", 10000000 , 6);

查询

  1. 直接new接口的匿名实现类

    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

    Account account = queryRunner.query(“select * from account where id = ?”, new ResultSetHandler(){

     @Override
     public Account handle(ResultSet rs) throws SQLException {
     	Account account  =  new Account();
     	while(rs.next()){
     		String name = rs.getString("name");
     		int money = rs.getInt("money");
     		
     		account.setName(name);
     		account.setMoney(money);
     	}
     	return account;
     }
    

    }, 6);

    System.out.println(account.toString());

  2. 直接使用框架已经写好的实现类。

    • 查询单个对象
      QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
      //查询单个对象
      Account account = queryRunner.query(“select * from account where id = ?”,
      new BeanHandler(Account.class), 8);

    • 查询多个对象

    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
    List list = queryRunner.query("select * from account ",
    new BeanListHandler(Account.class));

###ResultSetHandler 常用的实现类
以下两个是使用频率最高的

BeanHandler,  查询到的单个数据封装成一个对象
BeanListHandler, 查询到的多个数据封装 成一个List<对象>

ArrayHandler,  查询到的单个数据封装成一个数组
ArrayListHandler,  查询到的多个数据封装成一个集合 ,集合里面的元素是数组。 



MapHandler,  查询到的单个数据封装成一个map
MapListHandler,查询到的多个数据封装成一个集合 ,集合里面的元素是map。 

ColumnListHandler
KeyedHandler
ScalarHandler

总结

事务

脏读, 不可重复度,幻读
丢失更新
悲观锁
乐观锁

  • 4 个隔离级别

读未提交
读已提交
可重复读
可串行化

数据库连接池

  • DBCP

不使用配置
使用配置

  • C3P0

不使用配置
使用配置(必须掌握)

DBUtils

简化了CURD 。里面定义了通用的CURD 方法
queryRunner.updata();
queryRunner.query

自定义连接池

装饰者模式

你可能感兴趣的:(web)