学习日志——2019/08/05

事务& 数据连接池& DBUtils

在这里插入图片描述

事务

  • 什么是事务?

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

  • 为什么要有事务?

为了确保逻辑的成功。 例子: 银行的转账。

  • 使用命令行方式演示事务。

  • 开启事务

    start transaction;

  • 提交或者回滚事务

    commit; 提交事务, 数据将会写到磁盘上的数据库
    rollback ; 数据回滚,回到最初的状态。

  • 使用代码方式演示事务

  1. 通过conn.setAutoCommit(false )来关闭自动提交的设置。
  2. 提交事务 conn.commit();
  3. 回滚事务 conn.rollback();
package e.test;

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

import org.junit.jupiter.api.Test;

import Util.JDBCUtil;

public class testDemo {
	@Test
	public void testTransaction() {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		try {
			conn = JDBCUtil.getConn();
			String sql="select *from account";
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getString("name")+"---"+rs.getInt("money"));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps,rs);
		}
		
	}
}

  • 模拟转账
package e.test;

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

import org.junit.jupiter.api.Test;

import Util.JDBCUtil;

public class testDemo {
	@Test
	public void testTransaction() {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		try {
			conn = JDBCUtil.getConn();
			String sql="update account set money=money-? where id=?";
			ps = conn.prepareStatement(sql);
			
			//扣钱,扣id为一的100块钱
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();
			
			//加钱,给id=3加100块钱
			ps.setInt(1, -100);
			ps.setInt(2, 3);
			ps.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps,rs);
		}
		
	}
}

  • 关闭自动提交
package e.test;

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

import org.junit.jupiter.api.Test;

import Util.JDBCUtil;

public class testDemo {
	@Test
	public void testTransaction() {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		try {
			conn = JDBCUtil.getConn();
			//默认事务就是自动提交的,关闭自动提交
			conn.setAutoCommit(false);
			
			String sql="update account set money=money-? where id=?";
			ps = conn.prepareStatement(sql);
			
			//扣钱,扣id为一的100块钱
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();
			
			int a= 10/0;
			
			//加钱,给id=3加100块钱
			ps.setInt(1, -100);
			ps.setInt(2, 3);
			ps.executeUpdate();
			
			//成功,提交事务
			conn.commit();
			
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			try {
				//事变,回滚事务
				conn.rollback();
			}catch(SQLException e1) {
			e1.printStackTrace();
			}
		}finally {
			JDBCUtil.release(conn, ps,rs);
		}
		
	}
}

事务的特性
  • 原子性

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

  • 一致性

指的是 事务执行前后。数据完整性

  • 隔离性

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

  • 持久性

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

事务的安全隐患

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

脏读 不可重读读 幻读.

  • 脏读

一个事务读到另外一个事务还未提交的数据

  • 不可重复读

一个事务读到了另外一个事务提交的数据 ,造成了前后两次查询结果不一致。

事务总结

  1. 在代码里面会使用事务

     conn.setAutoCommit(false);
    
    
     conn.commit();
    
     conn.rollback();
    
  2. 事务只是针对连接连接对象,如果再开一个连接对象,那么那是默认的提交。

  3. 事务是会自动提交的。

数据连接池

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

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.扩容
 *
 */
public class MyDateSource implements DataSource {
	List<Connection> list=new ArrayList<Connection>();
	public MyDateSource() {
		// TODO Auto-generated constructor stub
		
		for (int i=0;i<10;i++) {
			Connection conn=JDBCUtil.getConn();
			list.add(conn);
		}
	}
	//该连接池对外公布的获取连接的方法
	@Override
	public Connection getConnection() throws SQLException {
		// TODO Auto-generated method stub
		//来拿连接的时候,先看看,池子里面还有没有
		if(list.size()==0) {
			for (int i=0;i<5;i++) {
				Connection conn=JDBCUtil.getConn();
				list.add(conn);
			}
		}
		//remove(0)移除第一个
		Connection conn = list.remove(0);
		return conn;
	}
	/**
	 * 用完之后,记得归还
	 * @param conn
	 */
	public void addback(Connection conn) {
		list.add(conn);
	}


