连接池简介
1、连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
作用:避免频繁地创建与消毁,给服务器减压力。
2、数据库的弊端:
1.当用户群体少服务器可以支撑,如果群体比较大万级别服务器直接死机。数据库默认的并发访问50.
2.每一个用完数据库之后直接关闭,不能重复利用太浪费资源。
3、设计连接池:
1.在池子中创建出多个连接供使用。
2.当用户需要操作数据库时直接从池子中获取连接即可。
3.当用户使用完毕之后把连接归还给连接池,可以达到重复使用。
4.可以设定池子的最大容器。比如50个连接,当第51个人访问的时候,需要等待。
5.其它用户释放资源的时候,可以使用。
4、手动实现连接池
importjava.io.IOException;importjava.io.InputStream;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.util.Properties;public classJDBCTool {private staticConnection conn;private staticString driverName;private staticString username;private staticString url;private staticString password;static{
InputStream is= JDBCTool.class.getClassLoader().getResourceAsStream("qq.properties");
Properties prop= newProperties();try{
prop.load(is);
}catch(IOException e) {
e.printStackTrace();
}
driverName= (String)prop.get("driverClassName");
username= (String)prop.get("username");
url= (String)prop.get("url");
password= (String)prop.get("password");try{
Class.forName(driverName);
}catch(Exception e) {
e.printStackTrace();
}
}public staticConnection getConn() {try{
conn=DriverManager.getConnection(url, username, password);
}catch(SQLException e) {
e.printStackTrace();
}returnconn;
}public static voidclose(Connection conn,PreparedStatement ps,ResultSet rs) {if(conn!=null) {try{
conn.close();
}catch(SQLException e) {
e.printStackTrace();
}
}if(ps!=null) {try{
ps.close();
}catch(SQLException e) {
e.printStackTrace();
}
}if(rs!=null) {try{
rs.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
}
}
手动实现连接池第一步
importjava.sql.Connection;importjava.util.ArrayList;importjava.util.List;public classMyJDBCPool {/*1.定义一个容器 集合
2.初始化连接,并放在池子中。
3.创建一个从容器中获取连接的方法
4.创建一个归还连接的方法。*/
private static List pool = new ArrayList<>();private static int List_size = 3;static{for(int i=0;i
Connection conn=null;
conn=JDBCTool.getConn();
pool.add(conn);
System.out.println("当前存放连接为:"+conn+",当前池子中剩余连接数为:"+pool.size());
}
System.out.println("=====================");
}//取出一个连接 要判断是否为空
public static Connection getConn() throwsNullPointerException{if(pool.isEmpty()) {
System.out.println("请等待");throw new NullPointerException("已经空了,别拿了");
}
Connection conn= pool.remove(0);
System.out.println("当前取出的连接为:"+conn+",当前剩余连接数为:"+pool.size());returnconn;
}//归还连接 要判断是否为真正的连接
public static voidreturnConn(Connection conn) {if(conn==null) {
System.out.println("你玩我?");return;
}
pool.add(conn);
System.out.println("当前存入的连接为:"+conn+",当前剩余连接数为:"+pool.size());
}
}
手动实现连接池第二步
importjava.sql.Connection;public classTest1 {public static voidmain(String[] args) {
Connection conn=MyJDBCPool.getConn();
Connection conn1=MyJDBCPool.getConn();
Connection conn2=MyJDBCPool.getConn();
MyJDBCPool.returnConn(conn2);
MyJDBCPool.returnConn(conn1);
MyJDBCPool.returnConn(conn);
}
}
手动连接池的测试
注:这里涉及到一个properties文件,文件内容是key =value形式存在的,先使用类加载器(或输入流)将文件加载进来,然后使用properties对象处理文件,使用get()方法获取内容。
常用连接池
1、导入连接池的步骤
导包 buildpath
配置文件 properties
加载文件流,使用properties处理文件
使用连接池的API读取prop对象,创建连接池
getConnection获取连接
返回连接的引用
2、dbcp连接池 开源连接池,效率高,但安全性不强
导包 commons-dbcp 和commons-pool mysql-connection
配置文件导入*.properties 建议放在src根目录
importjava.sql.Connection;importorg.apache.commons.dbcp.BasicDataSource;public classTest1{public static void main(String[] args) throwsException{
BasicDataSource bds= newBasicDataSource();//4个必须设置的属性
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql:///db0619");
bds.setUsername("root");
bds.setPassword("root");//其他属性,都是可选属性
bds.setInitialSize(100); //初始创建100个连接
bds.setMaxActive(50); //最大活动数
bds.setMaxIdle(20); //最大空闲数
bds.setMinIdle(10); //最小空闲数
bds.setMaxWait(-1); //最大等待时间
Connection conn=bds.getConnection();
System.out.println(conn);
Connection conn2=bds.getConnection();
System.out.println(conn2);
}
}
手动创建DBCP连接池
importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.InputStream;importjava.sql.Connection;importjava.sql.SQLException;importjava.util.Properties;importjavax.sql.DataSource;importorg.apache.commons.dbcp.BasicDataSourceFactory;public classDBCPUtil {private staticDataSource ds;static{try{
Properties p= newProperties();
InputStream is= new FileInputStream("src/db.properties");
p.load(is);
ds=BasicDataSourceFactory.createDataSource(p);
}catch(Exception e) {
e.printStackTrace();
}
}public staticDataSource getDataSource(){returnds;
}public staticConnection getConnection(){
Connection conn= null;try{
conn=ds.getConnection();
}catch(SQLException e) {
e.printStackTrace();
}returnconn;
}
}
使用配置文件自动创建DBCP连接池,工具类
3、c3p0连接池
导包 c3p0-0.9.5.2.jar mchange-commons-java... mysql-connection...
配置文件名称:c3p0-config.xml c3p0.properties
com.mysql.jdbc.Driver
jdbc:mysql:///db0619
root
root
30000
30
10
30
100
10
200
com.mysql.jdbc.Driver
jdbc:mysql:///test
root
root
30000
30
10
30
100
10
200
c3p0 xml配置文件
importjava.sql.Connection;importcom.mchange.v2.c3p0.ComboPooledDataSource;public classTest1{public static void main(String[] args) throwsException{
ComboPooledDataSource cds= newComboPooledDataSource();//4 个基本设置
cds.setDriverClass("com.mysql.jdbc.Driver");
cds.setJdbcUrl("jdbc:mysql:///db0619");
cds.setUser("root");
cds.setPassword("root");
Connection conn=cds.getConnection();
System.out.println(conn);
}
}
手动设置c3p0连接池
importjava.sql.Connection;importjava.sql.SQLException;importjavax.sql.DataSource;importcom.mchange.v2.c3p0.ComboPooledDataSource;public classC3P0Util {private staticDataSource ds;static{//ds = new ComboPooledDataSource();//加载src/c3p0-config.xml,并使用配置文件中默认的配置配置
ds = new ComboPooledDataSource("offcn"); //加载src/c3p0-config.xml,并使用名字为offcn的配置配置
}public staticDataSource getDataSource(){returnds;
}public staticConnection getConnection(){
Connection conn= null;try{
conn=ds.getConnection();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}returnconn;
}
}
使用xml文件自动创建c3p0,工具类
4、druid连接池
工具包:
导包:druid-1....jar mysql-connection...
配置文件名称 druid.properties
importjava.io.InputStream;importjava.sql.Connection;importjava.sql.SQLException;importjava.util.Properties;importjavax.sql.DataSource;importcom.alibaba.druid.pool.DruidDataSourceFactory;public classDruidUtils {private staticDataSource ds;private staticConnection conn;static{try{
InputStream is= MyJDBC.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop= newProperties();
prop.load(is);
ds=DruidDataSourceFactory.createDataSource(prop);
}catch(Exception e) {
e.printStackTrace();
}
}public staticConnection getConnection() {try{
conn=ds.getConnection();
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
}returnconn;
}public staticDataSource getDataSource() {returnds;
}
}
使用配置文件自动创建Druid连接池,工具类
DBUTils工具类
1、DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
2、核心类QueryRunner
3、数据更新操作使用update(String sql,Object[] params...); 若无指定连接,也可以在参数一前传Connection
参数一:sql语句 ,参数二 Object数组,对sql语句中的占位符赋值 返回值为int 代表更新了几行数据
4、数据查询操作 query(String sql,handler,Object[] params...); 若无指定连接,也可以在参数一前传Connection
参数一:sql语句 ,
参数二 :如何处理返回的二维表格Object[],List,map, list,javaBean,List,Object…
参数三:对sql语句中的占位符赋值
5、ResultSetHandler中常用的处理方式
BeanHandler
将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler
将结果集中每一条记录封装到指定的javaBean中,将这些javaBean在封装到List集合中
MapHandler
将结果集中第一条记录封装到了Map集合中,key就是字段名称,value就是字段值
MapListHandler
将结果集中每一条记录封装到了Map集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中。
ScalarHandler
它是用于单数据。例如select count(*) from表操作。
importjava.sql.Connection;importjava.sql.SQLException;importjava.util.List;importjava.util.Map;importorg.apache.commons.dbutils.QueryRunner;importorg.apache.commons.dbutils.handlers.ArrayHandler;importorg.apache.commons.dbutils.handlers.BeanHandler;importorg.apache.commons.dbutils.handlers.BeanListHandler;importorg.apache.commons.dbutils.handlers.MapHandler;importorg.apache.commons.dbutils.handlers.MapListHandler;importorg.apache.commons.dbutils.handlers.ScalarHandler;importcom.bean.Users;importcom.utils.MyJDBC;public class UsersdaoImpl implementsUsersdao {
@Overridepublic intinsertUser(Users user) {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql="insert into users values(?,?,?)";int result=0;try{
result= qr.update(sql,newObject[] {user.getId(),user.getUsername(),user.getPwd()});
}catch(SQLException e) {
e.printStackTrace();
}returnresult;
}
@Overridepublic intdeleteUserById(Integer id) {
QueryRunner qr= newQueryRunner();
Connection conn=MyJDBC.getConnection();
String sql= "delete from users where id=?";int result = 0;try{
result=qr.update(conn, sql, id);
}catch(SQLException e) {
e.printStackTrace();
}returnresult;
}
@Overridepublic intupdateUser(Users user) {
QueryRunner qr= newQueryRunner();
Connection conn=MyJDBC.getConnection();
String sql= "update users set username=?,pwd=? where id=?";int result=0;try{
result= qr.update(conn, sql,newObject[] {user.getUsername(),user.getPwd(),user.getId()});
}catch(SQLException e) {
e.printStackTrace();
}returnresult;
}
@Overridepublic ListfindAllStudent() {
QueryRunner qr= newQueryRunner();
Connection conn=MyJDBC.getConnection();
String sql= "select * from users";
List list=null;try{
list= qr.query(conn,sql, new BeanListHandler<>(Users.class));
}catch(SQLException e) {
e.printStackTrace();
}returnlist;
}
@OverridepublicUsers findUserById(Integer id) {
QueryRunner qr= newQueryRunner();
Connection conn=MyJDBC.getConnection();
String sql= "select * from users where id=?";
Users user=null;try{
user= qr.query(conn,sql,new BeanHandler<>(Users.class),id);
}catch(SQLException e) {
e.printStackTrace();
}returnuser;
}
@Overridepublic ListfindUserByName(String name) {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql= "select * from users where username like ?";
List list = null;try{
list= qr.query(sql,new BeanListHandler(Users.class), "%"+name+"%");
}catch(SQLException e) {
e.printStackTrace();
}returnlist;
}
@Overridepublic MapfindFirstRow() {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql= "select * from users";
Map map = null;try{
map=qr.query(sql, newMapHandler());
}catch(SQLException e) {
e.printStackTrace();
}returnmap;
}
@Overridepublic List>findAllStudent2() {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql= "select * from users";
List> lm=null;try{
lm= qr.query(sql, newMapListHandler());
}catch(SQLException e) {
e.printStackTrace();
}returnlm;
}
@Overridepublic longfindUserCount() {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql= "select count(username) from users where username='HH'";long l=0;try{
l=(long)qr.query(sql,newScalarHandler());
}catch(SQLException e) {
e.printStackTrace();
}returnl;
}
@OverridepublicObject[] findStudent() {
QueryRunner qr= newQueryRunner(MyJDBC.getDataSource());
String sql= "select * from users";
Object[] s=null;try{
s= qr.query(sql, newArrayHandler());
}catch(SQLException e) {
e.printStackTrace();
}returns;
}
}
UsersdaoImpl
importjava.util.List;importjava.util.Map;importorg.junit.Test;importcom.bean.Users;importcom.dao.Usersdao;importcom.dao.UsersdaoImpl;public classTest {
Usersdao us= newUsersdaoImpl();
@Testpublic voidtest1() {
Users user1= newUsers();
user1.setId(null);
user1.setUsername("小红");
user1.setPwd("112233");int result =us.insertUser(user1);if(result>0) {
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}
@Testpublic voidtest2() {int result = us.deleteUserById(7);if(result>0) {
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
@Testpublic voidtest3() {
Users user= newUsers();
user.setId(10);
user.setUsername("HH");
user.setPwd("123456");int result =us.updateUser(user);if(result>0) {
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}
@Testpublic voidtest4() {
List list =us.findAllStudent();for(Users user:list) {
System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
}
}
@Testpublic voidtest5() {
Users user= us.findUserById(3);
System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
}
@Testpublic voidtest6() {
List list = us.findUserByName("刘");for(Users user:list) {
System.out.println(user.getId()+"\t"+user.getUsername()+"\t"+user.getPwd());
}
}
@Testpublic voidtest7() {
Map map = us.findFirstRow();//返回查询的第一条数据 key为字段 value为字段值
System.out.println(map);
}
@Testpublic voidtest8() {
List> lm =us.findAllStudent2();for(Mapmap:lm) {
System.out.println(map);
}
}
@Testpublic voidtest9() {long l =us.findUserCount();
System.out.println(l);
}
@Testpublic voidtest10() {
Object[] obj=us.findStudent();
System.out.println(obj);
}
}
Test
importjava.util.List;importjava.util.Map;importcom.bean.Users;public interfaceUsersdao {public intinsertUser(Users user);public intdeleteUserById(Integer id);public intupdateUser(Users user);public ListfindAllStudent();publicUsers findUserById(Integer id);public ListfindUserByName(String name);public MapfindFirstRow();public List>findAllStudent2();public longfindUserCount();publicObject[] findStudent();
}
Usersdao
事务
1、什么是事务?
事务: 一组SQL操作,要么同时成功,要么同时失败。
2、在实际的业务开发中,有些业务操作要多次访问数据库。一个业务要发送多条SQL语句给数据库执行。需要将多次 访问数据库的操作视为一个整体来执行,要么所有的SQL语句全部执行成功。如果其中有一条SQL语句失败,就进行事务的回滚,所有的SQL语句全部执行失败。 例如: jack给tom转账,jack账号减钱,tom账号加钱。
3、MYSQL中可以有两种方式进行事务的操作:
自动提交事务:默认
手动提交事务:Start transaction; //开启事务
Sql1
Sql2
Sql3
...
Sqln
Commit;
4、回滚点 savepoint
在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都已经成功, 可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之为 回滚点。
Start transaction;
Sql1
Sql2;
Savepoint ‘aaa’;
Sql3
...
Sqln
Rollback to ‘aaa’
Commit;
5、java中操作事务的方式
try{
conn.setAutoCommit(false); //默认为true 自动提交事务 false设置为手动提交
操作1,2,3,。。。 //注意,这些操作需是同一个conn连接去执行
conn.commit() //提交事务
}catch(SQLException e){
conn.rollback(); //回滚
e.printStackTrace();
}
importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.SQLException;importcom.utils.C3P0Util;public classMyTransaction {public static voidmain(String[] args) {
Connection conn=C3P0Util.getConnection();
PreparedStatement ps= null;try{
conn.setAutoCommit(false); //默认值时true --- 设置为手动提交事务
String sql= "update account set money=money+1000 where name='Tom'";
String sql2= "update account set money=money+1000 where name='Tom'";
ps=conn.prepareStatement(sql);
ps.executeUpdate();
ps=conn.prepareStatement(sql2);
ps.executeUpdate();
conn.commit();//提交事务
}catch(SQLException e) {try{
conn.rollback();//回滚
} catch(SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
java中事务的操作方式
6、事务的四大特性
事务特性
含义
原子性
(Atomicity)
事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性
(Consistency)
事务前后数据的完整性必须保持一致
隔离性
(Isolation)
是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个 并发事务之间数据要相互隔离,不能相互影响。
持久性
(Durability)
指一个事务一旦被提交(commit),它对数据库中数据的改变就是永久性的,接下来即使数据库发 生故障也不应该对其有任何影响
7、几类事务的问题
第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。
第2类丢失更新:事务A提交时覆盖事务B已经提交的数据,造成事务B所做的操作丢失。
解决方法:对行加锁,只允许并发一个更新事务。
脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。
解决方法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题。
不可重复读(Non-repeatable Reads)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。
幻象读:指两次执行同一条 select 语句会出现不同的结果,第二次读会增加一数据行,并没有说这两次执行是在同一个事务中。
解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。
8、数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。
Read uncommitted(未授权读取、读未提交):
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。
Read committed(授权读取、读提交):
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
Repeatable read(可重复读取):
可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现。
Serializable(序列化):
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。