JDBC与DAO篇--02 JDBC核心API


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mind_programmonkey

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值