JDBC執行SQL語句的兩種方式:Statement和PreParedStatement,它們用於發送SQL語句給數據庫執行。在開發過程中,通常會把增刪改查語句封裝在DAO層(數據庫訪問層)中,接下來的使用案例會以Dao層封裝JDBC操作的形式來演示Stament和PreparedStatement。
用於測試的表,可根據個人需求更改
create table t_user(
u_id int identity(1,1) not null,
u_name varchar(16) not null,
u_psw varchar(16) not null,
u_age int not null,
u_sex varchar(2) not null,
primary key(u_id),
)
//用戶類,與測試表進行對應
public class User {
private String name;
private String psw;
private int age;
private String sex;
public User(String name, String psw, int age, String sex) {
this.name = name;
this.psw = psw;
this.age = age;
this.sex = sex;
}
//省略getter 和 setter
}
Statement案例代碼
executeUpdate()執行插入語句演示
public int insert(User u) {
Connection con = JdbcUtils.getConnection();
Statement stmt = null;
int count = 0; //影響行數
String sql = "insert into t_user values('" +u.getName() +"','" +u.getPsw()+"',"+ u.getAge()+",'"+ u.getSex()+"');";
System.out.println("插入的sql語句: " + sql);
try {
stmt = con.createStatement();
count = stmt.executeUpdate(sql);
stmt.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
executeQuery()執行查詢語句演示
//根據姓名查詢
public void select(User u) {
Connection con = JdbcUtils.getConnection();
Statement stmt = null;
ResultSet rs = null;
String sql = "select * from t_user where u_name ='" + u.getName() +"';";
System.out.println("執行的sql語句: " + sql);
try {
stmt = con.createStatement();
rs = stmt.executeQuery(sql);
while(rs.next()) {
System.out.print(rs.getString("u_name") +"\t"+ rs.getString("u_psw"));
System.out.println("\t"+ rs.getString("u_age") +"\t"+ rs.getString("u_sex"));
}
stmt.close();
con.close();
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
execute()執行任意語句演示
public void delete(User u) {
Connection con = JdbcUtils.getConnection();
Statement stmt = null;
boolean flag = false;
String sql = "delete from t_user where u_name = '"+ u.getName()+"';";
System.out.println("執行的刪除語句: " + sql);
try {
stmt = con.createStatement();
flag = stmt.execute(sql);
if(flag) {//true,則返回結果集
ResultSet rs = stmt.getResultSet();
while(rs.next()) {
System.out.println("說明結果集有值");
}
}else {//false,返回更新次數(影響行數)
int num = stmt.getUpdateCount();
System.out.println("更新次數:" + num);
}
stmt.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
- 使用說明:execute()可以執行任意SQL語句,根據返回值得到不同的結果,如果返回true,則是結果集;返回false,則執行的是DDL語句,返回更新次數。
- 使用execute()方法之後,應馬上使用getResultSet()或者getUpdateCount()來獲取execute()返回的結果。
- 相較於executeQuery()和executeUpdate(),execute()一般很少用到,瞭解該用法即可。
PreparedStatement案例代碼
/**
* 提取出連接數據庫方法和關閉連接對象的方法,類似Hibernate.
* 讓每一個Dao方法只專門執行SQL語句.
* 執行SQL語句改用PreparedStatement來執行.
*/
public class UserDao2 {
private Connection conn = null;
private PreparedStatement psmt = null;
private ResultSet rs = null;
public int insert(User u) {
String sql = "insert into t_user values(?, ?, ?, ?)";//帶佔位符的sql語句
int result = 0;//影響行數
try {
psmt = conn.prepareStatement(sql);//預編譯SQL
//設置參數
psmt.setString(1, u.getName());
psmt.setString(2, u.getPsw());
psmt.setInt(3, u.getAge());
psmt.setString(4, u.getSex());
System.out.println("執行的預編譯語句: " + sql);
result = psmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
//根據姓名查詢
public void select(User u) {
String sql = "select * from t_user where u_name = ?";
try {
psmt = conn.prepareStatement(sql);
psmt.setString(1, u.getName());
rs = psmt.executeQuery();//執行查詢語句
while(rs.next()) {
System.out.print(rs.getString("u_name") +"\t"+ rs.getString("u_psw"));
System.out.println("\t"+ rs.getString("u_age") +"\t"+ rs.getString("u_sex"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//根據id刪除對應列
public int delete(int id) {
String sql = "delete from t_user where u_id = ?";
int result = 0;
try {
psmt = conn.prepareStatement(sql);
psmt.setInt(1, id);
result = psmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
//開啓連接
public void begin() {
conn = JdbcUtils.getConnection();//獲得連接對象
}
//關閉連接
public void close() {
if(rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(psmt != null)
try {
psmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(conn != null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Statement和PreparedStatement的對比
- Statement執行的SQL語句採用拼接字符串形式,極容易寫錯寫漏,而且拼接代碼不容易閱讀。最重要一點就是Statement容易被SQL注入。
- PreparedStatement採用預編譯sql語句,通過爲每一個佔位符?來設置參數,代碼簡潔明瞭,方便閱讀和修改。
- PreparedStatement執行多條語句時效率高,並且可以有效防止SQL注入。所以推薦使用預編譯語句。
ResultSet使用詳解
案例一(不可回滾結果集)
public class ResultSetTest {
private static Connection conn = null;
private static PreparedStatement psmt = null;
private static ResultSet rs = null;
static {
conn = JdbcUtils.getConnection();
}
public static void main(String[] args) throws SQLException {
String sql = "select * from t_user";
//常規的預編譯對象
psmt = conn.prepareStatement(sql);
rs = psmt.executeQuery();
System.out.println("+-----+--------+-----+---+");
while(rs.next()) {
System.out.print(rs.getString("u_name")+"\t"+rs.getString("u_psw")+"\t");
System.out.print(rs.getString("u_age")+"\t"+rs.getString("u_sex"));
System.out.println("\t遊標指向行數:"+rs.getRow());
}
rs.beforeFirst();//移動光標引發異常
數據庫數據請自行插入,或者用上面的Dao層方法來完成都均可。輸出結果如下:- 案例一代碼分析:ResultSet有一個類似於數據庫的遊標指向結果集中的每一行,默認從1開始,而且不支持回滾,默認只能迭代下去。所以當使用移動遊標的方法時會引發異常。在下面的案例中,將會改成支持回滾的結果集。
案例二(可回滾的結果集)
public static void main(String[] args) throws SQLException {
String sql = "select * from t_user";
// 結果集可回滾 可更新的結果集併發模式
psmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs = psmt.executeQuery();
System.out.println("+-----+--------+-----+---+");
while(rs.next()) {
System.out.print(rs.getString("u_name")+"\t"+rs.getString("u_psw")+"\t");
System.out.print(rs.getString("u_age")+"\t"+rs.getString("u_sex"));
System.out.println("\t遊標指向行數:"+rs.getRow());
rs.afterLast();//移動遊標到最後一行之後
//下一個循環next()無法移動遊標到下一行,所以只輸出張三的數據。
}
System.out.println("+-----+--------+-----+---+");
while(rs.previous()) {//讓遊標向上移動一行。因爲此時遊標在最後一行之後,所以結果是倒序輸出
System.out.print(rs.getString("u_name")+"\t"+rs.getString("u_psw")+"\t");
System.out.println(rs.getString("u_age")+"\t"+rs.getString("u_sex"));
}
/*+-----+--------+-----+---+
張三 123456 15 男 遊標指向行數:1
+-----+--------+-----+---+
清風 343454 35 男
阿水 115286 19 女
一號 123456 5 男
小六 115286 19 男
小二 123456 25 男*/
}
除了上面代碼中使用的afterLast()和previous()方法來移動遊標,還有以下方法: rs.first();//移動遊標到第一行
System.out.println(rs.isFirst());//判斷遊標是否位於ResultSet對象第一行
rs.last();//移動遊標到最後一行
System.out.println(rs.isLast());//判斷遊標是否位於ResultSet對象最後一行
//絕對定位到指定行,如果超出結果集行數,則操作無效,獲取的行數爲0
rs.absolute(9);//超出結果集行數時,使用previous()會自動找到上一行
System.out.println(rs.getRow());//0
//......不再詳述別的移動遊標方法,請自行查看JDK文檔,或單獨測試。
案例二(ResultSet中執行刪改操作)
/** 對結果集執行刪除操作 */
String sql = "select * from t_user";
// 結果集可回滾 可更新的結果集併發模式
psmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs = psmt.executeQuery();
System.out.println("+-----+--------+-----+---+");
while(rs.next()) {
System.out.print(rs.getString("u_name")+"\t"+rs.getString("u_psw")+"\t");
System.out.print(rs.getString("u_age")+"\t"+rs.getString("u_sex"));
System.out.println("\t遊標指向行數:"+rs.getRow());
}
rs.absolute(4);//定位到第4行
System.out.println(rs.getRow());
rs.deleteRow();//刪除當前遊標指向的行,即第4行
//...請自行進行迭代操作,會發現剛纔的4行的數據已被刪除,這裏不再演示。
/** 對結果集中游標指向的行執行更新操作 */
rs.absolute(2);//定位到2行
System.out.println(rs.getRow());
rs.updateObject("u_name", "小二更新");//更新指定列名的數據
rs.updateRow();//更新到底層數據
//...省略輸出的結果
- 使用說明:ResultSet更新列的數據有很多方法,例如updateXxx(),如果知道該列是什麼類型就可以指定對應的數據類型,但也可以使用updateString()和updateObject()來更新列,最後還需要使用updateRow()方法更新底層數據庫。