JDBC(Java Database Connectivity,Java數據庫連接)是一種用於連接多種數據庫的標準工具,使Java語言通過該接口可以連接到Oracle、MySQL,SQL server等多種不同數據庫。
1、使用步驟
1、導入JDBC驅動
在Java中使用jdbc連接MySQL數據庫需要導入jdbc驅動,從MySQL官網https://dev.mysql.com/downloads/connector/j/下載驅動如下:
解壓zip文件得到mysql-connector-java的jar文件,將該文件粘貼到項目的lib文件夾下,並將lib文件添加到依賴(在IDEA中右擊lib文件夾,選擇Add as Library)。也可以將jar包粘貼到lib目錄下,然後右鍵選擇Add as Library,添加到項目依賴,添加之後文件左側會有一個箭頭
2、連接MySQL
接下來在代碼中使用jdbc連接數據庫並獲取student表中的內容:
import java.sql.*;
public class MysqlConnector {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
String user="root";
String password="123456";
Connection connection= null;
Statement statement=null;
ResultSet res=null;
try {
//1、註冊驅動
Class.forName("com.mysql.cj.jdbc.Driver");
//2、獲取連接對象
connection = DriverManager.getConnection(url,user,password);
//3、創建statement對象
statement=connection.createStatement();
//4、執行SQL語句
String sql="select * from student";
ResultSet res=statement.executeQuery(sql);
//5、處理結果集
while(res.next()) {
String name=res.getString("Name");
int age=res.getInt("Age");
System.out.println("姓名:"+name+",年齡:"+age);
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
//6、釋放資源
try {
res.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
其中在獲取connection對象時要傳入url、用戶名和密碼三個參數,例如:url爲jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai,其中jdbc爲固定,mysql要連接數據庫名稱,localhost:3306爲數據庫的地址和端口號,test爲選擇其中的Database數據庫名,?後面爲指定的連接參數,這裏對連接的時區進行了設置,還可以指定字符集、編碼方式等。
如果執行數據庫增刪改操作,使用execute()方法,如果執行查詢操作,使用executeQuery(),並且會返回一個結果集對象ResultSet
結果集的next()方法可以將光標移動到下一條記錄,在每條記錄中可以通過getXxx("fieldname")方法獲取對應l類型的數據,括號中傳入字段名作爲參數。
3、通過預處理防注入
由於數據庫在查詢時可能收到注入攻擊,可以通過預處理對象來執行數據庫語句防止注入攻擊。首先在sql語句中將需要查詢的參數用佔位符?代替,之後創建PreparedStatement對象,在利用setXxx()方法將參數傳入,最後再執行即可。
//利用?填充並創建Preparedstatement對象
String sql="select * from student where name=? and password=?";
PreparedStatement pst=conn.prepareStatement(sql);
//得到用戶輸入的查詢內容並填充到statement
String name=scanner.next();
String pwd=scanner.next();
pst.setString(1, name); // 將name變量填充到第1個?的位置
pst.setString(2, pwd);
//執行sql語句並得到結果集
ResultSet res= pst.executeQuery();
2、調用存儲過程
MySQL數據中可以把常用的操作定義爲存儲過程,這樣可以更加方便地執行一些常用sql語句。
存儲操作過程可以傳入參數(入參),也可以將返回的結果當作參數傳出(出參),如下所示創建一個帶入參的存儲過程
CREATE PROCEDURE sp_query_name(IN sp_id INT)
BEGIN
IF sp_id = 0 THEN
SELECT * FROM student;
ELSE
SELECT * FROM student WHERE id=sp_id;
END IF;
END;
直接在控制檯調用該過程:CALL sp_query_name(1001);
通過jdbc調用時創建CallableStatement對象執行sql語句並獲取查詢結果集
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1、獲取連接對象
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
String user="root";
String password="123456";
Connection connection= DriverManager.getConnection(url,user,password);
//2、創建CallableStatement對象
CallableStatement callableStatement=connection.prepareCall("call sp_query_name(?)");
//3、傳入參數並執行存儲過程
callableStatement.setInt(1,0);
callableStatement.execute();
//4、獲取結果集並顯示
ResultSet res=callableStatement.getResultSet();
while (res.next()){
String name=res.getString("Name");
int age=res.getInt("Age");
System.out.println("姓名:"+name+",年齡:"+age);
}
}
出參存儲過程將結果保存在傳入的參數中返回,例如統計student表中學生數量,將結果保存在傳入變量num中
#創建存儲過程
CREATE PROCEDURE sp_count(OUT num INT)
BEGIN
SELECT count(*) INTO num FROM student;
END;
#調用出參過程並顯示參數值
call sp_count(@stu_num);
select @stu_num;
通過JDBC調用存儲過程如下,不同的是調用時不傳入參數,而是註冊參數的類型,然後在執行結束後通過getXxx()獲取返回的傳出參數
//1、獲取連接對象
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//2、創建CallableStatement對象
CallableStatement callableStatement = connection.prepareCall("call sp_count(?)");
//3、註冊參數並執行
callableStatement.registerOutParameter(1, Types.INTEGER);
callableStatement.execute();
//4、獲取傳出參數
int studentNum = callableStatement.getInt(1);
System.out.println("學生人數:" + studentNum);
3、使用事務
數據庫的事務(Transaction)是作爲單個邏輯單元執行的一系列操作,這些操作作爲一個整體向系統提交,要麼都執行,要麼都不執行。事務具有原子性、一致性、隔離性(併發事務隔離互斥訪問)、永久性(對數據庫內容永久地修改)。
例如在一個銀行轉賬過程,賬戶A將100元轉給賬戶B,需要將賬戶A-100並且將賬戶B+100,如果在賬戶A-100之後出錯,那麼賬戶B沒有增加,這100元就憑空消失了。因此這兩個步驟需要作爲一個整體來執行。首先需要將數據庫的自動提交關閉,在執行相關操作之後再手動提交事務,反之如果執行過程中出錯,則需要回滾事務,撤銷之前的操作。
connection.setAutoCommit(false); //關閉自動提交
try{ //執行一個完整的事務操作
//賬戶A-100
//賬戶B+100
connection.commit(); //提交事務
}catch(Exception e){ //如果在執行中報錯
connection.rollback(); //回滾事務
}
4、JDBC連接池
之前採用的數據庫連接方式都是使用JDBC直接連接MySQL的,如果建立的連接數量過多會導致數據庫崩潰,因此可以採用連接池來管理與數據庫之間的連接數量。當一個連接請求到達時,從連接池中取出一個連接,當連接數量過多連接池中的連接分配完時,其他新到達的連接請求則需要等待。常用的兩個連接池爲dbcp和c3p0。
使用dbcp首先需要導入相關jar包,並且需要在項目根目錄下增加配置文件,通過apache官網下載dbcp所需的jar包,將下載後的內容解壓到項目lib文件夾下
dbcp.jar:http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
logging.jar:http://commons.apache.org/proper/commons-logging/download_logging.cgi
pool.jar:http://commons.apache.org/proper/commons-pool/download_pool.cgi
在項目目錄下新建resources/dbcp.properties文件用於保存dbcp的相關配置,其內容如下:
#驅動類名
driverClassName=com.mysql.jdbc.Driver
#連接到的數據庫url
url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
#用戶名
username=root
#密碼
password=123456
#線程池同一時間分配最大連接數,如果爲負數,代表不限制
maxActive=30
#線程池中允許最大的保持空閒連接數量,超過的將被釋放
maxIdle=10
#最大等待時間,等待連接池的最長時間,超出則拋出異常
maxWait=1000
#連接池啓動時創建的鏈接數
initialSize=1
新建一個DbcpUtils工具類,並通過getConnection()返回Dbcp的連接對象
public class DbcpUtils {
private static DataSource DS;
private static final String configFile = "resources/dbcp.properties";
//構造方法中初始化DBCP數據源
public DbcpUtils() throws Exception {
//讀取properties文件
Properties properties = new Properties();
FileInputStream inputStream = new FileInputStream(configFile);
properties.load(inputStream);
//創建數據源
DS = BasicDataSourceFactory.createDataSource(properties);
}
//返回連接對象
public Connection getConnection() {
Connection con = null;
try {
con = DS.getConnection();
con.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
}
在主函數中測試Dbcp,通過調用getConnection()得到一個Connection對象,之後就正常操作數據庫一樣了
public static void main(String[] args) throws Exception {
//新建Dbcp對象並返回一個連接對象
DbcpUtils db=new DbcpUtils();
Connection con=db.getConnection();
//執行SQL操作並查看結果
Statement statement=con.createStatement();
String sql="select * from student";
ResultSet res=statement.executeQuery(sql);
while(res.next()) {
String name=res.getString("Name");
int age=res.getInt("Age");
System.out.println("姓名:"+name+",年齡:"+age);
}
}
同理使用c3p0也需要先導入兩個jar包
c3p0:https://www.mvnjar.com/com.mchange/c3p0/0.9.5.1/detail.html
mchange-commons:https://www.mvnjar.com/com.mchange/mchange-commons-java/0.2.16/detail.html
然後在resources下創建c3p0.properties文件
c3p0.driverClass = com.mysql.jdbc.Driver
c3p0.jdbcUrl = jdbc:mysql://localhost:3306/test
c3p0.user = root
c3p0.password = 123456
c3p0.maxPoolSize = 20
c3p0.minPoolSize = 3
c3p0.maxStatements = 30
c3p0.maxIdleTime = 150
最後與dbcp不同的是,c3p0將讀取properties文件、構建數據源等操作已經封裝好了,可以直接構建並獲得connection對象
//直接創建c3p0的數據源並調用getConnection()
ComboPooledDataSource c3Source=new ComboPooledDataSource();
Connection con=c3Source.getConnection();
//執行SQL操作並查看結果
Statement statement=con.createStatement();
......