1.JDBC的概念
JDBC的全稱是Java Database Connectivity;
JDBC是Java連接配接資料庫的一組API,API與Java的接口部分是通用的,API與資料庫的接口部分根據資料庫的不同而不同,由廠家提供;
JDBC模仿了ODBC,一種開放的資料庫連接配接;
2.JCBC常用的接口和類簡介
- 參見page617
2.JDBC的連接配接方式
使用的jar包:mysql-connector-java-8.0.22
/*
2.JDBC程式設計步驟
(1)加載資料驅動;
(2)通過DriverManager擷取資料庫連接配接;
(3)通過Connection對象建立Statement對象;
(4)使用Statement執行SQL語句;
(5)操作結果集;
*/
import java.sql.*;
public class ConnMySQL
{
public static void main(String[] args) throws Exception
{
//加載驅動
Class.forName("com.mysql.cj.jdbc.Driver");
try (
//建立資料庫連接配接;
Connection conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/select_test?useSSL=false&serverTimezone=UTC",
"root", "123456");
//建立執行者;
Statement stmt = conn.createStatement();
//執行SQL,獲得結果;
ResultSet rs = stmt.executeQuery("select s.*, teacher_name"
+ " from student_table s, teacher_table t"
+ " where t.teacher_id = s.java_teacher"))
{
//周遊結果
while (rs.next())
{
System.out.println(rs.getInt(1) + "\t"
+ rs.getString(2) + "\t"
+ rs.getString(3) + "\t"
+ rs.getString(4));
}
}
}
}
3.使用executeUpdate()執行DDL和DML語句
/*
1.使用executeUpdate()執行DDL和DML語句
1.使用executeUpdate()執行DDL,傳回0;執行DML,傳回受影響的行數;
*/
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDDL
{
private String driver;
private String url;
private String user;
private String pass;
//initParam用于初始化上述資訊;
//Properties能夠根據配置資訊的名稱來加載配置資訊,進而簡化代碼量;
public void initParam(String paramFile)
throws Exception
{
var props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
public void createTable(String sql) throws Exception
{
Class.forName(driver);
try (
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement())
{
stmt.executeUpdate(sql);
}
}
public static void main(String[] args) throws Exception
{
var ed = new ExecuteDDL();
ed.initParam("mysql.ini");
//用executeUpdate執行DDL;
ed.createTable("create table jdbc_test "
+ "( jdbc_id int auto_increment primary key, "
+ "jdbc_name varchar(255), "
+ "jdbc_desc text);");
System.out.println("-----建表成功-----");
}
}
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDML
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile)
throws Exception
{
var props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
public int insertData(String sql) throws Exception
{
Class.forName(driver);
try (
Connection conn = DriverManager.getConnection(url,
user, pass);
Statement stmt = conn.createStatement())
{
return stmt.executeUpdate(sql);
}
}
public static void main(String[] args) throws Exception
{
var ed = new ExecuteDML();
ed.initParam("mysql.ini");
int result = ed.insertData("insert into jdbc_test(jdbc_name, jdbc_desc)"
+ "select s.student_name, t.teacher_name "
+ "from student_table s, teacher_table t "
+ "where s.java_teacher = t.teacher_id;");
System.out.println("系統中共有" + result + "條記錄受影響");
}
}
4.使用execute()執行SQL語句
/*
1.如果不知道要處理的SQL是什麼類型的(查詢或者插入),就用execute()方法,可以執行全部的SQL語句;
2.execute方法傳回是否傳回結果集的boolean值,擷取結果集,要用Statement對象的getResultSet()方法和getUpdateCount()方法;
*/
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteSQL
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception
{
var props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
public void executeSql(String sql) throws Exception
{
Class.forName(driver);
try (
Connection conn = DriverManager.getConnection(url,
user, pass);
Statement stmt = conn.createStatement())
{
//執行execute
boolean hasResultSet = stmt.execute(sql);
if (hasResultSet) //結果集存在,執行的是查詢語句
{
try (
ResultSet rs = stmt.getResultSet())
{
ResultSetMetaData rsmd = rs.getMetaData(); //ResultSetMetaData是用于分析結果集的中繼資料接口;
int columnCount = rsmd.getColumnCount(); //擷取行數
while (rs.next())
{
for (var i = 0; i < columnCount; i++)
{
System.out.print(rs.getString(i + 1) + "\t");
}
System.out.print("\n");
}
}
}
else //結果集不存在,執行的是插入或者删除語句
{
System.out.println("該SQL語句影響的記錄有"
+ stmt.getUpdateCount() + "條");
}
}
}
public static void main(String[] args) throws Exception
{
var es = new ExecuteSQL();
es.initParam("mysql.ini");
System.out.println("------執行删除表的DDL語句-----");
es.executeSql("drop table if exists my_test");
System.out.println("------ִ執行建表的DDL語句-----");
es.executeSql("create table my_test"
+ "(test_id int auto_increment primary key, "
+ "test_name varchar(255))");
System.out.println("------ִ執行插入的DML語句-----");
es.executeSql("insert into my_test(test_name) "
+ "select student_name from student_table");
System.out.println("------ִ執行查詢的語句-----");
es.executeSql("select * from my_test");
}
}
5.使用PreparedStatement執行SQL語句
/*
1.有時候執行的語句是類似的,隻是資料不同,為了能簡化代碼,可以采用占位符的形式:
insert into student_table values(null, ?, ?);
但是Statement執行不了,是以采用PreparedStatement可以執行這個語句;
2.使用PrepareStatement還可以防止SQL注入:
*/
import java.util.*;
import java.io.*;
import java.sql.*;
public class PreparedStatementTest
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception
{
var props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void insertUseStatement() throws Exception
{
long start = System.currentTimeMillis(); //記錄目前時間;
try (
Connection conn = DriverManager.getConnection(url,
user, pass);
Statement stmt = conn.createStatement())
{
for (var i = 0; i < 100; i++)
{
stmt.executeUpdate("insert into student_table values("
+ " null, '姓名" + i + "', 1)");
}
System.out.println("ʹ��Statement��ʱ:"
+ (System.currentTimeMillis() - start)); //計算花費時間;
}
}
public void insertUsePrepare() throws Exception
{
long start = System.currentTimeMillis();
try (
Connection conn = DriverManager.getConnection(url,
user, pass);
PreparedStatement pstmt = conn.prepareStatement(
"insert into student_table values(null, ?, 1)"))
{
for (var i = 0; i < 100; i++)
{
pstmt.setString(1, "姓名" + i); //設定SQL語句
pstmt.executeUpdate();
}
System.out.println("使用PrepareStatement耗時:"
+ (System.currentTimeMillis() - start));
}
}
public static void main(String[] args) throws Exception
{
var pt = new PreparedStatementTest();
pt.initParam("mysql.ini");
pt.insertUseStatement();
pt.insertUsePrepare();
}
}
6.SQL注入及防範辦法
- Sql注入是一種常見的Cracker式入侵方式,他利用SQL語句的漏洞來入侵
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.sql.*;
public class LoginFrame
{
private final String PROP_FILE = "mysql.ini";
private String driver;
private String url;
private String user;
private String pass;
private JFrame jf = new JFrame("登入");
private JTextField userField = new JTextField(20);
private JTextField passField = new JTextField(20);
private JButton loginButton = new JButton("登入");
public void init() throws Exception
{
var connProp = new Properties();
connProp.load(new FileInputStream(PROP_FILE));
driver = connProp.getProperty("driver");
url = connProp.getProperty("url");
user = connProp.getProperty("user");
pass = connProp.getProperty("pass");
Class.forName(driver);
loginButton.addActionListener(e -> {
if (validate(userField.getText(), passField.getText()))
{
JOptionPane.showMessageDialog(jf, "登入成功");
}
else
{
JOptionPane.showMessageDialog(jf, "登入失敗");
}
});
jf.add(userField, BorderLayout.NORTH);
jf.add(passField);
jf.add(loginButton, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
}
// private boolean validate(String userName, String userPass)
// {
//
// var sql = "select * from jdbc_test "
// + "where jdbc_name = '" + userName
// + "' and jdbc_desc = '" + userPass + "'";
// System.out.println(sql);
// try (
// Connection conn = DriverManager.getConnection(url, user, pass);
// Statement stmt = conn.createStatement();
// ResultSet rs = stmt.executeQuery(sql))
// {
// //假設在文本框輸入的語句為true,sql的結果集就會傳回true,造成誤判
// if (rs.next())
// {
// return true;
// }
// }
// catch (Exception e)
// {
// e.printStackTrace();
// }
// return false;
// }
//利用PreparedStatement可以避免SQL注入
private boolean validate(String userName, String userPass)
{
try (
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement pstmt = conn.prepareStatement(
"select * from jdbc_test where jdbc_name = ? and jdbc_desc = ?"))
{
pstmt.setString(1, userName);
pstmt.setString(2, userPass);
try (
ResultSet rs = pstmt.executeQuery())
{
if (rs.next())
{
return true;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
public static void main(String[] args) throws Exception
{
new LoginFrame().init();
}
}