	@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;
	}

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

	@Override
	public <T> T unwrap(Class<T> 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 Connection getConnection(String username, String password) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

}

  • 测试
package Util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.junit.jupiter.api.Test;

public class TestPool {
	@Test
	public void testPool() {
		Connection conn =null;
		PreparedStatement ps=null;
		MyDateSource dateSource = new MyDateSource();
		try {
			conn= dateSource.getConnection();
			String sql="insert into account values(null,'huanqi',20)";
			ps = conn.prepareStatement(sql);
			ps.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//归还连接
			dateSource.addback(conn);
		}
	}

}

  • 出现的问题:

    1. 需要额外记住 addBack方法

    2. 单例。

    3. 无法面向接口编程。

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

      DataSource dataSource = new MyDataSource();

      因为接口里面没有定义addBack方法。

    4. 怎么解决? 以addBack 为切入点。

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

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

我们打算修改接口中的那个close方法。 原来的Connection对象的close方法,是真的关闭连接。
打算修改这个close方法,以后在调用close, 并不是真的关闭,而是归还连接对象。

如何扩展某一个方法?

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

  1. 直接改源码 无法实现。

  2. 继承, 必须得知道这个接口的具体实现是谁。

  3. 使用装饰者模式。

DBUtils

  1. 导入jar文件

  2. 不使用配置文件:

		public void testDBCP01(){
		
			
			Connection conn = null;
			PreparedStatement ps = null;
			try {
				
				//1. 构建数据源对象
				BasicDataSource dataSource = new BasicDataSource();
				//连的是什么类型的数据库, 访问的是哪个数据库 , 用户名, 密码。。
				//jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库
				dataSource.setDriverClassName("com.mysql.jdbc.Driver");
				dataSource.setUrl("jdbc:mysql://localhost/bank");
				dataSource.setUsername("root");
				dataSource.setPassword("root");
				
				
				//2. 得到连接对象
				conn = dataSource.getConnection();
				String sql = "insert into account values(null , ? , ?)";
				ps = conn.prepareStatement(sql);
				ps.setString(1, "admin");
				ps.setInt(2, 1000);
				
				ps.executeUpdate();
				
			} catch (SQLException e) {
				e.printStackTrace();
			}finally {
				JDBCUtil.release(conn, ps);
			}
			
		}
  1. 使用配置文件方式:

		Connection conn = null;
		PreparedStatement ps = null;
		try {
			BasicDataSourceFactory factory = new BasicDataSourceFactory();
			Properties properties = new Properties();
			InputStream is = new FileInputStream("src//dbcpconfig.properties");
			properties.load(is);
			DataSource dataSource = factory.createDataSource(properties);
			
			//2. 得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account values(null , ? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "liangchaowei");
			ps.setInt(2, 100);
			
			ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps);
		}


		
  • C3P0

拷贝jar文件 到 lib目录

不使用配置文件方式

		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1. 创建datasource
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			//2. 设置连接数据的信息
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			
			//忘记了---> 去以前的代码 ---> jdbc的文档
			dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");
			dataSource.setUser("root");
			dataSource.setPassword("root");
			
			//2. 得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account values(null , ? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "admi234n");
			ps.setInt(2, 103200);
			
			ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps);
		}

使用配置文件方式

			//默认会找 xml 中的 default-config 分支。 
			ComboPooledDataSource dataSource = new ComboPooledDataSource();
			//2. 设置连接数据的信息
			dataSource.setDriverClass("com.mysql.jdbc.Driver");
			

			//忘记了---> 去以前的代码 ---> jdbc的文档
			dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");
			dataSource.setUser("root");
			dataSource.setPassword("root");
			
			//2. 得到连接对象
			conn = dataSource.getConnection();
			String sql = "insert into account values(null , ? , ?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1, "admi234n");
			ps.setInt(2, 103200);

