7.事务&数据库连接池&DBUtil

事务&数据库连接池&DBUtil

事务

Transaction 其实是指一组操作,里卖弄包含许多个单一的逻辑,只要有一个逻辑没有子hi习惯成功,那么就算失败,所有的数据就都回归到最初的状态。

使用命令行的方式演示事务

  • 开启事务 start transaction
  • 提交或回滚事务
    • commit : 提交事务
    • rollback : 回滚事务

      1.关闭自动提交功能
      在这里插入图片描述

      2.演示事务

      在这里插入图片描述

使用代码演示事务

代码里面的事务,主要是针对连接

  • 步骤
    • 1.通过conn.setAutoCommit(flase)来关闭自动提交的设置
    • 2.提交事务 conn.commit();
    • 3.回滚事务 conn.rollback();
  • 代码
@Test
	public void testTransaction(){		
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtil.getConnection();
			
			//默认的连接,事务是自动提交的
			//关闭自动提交
			conn.setAutoCommit(false);
			
			//转账事务
			String sql = "update account set money = money - ? where id = ?";
			ps = conn.prepareStatement(sql);
			
			//扣钱 ,扣id为1的用户100块
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();
			
			//加钱,给id为2的用户加100元
			ps.setInt(1, -100);
			ps.setInt(2, 2);
			ps.executeUpdate();
			
			//成功:提交事务
			conn.commit();
			
		} catch (SQLException e) {
			try {
				//失败: 回滚事务
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
			JDBCUtil.close(rs, ps, conn);
		}		
	}

事物的特性

  • 原子性
    指的是 事务中包含的逻辑,不可分割
  • 一致性
    指的是 事务在执行前后,数据的完整性
  • 隔离性
    指的是 事务在执行期间不应该受到其他事务的影响
  • 持久性
    指的是 事务执行成功,那么数据应该持久保存在磁盘上

事务的安全隐患

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

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

隔离级别

mysql默认的隔离级别是 可重复读

  • Read Uncommitted【读未提交】
  • Read Committed 【读已提交】
  • Repeatable 【重复读】
  • Serializable 【可串行化】
1.读未提交的演示

1.设置A窗口的隔离级别为 读未提交

在这里插入图片描述

2.两个窗口都分别开启事务

在这里插入图片描述

2.读已提交的演示

1.设置隔离级别为 读已提交

在这里插入图片描述

2.A B 两个窗口都开启事务,在B窗口执行更新操作

在这里插入图片描述

3.在A窗口执行的查询结果不一致,一次是在B窗口提交事务之前,一次是在B窗口提交事务之后

在这里插入图片描述

这个隔离级别能够屏蔽脏读的现象,但是也引发了另一个问题,不可复读。

3.可串行化

如果有一个连接的隔离级别设置为可串行化,那么谁先打开了事务,谁就有了先执行的权利,谁后打开事务,谁就只能等之前的事务提交或回滚后,才能执行。
但是这种隔离级别容易造成性能上的问题,效率较低。

  • 按效率分,从高到低
    读未提交 > 读已提交 > 可重复读 > 可串行化
  • 按拦截程度
    可串行化 > 可重复读 > 读已提交 > 读未提交

丢失更新

在这里插入图片描述

  • 悲观锁

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

在这里插入图片描述

  • 乐观锁

需要程序员自己控制

在这里插入图片描述

数据库连接池

一开始先在内存中开辟一块空间(集合),一开始先网池子里放置多个连接对象。当需要连接的时候,直接俄从池子里取,使用完毕后,归还连接,确保连接对象能循环利用。

在这里插入图片描述

自定义连接池

开源连接池

DBCP

1.导入jar文件

  • commons-dbcp.jar
  • commons-pool.jar

    2.不使用配置文件
