1. 程式人生 > >JDBC 之插入Blob(圖片)& 批處理 & 資料庫事務

JDBC 之插入Blob(圖片)& 批處理 & 資料庫事務

插入Blob欄位型別(如圖片) 實現批處理和 資料庫事務的一致性

一、插入Blob型別資料(如:圖片)

使用JDBC來寫入Blob型資料到資料庫中
資料庫中的Blob欄位比long欄位的效能要好,可以用來儲存如圖片之類的二進位制資料。

BLOB欄位由兩部分組成:資料(值)和指向資料的指標(定位器)。
儘管值與表自身一起儲存,但是一個BLOB列並不包含值,僅有它的定位指標。
為了使用大物件,程式必須宣告定位器型別的本地變數。
當資料庫內部LOB被建立時,定位器被存放在列中,值被存放在LOB段中,
LOB段是在資料庫內部表的一部分。
因為Blob自身有一個cursor,當寫入Blob欄位必須使用指標(定位器)
對Blob進行操作,因而在寫入Blob之前,必須獲得指標(定位器)才能進行寫入
如何獲得Blob的指標(定位器) :需要先插入一個empty的blob,
這將建立一個blob的指標,然後再把這個empty的blob的指標查詢出來,
這樣通過兩步操作,就獲得了blob的指標,可以真正的寫入blob資料了。

<1>第一步:連線資料庫和釋放資源的JDBCUtils是不可少的

封裝使用C3P0獲取連線和關閉連線 這兩個方法封裝如下:

public class JDBCUtils {

//資料庫連線池值應該被初始化一次放在靜態程式碼塊中
private static DataSource datasource=null;
static{
    datasource =new ComboPooledDataSource("helloc3p0");
}
public static  Connection getConnection() throws SQLException{

    return datasource.getConnection();
}

//用完資源後需要關閉

/*釋放資源:
Connection
Statement
ResultSet
1 儘量晚建立早釋放
2 後使用的先關閉*/

public static void release(ResultSet rs,Statement statement,Connection conn){
     if(rs!=null){
            try{
                rs.close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

      if(statement!=null){
    try{
        statement.close();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
       if(conn!=null){
        try {
            conn.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
    }
  }
}

<2> 編碼格式 資料庫utf8

<2>第二步:實現插入Blob型別(圖片)的資料如下:

public class TestBlob {
@Test
public void testInsert() throws Exception{
    //獲取連線
    Connection connection = JDBCUtils.getConnection();

    PreparedStatement statement = connection.prepareStatement("insert into star values(null,'mnls',?)");

    //插入圖片佔位符值 圖片儲存在src包下
    statement.setBlob(1, new FileInputStream("src/mnls.jpg"));

    //執行插入圖片
    int executeUpdate = statement.executeUpdate();
    if (executeUpdate>0) {
        System.out.println("success");
    } else {
        System.out.println("failure");
    }
    JDBCUtils.release(connection, statement, null);
}

<2>第三步:實現將資料庫中Blob型別(圖片)的資料取出:

//查詢blob資料
@Test
public void testRead() throws Exception{
    Connection connection = DBUtils.getConnection();

    PreparedStatement statement = connection.prepareStatement("select * from star where id=1");
    ResultSet set = statement.executeQuery();
    if (set.next()) {

        Blob blob = set.getBlob("photo");

        //獲取二進位制流物件  ★
        InputStream stream = blob.getBinaryStream();

        //一邊讀一邊寫 寫入src下存為copy.jpg
        //將讀到的資料轉換成字元陣列(方法如下)
        byte[] arrs = StreamUtils.streamToByteArray(stream);

        FileOutputStream fos = new FileOutputStream("src/copy.jpg");

        fos.write(arrs);

        fos.close();
    }
    DBUtils.release(connection, statement, set);
}

}

//將輸入流轉換成byte[] 如下

public class StreamUtils {
/**
 * 
 * 將 輸入流 轉換成byte[]
 * @param is
 * @return
 */
public static byte[] streamToByteArray(InputStream is) throws IOException{

    //建立位元組陣列的輸出流
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] b=new byte[1024];

    int len;
    while((len=is.read(b))!=-1){
        baos.write(b, 0, len);//寫到位元組陣列的輸出流
    }
    is.close();

    baos.close();

    return  baos.toByteArray();
}
}

二、JDBC之批處理操作

描述:

批量處理JDBC語句提高處理速度 當需要成批插入或者更新記錄時。
可以採用Java的批量更新機制,這一機制允許多條語句一次性
提交給資料庫批量處理。通常情況下比單獨提交處理更有效率
JDBC的批量處理語句包括下面兩個方法:
addBatch(String):新增需要批量處理的SQL語句或是引數;
executeBatch():執行批量處理語句;
clearBatch():清空快取的資料
通常我們會遇到兩種批量執行SQL語句的情況:
多條SQL語句的批量處理;
一個SQL語句的批量傳參;

<1> 注意url的配置 和資料庫與java專案的編碼一致utf8

<2> 加入jar包commons-dbutils-1.3.jar

url:

jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true

<3>實現批處理如下

/**
 * 此類用於演示批處理
 * @author liyuting
 *
 */
public class TestBatch {

@Test
public void testBatch(){
    Connection connection=null;
    PreparedStatement statement =null;

    try {
        connection = JDBCUtils.getConnection();
    statement= connection.prepareStatement
    ("insert into 表名 values(null,?,'男','[email protected]',now())");

        for (int i =1; i <=20000; i++) {

        statement.setString(1, "李四"+i);

        statement.addBatch();//新增到批處理的語句中

        if (i%500==0) {//每500條sql語句執行一次

            statement.executeBatch();//真正的執行

            statement.clearBatch();//清空快取
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    finally{
        JDBCUtils.release(connection, statement, null);
    }

}

}

三、JDBC之資料庫事務(控制多條sql執行中其中一條會出現異常)

<1>描述:

JDBC 事務處理:
當一個連線物件被建立時,預設情況下是自動提交事務:
每次執行一個 SQL 語句時,如果執行成功,
就會向資料庫自動提交,而不能回滾
為了讓多個 SQL 語句作為一個事務執行:
呼叫 Connection 物件的 setAutoCommit(false); 
以取消自動提交事務
在所有的 SQL 語句都成功執行後,呼叫 commit(); 方法提交事務
在出現異常時,呼叫 rollback(); 方法回滾事務
若此時 Connection 沒有被關閉, 則需要恢復其自動提交狀態

<2>實現如下:

/**
 * 此類用於演示演示事務
 * @author liyuting
 *
 */
public class TestTransaction {
@Test
public void testBatch(){
    Connection connection = null;
    try {

        //1獲取連線
            connection=DBUtils.getConnection();

         //2.開啟事務 (start transaction)
         connection.setAutoCommit(false);//取消每一行的自動提交

         //執行一條修改語句
         update(connection,"update users set name='張三' where id=?",1);

         int i = 10/1;//模擬異常

        //執行第二條修改語句
         update(connection,"update users set name='李四' where id=?",2);

         //提交事務(commit)
         connection.commit();
    } catch (SQLException e) {

        //回滾事務(rollback)
        try {
            connection.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }
}