1. 程式人生 > >03jdbc常用介面--DriverManeger,Connection,PreparedStatement,ResultSet介面淺析

03jdbc常用介面--DriverManeger,Connection,PreparedStatement,ResultSet介面淺析

在之前的博文中簡單提到了jdbc規範主要有4個核心介面

  1. DriverManager:用於註冊驅動並建立符合該驅動的資料庫的連線
  2. Connection: 表示與資料庫建立的連線物件,即一個connection對應著一個會話,相當於在mysql workbench(資料庫視窗化工具)中打開了一個連線。
  3. Statement: 操作資料庫sql語句的物件,有個兩個實現類:Statement和PreparedStatement(常用)。
  4. ResultSet: 從資料庫中查詢的結果集。

下面簡單分析下這幾個介面

1 註冊驅動

先看下mysql.Driver中原始碼,很簡潔,一個靜態程式碼塊,一個建立驅動的方法

Class.forName(String className)將對應的驅動類載入到記憶體中,然後執行記憶體中的static靜態程式碼段,程式碼段中,會建立一個驅動Driver的例項,放入DriverManager中,供DriverManager使用

注意mysql8.0驅動中className="com.mysql.cj.jdbc.Driver"

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // Register ourselves with the DriverManager
       static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

2 DriverManager介面  

java.sql包下java.sql.DriverManager   DriverManager的主要作用是建立連線

常用方法:

static Connection getConnection(String url);

static Connection getConnection(String url,String user, String password);引數1是資料庫連線,mysql8.0版本驅動的url為

"jdbc:mysql://資料庫所在機器ip:資料庫埠號/資料庫名?useSSL=false&serverTimezone=UTC"

第2個引數是使用者名稱,第3個引數是密碼,都是字串型別 

static Connection getConnection(String url,java.util.Properties info);

private static Connection getConnection(String url, java.util.Properties info, Class<?> caller);獲取資料庫的連線

前3個方法都呼叫了第4個私有方法建立Connection連線物件,下面是第2個getConnection方法的原始碼

 @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();
        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

其中java.util.Properties類繼承自HashTable,是一個跟配置檔案相關的類,HashTable類是執行緒安全的,它和HashMap中的方法類似

Properties類是配置類,將使用者名稱和密碼儲存到info物件中,再呼叫第4個getConnection方法,將info作為引數傳入該方法中,建立連線物件

 

3 Connection介面

java.sql.Connection  它是與資料庫連線會的物件,只有獲得特定的資料庫連線物件,才可以訪問資料庫進行資料庫操作

常用方法:

void close()關閉Connection連線

Statement createStatement()建立Statement物件

 PreparedStatement  prepareStatement(String sql)建立PreparedStatement物件

 

4 Statement介面

java.sql.Statement,它的作用是操作sql語句,並返回相應結果的物件,好吧,這個介面會有sql注入問題,要認為deprived它,用PreparedStatement介面代替它

常用方法

ResultSet executeQuery(String sql) 執行查詢sql語句,返回ResultSet物件,注意引數sql語句不能加分號

int executeUpdate(String sql)執行DML語句,即增,刪,改操作,返回int型別,成功返回更新表的行數,失敗返回0,注意引數sql語句不能加分號

void close()立即釋放此Statement物件的資料庫和JDBC資源

boolean excute(String sql) throws SQLException 該方法是執行指定的SQL語句。如果sql語句返回結果,此方法返回true,否則返回false

boolean isClosed() throws SQLException 該方法用來判斷Statement物件是否已被關閉

5 PreparedStatement介面

java.sql.PreparedStatement     PreparedStatement是Statement的子介面,不需要動態拼接字串,使用佔位符來代替引數,可以簡化拼接sql的過程,可以預防sql注入問題

能夠對sql語句進行預編譯,預編譯後能夠提高資料庫sql語句執行效率。PreparedStatement 例項包含已編譯的 SQL 語句。包含於 PreparedStatement 物件中的 SQL 語句可具有一個或多個 IN 引數。IN引數的值在 SQL 語句建立時未被指定。相反的,該語句為每個 IN 引數保留一個問號(“?”,注意是英文狀態)作為佔位符。每個問號的值必須在該語句執行之前,通過適當的setXXX 方法來提供。

常用方法:

boolean execute()   如果第一個結果是ResultSet物件,則返回true;如果第一個結果是執行語句後更新的行數或者沒有結果,則返回false。

ResultSet executeQuery(): 返回執行查詢語句後得到的ResultSet結果集

int executeUpdate(): 返回一個int型別的值,該值代表執行insert,delete,update操作後的更新行數。對於create table table_name或者drop table table_name等操作,返回0,對於沒有執行的操作,沒有返回值

列舉常用的setxxx方法,用第二個引數替換第(引數1)上的佔位符,達到修改sql語句的目的,引數1是第幾個佔位符,從1開始算起,有很多這樣的方法,具體可查api

void setInt(int parameterIndex, int x)用引數2替換第(引數1)個佔位符

void setString(int parameterIndex, String x)用引數2替換第(引數1)個佔位符,字串如''yyyy-MM-dd'可以代替日期,mysql預設識別此種字串形式的日期

void setFloat(int parameterIndex, float x)用引數2替換第(引數1)個佔位符

void setDate(int parameterIndex, java.sql.Date x)用引數2替換第(引數1)個佔位符

void close()立即釋放此PreparedStatement物件的資料庫和JDBC資源

 

6 ResultSet介面

java.sql.ResultSet,該介面的作用主要用來封裝結果集。在sql中select語句查詢的結果可以看作一張臨時表,而ResultSet就代表這張表

常用方法:

boolean next() throws SQLException 將指標移向下一行,如果該行有值返回true,否則返回false。

void close() 關閉Resultset物件釋放jdbc資源

getObject(String ColomnName); 根據列名取值。

getObject(int columnIndex); 根據序號取值,索引從1開始,可讀性不強,不建議使用

下面四個方法都有過載的方法,引數為int型的索引,可讀性不強,不建議使用就沒列出來啦

int getInt(String columnName)以整數的形式獲取指定列的內容

float getFloat(String columnName)以浮點數的形式獲取指定列的內容

String getString(String columnName)以字串的形式獲取指定列的內容

Date getDate(String columnName)以Date的形式獲取指定列的內容,返回值java.sql.Date繼承自java.util.Date,兩個都可以用

InputStream getBinaryStream(String columnLabel) throws SQLException 以byte流的方式獲取ResultSet物件當前行中指定列的值,引數columnLabel為列名稱。

boolean previous() throws SQLException 將指標移向上一行,如果該行有效返回true,否則返回false.

boolean isClosed() throws SQLException 判斷當前ResultSet物件是否已關閉。boolean absolute(int row) throws SQLException 將游標移動到此ResultSet物件的給定行編號,引數row為行編號。

boolean first() throws SQLException 將游標移動到此ResultSet物件的第一行

boolean last() throws SQLException 將游標移動到此ResultSet物件的最後一行。

 

 

示例:

執行以下指令碼在資料庫study1中建立新表t_user,並插入三條資料

create table t_user(
    id int primary key auto_increment,
    name varchar(40),
    password varchar(40),
    email varchar(60),
    birthday date
);
insert into t_user(name,password,email,birthday) values('tiger','123456','[email protected]','1994-12-01'),
('rabbit','123456','[email protected]','1997-06-11'),
('sheep','123456','[email protected]','1995-07-15');

說明,欄位id設定為主鍵,自增,在不指定id的情況下,插入資料id會自動增長,如果某id的這行資料被刪除,下次會自動跳過該id值繼續增長 

下面示例,jdbc增刪改查操作

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

import com.mysql.cj.xdevapi.Result;

public class JdbcTest02 {

	@Test
	public void testJDBC() throws Exception {
	String driver="com.mysql.cj.jdbc.Driver";
	Class.forName(driver);
	Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/study1?useSSL=false&serverTimezone=UTC", "root", "root");
	Statement stat=conn.createStatement();
	int in1=stat.executeUpdate("insert into t_user(name,password,email,birthday) values ('bird',123,'[email protected]','1992-01-20') ");
	int in2=stat.executeUpdate("update t_user set password=321 where name='bird'");
	ResultSet rs=stat.executeQuery("select * from t_user");
	while(rs.next()) {
		System.out.println(rs.getObject("id"));
		System.out.println(rs.getString("name"));
		System.out.println(rs.getInt("password"));
		System.out.println(rs.getString("email"));
		System.out.println(rs.getDate("birthday"));
		System.out.println("==================");
	}
	int in3=stat.executeUpdate("delete from t_user where name='bird'");
	rs.close();
	stat.close();
	conn.close();
	}
}
out:
1 tiger 123456 [email protected] 1994-12-01 ==================
2 rabbit 123456 [email protected] 1997-06-11 ==================
3 sheep 123456 [email protected] 1995-07-15 ==================
4 bird 321 [email protected] 1992-01-20 ==================

 

另外 在做查詢操作時,可能會返回多條資料結果,此時可以定義一個JavaBean類,將資料封裝到該JavaBean中,多條資料的話將此JavaBean放到集合中。那咱們就建立一個User類

package bean;

import java.util.Date;

public class User {
	private int id;
	private String name;
	private String password;
	private String email;
    private Date birthday;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", password=" + password + ", email=" + email + ", birthday="
				+ birthday + "]";
	}
}

執行JDBC查詢操作,將多條查詢結果封裝到JavaBean物件中並將JavaBean物件放入到集合裡面: 

@Test
	public void testJDBC01() throws Exception {
		String driver="com.mysql.cj.jdbc.Driver";
		Class.forName(driver);
		Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/study1?useSSL=false&serverTimezone=UTC", "root", "root");
		Statement stat=conn.createStatement();
		ResultSet rs=stat.executeQuery("select id,name,password,email,birthday from t_user");
		List<User> list=new ArrayList<>();
		while(rs.next()) {
			User u=new User();
			u.setId(rs.getInt("id"));
			u.setName(rs.getString("name"));
			u.setPassword(rs.getString("password"));
			u.setEmail(rs.getString("email"));
			u.setBirthday(rs.getDate("birthday"));
			list.add(u);
		}
		System.out.println(list);
		rs.close();
		stat.close();
		conn.close();
	}

注意:上述程式中將每一行的資料封裝進一個javaBean物件,再將每個javaBean物件放入List中,集合List放入的是該物件的引用

建立javaBean物件時應在while迴圈體中建立,這樣才能保證創建出來的物件都是不同的

分析上面程式的執行過程如圖

如下程式碼片段是有問題的

        List<User> list=new ArrayList<>();
        User u=new User();
		while(rs.next()) {
			u.setId(rs.getInt("id"));
			u.setName(rs.getString("name"));
			u.setPassword(rs.getString("password"));
			u.setEmail(rs.getString("email"));
			u.setBirthday(rs.getDate("birthday"));
			list.add(u);
		}

它將會造成完全不同的結果,分析下這個程式碼段,如圖所示

每執行一次迴圈,更改一次User物件的值,其引用放入到List中,三次迴圈之後,List中的三個User物件的引用指向的是同一塊記憶體地址,所以這個List中的元素是一樣的 

參考:小猴子視訊