Linux之自動化部署
流程
Step1.呼叫DriverManager類中的靜態方法getConnection()建立連線物件
getConnection(String url, String uesr, String password)
getConnection(String url, Properties info)
引數:
url:連線字串,不同的資料庫URL是不同的,
協議名:子協議://伺服器名或IP地址:埠號/資料庫名?引數=引數值
- mysql的寫法(本地時) jdbc:mysql://localhost:3306/資料庫[?引數名=引數值] (中括號表示附加條件,可無)
user:登入使用者名稱 ; password:密碼
Connection connection = DriverManager.getConnection(url, "root", "root");
or:
String url = "jdbc:mysql://localhost:3306/day24"; Properties info = new Properties(); //把使用者名稱和密碼放在info物件中 info.setProperty("user","root"); info.setProperty("password","root"); Connection connection = DriverManager.getConnection(url, info);
Step2.呼叫Connection介面的方法createStatement()建立語句宣告物件
Step3.對語句物件傳入sql
Step4.執行sql,用Statement物件的方法
int executeUpdate(String sql)
用於傳送DML
語句,增刪改的操作,insert引數:SQL語句
返回值:返回對資料庫影響的行數
ResultSet executeQuery(String sql)
用於傳送DQL
語句,執行查詢的操作。select
引數:SQL語句
返回值:查詢的結果集
Step5.處理結果
Step6.釋放資源,先開後關,後開先關。包括ResultSet 結果集,Statement 語句,Connection 連線
Statement舉例
DDL建立表
try {
conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root");
//2. 通過連線物件得到語句物件
statement = conn.createStatement();
//3. 通過語句物件傳送SQL語句給伺服器
//4. 執行SQL
statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " +
"name varchar(20) not null, gender boolean, birthday date)");
//5. 返回影響行數(DDL沒有返回值)
System.out.println("建立表成功");
} catch (SQLException e) {
e.printStackTrace();
}
//6. 釋放資源
finally {
//關閉之前要先判斷
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
DML新增記錄
public static void main(String[] args) throws SQLException {
// 1) 建立連線物件
Connection connection = DriverManager.getConnection("jdbc:mysql:///day24", "root",
"root");
// 2) 建立Statement語句物件
Statement statement = connection.createStatement();
// 3) 執行SQL語句:executeUpdate(sql)
int count = 0;
// 4) 返回影響的行數
count += statement.executeUpdate("insert into student values(null, '孫悟空', 1, '1993-03-
24')");
count += statement.executeUpdate("insert into student values(null, '白骨精', 0, '1995-03-
24')");
count += statement.executeUpdate("insert into student values(null, '豬八戒', 1, '1903-03-
9 / 21
24')");
count += statement.executeUpdate("insert into student values(null, '嫦娥', 0, '1993-03-
11')");
System.out.println("插入了" + count + "條記錄");
// 5) 釋放資源
statement.close();
connection.close();
}
}
DQL查詢
ResultSet 介面:
作用:封裝資料庫查詢的結果集,對結果集進行遍歷,取出每一條記錄。
方法:
-
boolean next()
- 遊標向下移動1 行
- 返回boolean 型別,如果還有下一條記錄,返回true,否則返回false
-
資料型別getXxx()
- 通過欄位名,引數是String 型別。返回不同的型別
- 通過列號,引數是整數,從1 開始。返回不同的型別
public static void main(String[] args) throws SQLException {
//1) 得到連線物件
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/day24","root","root");
//2) 得到語句物件
Statement statement = connection.createStatement();
//3) 執行SQL語句得到結果集ResultSet物件
ResultSet rs = statement.executeQuery("select * from student");
//4) 迴圈遍歷取出每一條記錄
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
boolean gender = rs.getBoolean("gender");
Date birthday = rs.getDate("birthday");
//5) 輸出的控制檯上
System.out.println("編號:" + id + ", 姓名:" + name + ", 性別:" + gender + ", 生日:" +
birthday);
}
//6) 釋放資源
rs.close();
statement.close();
connection.close();
}
關於ResultSet 介面中的注意事項:
- 如果游標在第一行之前,使用rs.getXX()獲取列值,報錯:Before start of result set
- 如果游標在最後一行之後,使用rs.getXX()獲取列值,報錯:After end of result set
- 使用完畢以後要關閉結果集ResultSet,再關閉Statement,再關閉Connection
JdbcUtils
資料庫工具類JdbcUtils
如果一個功能經常要用到,我們建議把這個功能做成一個工具類,可以在不同的地方重用。
程式碼中出現了很多重複的程式碼,可以把這些公共程式碼抽取出來。
建立類JdbcUtil 包含3 個方法:
- 可以把幾個字串定義成常量:使用者名稱,密碼,URL,驅動類
- 得到資料庫的連線:getConnection()
- 關閉所有開啟的資源:
close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
public class JdbcUtils {
//可以把幾個字串定義成常量:使用者名稱,密碼,URL,驅動類
private static final String USER = "root";
private static final String PWD = "root";
private static final String URL = "jdbc:mysql://localhost:3306/day24";
private static final String DRIVER= "com.mysql.jdbc.Driver";
/**
* 註冊驅動
*/
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 得到資料庫的連線
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
/**
* 關閉所有開啟的資源
*/
public static void close(Connection conn, Statement stmt) {
if (stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 關閉所有開啟的資源
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
13 / 21
}
}
close(conn, stmt);
}
}
PreparedStatement
PreparedStatement是Statement介面的子介面,繼承於父介面中所有的方法。它是一個預編譯的SQL 語句,優點:
- prepareStatement()會先將SQL語句傳送給資料庫預編譯。PreparedStatement會引用著預編譯後的結果。可以多次傳入不同的引數給PreparedStatement物件並執行。減少SQL編譯次數,提高效率。
- 安全性更高,沒有SQL注入的隱患。
- 提高了程式的可讀性
Connection 建立PreparedStatement 物件:
PreparedStatement prepareStatement(String sql)
指定預編譯的SQL語句,SQL語句中使用佔位符?建立一個語句物件.
PreparedStatement 介面中的方法:
int executeUpdate() 執行DML,增刪改的操作,返回影響的行數。
ResultSet executeQuery() 執行DQL,查詢的操作,返回結果集
舉例
1)編寫SQL語句,未知內容使用?佔位:"SELECT * FROM user WHERE name=? AND password=?";
2)獲得PreparedStatement物件
3)設定實際引數:setXxx(佔位符的位置,真實的值)
4)執行引數化SQL語句
5)關閉資源
public class DemoLogin {
//從控制檯上輸入的使用者名稱和密碼
public static void main(String[] args) throws SQLException {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入使用者名稱:");
String name = sc.nextLine();
System.out.println("請輸入密碼:");
String password = sc.nextLine();
login(name, password);
}
/**
* 登入的方法
* @param name
17 / 21
* @param password
*/
private static void login(String name, String password) throws SQLException {
Connection connection = JdbcUtils.getConnection();
//寫成登入SQL語句,沒有單引號
String sql = "select * from user where name=? and password=?";
//得到語句物件
PreparedStatement ps = connection.prepareStatement(sql);
//設定引數
ps.setString(1, name);
ps.setString(2,password);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
System.out.println("登入成功:" + name);
}
else {
System.out.println("登入失敗");
}
//釋放資源,子介面直接給父介面
JdbcUtils.close(connection,ps,resultSet);
}
}
public class DemoStudent {
public static void main(String[] args) throws SQLException {
//建立學生物件
Student student = new Student();
18 / 21
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from student where id=?");
//設定引數
ps.setInt(1,2);
ResultSet resultSet = ps.executeQuery();
if (resultSet.next()) {
//封裝成一個學生物件
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getBoolean("gender"));
student.setBirthday(resultSet.getDate("birthday"));
}
//釋放資源
JdbcUtils.close(connection,ps,resultSet);
System.out.println(student);
}
}
public class Demo10List {
public static void main(String[] args) throws SQLException {
//建立一個集合
List<Student> students = new ArrayList<>();
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from student");
//沒有引數替換
ResultSet resultSet = ps.executeQuery();
while(resultSet.next()) {
//每次迴圈是一個學生物件
Student student = new Student();
//封裝成一個學生物件
student.setId(resultSet.getInt("id"));
19 / 21
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getBoolean("gender"));
student.setBirthday(resultSet.getDate("birthday"));
//把資料放到集合中
students.add(student);
}
//關閉連線
JdbcUtils.close(connection,ps,resultSet);
//使用資料
for (Student stu: students) {
System.out.println(stu);
}
}
}
DML:
public class DemoDML {
public static void main(String[] args) throws SQLException {
//insert();
//update();
delete();
}
//插入記錄
private static void insert() throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("insert into student
values(null,?,?,?)");
ps.setString(1,"小白龍");
ps.setBoolean(2, true);
ps.setDate(3,java.sql.Date.valueOf("1999-11-11"));
int row = ps.executeUpdate();
System.out.println("插入了" + row + "條記錄");
JdbcUtils.close(connection,ps);
}
//更新記錄: 換名字和生日
private static void update() throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=?
where id=?");
ps.setString(1,"黑熊怪");
ps.setDate(2,java.sql.Date.valueOf("1999-03-23"));
ps.setInt(3,5);
int row = ps.executeUpdate();
System.out.println("更新" + row + "條記錄");
JdbcUtils.close(connection,ps);
}
20 / 21
//刪除記錄: 刪除第5條記錄
private static void delete() throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
ps.setInt(1,5);
int row = ps.executeUpdate();
System.out.println("刪除了" + row + "條記錄");
JdbcUtils.close(connection,ps);
}
}
JDBC處理事務
void setAutoCommit(boolean autoCommit)
引數是true或false
如果設定為false,表示關閉自動提交,相當於開啟事務
void commit() 提交事務
void rollback() 回滾事務
步驟:
1)獲取連線
2)開啟事務
3)獲取到PreparedStatement
4)使用PreparedStatement執行兩次更新操作
5)正常情況下提交事務
6)出現異常回滾事務
7)最後關閉資源
public class Demo12Transaction {
//沒有異常,提交事務,出現異常回滾事務
public static void main(String[] args) {
21 / 21
//1) 註冊驅動
Connection connection = null;
PreparedStatement ps = null;
try {
//2) 獲取連線
connection = JdbcUtils.getConnection();
//3) 開啟事務
connection.setAutoCommit(false);
//4) 獲取到PreparedStatement
//從jack扣錢
ps = connection.prepareStatement("update account set balance = balance - ? where
name=?");
ps.setInt(1, 500);
ps.setString(2,"Jack");
ps.executeUpdate();
//出現異常
System.out.println(100 / 0);
//給rose加錢
ps = connection.prepareStatement("update account set balance = balance + ? where
name=?");
ps.setInt(1, 500);
ps.setString(2,"Rose");
ps.executeUpdate();
//提交事務
connection.commit();
System.out.println("轉賬成功");
} catch (Exception e) {
e.printStackTrace();
try {
//事務的回滾
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
System.out.println("轉賬失敗");
}
finally {
//7) 關閉資源
JdbcUtils.close(connection,ps);
}
}
資料庫連線池
參考:https://blog.csdn.net/crankz/article/details/82874158
資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個。
資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現得尤為突出。 一個數據庫連線物件均對應一個物理資料庫連線,每次操作都開啟一個物理連線,使用完都關閉連線,這樣造成系統的 效能低下。 資料庫連線池的解決方案是在應用程式啟動時建立足夠的資料庫連線,並講這些連線組成一個連線池(簡單說:在一個“池”裡放了好多半成品的資料庫聯接物件),由應用程式動態地對池中的連線進行申請、使用和釋放。對於多於連線池中連線數的併發請求,應該在請求佇列中排隊等待。並且應用程式可以根據池中連線的使用率,動態增加或減少池中的連線數。 連線池技術儘可能多地重用了消耗記憶體地資源,大大節省了記憶體,提高了伺服器地服務效率,能夠支援更多的客戶服務。通過使用連線池,將大大提高程式執行效率,同時,我們可以通過其自身的管理機制來監視資料庫連線的數量、使用情況等。
一次訪問的時候,需要建立連線。 但是之後的訪問,均會複用之前建立的連線,直接執行SQL語句。
步驟:
第一、連線池的建立。一般在系統初始化時,連線池會根據系統配置建立,並在池中建立了幾個連線物件,以便使用時能從連線池中獲取。連線池中的連線不能隨意建立和關閉,這樣避免了連線隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連線池,例如Vector、Stack等。
第二、連線池的管理。連線池管理策略是連線池機制的核心,連線池內連線的分配和釋放對系統的效能有很大的影響。其管理策略是:
當客戶請求資料庫連線時,首先檢視連線池中是否有空閒連線,如果存在空閒連線,則將連線分配給客戶使用;如果沒有空閒連線,則檢視當前所開的連線數是否已經達到最大連線數,如果沒達到就重新建立一個連線給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則丟擲異常給客戶。
當客戶釋放資料庫連線時,先判斷該連線的引用次數是否超過了規定值,如果超過就從連線池中刪除該連線,否則保留為其他客戶服務。
該策略保證了資料庫連線的有效複用,避免頻繁的建立、釋放連線所帶來的系統資源開銷。
第三、連線池的關閉。當應用程式退出時,關閉連線池中所有的連線,釋放連線池相關的資源,該過程正好與建立相反。
使用連線池時,要配置一下引數:
最小連線數:是連線池一直保持的資料庫連線,所以如果應用程式對資料庫連線的使用量不大,將會有大量的資料庫連線資源被浪費.
最大連線數:是連線池能申請的最大連線數,如果資料庫連線請求超過次數,後面的資料庫連線請求將被加入到等待佇列中,這會影響以後的資料庫操作
最大空閒時間
獲取連線超時時間
超時重試連線次數
Druid 相對於其他資料庫連線池的優點
強大的監控特性,通過Druid提供的監控功能,可以清楚知道連線池和SQL的工作情況。
-
監控SQL的執行時間、ResultSet持有時間、返回行數、更新行數、錯誤次數、錯誤堆疊資訊;
-
SQL執行的耗時區間分佈。什麼是耗時區間分佈呢?比如說,某個SQL執行了1000次,其中01毫秒區間50次,110毫秒800次,10100毫秒100次,1001000毫秒30次,1~10秒15次,10秒以上5次。通過耗時區間分佈,能夠非常清楚知道SQL的執行耗時情況;
-
監控連線池的物理連線建立和銷燬次數、邏輯連線的申請和關閉次數、非空等待次數、PSCache命中率等。
-
方便擴充套件。Druid提供了Filter-Chain模式的擴充套件API,可以自己編寫Filter攔截JDBC中的任何方法,可以在上面做任何事情,比如說效能監控、SQL審計、使用者名稱密碼加密、日誌等等。Druid集合了開源和商業資料庫連線池的優秀特性,並結合阿里巴巴大規模苛刻生產環境的使用經驗進行優化
Druid:資料庫連線池實現技術,由阿里巴巴提供
1. 步驟:
1. 匯入jar包 druid-1.0.9.jar
2. 定義配置檔案:
* 是properties形式的
* 可以叫任意名稱,可以放在任意目錄下
3. 載入配置檔案Properties
4. 獲取資料庫連線池物件:通過工廠來來獲取 DruidDataSourceFactory
5. 獲取連線:getConnection
* 程式碼:
//3.載入配置檔案
```
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
```
//4.獲取連線池物件
```
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
```
//5.獲取連線
Connection conn = ds.getConnection();
2. 定義工具類
1. 定義一個類 JDBCUtils
2. 提供靜態程式碼塊載入配置檔案,初始化連線池物件
3. 提供方法
1. 獲取連線方法:通過資料庫連線池獲取連線
2. 釋放資源
3. 獲取連線池的方法
* 程式碼:
public class JDBCUtils {
//1.定義成員變數 DataSource
private static DataSource ds ;
static{
try {
//1.載入配置檔案
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.獲取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連線
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* 釋放資源
*/
public static void close(Statement stmt,Connection conn){
/* if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();//歸還連線
} catch (SQLException e) {
e.printStackTrace();
}
}*/
close(null,stmt,conn);
}
SpringJDBC
-
Spring框架對JDBC的簡單封裝。提供了一個JDBCTemplate物件簡化JDBC的開發
-
步驟:
-
匯入jar包
-
建立JdbcTemplate物件。依賴於資料來源DataSource
JdbcTemplate template = new JdbcTemplate(ds);
- 呼叫JdbcTemplate的方法來完成CRUD的操作:
-
* update():執行DML語句。增、刪、改語句
* queryForMap():查詢結果將結果集封裝為map集合,將列名作為key,將值作為value 將這條記錄封裝為一個map集合.注意:這個方法查詢的結果集長度只能是1
* queryForList():查詢結果將結果集封裝為list集合.注意:將每一條記錄封裝為一個Map集合,再將Map集合裝載到List集合中
* query():查詢結果,將結果封裝為JavaBean物件
引數:RowMapper
一般我們使用BeanPropertyRowMapper實現類。可以完成資料到JavaBean的自動封裝
new BeanPropertyRowMapper<型別>(型別.class)
* queryForObject():查詢結果,將結果封裝為物件,一般用於聚合函式的查詢
- 練習:
- 需求:
- 修改1號資料的 salary 為 10000
- 新增一條記錄
- 刪除剛才新增的記錄
- 查詢id為1的記錄,將其封裝為Map集合
- 查詢所有記錄,將其封裝為List
- 查詢所有記錄,將其封裝為Emp物件的List集合
- 查詢總記錄數
- 需求:
-
程式碼:
public class JdbcTemplateDemo2 {
//Junit單元測試,可以讓方法獨立執行
//1. 獲取JDBCTemplate物件
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 1. 修改1號資料的 salary 為 10000
*/
@Test
public void test1(){
//2. 定義sql
String sql = "update emp set salary = 10000 where id = 1001";
//3. 執行sql
int count = template.update(sql);
System.out.println(count);
}/** * 2. 新增一條記錄 */ @Test public void test2(){ String sql = "insert into emp(id,ename,dept_id) values(?,?,?)"; int count = template.update(sql, 1015, "郭靖", 10); System.out.println(count); } /** * 3.刪除剛才新增的記錄 */ @Test public void test3(){ String sql = "delete from emp where id = ?"; int count = template.update(sql, 1015); System.out.println(count); } /** * 4.查詢id為1001的記錄,將其封裝為Map集合 * 注意:這個方法查詢的結果集長度只能是1 */ @Test public void test4(){ String sql = "select * from emp where id = ? or id = ?"; Map<String, Object> map = template.queryForMap(sql, 1001,1002); System.out.println(map); //{id=1001, ename=孫悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20} } /** * 5. 查詢所有記錄,將其封裝為List */ @Test public void test5(){ String sql = "select * from emp"; List<Map<String, Object>> list = template.queryForList(sql); for (Map<String, Object> stringObjectMap : list) { System.out.println(stringObjectMap); } } /** * 6. 查詢所有記錄,將其封裝為Emp物件的List集合 */ @Test public void test6(){ String sql = "select * from emp"; List<Emp> list = template.query(sql, new RowMapper<Emp>() { @Override public Emp mapRow(ResultSet rs, int i) throws SQLException { Emp emp = new Emp(); int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); double salary = rs.getDouble("salary"); double bonus = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); emp.setId(id); emp.setEname(ename); emp.setJob_id(job_id); emp.setMgr(mgr); emp.setJoindate(joindate); emp.setSalary(salary); emp.setBonus(bonus); emp.setDept_id(dept_id); return emp; } }); for (Emp emp : list) { System.out.println(emp); } } /** * 6. 查詢所有記錄,將其封裝為Emp物件的List集合 */ @Test public void test6_2(){ String sql = "select * from emp"; List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class)); for (Emp emp : list) { System.out.println(emp); } } /** * 7. 查詢總記錄數 */ @Test public void test7(){ String sql = "select count(id) from emp"; Long total = template.queryForObject(sql, Long.class); System.out.println(total); } }