1.PreparedStatement物件
PreparedStatement物件繼承Statement物件,它比Statement物件更強大,使用起來更簡單
- Statement物件編譯SQL語句時,如果SQL語句有變數,就需要使用分隔符來隔開,如果變數非常多,就會使SQL變得非常複雜。PreparedStatement可以使用佔位符,簡化sql的編寫
- Statement會頻繁編譯SQL。PreparedStatement可對SQL進行預編譯,提高效率,預編譯的SQL儲存在PreparedStatement物件中
- PreparedStatement防止SQL隱碼攻擊。【Statement通過分隔符`++`,編寫永等式,可以不需要密碼就進入資料庫】
//模擬查詢id為2的資訊
String id = "2";
Connection connection = UtilsDemo.getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement = connection.preparedStatement(sql);
//第一個參數列示第幾個佔位符【也就是?號】,第二個參數列示值是多少
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
//釋放資源
UtilsDemo.release(connection, preparedStatement, resultSet);
2.批處理
當需要向資料庫傳送一批SQL語句執行時,應避免向資料庫一條條傳送執行,採用批處理以提升執行效率
批處理有兩種方式:
- Statement
- PreparedStatement
通過executeBath()方法批量處理執行SQL語句,返回一個int[]陣列,該陣列代表各句SQL的返回值
以下程式碼是以Statement方式實現批處理
/*
* Statement執行批處理
*
* 優點:
* 可以向資料庫傳送不同的SQL語句
* 缺點:
* SQL沒有預編譯
* 僅引數不同的SQL,需要重複寫多條SQL
* */
Connection connection = UtilsDemo.getConnection();
Statement statement = connection.createStatement();
String sql1 = "UPDATE users SET name=`zhongfucheng` WHERE id=`3`";
String sql2 = "INSERT INTO users (id, name, password, email, birthday)" +
" VALUES(`5`,`nihao`,`123`,`ss@qq.com`,`1995-12-1`)";
//將sql新增到批處理
statement.addBatch(sql1);
statement.addBatch(sql2);
//執行批處理
statement.executeBatch();
//清空批處理的sql
statement.clearBatch();
UtilsDemo.release(connection, statement, null);
以下方式以PreparedStatement方式實現批處理
/*
* PreparedStatement批處理
* 優點:
* SQL語句預編譯了
* 對於同一種型別的SQL語句,不用編寫很多條
* 缺點:
* 不能傳送不同型別的SQL語句
*
* */
Connection connection = UtilsDemo.getConnection();
String sql = "INSERT INTO test(id,name) VALUES (?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 1; i <= 205; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, (i + "zhongfucheng"));
//新增到批處理中
preparedStatement.addBatch();
if (i %2 ==100) {
//執行批處理
preparedStatement.executeBatch();
//清空批處理【如果資料量太大,所有資料存入批處理,記憶體肯定溢位】
preparedStatement.clearBatch();
}
}
//不是所有的%2==100,剩下的再執行一次批處理
preparedStatement.executeBatch();
//再清空
preparedStatement.clearBatch();
UtilsDemo.release(connection, preparedStatement, null);
3.處理大文字和二進位制資料
clob和blob
- clob用於儲存大文字
- blob用於儲存二進位制資料
MYSQL
MySQL儲存大文字是用Test【代替clob】,Test又分為4類
- TINYTEXT
- TEXT
- MEDIUMTEXT
- LONGTEXT
同理blob也有這4類
下面用JDBC連線MySQL資料庫去操作大文字資料和二進位制資料
/*
*用JDBC操作MySQL資料庫去操作大文字資料
*
*setCharacterStream(int parameterIndex,java.io.Reader reader,long length)
*第二個引數接收的是一個流物件,因為大文字不應該用String來接收,String太大會導致記憶體溢位
*第三個引數接收的是檔案的大小
*
* */
public class Demo5 {
@Test
public void add() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "INSERT INTO test2 (bigTest) VALUES(?) ";
preparedStatement = connection.prepareStatement(sql);
//獲取到檔案的路徑
String path = Demo5.class.getClassLoader().getResource("BigTest").getPath();
File file = new File(path);
FileReader fileReader = new FileReader(file);
//第三個引數,由於測試的Mysql版本過低,所以只能用int型別的。高版本的不需要進行強轉
preparedStatement.setCharacterStream(1, fileReader, (int) file.length());
if (preparedStatement.executeUpdate() > 0) {
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection, preparedStatement, null);
}
}
/*
* 讀取大文字資料,通過ResultSet中的getCharacterStream()獲取流物件資料
*
* */
@Test
public void read() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "SELECT * FROM test2";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
Reader reader = resultSet.getCharacterStream("bigTest");
FileWriter fileWriter = new FileWriter("d:\abc.txt");
char[] chars = new char[1024];
int len = 0;
while ((len = reader.read(chars)) != -1) {
fileWriter.write(chars, 0, len);
fileWriter.flush();
}
fileWriter.close();
reader.close();
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection, preparedStatement, resultSet);
}
}
/*
* 使用JDBC連線MYsql資料庫操作二進位制資料
* 如果我們要用資料庫儲存一個大視訊的時候,資料庫是儲存不到的。
* 需要設定max_allowed_packet,一般我們不使用資料庫去儲存一個視訊
* */
public class Demo6 {
@Test
public void add() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "INSERT INTO test3 (blobtest) VALUES(?)";
preparedStatement = connection.prepareStatement(sql);
//獲取檔案的路徑和檔案物件
String path = Demo6.class.getClassLoader().getResource("1.wmv").getPath();
File file = new File(path);
//呼叫方法
preparedStatement.setBinaryStream(1, new FileInputStream(path), (int)file.length());
if (preparedStatement.executeUpdate() > 0) {
System.out.println("新增成功");
}
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection, preparedStatement, null);
}
}
@Test
public void read() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "SELECT * FROM test3";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
//如果讀取到資料,就把資料寫到磁碟下
if (resultSet.next()) {
InputStream inputStream = resultSet.getBinaryStream("blobtest");
FileOutputStream fileOutputStream = new FileOutputStream("d:\aa.jpg");
int len = 0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
fileOutputStream.write(bytes, 0, len);
}
fileOutputStream.close();
inputStream.close();
}
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection, preparedStatement, null);
}
}
Oracle
下面用JDBC連線Oracle資料庫去操作大文字資料和二進位制資料
//使用JDBC連線Oracle資料庫操作二進位制資料
/*
* 對於Oracle資料庫和Mysql資料庫是有所不同的。
* 1.Oracle定義了BLOB欄位,但是這個欄位不是真正地儲存二進位制資料
* 2.向這個欄位存一個BLOB指標,獲取到Oracle的BLOB物件,把二進位制資料放到這個指標裡面,指標指向BLOB欄位
* 3.需要事務支援
*
* */
public class Demo7 {
@Test
public void add() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = UtilsDemo.getConnection();
//開啟事務
connection.setAutoCommit(false);
//插入一個BLOB指標
String sql = "insert into test4(id,image) values(?,empty_blob())";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1);
preparedStatement.executeUpdate();
//把BLOB指標查詢出來,得到BLOB物件
String sql2 = "select image from test4 where id= ? for update";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.setInt(1, 1);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
//得到Blob物件--當成是Oracle的Blob,不是JDBC的,所以要強轉[導的是oracle.sql.BLOB包]
BLOB blob = (BLOB) resultSet.getBlob("image");
//寫入二進位制資料
OutputStream outputStream = blob.getBinaryOutputStream();
//獲取到讀取檔案讀入流
InputStream inputStream = Demo7.class.getClassLoader().getResourceAsStream("01.jpg");
int len=0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
outputStream.close();
inputStream.close();
connection.setAutoCommit(true);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
UtilsDemo.release(connection, preparedStatement, null);
}
}
@Test
public void find() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = UtilsDemo.getConnection();
String sql = "SELECT * FROM test4 WHERE id=1";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
//獲取到BLOB物件
BLOB blob = (BLOB) resultSet.getBlob("image");
//將資料讀取到磁碟上
InputStream inputStream = blob.getBinaryStream();
FileOutputStream fileOutputStream = new FileOutputStream("d:\zhongfucheng.jpg");
int len=0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
fileOutputStream.write(bytes, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
} catch (SQLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
UtilsDemo.release(connection, preparedStatement, null);
}
}
}
對於JDBC連線Oracle資料庫操作CLOB資料,我就不再重複了,操作跟BLOB幾乎相同
4.獲取資料庫的自動主鍵列
為什麼要獲取資料庫的自動主鍵列資料?
應用場景:
有一張老師表,一張學生表。現在來了一個新的老師,學生要跟著新老師上課。
我首先要知道老師的id編號是多少,學生才能知道跟著哪個老師學習【學生外來鍵參照老師主鍵】。
@Test
public void test() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "INSERT INTO test(name) VALUES(?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "ouzicheng");
if (preparedStatement.executeUpdate() > 0) {
//獲取到自動主鍵列的值
resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()) {
int id = resultSet.getInt(1);
System.out.println(id);
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection, preparedStatement, null);
}
5.呼叫資料庫的儲存過程
呼叫儲存過程的語法:
{call <procedure-name>[(<arg1>,<arg2>, ...)]}
呼叫函式的語法:
{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
如果是Output型別的,那麼在JDBC呼叫的時候是要註冊的。如下程式碼所示:
/*
jdbc呼叫儲存過程
delimiter $$
CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam varchar(255))
BEGIN
SELECT CONCAT(`zyxw---`, inputParam) into inOutParam;
END $$
delimiter ;
*/
//我們在JDBC呼叫儲存過程,就像在呼叫方法一樣
public class Demo9 {
public static void main(String[] args) {
Connection connection = null;
CallableStatement callableStatement = null;
try {
connection = JdbcUtils.getConnection();
callableStatement = connection.prepareCall("{call demoSp(?,?)}");
callableStatement.setString(1, "nihaoa");
//註冊第2個引數,型別是VARCHAR
callableStatement.registerOutParameter(2, Types.VARCHAR);
callableStatement.execute();
//獲取傳出引數[獲取儲存過程裡的值]
String result = callableStatement.getString(2);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
connection.close();
callableStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
參考資料:
----------------------------------------------------------------------------------過程
#修改mysql語句的結果符為//
mysql > delimiter //
#定義一個過程,獲取users表總記錄數,將10設定到變數count中
create procedure simpleproc(out count int)
begin
select count(id) into count from users;
end
//
#修改mysql語句的結果符為;
mysql > delimiter ;
#呼叫過程,將結果覆給變數a,@是定義變數的符號
call simpleproc(@a);
#顯示變數a的值
select @a;
//以下是Java呼叫Mysql的過程
String sql = "{call simpleproc(?)}";
Connection conn = JdbcUtil.getConnection();
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.registerOutParameter(1,Types.INTEGER);
cstmt.execute();
Integer count = cstmt.getInt(1);
System.out.println("共有" + count + "人");
----------------------------------------------------------------------------------函式
#修改mysql語句的結果符為//
mysql > delimiter //
#定義一個函式,完成字串拼接
create function hello( s char(20) ) returns char(50)
return concat(`hello,`,s,`!`);
//
#修改mysql語句的結果符為;
mysql > delimiter ;
#呼叫函式
select hello(`world`);
//以下是Java呼叫Mysql的函式
String sql = "{? = call hello(?)}";
Connection conn = JdbcUtil.getConnection();
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.registerOutParameter(1,Types.VARCHAR);
cstmt.setString(2,"zhaojun");
cstmt.execute();
String value = cstmt.getString(1);
System.out.println(value);
JdbcUtil.close(cstmt);
JdbcUtil.close(conn);
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y