1.为什么要使用连接池
数据库连接的建立及关闭资源消耗巨大。传统的数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开、关闭该物理连接,系统性能严重受损。
解决措施:系统初始运行时,主动建立足够的连接,组成一个池,每次应用程序请求数据库连接时,无序重新打开连接,而是从池中取出已有连接,使用完后已有连接,使用完后,不再关闭,而是归还。
2.使用Apache DBCP连接池
DBCP:数据库连接池;Apache的一个Java连接池开源项目,同时也是Tomcat使用的连接池组件。连接池是创建和管理连接的缓冲池的技术,将连接准备好被任何需要他们的应用使用。
数据库连接池是管理并发访问数据库连接的理想解决方案。
DriverManager管理数据库连接适合单线程使用,在多线程并发情况下,为了能够重用数据库连接,同时控制并发连接总数,保护数据库避免连接过载,一定要使用数据库连接池。
例子1:连接池的用法
package com.lcz.jdbc.day02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbcp.BasicDataSource;
/**
* 连接池的用法
* @author LvChaoZhang
*
*/
public class Demo01 {
public static void main(String[] args) throws SQLException {
String driver="com.mysql.jdbc.Driver";
String url="jdbc:mysql://localhost:3306/test2";
String username="root";
String password="root";
BasicDataSource ds=new BasicDataSource();
//设置必须参数
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
//设置连接池的管理策略参数
ds.setInitialSize(20);//初始化建立20条连接
ds.setMaxActive(100);
//使用连接池中的数据库连接
Connection conn=ds.getConnection();
//执行sql语句
String sql="select 'hello' as a from dual";
Statement st=conn.createStatement();
ResultSet rs=st.executeQuery(sql);
while(rs.next()) {
String str = rs.getString("a");
System.out.println(str);
}
st.executeQuery(sql);
//归还连接到数据库连接池中!!!
conn.close();
}
}
例子2:单独将连接池部分写到DbUtils这个类中
package com.lcz.jdbc.day02;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
/**
* 连接池版本的数据库连接管理工具类
* 适合于并发场合
* @author LvChaoZhang
*
*/
public class DbUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
private static int initSize;
private static int maxActive;
private static BasicDataSource bs;
static {
//连接池
bs=new BasicDataSource();
Properties cfg=new Properties();
try {
InputStream in =DbUtils.class.getClassLoader().getResourceAsStream("db.properties");
cfg.load(in);
//初始化参数
driver=cfg.getProperty("jdbc.driver");
url=cfg.getProperty("jdbc.url");
username=cfg.getProperty("jdbc.user");
password=cfg.getProperty("jdbc.password");
initSize=Integer.parseInt(cfg.getProperty("initSize"));
maxActive=Integer.parseInt(cfg.getProperty("maxActive"));
in.close();
//初始化连接池
bs.setDriverClassName(driver);
bs.setUrl(url);
bs.setUsername(username);
bs.setPassword(password);
bs.setInitialSize(initSize);
bs.setMaxActive(maxActive);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() {
try {
//getConnection()从连接池中获取重用的连接,如果连接池满了,则等待,如果有连接归还,则获取重用的连接
Connection conn = bs.getConnection();
return conn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
public static void close(Connection conn) {
if(conn!=null) {
try {
//将用过的连接归还到连接池中
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
连接部分:
package com.lcz.jdbc.day02;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo4 {
public static void main(String[] args) throws Exception {
//创建连接
DbUtils db=new DbUtils();
Connection conn = db.getConnection();
Statement statement = conn.createStatement();
String sql="select *from emp";
ResultSet set = statement.executeQuery(sql);
while(set.next()) {
String name=set.getString("ename");
System.out.println(name);
}
conn.close();
}
}
例子3:演示预编译的SQL执行计划
package com.lcz.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* 演示预编译的SQL执行计划
* @author LvChaoZhang
*
*/
public class Demo5 {
public static void main(String[] args) {
Connection conn=null;
try {
//获取连接
conn=DbUtils.getConnection();
//带参数的sql语句
String sql="insert into student(id,name) values(?,?)";
//将sql发送到数据库,然后创建执行计划,返回值ps代表执行计划
PreparedStatement ps = conn.prepareStatement(sql);
//替换执行计划中的参数,2个参数
//按照序号发送参数
ps.setInt(1, 10000);
ps.setString(2, "张三");
//执行“执行计划”
int n = ps.executeUpdate();
System.out.println(n);
//再执行一次
ps.setInt(1, 10001);
ps.setString(2, "李四");
n=ps.executeUpdate();
System.out.println(n);
} catch (Exception e) {
e.printStackTrace();
}finally {
DbUtils.close(conn);
}
}
}
例子4:使用执行计划更新数据库语句
package com.lcz.jdbc.day02;
/**
* 使用执行计划更新数据库语句
*/
import java.sql.Connection;
import java.sql.PreparedStatement;
public class Demo6 {
public static void main(String[] args) {
Connection conn=null;
try {
conn=DbUtils.getConnection();
//
String sql="update student set name=? where id=? ";
PreparedStatement ps = conn.prepareStatement(sql);
//
ps.setString(1, "隔壁老王");
ps.setInt(2, 10000);
//
int n = ps.executeUpdate();
System.out.println(n);
} catch (Exception e) {
e.printStackTrace();
}finally {
DbUtils.close(conn);
}
}
}
例子5:使用执行计划实现查询功能
package com.lcz.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 使用执行计划实现查询功能
* @author LvChaoZhang
*
*/
public class Demo06 {
public static void main(String[] args) {
Connection conn=null;
try {
conn=DbUtils.getConnection();
String sql="select * from student where name like ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "%王%");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
int id=rs.getInt("id");
String name=rs.getString("name");
System.out.println(id+":"+name);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DbUtils.close(conn);
}
}
}
例子6:多线程并发访问连接池
package com.jdbc.practice;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class ThreadPoolDemo {
public static void main(String[] args) {
//生成三个线程对象
ThreadDemo t1=new ThreadDemo(5000);
ThreadDemo t2=new ThreadDemo(6000);
ThreadDemo t3=new ThreadDemo(2000);
t1.start();
t2.start();
t3.start();
}
}
//线程类
class ThreadDemo extends Thread{
//等待时间
int waitTime=0;
//有参的构造方法
public ThreadDemo(int waitTime) {
this.waitTime=waitTime;
}
public void run() {
Connection con=null;
try {
//开启连接
con=DbUtils.getConnetion();
System.out.println("获取了连接"+con);
//睡眠
Thread.sleep(waitTime);
//执行sql语句
Statement st = con.createStatement();
String sql="select 'hello' as a from dual";
ResultSet rs = st.executeQuery(sql);
//输出
while(rs.next()) {
String str = rs.getString("a");
System.out.println(str);
}
System.out.println(waitTime+"结束了");
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭连接
DbUtils.close(con);
}
}
}
2.PreparedStatement原理(能够提高性能)
Statement主要用于执行静态SQL语句,即内容不变的SQL语句。Statement每执行一次都要对传入的SQL语句编译一次,效率较差。某些情况下,SQL语句只是其中的参数有所不同,其余完全相同,适用于PreparedStatement。预防sql注入攻击。
package com.lcz.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* SQL注入演示
* 用户名:lcz
* 密码:1 ' or '1' ='1
* 用户输入了含有SQL成分的参数,参数在拼接SQL时候造成SQL语句的语义改变!改变SQL语句的执行计划!
* 最终的执行结果就完全改变了!
* 如何避免?
* 1.拦截用户输入的SQL成分
* 2.固定执行计划,避免改变执行逻辑
* @author LvChaoZhang
*
*/
public class Demo07 {
public static void main(String[] args) {
//获取用户输入
//检查登录情况
Scanner sc=new Scanner(System.in);
System.out.print("用户名:");
String name = sc.nextLine();
System.out.print("密码:");
String pwd = sc.nextLine();
boolean pass = login1(name,pwd);
if(pass) {
System.out.println("欢迎您!"+name);
}else {
System.out.println("账号或密码不正确");
}
}
//检查用户是否能够登录
public static boolean login(String name,String pwd) {
String sql="select count(*) as c from user where name=\'"+name+"\' and pwd=\'"+pwd+"\'";
System.out.println(sql);
Connection conn=null;
try {
//获得连接
conn=DbUtils.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) {
int n=rs.getInt("c");
//System.out.println(n);
return n>=1;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DbUtils.close(conn);
}
return false;
}
//防止SQL注入的登录方法
public static boolean login1(String name,String pwd) {
String sql="select count(*) as c from user where name=? and pwd=?";
System.out.println(sql);
Connection conn=null;
try {
conn=DbUtils.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
int n=rs.getInt("c");
return n>=1;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DbUtils.close(conn);
}
return false;
}
}