1、獲取連接
1) 把jar包複製到項目目錄下在當前工程下創建jdbc.properties文件,其中內容如下(是連接Oracle、MySql數據庫所需要的四大參數分別是:加載驅動、獲取連接數據庫的IP及端口、用戶名,密碼),目的是方便調用和修改!
2) 把jar包加入項目的buildpath環境變量中, 使得classpath也和buildpath一致, 讓類加載器便於加載具體子類
3) 反射加載驅動程序類, 會自動註冊驅動程序4) 通過驅動程序管理器獲取連接.
#driverClass = oracle.jdbc.driver.OracleDriver
#url = jdbc:oracle:thin:@127.0.0.1:1521:orcl
#user = scott
#password = tiger
driverClass = com.mysql.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/company
user = root
password = 123456
獲取連接以及關閉連接的具體實現如下:
package com.atguigu.jdbc;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import org.junit.Test;
/**
* 獲取連接
* 1) 把jar包複製到項目目錄下
* 2) 把jar包加入項目的buildpath環境變量中, 使得classpath也和buildpath一致, 讓類加載器便於加載具體子類
* 3) 反射加載驅動程序類, 會自動註冊驅動程序
* 4) 通過驅動程序管理器獲取連接.
* @author Administrator
*
*/
public class DriverTest {
// 使用Properties類對象的getPropety方法與FileInputStream方法獲取文件中的內容,從而創建Connection對象
@Test
public void test5() throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("jdbc.properties"));
String driverClass = properties.getProperty("driverClass");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
Class.forName(driverClass); // 只需要加載類, 類的靜態語句塊就會被執行, 創建驅動程序對象,並把此對象註冊到驅動程序管理器中.
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
connection.close();
}
}
鑑於每一次jdbc的操作都需要建立連接,我們將建立連接的方法、關閉資源的方法作爲固定方法放在JdbcUtil類中,方便使用:
package com.atguigu.jdbc;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtil {
// 獲取建立連接對象
public static Connection getConnection() throws IOException,
ClassNotFoundException,
SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("jdbc.properties"));
String driverClass = properties.getProperty("driverClass");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
Class.forName(driverClass); // 只需要加載類, 類的靜態語句塊就會被執行, 創建驅動程序對象,並把此對象註冊到驅動程序管理器中.
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
// 關閉資源
public static void close(Connection connection) {
close(connection, null);
}
public static void close(Connection connection, Statement statement) {
close(connection, statement, null);
}
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Statement類
建立連接成功,現在進行與數據庫的交流,與數據庫交流需要使用到Statement類,首先我們需要建立一個User類以與Statement類中創建的在company數據庫中的的user表相對應:
package com.atguigu.jdbc;
public class User {
private String user;
private String password;
public User() {
}
public User(String user, String password) {
super();
this.user = user;
this.password = password;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [user=" + user + ", password=" + password + "]";
}
}
使用Statement執行創建user表,並插入三行數據:
package com.atguigu.jdbc;
import java.sql.Connection;
import java.sql.Statement;
import org.junit.Test;
// 使用Statement執行創建user表,並插入三行數據
public class StatementTest {
@Test
public void test1() {
Connection connection = null;
Statement statement = null;
try {
connection = JdbcUtil.getConnection();
statement = connection.createStatement();// 獲取執行體對象
// 執行SQL
// 創建user表
String sql = "create table if not exists user(user varchar(50), password varchar(100))";
int rows = statement.executeUpdate(sql); // 執行的DDL語句, 還可以執行DML
System.out.println(rows + " rows affected..");
rows = statement.executeUpdate("insert into user values('admin','admin')");
System.out.println(rows + " rows affected..");
rows = statement.executeUpdate("insert into user values('user1','user1')");
System.out.println(rows + " rows affected..");
rows = statement.executeUpdate("insert into user values('user2','123456')");
System.out.println(rows + " rows affected..");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, statement);
}
}
}
然而Statement存在弊端:不僅需要拼寫sql語句,並且存在SQL注入的問題,具體問題在下面的小例子中體現
package com.atguigu.jdbc;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Scanner;
import org.junit.Test;
import java.sql.Statement;
public class TestStatement {
// 弊端:需要拼寫sql語句,並且存在SQL注入的問題
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用戶名:");
String userName = scan.nextLine();
System.out.print("密 碼:");
String password = scan.nextLine();
String sql = "select user, password from user where user = '"
+ userName + "' and password = '" + password + "'";
System.out.println(sql);
User user = get(sql, User.class);
if(user != null){
System.out.println("登陸成功!");
}else{
System.out.println("用戶名或密碼錯誤!");
}
}
public <T> T get(String sql, Class<T> clazz) {// (sql, Customer.class)
T t = null;
Connection conn = null;
Statement stam = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
stam = conn.createStatement();
rs = stam.executeQuery(sql);
// 獲取結果集的元數據
ResultSetMetaData rsmd = rs.getMetaData();
// 獲取結果集的列數
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
// //1. 獲取列的名稱
// String columnName = rsmd.getColumnName(i+1);
// 1. 獲取列的別名
String columnName = rsmd.getColumnLabel(i + 1); // 注意:
// 獲取結果集中(相當於數據表)列的名稱(別名)
// 2. 根據列名獲取對應數據表中的數據
Object columnVal = rs.getObject(columnName);
// 3. 將數據表中得到的數據,封裝進對象
Field field = clazz.getDeclaredField(columnName); // 注意:反射根據Java中類的屬性獲取Field對象
field.setAccessible(true);
field.set(t, columnVal);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//JDBCUtils.close(rs, stam, conn);
}
return t;
}
}
此程序我們需要使用正確的用戶名,和對應的密碼才能進入登陸成功,通過上面的程序我們可知用戶名和密碼分別是:
+-------+----------+
| user | password |
+-------+----------+
| admin | admin |
| user1 | user1 |
| user2 | 123456 |
+-------+----------+
正常情況下我們登陸情況如下圖:
但是SQL高手通過如下操作就能登陸成功,這就是SQL注入的問題
PreparedStatement類
爲了防止SQL注入問題,於是我們需要使用他的子類PreparedStatement預編譯執行體,下面我們進行PreparedStatement的練習:
首先先創建Customer類
// 寫一個類Customer, 包含屬性name, gender, phone
package com.atguigu.jdbc;
public class Customer {
private String name;
private String gender;
private String phone;
public Customer(String name, int age, String gender, String phone,) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.phone = phone;
}
public Customer() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Customer [name=" + name + ", gender=" + gender + ", phone=" + phone + "]";
}
}
在測試類中使用PreparedStatment執行體對象創建相應的表, 並插入2條數據。public class PreparedStatementTest {
@Test
public void exer1() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
String sql = "create table if not exists customer(name varchar(30), gender enum('男','女') default '男', phone varchar(20))";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
JdbcUtil.close(null, preparedStatement); // 在獲取新的預編譯對象前,一定要先關閉原來的.
String sql2 = "insert into customer(name, gender, phone) values(?, ?, ?)";
preparedStatement = connection.prepareStatement(sql2); // 要想重新執行新的SQL,必須再次預編譯
preparedStatement.setString(1, "張三");
preparedStatement.setString(2, "男");
preparedStatement.setString(3, "13343493434");
int rows = preparedStatement.executeUpdate();
System.out.println(rows + " rows affected.");
preparedStatement.setString(1, "李四");
preparedStatement.setString(2, "女");
preparedStatement.setString(3, "1322243434");
rows = preparedStatement.executeUpdate();
System.out.println(rows + " rows affected.");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, preparedStatement);
}
// 並通過客戶端驗證.
}
}
在Customer類中添加屬性int age,double weight,給Customer表中添加對應的列,並添加記錄
public class PreparedStatementTest {
// 在Customer類中添加屬性int age,double weight,給Customer表中添加對應的列,並添加記錄
@Test
public void test3() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
String sql = "alter table customer add age int after name";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
JdbcUtil.close(null, preparedStatement);
sql = "alter table customer add weight double";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
JdbcUtil.close(null, preparedStatement);
sql = "insert into customer(name, age, gender, phone, weight) values (?, ?, ?, ?, ?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "王五");
preparedStatement.setInt(2, 50);
preparedStatement.setString(3, "男");
preparedStatement.setString(4, "134234234234");
preparedStatement.setDouble(5, 98.5);
int rows = preparedStatement.executeUpdate();
System.out.println(rows + " rows affected");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
JdbcUtil.close(connection, preparedStatement);
}
}
// 添加記錄
@Test
public void test4() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
String sql = "alter table customer add birthdate date";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
JdbcUtil.close(null, preparedStatement);
sql = "insert into customer(name, age, gender, phone, weight, birthdate) values (?, ?, ?, ?, ?, ?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "趙六");
preparedStatement.setInt(2, 60);
preparedStatement.setString(3, "女");
preparedStatement.setString(4, "13882342323");
preparedStatement.setDouble(5, 40);
preparedStatement.setString(6, "1960-2-3");
int rows = preparedStatement.executeUpdate();
System.out.println(rows + " rows affected");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
JdbcUtil.close(connection, preparedStatement);
}
}
// 再添加記錄
@Test
public void test5() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection();
String sql = "insert into customer(name, age, gender, phone, weight, birthdate) values (?, ?, ?, ?, ?, ?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1, "張七");
preparedStatement.setObject(2, 20);
preparedStatement.setObject(3, "女");
preparedStatement.setObject(4, "1343434343");
preparedStatement.setObject(5, 58.8);
preparedStatement.setObject(6, "1980-3-8");
int rows = preparedStatement.executeUpdate();
if (rows == 1) {
System.out.println("插入成功");
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
JdbcUtil.close(connection, preparedStatement);
}
}
}
像上面那樣添加每次重複同樣的代碼,我們這裏設置通用的更新操作,並存入CommonUtil類中,作爲工具類通用更新操作。具體實現如下:
package com.atguigu.jdbc;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class CommonUtil {
/**
* 通用更新操作
* @param sql 一個可以包含?的SQL語句
* @param values SQL中有多少個?,可變參數就有多少個具體值
* @return 更新以後影響的記錄數
*/
public static int commonUpdate(String sql, Object... values) throws FileNotFoundException, ClassNotFoundException, IOException,
SQLException {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JdbcUtil.getConnection(); // 獲取連接
preparedStatement = connection.prepareStatement(sql); // 把帶有?的SQL編譯
for (int i = 0; i < values.length; i++) { // 可變參數的長度就是?的個數
preparedStatement.setObject(i + 1, values[i]); // 統一給所有的?替換成具體的值
}
return preparedStatement.executeUpdate(); // 替換完?或沒有?, 執行更新操作
} finally {
JdbcUtil.close(connection, preparedStatement); // 關閉資源
}
}
}
ResultSet類
至此我們實現了數據庫數據的建表,插入,更新,刪除,下面進行數據庫操作中比較難的查詢,需要使用到查詢結果集ResultSet類,循序漸進的練習實現如下:package com.atguigu.jdbc;
import java.util.List;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import org.junit.Test;
public class ResultSetTest {
@Test
public void test1() {
// select * from customer; 結果如下
/*
+------+------+--------+------------+--------+------------+
| name | age | gender | phone | weight | birthdate |
+------+------+--------+------------+--------+------------+
| 張七 20 | 女 | 1343434343 | 58.8 | 1980-03-08 |
| 張九 | 40 | 男 | 1349932423 | 78.2 | 1977-08-08 |
| 張三 | 3 | 女 | 1332324234 | 70 | 1979-08-08 |
+------+------+--------+------------+--------+------------+
*/
Connection connection = null;
PreparedStatement prepareStatment = null;
ResultSet resultSet = null;
try {
connection = JdbcUtil.getConnection();
String sql = "select * from customer";
prepareStatment = connection.prepareStatement(sql);
// 調用PreparedStatement對象的executeQuery方法產生ResultSet對象
resultSet = prepareStatment.executeQuery();
System.out.println(resultSet);
while (resultSet.next()) { // 根據列的<span style="color:#ff0000;">序號</span>,先檢測當前遊標的下一行是否有數據, 如果有數據則移動遊標,並返回true, 如果沒有數據,直接返回false
String name = resultSet.getString(1); // 獲取到的是當前遊標指向的行的第1列的值
int age = resultSet.getInt(2);// 獲取到的是當前遊標指向的行的第2列的值
String gender = resultSet.getString(3);// 獲取到的是當前遊標指向的行的第3列的值
String phone = resultSet.getString(4);// 獲取到的是當前遊標指向的行的第4列的值
double weight = resultSet.getDouble(5);// 獲取到的是當前遊標指向的行的第5列的值
Date birthDate = resultSet.getDate(6);// 獲取到的是當前遊標指向的行的第6列的值
System.out.println(name + "\t" + age + "\t" + gender + "\t" + phone + "\t" + weight + "\t" + birthDate);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, prepareStatment, resultSet);
}
}
// 我們改變查詢列名的順序進行查詢操作,使用下標不能實現了,我們需要根據虛表中的<span style="color:#ff0000;">列名</span>進行查詢,並將查詢結果封裝成對象,添加到list集合中
@Test
public void test2() {
Connection connection = null;
PreparedStatement prepareStatment = null;
ResultSet resultSet = null;
try {
connection = JdbcUtil.getConnection();
// name | age | gender | phone | weight | birthdate |
String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer";
prepareStatment = connection.prepareStatement(sql);
resultSet = prepareStatment.executeQuery();
List<Customer> list = new ArrayList<Customer>();
// 基於虛表的結果集
while (resultSet.next()) {
String name = resultSet.getString("custName");// 獲取到的是當前遊標指向的行的虛表中名字爲name列的值
int age = resultSet.getInt("age");// 獲取到的是當前遊標指向的行的虛表中名字爲age列的值
String gender = resultSet.getString("gender");// 獲取到的是當前遊標指向的行的虛表中名字爲gender列的值
String phone = resultSet.getString("phone");
double weight = resultSet.getDouble("weight");
Date birthDate = resultSet.getDate("birth");
Customer customer = new Customer(name, age, gender, phone, weight, birthDate); // 封裝成對象 O/R mapping
list.add(customer);
}
// 遍歷集合
for (Customer cust : list) {
System.out.println(cust);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, prepareStatment, resultSet);
}
}
// 我們改變查詢列名的順序進行查詢年齡大於任意數的操作,使用下標不能實現了,我們需要根據<span style="color:#ff0000;">虛表中的列名</span>進行查詢,並將查詢結果封裝成對象,添加到list集合中
@Test
public void test3() {
Connection connection = null;
PreparedStatement prepareStatment = null;
ResultSet resultSet = null;
try {
connection = JdbcUtil.getConnection();
// name | age | gender | phone | weight | birthdate |
String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer where age > ?";
prepareStatment = connection.prepareStatement(sql);
prepareStatment.setObject(1, 20);
resultSet = prepareStatment.executeQuery();
List<Customer> list = new ArrayList<Customer>();
// <span style="color:#ff0000;">基於虛表的結果集</span>
while (resultSet.next()) {
String name = resultSet.getString("custName");// 獲取到的是當前遊標指向的行的<span style="color:#ff0000;">虛表</span>中名字爲name列的值
int age = resultSet.getInt("age");// 獲取到的是當前遊標指向的行的虛表中名字爲age列的值
String gender = resultSet.getString("gender");// 獲取到的是當前遊標指向的行的虛表中名字爲gender列的值
String phone = resultSet.getString("phone");
double weight = resultSet.getDouble("weight");
Date birthDate = resultSet.getDate("birth");
Customer customer = new Customer(name, age, gender, phone, weight, birthDate); // 封裝成對象 O/R mapping
list.add(customer);
}
// 遍歷集合
for (Customer cust : list) {
System.out.println(cust);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, prepareStatment, resultSet);
}
}
// 獲取查詢結果,若查詢結果並沒有類與其對應,就需要獲取虛表的表結構進行查詢操作的打印輸出
@Test
public void test4() {
Connection connection = null;
PreparedStatement prepareStatment = null;
ResultSet resultSet = null;
try {
connection = JdbcUtil.getConnection();
// name | age | gender | phone | weight | birthdate |
String sql = "select name custName, gender, age, weight, phone, birthdate birth from customer";
prepareStatment = connection.prepareStatement(sql);
resultSet = prepareStatment.executeQuery();
/*
+----------+--------+------+--------+------------+------------+
| custName | gender | age | weight | phone | birth |
+----------+--------+------+--------+------------+------------+
| 張七 | 女 | 20 | 58.8 | 1343434343 | 1980-03-08 |
| 張九 | 男 | 40 | 78.2 | 1349932423 | 1977-08-08 |
| 張三 | 女 | 30 | 70 | 1332324234 | 1979-08-08 |
+----------+--------+------+--------+------------+------------+
*/
ResultSetMetaData metaData = resultSet.getMetaData(); // 獲取虛表的表結構對象
int cols = metaData.getColumnCount(); // 獲取虛表共有多少列
//System.out.println(cols);
String colName = metaData.getColumnName(3);// 獲取基表的列名, 參數中的是第幾列
//System.out.println(colName);
colName = metaData.getColumnName(6);// 獲取基表列名, 參數中的是第幾列
//System.out.println(colName);
String colLabel = metaData.getColumnLabel(6); // 獲取虛表列名, 參數是第6列
//System.out.println(colLabel);
System.out.println("---------------------------------");
for (int i = 0; i < cols; i++) {
String columnLabel = metaData.getColumnLabel(i + 1); // 列的索引總是從1開始
System.out.print(columnLabel + "\t");
}
System.out.println();
while (resultSet.next()) {
for (int i = 0; i < cols; i++) {
String columnLabel = metaData.getColumnLabel(i + 1); // 先獲取列的別名
Object value = resultSet.getObject(columnLabel); // 根據列的別名再獲取對應的值
System.out.print(value + "\t");
}
System.out.println();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(connection, prepareStatment, resultSet);
}
}
}