四.数据库连接池技术
4.1 连接池技术的简介
在与数据库连接过程中,会非常消耗内存,性能大打折扣。如果每次请求都去重新连接数据库。那么,宕机的几率很高。因此,我们可以使用连接池技术。
连接池的工作原理:
连接池对象在初始化阶段 一次性创建N个连接对象,这些连接对象存储在连接池对象中。当有请求过来时,先从连接池中寻找空闲连接对象并使用,当使用完后,将连接对象归还给连接池,而不是真正意义上断开连接。这样也可以满足成千上万个请求,同时并提高了数据库的性能。
常用的连接池技术:
- dbcp :是apache组织旗下的一个数据库连接池技术产品
- c3p0 :是一个开源的连接池技术
- druid :是阿里的数据库连接池技术(最常用)
4.2 dbcp
4.2.1资源jar包:
commons-dbcp2-2.6.0.jar
commons-pool2-2.4.3.jar
commons-logging.jar
4.2.2 配置文件dbcp.properties
此配置文件请放在src目录下:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bd1906
username=root
pwd=123456
maxTotal=50
maxIdle=10
minIdle=3
initialSize=5
maxWaitMillis=60000
4.3 c3p0
4.3.1 资源jar包
c3p0-0.9.5-pre8.jar
mchange-commons-java-0.2.7.jar
4.3.2 配置文件c3p0-config.xml
配置文件请放在src目录下
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">123456</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/bd1901</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="acquireIncrement">10</property>
<property name="maxPoolSize">50</property>
<property name="minPoolSize">2</property>
<property name="initialPoolSize">5</property>
<property name="maxIdleTime">600</property>
</default-config>
</c3p0-config>
4.3.3 DBUtilc3p0类型的编写
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtilC3p0 {
//构造器会自动检索src下有没有指定文件名称的配置文件,然后会自动赋值给其相应的属性
private static ComboPooledDataSource pool = new ComboPooledDataSource("c3p0-config");
public static Connection getConnection() throws SQLException {
//从连接池中获取空闲对象
return pool.getConnection();
}
public static void closeConnection(Connection conn, Statement stat, ResultSet rs){
try {
if(rs!=null){
rs.close();
}
if(stat!=null){
stat.close();
}
if(conn !=null){
conn.close(); //会将连接对象归还给连接池内
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws SQLException {
Connection conn = getConnection();
System.out.println(conn);
conn.close();
}
}
4.4 druid
4.4.1 资源jar包
druid-1.1.18.jar
4.4.2 配置文件druid.properties
放在src目录下。注意,前面的key值是固定写法
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bd1901
username=root
password=123456
maxActive=20
minIdle=3
initialSize=5
maxWait=60000
5.4.3 DBUtildruid类型的编写
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class DBUtil_druid {
//创建连接池对象
private static DataSource pool = null;
static {
try {
//使用类加载器提供的方法读取db.properties,返回一个字节流对象
InputStream is = DBUtil_druid.class.getClassLoader().
getResourceAsStream("druid.properties");
//创建Properties对象,用于加载流内部的数据
Properties prop = new Properties();
prop.load(is); //加载流内部的信息,以key-value的形式进行加载
//调用静态方法,会自动给自己的属性赋值
pool = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
System.out.println("注册驱动失败");
e.printStackTrace();
}
}
/**
* 获取连接对象
* @return 连接对象
* @throws SQLException
* @throws ClassNotFoundException
*/
public static Connection getConnection() throws SQLException,
ClassNotFoundException {
//return DriverManager.getConnection(url, username, password);
//从连接池中获取连接对象
return pool.getConnection();
}
/**
* 关闭数据库连接
* @param rs 结果集对象
* @param stat 处理sql的执行对象Statement
* @param conn 连接对象
*/
public static void closeConnection(ResultSet rs, Statement stat, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (stat != null) {
stat.close();
}
if (conn != null) {
conn.close();//释放连接,归还给连接池
}
} catch (Exception e) {
System.out.println("数据库连接关闭失败");
e.printStackTrace();
}
}
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Connection conn = getConnection();
System.out.println(conn);
closeConnection(null, null, conn);
}
}
五. DAO设计模式
5.1 DAO简介
- DAO是数据访问对象(Data Access Object)的简写。
- 建立在数据库与业务层之间,封装所有对数据库的访问操作,我们也可称之为持久层。
- 目的: 将数据访问逻辑和业务逻辑分开。
一个DAO设计模式包含以下内容
-
定义实体类: 通过对象关系映射(ORM)将数据库的表结构映射成java类型;表中的每一条记录映射成类的实例。用于数据的传 递。
-
定义一个接口: 在此接口中,定义应用程序对此表的所有访问操作,如增,删,改、查,等方法。
-
定义接口的实现类 实现接口中的所有抽象方法。
-
定义一个DAO工厂类型 用于返回接口实例 这样,开发人员只需要使用DAO接口即可,具体逻辑就变得透明了,无需了解内部细节。
扩展:项目的包名命名规则
规范: com.域名.项目名称.模块名称
com.qianfeng.jdbc03.util
com.qianfeng.jdbc03.entity
com.qianfeng.jdbc03.test
com.qianfeng.jdbc03.dao
com.qianfeng.jdbc03.dao.impl
com.qianfeng.jdbc03.service
5.2 DAO的案例示范
5.2.1 创建项目,导入相关资源
5.2.2 编写工具类DBUtil
5.2.3 编写实体类 import java.sql.Date;
import java.util.Objects;
/**
* 以orm关系将数据库中的emp表映射成java中的Emp类型
* 表的字段映射成类的属性
*/
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredate;
private double salary;
private double comm;
private int deptno;
public Emp(){}public Emp(int empno, String ename, String job, int mgr, Date hiredate, double salary, double comm, int deptno) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.salary = salary;
this.comm = comm;
this.deptno = deptno;
}public int getEmpno() {
return empno;
}public void setEmpno(int empno) {
this.empno = empno;
}public String getEname() {
return ename;
}public void setEname(String ename) {
this.ename = ename;
}public String getJob() {
return job;
}public void setJob(String job) {
this.job = job;
}public int getMgr() {
return mgr;
}public void setMgr(int mgr) {
this.mgr = mgr;
}public Date getHiredate() {
return hiredate;
}public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}public double getSalary() {
return salary;
}public void setSalary(double salary) {
this.salary = salary;
}public double getComm() {
return comm;
}public void setComm(double comm) {
this.comm = comm;
}public int getDeptno() {
return deptno;
}public void setDeptno(int deptno) {
this.deptno = deptno;
}@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Emp emp = (Emp) o;
return empno == emp.empno &&
mgr == emp.mgr &&
Double.compare(emp.salary, salary) == 0 &&
Double.compare(emp.comm, comm) == 0 &&
deptno == emp.deptno &&
Objects.equals(ename, emp.ename) &&
Objects.equals(job, emp.job) &&
Objects.equals(hiredate, emp.hiredate);
}@Override
public int hashCode() {
return Objects.hash(empno, ename, job, mgr, hiredate, salary, comm, deptno);
}@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", salary=" + salary +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
}
5.2.4 定义接口
import com.qianfeng.jdbc03.entity.Emp;
import java.util.List;/**
* 设计针对于实体类Emp和数据库里的emp表设计对数据库操作的接口
* 提供相应操作的抽象方法
*/
public interface EmpDao {
/**
* 提供向数据库中插入数据的方法,
* @param e 面向对象思想可以使用实体类的实例
*/
void addEmp(Emp e);
/**
* 提供删除数据库内的一条记录方法,通过id进行删除
* @param empno 数据库表中的主键
*/
void deleteById(int empno);/**
* 修改方法。
* @param e 传入前先设置成要修改的数据,然后传入方法中进行update语句赋值
*/
void modifyEmp(Emp e);/**
* 通过唯一键查询一条记录
* @param empno
* @return 封装成实体类实例
*/
Emp findById(int empno);/**
* 查询所有的记录。
* @return 封装成类的实例,并存入集合
*/
List<Emp> findAll();/**
* 分页查询
* @param page 要查询的页数
* @param pageSize 每页显示的条数
* @return 一页的所有记录,封装到集合中
*/
List<Emp> findByPage(int page,int pageSize);
}
5.2.5 编写实现类
public class EmpDaoImpl implements EmpDao {
@Override
public void addEmp(Emp e) {
Connection conn = null;
PreparedStatement ps = null;
try{
conn = DBUtil.getConnection();
String sql = "insert into emp values (?,?,?,?,?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1,e.getEmpno());
ps.setString(2,e.getEname());
ps.setString(3,e.getJob());
ps.setInt(4,e.getMgr());
ps.setDate(5,e.getHiredate());
ps.setDouble(6,e.getSalary());
ps.setDouble(7,e.getComm());
ps.setInt(8,e.getDeptno());
ps.executeUpdate();}catch (Exception e1){
e1.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,null);
}
}@Override
public void deleteById(int empno) {}
@Override
public void modifyEmp(Emp e) {}
@Override
public Emp findById(int empno) {
return null;
}@Override
public List<Emp> findAll() {
return null;
}@Override
public List<Emp> findByPage(int page, int pageSize) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Emp> emps = new ArrayList<Emp>();
try{
conn = DBUtil.getConnection();
String sql = "select * from emp order by empno limit ?,?";
ps = conn.prepareStatement(sql);
ps.setInt(1,(page-1)*pageSize);
ps.setInt(2,pageSize);rs = ps.executeQuery();
Emp e = null;
while(rs.next()){
int empno = rs.getInt(1);
String ename = rs.getString(2);
String job = rs.getString("job");
int mgr = rs.getInt("mgr");
Date hiredate = rs.getDate("hiredate");
double salary = rs.getDouble("sal");
double comm = rs.getDouble("comm");
int deptno = rs.getInt("deptno");
e = new Emp(empno,ename,job,mgr,hiredate,salary,comm,deptno);
emps.add(e);
}
}catch (Exception e1){
e1.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,rs);
}
return emps;
}
}
5.2.6 编写DAO工厂类
public class DaoFactory{
//定义属性EmpDao属性
private static EmpDao empdao = new EmpDaoImpl();
//让构造函数为 private,这样该类就不会被实例化
private DaoFactory(){}
public synchronized static EmpDao getInstance(){
return empdao;
}
}
5.2.7 编写测试类
public class TestDao {
@Test
public void testAddEmp(){
EmpDao dao = DaoFactory.getInstance();
Emp e = new Emp(9007,"huanghua","manager",7369,
Date.valueOf("2019-1-1"),3000.0,200.0,20);
dao.addEmp(e);
}
@Test
public void testFindByPage(){
EmpDao dao = DaoFactory.getInstance();
List<Emp> emps = dao.findByPage(3,5);
for(Emp e:emps){
System.out.println(e);
}
}
}
六 dbutils第三方工具类的使用
6.1 简介
- 此工具封装了DAO层(持久层)的逻辑。减少了开发周期。
- jar包:commons-dbutils-1.7.jar
- 常用API:
1. QueryRunner类型:可以直接使用连接池技术来操作数据库,进行增删改查
构造器:QueryRunner(DataSource ds)
返回一个指定数据库连接池得QueryRunner对象
非静态方法:query(String sql, ResultSetHandler<T> rsh)
通过sql,及其ReusltSetHandler的子类型来获取数据并封装成相应对象
2. ResultSetHandler:关于结果集的一个接口。
其实现类如下:
BeanHandler:将查询到的数据的第一条封装成实体类对象
BeanListHandler:将查询到的数据的第一条封装成实体类对象的集合
6.2 代码测试:
public class Testdbutils {
@Test
public void testFindOne() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
Emp emp = qr.query("select * from emp",new BeanHandler<Emp>(Emp.class));
System.out.println(emp);
}
@Test
public void testFindOneParam() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
Emp emp = qr.query("select * from emp where empno =?",
new BeanHandler<Emp>(Emp.class),9007);
System.out.println(emp);
}
@Test
public void testFindAll() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
List<Emp> emp = qr.query("select * from emp",
new BeanListHandler<Emp>(Emp.class));
System.out.println(emp);
}
}