事务&数据库连接池&DBUtils

事务

  • Transaction,其实指的是一组操作,里面包含多个单一的逻辑。只要有一个逻辑没有执行成功,那么就算失败。所有的数据都回归到最初的状态(回滚)
  • 为什么要有事务:为了确保逻辑的成功。(银行的转账)

使用代码方式演示事务

package com.java_01;

import java.sql.*;

public class MysqlTest {
	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		}

		String uri = "jdbc:mysql://localhost:3306/bank";
		String user = "root";
		String password = "root";

		Connection conn = null;
		PreparedStatment ps = null;
		try {
			conn = DriverManager.getConnection(uri,user,password);
			String sql = "update account set money = money - ? where id = ?";
			ps = conn.prepareStatement(sql);
			conn.setAutoCommit(false);

			//扣除100元
			ps.setInt(1,100);
			ps.setInt(2,1);
			ps.executeUpdate();

			//加上100元
			ps.setInt(1,-100);
			ps.setInt(2,2);
			ps.executeUpdate();
			conn.commit();
			
			conn.close();
		} catch(SQLException e) {
			try {
				conn.rollback();
			} catch(SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}
}

事务的特性

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

事务的安全隐患

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

  • 读:脏读,不可重复度,幻读

脏读:一个事务读到另外一个事务还未提交的数据
不可重复读:这个隔离级别能够屏蔽脏读的现象,但是引发了另外一个问题,不可重复读
幻读:一个事务读到了另外一个事务已经提交的插入的数据,导致多次查询结果不一致

  • 写:丢失更新

有两种方式:

  1. 悲观锁,可以在查询的时候,加入for update
  2. 乐观锁,要求程序员自己控制
    隔离级别:
  1. Read Uncommiitted[读未提交]
  2. Read Committed[读已提交]
  3. Repeatable Read[重复读]
  4. Serializable[可串行化]:如果一个连接的隔离级别为了串行化,那么谁先打开了事务,谁就有了先执行的权力,谁后打开事务,谁就只能等着,待前面的那个事务提交或者回滚后,才能执行。但是这种隔离级别一般比较少用。容易造成性能上的问题。效率比较低
  • 按照效率划分,从高到低:读未提交>读已提交>可重复度>可串行化
  • 按照拦截程序,从高到低:可串行化>可重复读>读已提交>读未提交

数据库连接池

简单介绍

  1. 数据库的连接对象创建工作,比较消耗性能
  2. 一开始在内存中开辟一块空间(集合),一开始先往池子里面放置多个连接对象。后面需要连接的话直接从池子中去取。不要去自己创建连接了。使用完毕,要记得归还连接。确保连接对象能循环利用

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

  • 问题:由于多了一个addBack方法,所以使用这个连接池的地方,需要额外记住这个方法,并且还不能面向接口编程
  • 解决方法:我们打算修改接口中的close方法。原来的Connection对象的close方法,是真的关闭连接。现在我们修改这个close方法,以后再调用close,并不是真的关闭,而是归还连接对象

如何扩展某一个方法

原有的方法逻辑,不是我们想要的。想修改自己的逻辑

  1. 直接改源码,无法实现
  2. 继承,必须得知道这个接口的具体实现是谁
  3. 使用修饰者模式

开源连接池

  • DBCP:(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由Apache开发,通过数据库连接池,可以让程序自动管理数据库连接的释放和断开
  1. 不使用配置文件:
public class DBCPDemo {
	
	Connection conn = null;
	PreparedStatement ps = null;
	
	@Test
	public void testDBCP() {
		try {
			//构建数据源对象
			BasicDataSource dataSource = new BasicDataSource();
			dataSource.setDriverClassName("com.mysql.jdbc.Driver");
			dataSource.setUrl("jdbc:mysql://localhost:3306/bank");
			dataSource.setUsername("root");
			dataSource.setPassword("root");
			//得到连接对象
			conn = dataSource.getConnection();
			
			String sql = "insert into account values(null,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "admin");
			ps.setInt(2, 100);
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}
  1. 使用配置文件方式
public class DBCPDemo02 {
	
	Connection conn = null;
	PreparedStatement ps = null;
	@Test
	public void testDBCP02() {
		
		try {
			BasicDataSourceFactory factory = new BasicDataSourceFactory();
			Properties properties = new Properties();
			InputStream is = getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");
			properties.load(is);
			DataSource dataSource = factory.createDataSource(properties);
			
			conn = dataSource.getConnection();
			String sql = "insert into account values(null,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "wangwu");
			ps.setInt(2, 999);
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			
			e.printStackTrace();
		} catch (Exception e) {
			
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
  • C3P0:是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
  1. 不适用配置文件方式
public class C3P0Demo {
	
	@Test
	public void testC3P0() {
		try {
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bank");
			dataSource.setUser("root");
			dataSource.setPassword("root");
			
			Connection conn = dataSource.getConnection();
			String sql = "update account set money = money - ? where id = ?;";
			PreparedStatement ps = conn.prepareStatement(sql);
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();
		} catch (PropertyVetoException e) {
			
			e.printStackTrace();
		} catch (SQLException e) {
			
			e.printStackTrace();
		}
	}
}
  1. 使用配置文件
public class C3P0Demo02 {
	
	@Test
	public void testC3P0() {
		try {
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			Connection conn = dataSource.getConnection();
			String sql = "update account set money = money - ? where id = ?";
			PreparedStatement ps = conn.prepareStatement(sql);
			ps.setInt(1, 100);
			ps.setInt(2, 2);
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
}

//注意:需要在src下面添加c3p0-config.xml
<?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/bank</property>
    <property name="user">root</property>
    <property name="password">root</property>
    <property name="automaticTestTable">con_test</property>
    <property name="checkoutTimeout">30000</property>
    <property name="idleConnectionTestPeriod">30</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>
  </default-config>

</c3p0-config>
  • Druid:数据库连接池实现技术,由阿里巴巴提供的
    使用步骤:
  1. 导入jar包 druid-1.0.9.jar
  2. 定义配置文件:是properties形式的,可以叫任意名称,可以放在任意目录下
  3. 加载配置文件
  4. 获取数据库连接池对象:通过工厂类来获取,DruidDataSourceFactory
  5. 获取连接:getConnection
public static void main(String[] args) throws Exception {
        //加载druid.properties文件
        Properties prop = new Properties();
        prop.load(new FileReader(DruidDemo.class.getClassLoader().getResource("druid.properties").getPath()));
        //prop.load(DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"));
        //创建数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        //获取连接数据库对象
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
    }

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 = ?",10000,6);

查询

  1. 直接new接口的匿名实现类
	String sql = "select * from account where id = ?";
		Account account = queryRunner.query(sql, new ResultSetHandler<Account>() {

			@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;
			}
			
		}, 2);
		System.out.println(account);
  1. 直接使用框架已经写好的实现类
  • 查询单个对象
		String sql = "select * from account where id = ?";
		Account account = queryRunner.query(sql,new BeanHandler<Account>(Account.class), 4);
		System.out.println(account);
  • 查询多个对象
		String sql = "select * from account";
		List<Account> list = queryRunner.query(sql,new BeanListHandler<Account>(Account.class));
		
		for (Account account : list) {
			System.out.println(account);
		}

ResultSetHandler常用的实现类

一下两个是使用频率最高的
BeanHandler:查询到的单个数据封装成一个对象
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、付费专栏及课程。

余额充值