JavaEEDay34數據庫
@toc
筆記總結:2019-8-1
首先是用戶管理,即新建用戶並授予權限以及刪除用戶;
然後介紹事務;
接着介紹數據庫數據導入和導出的方法;
最後介紹使用 Java 連接數據庫;
- 第一個代碼是連接數據庫的方式一;
- 第二個代碼是連接數據庫和釋放資源的方式二:
- 下面的代碼是基於第二種連接數據庫的方式進行增刪改查操作;
最最後介紹了一種防止 SQL 注入的方式:PreparedStatement
一、DCL 操作:
一般是項目經理或者 DBA 進行管理;
即管理用戶:創建用戶,給予用戶操作數據的權限;
二、用戶管理:
- 創建一個用戶,用戶名爲 zhangsan 所在數據庫的主機地址爲 localhost,初始化密碼:12345:
create user "zhangsan"@"localhost" identified by "12345";
- 授予新用戶權限,上面的 localhost 也可以是一個 IP 地址;
grant all on hello.* to "zhangsan" @"localhost";
//將 hello 數據庫賦值權限給 zhangsan; - 刷新權限:
flush privileges;
至此完成新用戶管理; - 取消授權:
revoke all on hello.* from "zhangsan" @ "localhost";
- 刪除用戶:
drop user "zhangsan" @ "localhost";
三、 DTL 事務處理
當出現操作錯誤的時候,給予回退的機會;
- 以銀行轉賬爲例,首先創建數據表:
create table bank(
userID tinyint not null primary key auto_increment,
money double(15,2),
name varchar(30)
);
- 插入部分數據,用於測試:
insert into bank(money,name) values(66,"zhangsan");
insert into bank(money,name) values(11,"lisi");
- 開啓事務:一共兩種方式:開始事務是將這個表加載在內存中進行處理,沒有改變原來結構
- 方式一:
start transaction;
- 方式二:
set autocommit = 0;
- 方式一:
start transaction;
- 然後開始進行操作:
update bank set money = money - 55 where userID = 1;
update bank set money = money + 55 where userID = 2;
- 操作完成之後,兩種確認方式:
- 回滾,即操作中出現了異常,可以回滾到開始事務之前;命令爲:
rollback;
- 提交,即確認操作無誤,提交開始事務之後的所有操作;命令爲:
commit;
- 回滾,即操作中出現了異常,可以回滾到開始事務之前;命令爲:
四、數據庫導入導出
使用命令行導入導出數據庫,必須是沒有登錄數據庫的狀態下;
(一)導出數據庫:
導出的是一個.sql 文件,裏面包含了所有的數據庫操作信息,包括創建數據庫和插入數據;
在未登錄數據庫的狀態下操作以下語句:
mysqldump -uroot -p hello > hello.sql;
將 hello 數據庫導出保存爲 hello.sql 文件;
注意: 導出文件的保存位置爲當前操作的命令的文件目錄下,可以通過先修改操作命令的目錄進行保存位置的修改;
(二)導入數據庫:
前提: 必須在數據庫中創建一個數據庫用於接收導入的數據庫,然後退出數據庫,操作以下語句:
mysql -uroot -p receiveHello < hello.sql;
將 hello.sql 數據庫導入到 receiveHello 數據庫中;
五、Java 連接數據庫方式
JDBC:Java Database Connectivity 操作數據庫的規範;
JDBC 主要通過接口實現,組成 JDBC 中兩個包:java.sql 和 javax.sql,以上兩個包是 JavaSE 中包含的,但是需要導入 JDBC 的實現類纔可以使用,該實現類是由第三方數據庫提供商完成;
JDBC 主要的接口和類:
- Driver 接口:連接數據庫的驅動 API;
- DriverManager 類:驅動管理類,負責驅動的註冊(加載),獲取數據庫連接;
- Statement 接口:負責 SQL 語句的執行;
- PreparedStatement 接口:負責 SQL 語句的預處理;
- ResultSet 接口:處理查詢數據庫的結果集;
(一)通過 JDBC 連接 MySQL
-
需要一:確定數據庫的 URL;
- 例如:
jdbc:mysql://localhost:3306/hello
協議:子協議://IP:端口號/數據庫名?參數- 協議:JDBC 總協議;
- 子協議:目前使用的是連接 MySQL 數據庫的協議;
- IP:是數據庫服務器的 IP 地址,localhost 表示本機的 IP 地址;
- 端口號:3306 MySQL 默認的端口號,可以修改;
- 數據庫名:目前連接的操作的數據庫是哪一個;
- 參數:通常爲:
useUnicode = true
characterEncoding = utf-8
;
- 例如:
-
需要二:連接數據庫需要用戶名和密碼:
具體的連接操作方式:
-
方式一:直接在代碼中寫入要操作的數據庫以及用戶名和密碼信息,但是代碼不可複用;
- 步一:通過
Class.forName(“com.mysql.jdbc.cj.Driver”);
註冊驅動; - 步二:準備 URL:
jdbc:mysql://數據庫IP:3306/數據庫名 ? serverTimezone = GMT%2B8
; - 步三:通過 DriverManager 連接對象:即調用:
DriverManager.getConnection(url, 用戶名, 密碼)
; - 步四:關閉數據庫:
連接對象.close()
;
- 步一:通過
-
方式二:使用.properties 文件
- 步一:建立.properties 文件,內容爲:
driver = com.mysql.jdbc.cj.Driver
user = 用戶名
password = 密碼
url = jdbc:mysql://IP:3306/數據庫名 ? serverTimezone = GMT%2B8
-
步二:建立 JdbcUtil.java 類,裏面包含數據庫連接和關閉的方式
- 準備工作:
- 步一:讀取配置文件,即.properties 文件:
Properties p = new Properties();
並將其加入到內存之中:InputStream is = new FileInputStream(文件路徑)
- 步二:利用 Properties 裏面的 load 方法:
p.load(is)
- 步三:通過 properties 類對象獲取想要的數據:
p.getProperty("url");等等
- 步四:加載類文件:
Class.forName(driver)
- 步五:關閉資源:在 finally 裏面加入:
is.close();
- 進行連接:
- 步一:建立新的連接方法:
getConnection(),返回值爲Connection類型
- 步二:建立連接:
DriveManager.getConnection(url, user, password)
- 步三:返回建立的連接:
return connection;
- 步一:建立新的連接方法:
- 關閉連接:
- 類一:沒有結果集的關閉(針對:增、刪、改操作)
- 步一:新建的關閉方法中參數爲 Connection 和 Statement 類型;
- 步二:逐個關閉資源:先關閉 statement,後關閉 Connection;
- 類二:含有結果集的關閉(針對:查操作)
- 步一:方法中的參數爲:Connection、Statement、ResultSet 類型;
- 步二:逐個關閉資源:順序爲:Statement、Connection、ResultSet
- 類一:沒有結果集的關閉(針對:增、刪、改操作)
-
具體實現類:(以創建數據表和查詢數據表爲例)
- 創建表格:同刪除、修改操作,僅僅是 SQL 語句不同
- 步一:通過已經創建好的工具類創建數據庫連接對象:
Connection connection = jdbcUtil.getConnection()
- 步二:獲取 Statement(SQL 語句運輸者):
Statement statement = connection.createStatement();
- 步三:準備 SQL 語句,語句的最後不需要分號:
String sql = "";
- 步四: 通過 statement 執行 SQL 語句:
statement.executeUpdate(sql);
- 步五:關閉資源:
JdbcUtil.closeConnection(conn.., sta);
- 步一:通過已經創建好的工具類創建數據庫連接對象:
- 查詢信息:即查操作
- 步一:連接數據庫,同上;
- 步二:獲取 Statement;
- 步三:準備 SQL 語句;
- 步四:通過 statement 執行 SQL 語句:
ResultSet set = statement.executeQuery(sql);
- 步五:關閉資源:
JdbcUtil.closeConnectionWithResult(con.., state.., set)
- 創建表格:同刪除、修改操作,僅僅是 SQL 語句不同
下面代碼中: Class.forName("com.mysql.jdbc.Driver");
這句話首先會加載com.mysql.jdbc.Driver類文件到內存當中,而在這個類文件中有一下這段代碼
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
這個代碼塊是一個靜態代碼塊,會在類文件加載到內存時,直接運行
而在這個靜態代碼塊中,完成了以下這些事情:
1. 創建的MySQL連接的Java程序的JDBC.Driver對象
2. 將這個創建的Driver對象,註冊到java.sql.DriverManager裏面
這樣做到好處:
簡化代碼的邏輯,提高效率
package jdbc.connection;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author GJXAIOU
* @create 2019-08-01-14:14
*/
public class JdbcConnection {
@Test
public void connection() {
// 1.註冊驅動 JDBC 連接MySQL
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.準備url,是JDBC所要連接MySQL數據庫的URL,後面參數爲設置時區
String url = "jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8" ;
// 3.通過DriverManager連接對象
Connection con = null;
con = DriverManager.getConnection(url, "root", "GJXAIOU");
System.out.println(con);
// 4.關閉數據庫連接,釋放資源
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
程序運行結果:com.mysql.cj.jdbc.ConnectionImpl@15761df8
- 方式二:將數據庫的具體信息寫入:.properties 文件中,然後使用 Java 代碼讀取文件中信息,更改數據庫信息只要在.properties 文件中修改就行;
db.properties
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/day34jdbc?serverTimezone = GMT%2B8
user = root
password = GJXAIOU
這裏 URL 後面對應的重要參數見下面:不同參數之間用 &
進行分割
參數名稱 | 參數說明 | 缺省值 | 最低版本要求 |
---|---|---|---|
user | 數據庫用戶名(用於連接數據庫) | 所有版本 | |
password | 用戶密碼(用於連接數據庫) | 所有版本 | |
useUnicode | 是否使用Unicode字符集,如果參數characterEncoding設置爲gb2312或gbk,本參數值必須設置爲true | false | 1.1g |
characterEncoding | 當useUnicode設置爲true時,指定字符編碼。比如可設置爲gb2312或gbk | false | 1.1g |
autoReconnect | 當數據庫連接異常中斷時,是否自動重新連接? | false | 1.1 |
autoReconnectForPools | 是否使用針對數據庫連接池的重連策略 | false | 3.1.3 |
failOverReadOnly | 自動重連成功後,連接是否設置爲只讀? | true | 3.0.12 |
maxReconnects | autoReconnect設置爲true時,重試連接的次數 | 3 | 1.1 |
initialTimeout | autoReconnect設置爲true時,兩次重連之間的時間間隔,單位:秒 | 2 | 1.1 |
connectTimeout | 和數據庫服務器建立socket連接時的超時,單位:毫秒。 0表示永不超時,適用於JDK 1.4及更高版本 | 0 | 3.0.1 |
socketTimeout | socket操作(讀寫)超時,單位:毫秒。 0表示永不超時 | 0 | 3.0.1 |
JdbcUtil.java 數據庫連接自定義工具類
- 自定義JDBC工具類
- 加載驅動
- 獲取連接對象
- 關閉連接
把連接數據庫需要的信息,都保存在一個文件中,這個文件是一個properties文件
package jdbc.connection;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**實現數據庫連接和資源關閉操作
* @author GJXAIOU
* @create 2019-08-01-15:16
*/
public class JdbcUtil {
private static String url = null;
private static String user = null;
private static String password = null;
private static String driver = null;
private static InputStream inputStream = null;
// 這裏利用靜態代碼塊的特徵,在類文件加載到內存的時候,
// 就會執行在靜態代碼塊中的代碼
static {
// 1.讀取配置文件信息,即讀取properties文件
Properties properties = new Properties();
// 如果一個properties文件加載到內存中,需要藉助IO流;
try {
inputStream = new FileInputStream(
"E:\\Program\\Java\\Project\\VideoClass\\JavaEEDay34" +
"\\src\\jdbc\\connection\\db.properties");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 2.利用properties裏面的load方法
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
// 3.可以通過properties類對象,獲取想要的數據
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
// 4.加載類文件
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("驅動加載失敗");
}finally {
// 關閉文件連接
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 獲取數據庫連接對象
* @return Connection對象
*/
public static Connection getConnection(){
Connection connection = null;
try {
connection = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
/**
* 關閉數據庫連接,釋放statement
* @param connection 數據庫連接對象
* @param statement statement對象
*/
public static void closeConnection(Connection connection, Statement statement){
try {
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
}catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 關閉帶有結果集的查詢語句資源
* @param connection 數據庫連接對象
* @param statement statement 對象
* @param set 結果集
*/
public static void closeConnectionWithResult(Connection connection, Statement statement, ResultSet set){
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (set != null){
try {
set.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
jdbcRealizationTest.java
實現常見的增刪改查操作:
package jdbc.connection;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**使用JdbcUtil工具類創建並操作數據表
* @author GJXAIOU
* @create 2019-08-01-16:07
*/
public class JdbcRealizationTest {
/**
* 通過JDBC創建數據表
*/
@Test
public void createTable() {
// 1.通過已經封裝好的JdbcUtil工具類,獲取數據庫的連接對象
Connection connection = JdbcUtil.getConnection();
try {
// 2.獲取Statement,即SQL語句的運輸者,作用是將SQL語句運輸到MySQL中,讓MySQL運行
Statement statement = connection.createStatement();
// 3.準備好SQL語句,語句最後不用分號
String sql = "create table person(" +
" id tinyint not null primary key auto_increment," +
" name char(5) not null," +
" gender char(1)," +
" score decimal(4, 2)," +
" home enum(\"江蘇\", \"上海\", \"杭州\", \"蘇州\")," +
" hobby set(\"游泳\", \"打球\", \"跑步\"))";
// 4.通過statement執行SQL語句
int count = statement.executeUpdate(sql);
// 5.查看創建的結果
System.out.println("影響的行數" + count);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 使用statement執行DML語句-- insert into
*/
@Test
public void testInsert(){
// 1.建立數據庫連接
Connection connection = JdbcUtil.getConnection();
Statement statement = null;
try {
// 2.獲取到Statement
statement = connection.createStatement();
// 3.準備SQL語句
String sql1 = "insert into person(name, gender, score, home, hobby) values(\"張三\", \"男\", 98.23, 2, 3)";
String sql2 = "insert into person(name, gender, score, home, hobby) values(\"李四五\", \"女\",99.00,\"江蘇\", \"游泳,打球\")";
// 4.通過statement執行SQL語句
int count1 = statement.executeUpdate(sql1);
int count2 = statement.executeUpdate(sql2);
// 5.打印影響的行數
System.out.println("改變的行數:" + (count1 + count2));
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 6.關閉所有資源:connection 是連接數據庫的資源,statement是引用程序到MySQL之間的SQL語句運輸者,都是資源
JdbcUtil.closeConnection(connection, statement);
}
}
@Test
/**
* 使用statement刪除數據庫中一條數據
*/
public void testDelete() {
// 1.建立數據庫連接
Connection connection = JdbcUtil.getConnection();
Statement statement = null;
try {
// 2.獲取到statement
statement = connection.createStatement();
// 3.準備SQL語句
String sql = "delete from person where id = 1";
// 4.使用statement執行SQL語句
int count = statement.executeUpdate(sql);
// 5.輸出影響的行數
System.out.println("改變的行數:" + count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 6.關閉所有資源
JdbcUtil.closeConnection(connection, statement);
}
}
/**
* 使用statement修改數據庫中一條數據
*/
public void testUpdate(){
// 1.建立數據庫連接
Connection connection = JdbcUtil.getConnection();
Statement statement = null;
try {
// 2.獲取到statement
statement = connection.createStatement();
// 3.準備SQL語句
String sql = "update person set name = '李四' where id = 2";
// 4.使用statement執行SQL語句
int count = statement.executeUpdate(sql);
// 5.輸出影響的行數
System.out.println("改變的行數" + count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 6.關閉所有的資源
JdbcUtil.closeConnection(connection, statement);
}
}
/**
* 使用statement查詢數據庫中數據並返回結果集set
*/
@Test
public void testSelect(){
// 1.連接數據庫
Connection connection = JdbcUtil.getConnection();
Statement statement = null;
// 查詢語句返回的結果集對象
ResultSet set = null;
try {
// 2.獲取到statement
statement = connection.createStatement();
// 3.準備SQL語句
String sql = "select * from person";
// 4.通過statement執行SQL語句, 獲得查詢結果集
set = statement.executeQuery(sql);
// 5.輸出影響的行數
while(set.next()){
int id = set.getInt("id");
String name = set.getString("name");
String gender = set.getString("gender");
BigDecimal score = set.getBigDecimal("score");
String home = set.getString("home");
String hobby = set.getString("hobby");
System.out.println("id :" + id + " name :" + name + " gender :" + gender +
" socore :" + score + " home :" + home + " hobby :" + hobby);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtil.closeConnectionWithResult(connection, statement, set);
}
}
}
第一個 Test 結果:影響的行數0
第二個 Test 結果:改變的行數:2
第三個 Test 結果:改變的行數:1
第四個 Test 結果:id :2 name :李四五 gender :女 socore :99.00 home :江蘇 hobby :游泳,打球
上面的 set.next()方法使用說明: 該 next()方法屬於 RestltSet 中
boolean next()
throws [SQLException](../../java/sql/SQLException.html "class in java.sql")
將光標從當前位置向前移動一行。 `ResultSet`光標最初位於第一行之前; 第一次調用方法`next`使第一行成爲當前行; 第二個調用使第二行成爲當前行,依此類推。
當調用`next`方法返回`false`時,光標位於最後一行之後。 任何調用需要當前行的`ResultSet`方法將導致拋出`SQLException` 。 如果結果集類型爲`TYPE_FORWARD_ONLY` ,這是他們指定的JDBC驅動程序實現是否會返回供應商`false`或拋出一個`SQLException`上的後續調用`next` 。
如果當前行的輸入流已打開,則對方法`next`的調用將隱式關閉它。 當讀取新行時, `ResultSet`對象的警告鏈將被清除。
結果
`true`如果新的當前行有效; `false`如果沒有更多的行
異常
`[SQLException](../../java/sql/SQLException.html "class in java.sql")` - 如果發生數據庫訪問錯誤,或者在關閉的結果集上調用此方法
(二)上面總結:JDBC 核心 API
-
Driver接口:
- connect(url, propertie);
- url: JDBC連接數據庫(目前爲 MySQL)URL
標準格式爲:jdbc:mysql://localhost:3306/javaee1707?useSSL=true
- propertie:
連接數據庫的屬性,主要包含的是數據庫的用戶名和密碼
- url: JDBC連接數據庫(目前爲 MySQL)URL
- connect(url, propertie);
-
DriverManager類:是驅動管理類,用於管理【加載/註冊】過的驅動程序
- registerDriver(driver); 註冊驅動程序
- Connection getConnection(url, user, password);返回值是獲取一個數據庫的連接對象,需要的參數是存在JDBC協議的URL, 數據庫用戶名 和 密碼
-
Connection接口:
- Statement createStament(); 創建一個Statement的實現類對象(因爲 Statement 是接口)
- PreparedStatement preparedStatement(String sql); 獲取到一個PreparedStatement SQL語句預處理對象
- CallableStatmenet preparedCall(String sql); 瞭解
-
Statement接口:
- int executeUpdate(String sql); 執行給定的SQL語句,通常用來執行DDL,DML,返回影響數據的行數
- ResultSet executeQuery(String sql); 執行給定的SQL語句 DQL 查詢語句,返回數據結果集
-
PreparedStatement接口:
- int executeUpdate(); 執行預處理的SQL語句,通常用來執行DDL,DML,返回影響數據的行數
- ResultSet executeQuery(); 執行預處理的SQL語句 DQL 查詢語句,返回數據結果集
預處理有利於防止 SQL注入
-
ResultSet接口: 查詢語句的數據結果集:
- boolean next(); 得到當前數據行,並且光標指向下一個數據行,如果沒有數據行,返回false
- getXXX(String “字段名”); 獲取指定數據類型的字段數據
使用 PreparedStatement 防止 SQL 注入代碼:
package jdbc.connection;
import org.junit.jupiter.api.Test;
import java.sql.*;
/**使用preparedStatement防止SQL注入,因爲在獲取preparedStatement時候,已經對SQL語句進行了預處理
* @author GJXAIOU
* @create 2019-08-01-19:19
*/
public class PreparedStatementTest {
@Test
public void loginInTest(){
// 1.建立連接
Connection connection = null;
connection= JdbcUtil.getConnection();
// 2.準備預處理SQL語句,其中?爲佔位符,且順序從1開始
String sql = "select * from person where id = ? and name = ?";
// 3.獲取preparedStatement對象
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
} catch (SQLException e) {
e.printStackTrace();
}
// 4.準備參數
try {
preparedStatement.setInt(1,2);
preparedStatement.setString(2,"李四五");
} catch (SQLException e) {
e.printStackTrace();
}
// 5.準備接收查詢結果
ResultSet set = null;
try {
set = preparedStatement.executeQuery();
System.out.println(set);
} catch (SQLException e) {
e.printStackTrace();
}
// 6.判斷輸入的參數在數據表中有沒有
try {
if (set.next()){
System.out.println("登錄成功");
}else{
System.out.println("登錄失敗");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtil.closeConnectionWithResult(connection, preparedStatement, set);
}
}
}
程序輸出的結果:
com.mysql.cj.jdbc.result.ResultSetImpl@23529fee
登錄成功
此時對應的數據庫中數據爲:
mysql> select * from person;
+----+-----------+--------+-------+--------+---------------+
| id | name | gender | score | home | hobby |
+----+-----------+--------+-------+--------+---------------+
| 2 | 李四五 | 女 | 99.00 | 江蘇 | 游泳,打球 |
+----+-----------+--------+-------+--------+---------------+