JavaEEDay37C3P0連接池
四、連接池
不再使用 JDBC 連接數據庫,採用連接池的方式
- 問題:發現在程序中,不斷的有連接數據庫的操作,但是也同時存在,每一次連接之後操作結束,立馬就會關閉 ,因爲涉及到數據庫的打開,關閉,這裏非常影響軟件的運行效率。
- 解決方案:把數據庫連接對象,放到一個池子裏
- 連接池功能如下:
1.初始化連接的個數,最大連接數,當前連接數,池子用集合來表示 ,一般使用LinkedList ;因爲增刪多,基本上沒有查找
2.構造方法:創建初始化連接
3.創建連接的方法
4.獲取連接的方法:
—> 判斷:池子中有沒有可用的連接
–> 有,直接拿走
–> 沒有 判斷是否達到了最大連接數,
–>到達則拋出異常
–>沒有達到,創建新的連接
5.釋放連接: 是將正在使用的數據庫連接對象,放回池子內
package connectionpool;
import metadata.JdbcUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
/**
* @author GJXAIOU
* @create 2019-08-02-20:37
*/
public class ConnectionPoolPractice {
/*
初始化連接數目,這裏默認爲3
*/
private int initCount = 3;
/*
連接池最大連接數目,這裏定義爲6
*/
private final int maxCount = 6;
/*
記錄當前的連接數目
*/
private int currentCount = 0;
// 新建連接池,使用linkedlist操作
private LinkedList<Connection> connectionPool = new LinkedList<Connection>();
// 1.構造方法,按照指定初始化連接個數並且創建新的連接
public ConnectionPoolPractice(){
for (int i = 0; i < initCount; i++) {
// 類內創建連接的方式
Connection connection = createConnection();
connectionPool.addLast(connection);
currentCount++;
}
}
public int getCurrentCount(){
return currentCount;
}
// 2.創建一個新的連接
private Connection createConnection(){
// 2.1加載驅動
final Connection connection = JdbcUtil.getConnection();
Connection proxy = (Connection)Proxy.newProxyInstance(connection.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 定義一個返回值
Object result = null;
// 獲取要執行方法的方法名
String methodName = method.getName();
// 這裏只是限制了close的操作,其他的方法按照原來的操作進行
// 修改關閉close執行的任務
if ("close".equals(methodName)) {
System.out.println("執行close方法");
// 放回連接池內
connectionPool.addLast(connection);
System.out.println("數據庫連接已經放回到連接池中");
}else{
result = method.invoke(connection, args);
}
return result;
}
});
return proxy;
}
// 3.從連接池中獲取連接的方法
public Connection getConnection(){
// 3.1判斷池子中有沒有連接
if (connectionPool.size() > 0){
return connectionPool.removeFirst();
}
// 3.2如果沒有連接,判斷當前連接數是否達到最大值限制
if (currentCount < maxCount){
currentCount++;
return createConnection();
}
throw new RuntimeException("當前連接數已經達到最大值");
}
// 4.釋放連接
public void realeaseConnection(Connection connection){
// 4.1如果池子中讀取的數目小於初始化連接,放入池子
if(connectionPool.size() < initCount){
connectionPool.addLast(connection);
}else {
currentCount--;
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ConnectionPoolPractice connectionPoolPractice = new ConnectionPoolPractice();
System.out.println("當前連接數目爲:" + connectionPoolPractice.getCurrentCount());
Connection connection = connectionPoolPractice.getConnection();
Connection connection1 = connectionPoolPractice.getConnection();
Connection connection2 = connectionPoolPractice.getConnection();
System.out.println(connection);
System.out.println(connection1);
System.out.println(connection2);
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
Connection connection3 = connectionPoolPractice.getConnection();
System.out.println(connection3);
}
}
使用 C3P0 連接池
一共兩種連接方式:
方式一:硬編碼方式
方式二:使用 XML 文件
- 方式一:使用硬編碼方式的代碼
package connectionpool;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author GJXAIOU
* @create 2019-08-02-21:42
*/
public class C3P0ConnectionPool {
/**
* 使用硬連接的方式
* @throws PropertyVetoException
* @throws SQLException
*/
public void HardcodeStyleTest() throws PropertyVetoException, SQLException {
// 1.創建連接池的核心工具類
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 2.設置連接數據庫所需的參數
comboPooledDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8 ");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("GJXAIOU");
// 3.設置C3P0連接池的屬性:初始化連接數、最大連接數、等待時間
comboPooledDataSource.setInitialPoolSize(3);
comboPooledDataSource.setMaxPoolSize(6);
comboPooledDataSource.setMaxIdleTime(1000);
// 4.從連接池中獲取數據庫連接對象
Connection connection = comboPooledDataSource.getConnection();
// 5.準備preparedStatement執行SQL語句
connection.prepareStatement("delete from person where id = 3").executeUpdate();
}
}
- 方式二:使用 XML 文件的形式
package connectionpool;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author GJXAIOU
* @create 2019-08-02-21:42
*/
public class C3P0ConnectionPool {
/**
* 通過配置XML文件,創建C3P0連接池
* @throws SQLException
*/
public void XmlStyleTest() throws SQLException {
// 1.創建C3P0核心類,會自動的加載s目錄下的c3p0-config.xml文件
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 2.創建連接
Connection connection = comboPooledDataSource.getConnection();
// 3.準備preparedStatement以及SQL語句
PreparedStatement preparedStatement = null;
String sql = "insert into person(name, gender, score, home, hobby) value(?, ?, ?, ?, ?)";
for (int i = 0; i < 10; i++) {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"錢十");
preparedStatement.setString(2,"男");
preparedStatement.setInt(3, 98);
preparedStatement.setString(4, "江蘇");
preparedStatement.setString(5, "游泳");
preparedStatement.executeUpdate();
}
preparedStatement.close();
connection.close();
}
}
針對第二種方式,一般使用 XML 配置文件,也可以使用.properties 文件,這裏使用 xml 文件,將其放置在上面代碼同一包下即可;命名爲:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8 </property>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">GJXAIOU</property>
<property name="initialPoolSize">3</property>
<property name="maxPoolSize">6</property>
<property name="maxIdleTime">1000</property>
</default-config>
</c3p0-config>
– MySQL增強
create database day38;
use day38;
一、 數據約束
(一) 默認值
create table student(
id int,
name varchar(20),
country varchar(20) default '中國' -- 默認值
);
– 沒有設置默認值的字段,在沒有賦值的情況下都是NULL
insert into student(id, name) values(1, “孬蛋”);
insert into student(id, name, country) values(2, “金豆”, “China”);
insert into student(id) values(3);
(二)非空
create table student(
id int,
name varchar(20),
gender varchar(2) not null -- 非空
);
-
如果存在非空字段,必須賦值,下面會報錯;
insert into student(id, name) values(1, "孬蛋");
ERROR 1364 (HY000): Field ‘gender’ doesn’t have a default value -
必須給非空字段賦值,下面語句正確;
insert into student(id, name, gender) values(1, "狗蛋", "男");
-
非空字段用在什麼地方?
用戶名,密碼,郵箱,手機
(三)唯一
create table student(
id int UNIQUE, -- 唯一
name varchar(20)
);
insert into student(id, name) values(1, “孬蛋”);
insert into student(id, name) values(1, “狗剩”);
ERROR 1062(23000):Duplicate entry '1' for key 'id'
– 唯一值不能重複
(四)主鍵 (非空+唯一)
create table student(
id int primary key,
name varchar(20)
);
insert into student(id, name) values(1, “狗蛋”);
insert into student(id, name) values(2, “狗剩”);
insert into student(name) values(‘辣雞’);
– ERROR 1364 (HY000): Field ‘id’ doesn’t have a default value
– ID值不能爲空,必須有數據
insert into student(id, name) values(1, “炸雞”);
– ERROR 1062 (23000): Duplicate entry ‘1’ for key ‘PRIMARY’
– ID值是唯一索引,不能爲空,並且不能重複
(五)自增長
CREATE TABLE student(
-- 自增長 ZEROFILL 零填充,從0開始
id INT ZEROFILL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
insert into student(name) values("張三");
insert into student(name) values("李四");
insert into student(name) values("王五");
insert into student(name) values("馬六");
(六)外鍵約束
下面表的設計中:每一個人都是JavaEE教學部,這裏每一個數據行中,都有JavaEE教學部,導致了數據的冗餘,是否可以把部門做成一張表
create table employee(
id int primary key, -- 員工ID
empName varchar(20), -- 員工名
deptName varchar(20) -- 部門名
);
insert into employee values(1, "老劉", "JavaEE教學部");
insert into employee values(2, "我黨", "JavaEE教學部");
insert into employee values(3, "大飛", "JavaEE教學部");
insert into employee values(4, "瑞哥", "JavaEE教學部");
insert into employee values(5, "賦賦", "JavaEE教學部");
– 設計一個獨立的部門表
create table dept(
id int primary key,
deptName varchar(20)
);
– 設計一個新的員工表,帶有部門ID
create table employee(
id int primary key,
empName varchar(20),
deptID int, -- 用部門的ID來表示當前員工的部門是哪一個
-- 建立一個外鍵約束
CONSTRAINT emp_dept_fk foreign key(deptID) references dept(id) on update cascade on delete cascade -- 級聯修改
--emp_dept_fk :外鍵名稱 deptID:外鍵 dept(id):連接的參考字段
);
insert into dept(id, deptName) values(1, "JavaEE教學部");
insert into dept(id, deptName) values(2, "PHP教學部");
insert into dept(id, deptName) values(3, "iOS教學部");
insert into employee values(1, "張三", 1);
insert into employee values(2, "李四", 2);
insert into employee values(3, "王五", 3);
insert into employee values(4, "趙六", 1);
insert into employee values(5, "喜峯", 4);
– 存在問題,因爲在部門中並沒有部門ID爲4的部門,這裏數據無法添加
– ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (day38
.employee
, CONSTRAINT emp_dept_fk
FOREIGN KEY (deptID
) REFERE
NCES dept
(id
) ON DELETE CASCADE ON UPDATE CASCADE)
– 級聯修改因爲後面的這句話: on update cascade on delete cascade
-
如果修改部門,這裏會隨之修改主表裏面的數據
update dept set id=4 where id=3;
-
刪除部門表 同時會幫助我們刪除員工信息
delete from dept where id=2;
-
修改員工表信息
update employee set deptID=2 where id=4;
-
如果沒有級聯修改
-
- 當存在外鍵約束,添加數據的順序:先添加主表,在添加副表
-
- 當存在外鍵約束,修改數據的順序:先修改副表,在修改主表
-
- 當存在外鍵約束,刪除數據的順序:先刪除副表,在刪除主表
-
二、關聯查詢
交叉查詢
select empName,deptName from employee, dept;
這個結果是有問題的 笛卡爾乘積 存在重複數據,不推薦使用
– 需求:查詢員工及其所在部門,顯示員工姓名和部門名稱
將上面的表格進行重新新建;
多表查詢
- 規則:
-
- 確定查詢那些表格 2. 確定要查詢的字段 3. 表和表之間的關係
-
內連接查詢。
只有滿足條件的結果纔會展示(使用最多的多表查詢)
select empName,deptName from employee, dept where employee.deptID = dept.id;
select empName,deptName – 要查詢的字段
from employee, dept – 要查詢的表格
where employee.deptID = dept.id; – 表和表之間的關係
-
inner join 內連接的另一種語法
select empName, deptName from employee inner join dept on employee.deptID = dept.id;
select empName, deptName
from employee – 主表
inner join dept – 連接的是哪一張表
on employee.deptID = dept.id; – 表示條件 -
或者 使用別名
select e.empName, d.deptName from employee e inner join dept d on e.deptID = d.id;
左右外連接
-
需求,查看每一個部門的員工
– 預期結果
– JavaEE 張三
– JavaEE 趙六
– iOS 王五
– PHP 李四 -
左[外]連接查詢:使用左邊表中的數據來匹配右邊表的數據,如果符合條件,展示數據
– 如果沒有符合條件的連接數據,顯示null
select d.deptName, e.empName from dept d left outer join employee e on d.id = e.deptID;
– 右[外]連接查詢:使用右邊表中的數據來匹配左邊表的數據,如果符合條件,展示數據
– 如果沒有符合條件的連接數據,顯示null
select d.deptName, e.empName from employee e right outer join dept d on d.id = e.deptID;
自連接查詢
– 修改員工表結構,添加上司
alter table employee add bossId int;
update employee set bossId=1 where id=2;
update employee set bossId=2 where id=3;
update employee set bossId=3 where id=4;
– 預期結果
– 張三 null
– 李四 張三
– 王五 李四
– 趙六 王五
select e.empName, b.empName from employee e left outer join employee b on e.bossId = b.id;