事务与连接池的基础学习

MySql事务

  1. 事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么 全部不成功。就比如:转账,A转账给B100元,A-100,B+100,如果其中一个操作出现错误实现不了,就会形成误差,导致数据不对等。
  2. mysql引擎是支持事务的,mysql默认自动提交事务。每条语句都处在单独的事务中。
  3. 手动控制事务
    • 开启事务:start transaction | begin
    • 提交事务:commit
    • 回滚事务:rollback
  4. 事务的特性:
    • 原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
    • 一致性:事务必须使数据库从一个一致性状态转换到另外一个一致性状态。
    • 隔离性:事务的隔离性是多用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
    • 持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。
  5. JDBC演示事务:
		Connection conn = null;
		PreparedStatement ps = null;
		
		try {
			conn = DBUtils.getConnection();
			//事务开启:begin
			conn.setAutoCommit(false);
			
			//转账100
			ps = conn.prepareStatement("update test set money = money - 100 where name = 'lwb'");
			ps.executeUpdate();
			//收到100
			ps = conn.prepareStatement("update test set money = money + 100 where name = 'lwn'");
			ps.executeUpdate();
			
			//提交事务commit
			conn.commit();
			
		} catch (Exception e) {
			if(conn!=null){
				try {
					//回滚事务 rollback
					conn.rollback();
				} catch (SQLException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
			}
			
			e.printStackTrace();
		}

事务的隔离级别

  1. 三种事务可能发生的错误:

    • 赃读:指一个事务读取了另一个事务未提交的数据。
    • 不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。一个事务读取到了另一个事务提交后的数据。(update)
    • 虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。(insert)
  2. 数据库通过设置事务的隔离级别防止以上情况的发生:

    • READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生
    • READ COMMITTED: 避免赃读不可重复读、虚读都有可能发生。(oracle默认的)
    • REPEATABLE READ:避免赃读、不可重复读虚读有可能发生。(mysql默认)
    • SERIALIZABLE: 避免赃读、不可重复读、虚读
    • 级别越高,性能越低,数据越安全(加粗的就是避免的,斜体的就是有可能法发生的)
  3. JDBC设置隔离等级:

    • Connection接口
    • Connection.setTransactionIsolation(int level);
    • level:
      • TRANSACTION_READ_UNCOMMITTED
      • TRANSACTION_READ_COMMITTED
      • TRANSACTION_REPEATABLE_READ
      • TRANSACTION_SERIALIZABLE

连接池

一开始没有使用连接池的时候是这样子的:在这里插入图片描述
每次访问就要建立一个Connection对象连接,浪费资源
如果有连接池,就可以通过连接池来连接,用完了就可以用连接池回收,不需要老是创建连接
在这里插入图片描述
模拟连接池:
自定义数据库连接池要实现javax.sql.DataSource接口,一般这样都叫数据源。

public class MyDataSource implements DataSource{
	 //创建一个存放连接的池子
	private static LinkedList<Connection> pool = (LinkedList<Connection>) Collections.synchronizedList(new LinkedList<Connection>());
	
	//初始化10个连接
	static{
			try {
				for (int i = 0; i < 10; i++) {
					Connection conn = DBUtils.getConnection();
					pool.add(conn);
				}
			} catch (Exception e) {
				throw new ExceptionInInitializerError("初始化数据库连接失败,请检查配置文件是否正确!");
			}
	}
	
	//从池中找出一个连接
	public Connection getConnection() throws SQLException {
		Connection conn = null;
		if(pool.size()>0){
			conn =  pool.removeFirst();//从池中取出一个连接
			return conn;
		}else{
			//等待
			//新创建一个连接
			throw new RuntimeException("服务器忙。。。");
		}
	}

但是在使用的时候就有问题:
在最后的conn是不能被关闭的,是要返回给连接池的,但是ds里面没有这样的方法。
在这里插入图片描述
所以就介绍两种模式:

装饰设计模式

  1. 改写已存在的类的某个方法或某些方法。
  2. 所以我们通过改写Connexction中Close的方法来实现
  3. 这种模式是通过下面的步骤来的:
    • 1、编写一个类,实现与被包装类相同的接口。(具备相同的行为)
    • 2、定义一个被包装类类型的变量。
    • 3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。
    • 4、对于不需要改写的方法,调用原有的方法。
    • 5、对于需要改写的方法,写自己的代码
public class MyConnection implements Connection{//实现Connection接口

	private Connection oldConnection;//com.mysql.jdbc.Connection
	private LinkedList<Connection> pool;//连接池对象
	public MyConnection(Connection oldConnection,LinkedList<Connection> pool){
		this.oldConnection = oldConnection;//得到com.mysql.jdbc.Connection
		this.pool = pool;//得到连接池对象
	}
	
	public void close() throws SQLException {
		pool.addLast(oldConnection);//将连接重新放回连接池
	}
	
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return oldConnection.prepareStatement(sql);
	}
	//...下面不需要用的代码就按照原有的方法
}

默认适配器模式

还有一种跟上面的模式差不多,不过要更加简洁一点
这个模式的步骤是:
* 1、编写一个类,继承包装类的适配器。(具备相同的行为)
* 2、定义一个被包装类类型的变量。
* 3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。
* 4、对于不需要改写的方法,调用原有的方法。

public class MyConnection extends MyConnectionWarper {
	private Connection oldConn;
	private LinkedList<Connection> pool;
	public MyConnection(Connection oldConn,LinkedList<Connection> pool) {
		super(oldConn);
		this.oldConn = oldConn;
		this.pool = pool;
	}
	@Override
	public void close() throws SQLException {
		pool.addLast(oldConn);
	}
}
public class MyConnectionWarper implements Connection {

	private Connection oldConn;
	public MyConnectionWarper(Connection oldConn){
		this.oldConn = oldConn;
	}
}

DBCP

  1. 前面这些介绍就是为了DBCP,前面都是模拟自己写的连接池,只需要了解其工作原理。这个就是包含连接池的技术
  2. 使用步骤:
    • 添加jar包 commons-dbcp-1.4.jar commons-pool-1.5.6.jar
    • 添加属性资源文件
    • 编写数据源工具类
  3. 例子:
    DBCPUtil.java
public class DBCPUtil {
	//定义一个DataSource接口
	private static DataSource ds = null;
	static{
		Properties prop = new Properties();
		try {
						prop.load(DBCPUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));//根据DBCPUtil的classes的路径,加载配置文件
		    ds = BasicDataSourceFactory.createDataSource(prop);//得到一个数据源 
		} catch (Exception e) {
			throw new ExceptionInInitializerError("初始化错误,请检查配置文件");
		}
	}
	//获得连接
	public static Connection getConnection(){
		try {
			return ds.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException("服务器忙。。。");
		}
	}
	
	public static void release(Connection conn,Statement stmt,ResultSet rs){
		//关闭资源
				if(rs!=null){
					try {
						rs.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
					rs = null;
				}
				if(stmt!=null){
					try {
						stmt.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
					stmt = null;
				}
				if(conn!=null){
					try {
						conn.close();//关闭
					} catch (Exception e) {
						e.printStackTrace();
					}
					conn = null;
				}
	}
	
}

dbcpconfig.properties配置文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/
username=root
password=

initialSize=10

maxActive=50

maxIdle=20

minIdle=5

maxWait=60000

connectionProperties=useUnicode=true;characterEncoding=utf8

defaultAutoCommit=true

defaultReadOnly=

defaultTransactionIsolation=REPEATABLE_READ

C3P0

另一种方式来获取数据源
使用步骤:
1、添加jar包
c3p0-0.9.1.2.jar
2、编写配置文件
c3p0-config.xml,放在classpath中,或classes目录中

<?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/day13</property>
	<property name="user">root</property>
	<property name="password">abc</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>

  </default-config>
</c3p0-config>

C3P0Util.java

public class C3P0Util {
	//得到一个数据源
	private static DataSource dataSource = new ComboPooledDataSource();
	
	//从数据源中得到一个连接对象
	public static Connection getConnection(){
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException("服务器错误");
		}
	}
	
	public static void release(Connection conn,Statement stmt,ResultSet rs){
		//关闭资源
				if(rs!=null){
					try {
						rs.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
					rs = null;
				}
				if(stmt!=null){
					try {
						stmt.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
					stmt = null;
				}
				if(conn!=null){
					try {
						conn.close();//关闭
					} catch (Exception e) {
						e.printStackTrace();
					}
					conn = null;
				}
	}
	
}

总结

事务我不是第一次了解,但是代码我还是第一次看到,因为学过一次在数据库上还是比较好理解,而连接池我就学的有点懵懵的,上午看了模拟之后,中午休息的时候理了一下,现在大致能了解原理,后面就直接使用两个技术,别人直接写好了,所以我还是会使用的,如果有些词说的不准确,评论告诉我。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值