###增删改

			//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<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;
     	}
     	 
      }, 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
代码:
c3p0配置

package e.c3p0;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.dbcp.BasicDataSource;
import org.junit.jupiter.api.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import Util.JDBCUtil;

public class C3P0Demo {
	@Test
	public void testC3P0() {
		Connection conn=null;
		PreparedStatement ps =null;
		try {
			ComboPooledDataSource dataSource = new ComboPooledDataSource(); 
			
			//2.得到连接对象
			conn=dataSource.getConnection();
			String sql="insert into account values(null,?,?)";
			ps = conn.prepareStatement(sql);
			ps.setString(1,"wangwu");
			ps.setInt(2, 100000);
			ps.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			JDBCUtil.release(conn, ps);
		}
		
	}
}


<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/bank?useSSL=false&serverTimezone=Asia/Shanghai</property>
    <property name="user">root</property>
    <property name="password"></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>
	
  • DBUtils的增删改查
package e.dbutils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.jupiter.api.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import domain.Account;
/**
 * 
 * 针对增加、删除、修改
 *queryRunner.update(sql);
 *针对查询
 *queryRunner.query(sql,rsh);
 *
 */
public class TestDBUtils {
	@Test
	public void testInsert() throws SQLException {
		ComboPooledDataSource dataSource=new ComboPooledDataSource();
		//dbutils 只是帮我们简化CRUD代码,但是连接及获取工作,不在它的考虑范围
		QueryRunner queryRunner=new QueryRunner(dataSource);
		//添加
		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=?", 100000,8);
		
		//查询,去执行查询,查询到的数据还是在哪个result里面,然后调用下面的handle方法,由用户手动去封装
		/*
		  Account account = queryRunner.query("select from account where id=?", new
		  ResultSetHandler<Account>() {
		 
		  @Override public Account handle(ResultSet rs) throws SQLException {
			  // TODO Auto-generated method stub
		  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());	*/ 
		/*
		//查单个对象
		Account account = queryRunner.query("select *from account where id=?",
				new BeanHandler<Account>(Account.class), 6);
		System.out.println(account.toString());
		*/
		List<Account> list = queryRunner.query("select *from account", new BeanListHandler<Account>(Account.class));
		for (Account account : list) {
			System.out.println(account.toString());
		}
	}
}

package domain;

public class Account {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	private int money;
	@Override
	public String toString() {
		return "Account [name=" + name + ", money=" + money + "]";
	}
}


package Util;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtil02 {
	static ComboPooledDataSource dataSource=null;
	static {
		dataSource=new ComboPooledDataSource();
	}

	/**
	 * 获取连接对象
	 * @return
	 * @throws SQLException 
	 */
	public static Connection getConn() throws SQLException {
		
		return dataSource.getConnection();
	}
	/**
	 * 释放资源
	 * @param conn
	 * @param st
	 * @param rs
	 */
	public static void release(Connection conn,Statement st,ResultSet rs) {
		closeRs(rs);
		closeSt(st);
		closeConn(conn);
	}
	public static void release(Connection conn,Statement st) {
	
		closeSt(st);
		closeConn(conn);
	}
	private static void closeRs(ResultSet rs) {
	try {
		if(rs !=null) {
			rs.close();
		}
	}catch(SQLException e) {
		e.printStackTrace();
		}finally {
			rs=null;
		}
	}
	private static void closeSt(Statement st) {
		try {
			if(st !=null) {
				st.close();
			}
		}catch(SQLException e) {
			e.printStackTrace();
			}finally {
				st=null;
			}
		}
	private static void closeConn(Connection conn) {
		try {
			if(conn !=null) {
				conn.close();
			}
		}catch(SQLException e) {
			e.printStackTrace();
			}finally {
			conn=null;
			}
		}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值