@Test
	public void testJDBC() {
			
		Connection conn = null;
		PreparedStatement ps = null;
		try {					
			//1.构建数据库连接对象
			BasicDataSource dataSource = new BasicDataSource();
			
			/*
			 * 连接数据库类型,访问的数据库、用户名、密码
			 * jdbc:mysql://localhost/test   主协议:子协议:// 本地 /数据库
			 */
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql://localhost:3306/test");
			dataSource.setUsername("root");
			dataSource.setPassword("root");
			
			//2.得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account value(3,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "wangwu");
			ps.setInt(2, 1000);
			
			ps.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
      }	

3.使用配置文件

  • 配置文件dbcpconfig.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/test
username=root
password=

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60-->
maxWait=60000


#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
#注意:"user""password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ

  • 连接数据库
@Test
	public void testJDBC() {

		Connection conn = null;
		PreparedStatement ps = null;
		try {
			BasicDataSourceFactory factory = new BasicDataSourceFactory();
			Properties properties = new Properties();
			InputStream is;
			is = new FileInputStream("src//dbcpconfig.properties");
			properties.load(is);
			DataSource dataSource = factory.createDataSource(properties);

			// 2.得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account value(4,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "zhaoliu");
			ps.setInt(2, 1000);

			ps.executeUpdate();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
C3P0

1.导入jar文件

  • c3p0-0.9.1.2.jar

    2.不使用配置文件
@Test
	public void testJDBC() {

		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.创建dataSource
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			//2.设置来捏数据的信息
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			dataSource.setJdbcUrl("jdbc:mysql://localhost/test");
			dataSource.setUser("root");
			dataSource.setPassword("root");

			// 2.得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account value(5,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "hhhhh");
			ps.setInt(2, 400);

			ps.executeUpdate();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

3.使用配置文件

  • 配置文件c3p0-config.xml,放在classpath中,或classes目录中(放在工程的src文件下即可)
	<?xml version="1.0" encoding="UTF-8"?>
	<c3p0-config>
	  <default-config>
	    <property name="driverClass">com.mysql.jdbc.Driver</property>
	    <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
	    <property name="user">root</property>
	    <property name="password">root</property>

	    <property name="initialPoolSize">10</property>
	    <property name="maxIdleTime">30</property>
	    <property name="maxPoolSize">100</property>
	    <property name="minPoolSize">10</property>
	    <property name="maxStatements">200</property>

	    <user-overrides user="test-user">
	      <property name="maxPoolSize">10</property>
	      <property name="minPoolSize">1</property>
	      <property name="maxStatements">0</property>
	    </user-overrides>
	  </default-config>
	</c3p0-config>
  • 创建连接
@Test
	public void testJDBC() {

		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.创建dataSource
			//默认会找配置文件c3p0-config.xml中的default-config分支
			ComboPooledDataSource dataSource = new ComboPooledDataSource();

			// 2.得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account value(6,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "wwww");
			ps.setInt(2, 400);

			ps.executeUpdate();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

DBUtil

基本用法

1.增删改
		//dbUtils:只是帮助简化了CRUD的代码,但是连接的创建以及获取工作,不在作用范围内
		
		//创建连接
		QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
		
		//update(): 更新,包括插入、删除、修改
		//插入
		queryRunner.update("insert into account values(6,?,?)", "aa",2333);
		
		//删除
		queryRunner.update("delete from account where id = ?", 6);
		
		//修改
		queryRunner.update("update account set money = ? where id = 1", 1);
2.查询
  • 直接new接口的匿名实现类
               //query(): 查询,去执行查询,查询到的数据还是在那个result里面,然后调用下面的handle方法,有用户去封装
		Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<Account>(){
			       public Account handle(ResultSet rs) throws SQLException {
				     Account account = new Account();
				     while(rs.next()) {
				    	 int id = rs.getInt("id");
				    	 String name = rs.getString("name");
				    	 int money = rs.getInt("money");
				    	 
				    	 account.setId(id);
				    	 account.setName(name);
				    	 account.setMoney(money);
				     }
				     return account;
			}
		},1);
  • 直接使用框架中的实现类
    • 查询单个对象
    		/*
    	 * 通过字节码得到该类的实例
    	 * Account a = new Account();
    	 * 
    	 * Account a2 = Account.class.newInstance();
    	 */
    	//查询单个对象
    	Account account2 = queryRunner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class),2);
    	//打印内容
    	System.out.println(account2.toString());
    
    • 查询多个对象
    	//查询对象集合
    	List<Account> list = queryRunner.query("select * from account",new BeanListHandler<Account>(Account.class));
    	//打印内容
    	for(Account account3: list) {
    		System.out.println(account3.toString());
    

常用实现类

  • BeanHeadler : 查询到的单个数据封装成一个对象
  • BeanListHandler : 查询到的单个数据封装成一个List<对象>

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

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

  • ColumnListHandler
  • KeyedHandler
  • ScalarHandler
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值