JDBC(java database connectivity)
面向接口開發(fā)思想
- java數(shù)據(jù)庫連接
- java語言和數(shù)據(jù)庫之間的鏈接(java提供的api),為多種數(shù)據(jù)庫提供統(tǒng)一的訪問
- 每個數(shù)據(jù)庫內(nèi)部的操作方式都不一樣,我們不可能自己寫實(shí)現(xiàn)類操作數(shù)據(jù)庫,因?yàn)檫@是數(shù)據(jù)庫公司的內(nèi)部機(jī)密,所以每個數(shù)據(jù)庫廠商都提供一套操作自己數(shù)據(jù)庫的實(shí)現(xiàn)類給我們調(diào)用
- 鏈接mysql需要用到mysql公司提供的mysql-connector-java-5.1.39-bin.jar文件(數(shù)據(jù)庫供應(yīng)商寫好的)
- java提供數(shù)據(jù)庫操作的接口,供應(yīng)商根據(jù)接口來進(jìn)行實(shí)現(xiàn)操作數(shù)據(jù)庫,這樣,通過同一套接口,可以實(shí)現(xiàn)不同數(shù)據(jù)庫的操作,而不需要不同數(shù)據(jù)庫需要調(diào)用不同的api
- 需要加載驅(qū)動,驅(qū)動程序中的類會實(shí)現(xiàn)JDBC的接口,我們只需要學(xué)習(xí)接口就好(驅(qū)動就是接口的實(shí)現(xiàn)類)
- JDBC是接口己肮,驅(qū)動是接口的實(shí)現(xiàn)
JDBC開發(fā)步驟
- 注冊驅(qū)動:加載廠商實(shí)現(xiàn)jdbc接口的實(shí)現(xiàn)類
- 通過drivermanager(驅(qū)動管理者),調(diào)用registerDriver(Driver)
- Driver驅(qū)動接口類,對象在導(dǎo)入的驅(qū)動中
- 傳入mysql驅(qū)動程序中的Driver對象
- 獲取連接:鏈接到廠商的數(shù)據(jù)庫
- 連接的效率比較低,tcp協(xié)議(保證安全性)
- 獲得語句執(zhí)行平臺:獲取操作數(shù)據(jù)庫的實(shí)現(xiàn)類(sql語句的執(zhí)行者)
- 執(zhí)行sql語句:操作數(shù)據(jù)庫
- 處理結(jié)果:得到結(jié)果
- 釋放資源:斷開連接
- 1,2,3,6都是固定的.只有4,5是開發(fā)人員可控的
- 因?yàn)榧虞d的驅(qū)動是jar包,里面都是編譯后的class文件,點(diǎn)進(jìn)入相應(yīng)的類中是沒有源碼的,這時需要自己導(dǎo)入源碼,在下載官方的驅(qū)動包時有一個src文件夾,里面放的就是源碼,我們attach resource the src floder,如果選擇導(dǎo)入file,則源碼需要是以jar的形式提供的
import com.mysql.jdbc.Driver;
public static void main(String[] args) throws SQLException {
// 注冊驅(qū)動
DriverManager.registerDriver(new Driver());
}
- 在Driver類源碼中,調(diào)用了一段靜態(tài)代碼塊,注冊一次驅(qū)動,只要一加載類,就會調(diào)用
- 如果按照上面的寫法寫,就會注冊2次驅(qū)動,造成浪費(fèi)
- 通過反射的方式把類加載進(jìn)內(nèi)存,這樣就可以只調(diào)用一次加載驅(qū)動
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
}
- 通過 DriverManager獲取鏈接,得到的鏈接對象是在驅(qū)動包中的實(shí)現(xiàn)類
- 比較耗時,因?yàn)橥ㄟ^TCP協(xié)議創(chuàng)建連接
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的實(shí)現(xiàn)類,在mysql驅(qū)動程序
//url: 數(shù)據(jù)庫地址 jdbc:mysql://連接主機(jī)IP:端口號//數(shù)據(jù)庫名字
String url = "jdbc:mysql://localhost:3306/mybase";
//用戶名和密碼用自己的
String username="root";
String password="root";
Connection con = DriverManager.getConnection(url, username, password);
- 通過得到的連接(connection)對象,去獲取數(shù)據(jù)庫的操作對象Statement
- Statement制定int executeUpDate(string sql),僅限于執(zhí)行insert,update,delete
- 通過SQLYog拼接sql語句
- 關(guān)閉資源
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCDemo {
public static void main(String[] args) throws Exception {
//1.注冊驅(qū)動 反射技術(shù),將驅(qū)動類加入到內(nèi)容
//使用java.sql.DriverManager類靜態(tài)方法 registerDriver(Driver driver)
// Diver是一個接口,參數(shù)傳遞,MySQL驅(qū)動程序中的實(shí)現(xiàn)類
//DriverManager.registerDriver(new Driver());
//驅(qū)動類源代碼,注冊2次驅(qū)動程序
Class.forName("com.mysql.jdbc.Driver");
//2.獲得數(shù)據(jù)庫連接 DriverManager類中靜態(tài)方法
//static Connection getConnection(String url, String user, String password)
//返回值是Connection接口的實(shí)現(xiàn)類,在mysql驅(qū)動程序
//url: 數(shù)據(jù)庫地址 jdbc:mysql://連接主機(jī)IP:端口號//數(shù)據(jù)庫名字
String url = "jdbc:mysql://localhost:3306/mybase";
String user = "root";
String password = "root";
//3.獲得語句執(zhí)行平臺, 通過數(shù)據(jù)庫連接對象,獲取到SQL語句的執(zhí)行者對象
// con對象調(diào)用方法 Statement createStatement() 獲取Statement對象,將SQL語句發(fā)送到數(shù)據(jù)庫
// 返回值是 Statement接口的實(shí)現(xiàn)類對象,,在mysql驅(qū)動程序
Connection conn = DriverManager.getConnection(url,user,password);
Statement statement = conn.createStatement();
//4.執(zhí)行sql語句
// 通過執(zhí)行者對象調(diào)用方法執(zhí)行SQL語句,獲取結(jié)果
// int executeUpdate(String sql) 執(zhí)行數(shù)據(jù)庫中的SQL語句, insert delete update
// 返回值int,操作成功數(shù)據(jù)表多少行
int rows = statement.executeUpdate("INSERT INTO sort(sname,sprice,sdesc) VALUES('xx',50, 'xx');");
System.out.println(rows);
//6.釋放資源 一堆close()
statement.close();
conn.close();
}
}
public class JDBCDemo1 {
public static void main(String[] args) throws Exception{
//1. 注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//2. 獲取連接對象
String url = "jdbc:mysql://localhost:3296/mybase";
String username="root";
String password="123";
Connection con = DriverManager.getConnection(url, username, password);
//3 .獲取執(zhí)行SQL 語句對象
Statement stat = con.createStatement();
// 拼寫查詢的SQL
String sql = "SELECT * FROM sort";
//4. 調(diào)用執(zhí)行者對象方法,執(zhí)行SQL語句獲取結(jié)果集
// ResultSet executeQuery(String sql) 執(zhí)行SQL語句中的select查詢
// 返回值ResultSet接口的實(shí)現(xiàn)類對象,實(shí)現(xiàn)類在mysql驅(qū)動中
ResultSet rs = stat.executeQuery(sql);
//5 .處理結(jié)果集
// ResultSet接口方法 boolean next() 返回true,有結(jié)果集,返回false沒有結(jié)果集
while(rs.next()){
//獲取每列數(shù)據(jù),使用是ResultSet接口的方法 getXX方法參數(shù)中,建議寫String列名
System.out.println(rs.getInt("sid")+" "+rs.getString("sname")+
" "+rs.getDouble("sprice")+" "+rs.getString("sdesc"));
}
rs.close();
stat.close();
con.close();
}
}
sql注入
- 登陸查詢
- SELECT * FROM 用戶表 WHERE NAME = 用戶輸入的用戶名 AND PASSWORD = 用戶輸?shù)拿艽a
- 此時抱冷,當(dāng)用戶輸入正確的賬號與密碼后胳蛮,查詢到了信息則讓用戶登錄
- 但是當(dāng)用戶輸入的賬號為XXX 密碼為:XXX’ OR ‘a(chǎn)’=’a時柒爵,則真正執(zhí)行的代碼變?yōu)?/li>
- SELECT * FROM 用戶表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’
- 此時,上述查詢語句時永遠(yuǎn)可以查詢出結(jié)果的。那么用戶就直接登錄成功了,顯然我們不希望看到這樣的結(jié)果,這便是SQL注入問題
select * from tbl_user where usr_name='xx' and usr_pwd='xx';
select * from tbl_user where usr_name='xx' and usr_pwd='xx or 1=1';
預(yù)編譯sql語句
- PrepareStatement接口預(yù)編譯SQL語句
# 使用PreparedStatement預(yù)處理對象時抖所,建議每條sql語句所有的實(shí)際參數(shù),都使用逗號分隔痕囱。
# String sql = "insert into sort(sid,sname) values(?,?)";;
# PreparedStatement預(yù)處理對象代碼:
# PreparedStatement psmt = conn.prepareStatement(sql)
//執(zhí)行SQL語句,數(shù)據(jù)表,查詢用戶名和密碼,如果存在,登錄成功,不存在登錄失敗
String sql = "SELECT * FROM users WHERE username=? AND PASSWORD=?";
//調(diào)用Connection接口的方法prepareStatement,獲取PrepareStatement接口的實(shí)現(xiàn)類
//方法中參數(shù),SQL語句中的參數(shù)全部采用問號占位符
PreparedStatement pst = con.prepareStatement(sql);
System.out.println(pst);
//調(diào)用pst對象set方法,設(shè)置問號占位符上的參數(shù)
pst.setObject(1, "username");
pst.setObject(2, "password");
//調(diào)用方法,執(zhí)行SQL,獲取結(jié)果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
pst.close();
con.close();
JDBCUtils(JSBC工具類)
- 簡化代碼,方便返回數(shù)據(jù)庫鏈接對象Connection和關(guān)閉資源
public class JDBCUtils {
//私有構(gòu)造方法
private JDBCUtils(){}
private static Connection con ;
//加載驅(qū)動
static{
try{
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase";
String username="root";
String password="root";
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException(ex+"數(shù)據(jù)庫連接失敗");
}
}
//獲取鏈接
public static Connection getConnection(){
return con;
}
//關(guān)閉資源
public static void close(Connection con,Statement stat){
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
//關(guān)閉資源
public static void close(Connection con,Statement stat , ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch(SQLException ex){}
}
close(conn,stat);
}
}
public class TestJDBCUtils {
public static void main(String[] args)throws Exception {
Connection con = JDBCUtils.getConnection();
PreparedStatement pst = con.prepareStatement("SELECT sname FROM sort");
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("sname"));
}
JDBCUtils.close(con, pst, rs);
}
}
properties配置文件
- DBUtils類通過讀取配置文件去設(shè)定數(shù)據(jù)庫地址,數(shù)據(jù)庫用戶名密碼,驅(qū)動地址等信息
- 通常都存在配置文件中田轧,方便后期維護(hù),程序如果需要更換數(shù)據(jù)庫,只需要修改配置文件即可
- property文件建議放在src目錄下
- 假設(shè)現(xiàn)在項目開發(fā)完成了,并且測試完畢,要交付給用戶了,我們給用戶的是bin目錄下的class文件,不可能把整個工程都給用戶的(包含源碼)
- 文件的擴(kuò)展名為property
- 內(nèi)容為key=value的形式
- 如果再好一點(diǎn),會放在遠(yuǎn)程服務(wù)器上,讓其讀取配置
- property文件的編寫
properties文件的編寫(內(nèi)容如下):
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3296/mybase
username=root
password=123
//獲得當(dāng)前類的類加載器(負(fù)責(zé)加載類的對象)
ClassLoader loader = 當(dāng)前類.class.getClassLoader();
//獲取文件的流對象
InputStrea, fis = loader.getRescoreAsStream(String path);//輸入文件名
Properties pro = new Properties();
pro.load(fis);
public class PropertiesDemo {
public static void main(String[] args) throws Exception{
//使用類的加載器
InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("database.properties");
System.out.println(in);
Properties pro = new Properties();
pro.load(in);
//獲取集合中的鍵值對
String driverClass=pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
Class.forName(driverClass);
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
}
}