大數據Blob Clob在Mysql與oracle寫入問題

寫在前面

寫這片文章是緣於在網上看到幾篇關於在MYSQL寫入圖片遇到亂碼時的問題,剛好最近在寫相關的代碼,就列出幾個解決的方法(雖然現實中是很少直接寫入圖片這種大數據的,但各數據庫廠商在技術上是完全可以實現的.

打開數據庫

String dbType="mysql.";
		Properties pros=new Properties();
		pros.load(new FileInputStream("src/db.properties"));
		Class.forName(pros.getProperty(dbType.concat(DRIVER)));
		Connection conn=DriverManager.getConnection(
				pros.getProperty(dbType.concat(URL)),pros.getProperty(dbType.concat(USERNAME)),pros.getProperty(dbType.concat(PASSWORD)));
		PreparedStatement pstmt=conn.prepareStatement("update dummy set image=? where username=?");


方法一

在原文無出現亂碼情況下,是可以直接將輸入流傳入PreparedStatement.setBlob(index,inputStream)的.

FileInputStream fis=new FileInputStream(file);
pstmt.setBlob(1, fis);

下面開始主題,在出現亂碼情況:

方法二

創建一個空的blob,並將輸入流寫入blob,這也是spring源碼的方法之一,關於inputStream寫到outputStream,我們還可以使用spring的org.springframework.util.FileCopyUtils.copy(input,output)代替,還可以省去流的關閉操作.
並且,該方法是在不出現寫入亂碼情況下是可以通用的,但在oracle下有點區別:
如果使用的ojdbc14.jar時,需用oracle自定義的BLOB(大寫)創建一個空的blob,而不能用Connection,oracle的Connection實現類是沒有createBlob()方法的.
如果使用ojdbc6.jar的話,則可以直接創建Blob對象.
//		method1
		//bellow codes are from oracle14.jar
//		blob.
//		BLOB blob=BLOB.createTemporary(conn, false, BLOB.DURATION_SESSION);
//		blob.open(BLOB.MODE_READWRITE);
		
//		method2-with ojdbc6.jar
		Blob blob=conn.createBlob();
		
		byte[] temp = new byte[4096];  
        int length;  
        OutputStream out = blob.setBinaryStream(1);
//        FileCopyUtils.copy(bis, out); the same function as bellow code
        while ((length = bis.read(temp)) != -1) {
            out.write(temp, 0, length);  
        } 
        out.flush();
        pstmt.setBlob(1, blob);


方法三

方法三是一個比較傳統的方法,我在這裏大概描述下過程,具體網上相關例子還是很多的:
  1. 插入一個空的blob
    Statement st = con.createStatement();
    			//插入一個空對象empty_blob()
    			st.executeUpdate("insert into dummy(id, username, password,image) values (1, 'other','psw', empty_blob())");
    
  2. 鎖定行(for update)
    ResultSet rs = st.executeQuery("select image from dummy where id=3 for update");
  3. 獲取結果集,並利用方法一將inputStream寫入oracle BLOB.getBinaryOutputStream().
  4. flush並關閉流
  5. 提交

方法四

這個方法是最通用的,它是使用PreparedStatement.setBytes(index,buff)方法.這也是hibernate源碼中使用的方法.參加方法六.這個方法也解決了開頭的亂碼問題.

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version 
for the right syntax to use near 'ºÛ¢çþlô£·ö3ñ³G‹ÿøì´éÂèØn–ÿGA朳P²0`¶á¦�q•n\\]ß½w8zû , .×¹*5r>xm?{¾<œ•' at line 1

方法五

使用開源框架進行操作.
spring的例子,
JdbcTemplate template=(JdbcTemplate)ctx.getBean("jdbcTemplate");
		final File file=new File("src/other.jpg");
		final InputStream bis=new BufferedInputStream(new FileInputStream(file));
		try{
		final LobHandler lobHandler=(LobHandler)ctx.getBean("oracleLobHandler");
		template.execute("update dummy set image=? where username='other'", new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
			
			protected void setValues(PreparedStatement ps, LobCreator lobCreator)
					throws SQLException, DataAccessException {
				lobCreator.setBlobAsBinaryStream(ps, 1, bis, (int)file.length());//注意這裏是int類型,PreparedStatement提供的是一個long類型
				
			}
		});
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			bis.close();
		}
使用hibernate的例子,
用hibernate時,需要將blob映射爲合適的類型(String,byte[],Blob,Text等),因爲各個廠商提供的driver有所不同,下面是hibernate幫助文檔的原文:
clob, blob

Type mappings for the JDBC classes java.sql.Clob and java.sql.Blob. These types can be inconvenient for some applications, since the blob or clob object cannot be reused outside of a transaction. Driver support is patchy and inconsistent.

下面是Dummy類的xml映射:
<hibernate-mapping>
	<class name="com.yan.entity.Dummy">
		<id name="id" />
		<property name="username" length="20"></property>
		<property name="password" length="20"></property>
		<property name="age"></property>
		<property name="path" length="80"></property>
		<property name="image" type="binary" length="23389042"></property>
	</class>
</hibernate-mapping>

注意,在MYSQL不加length屬性時,將默認映射爲tinyblob,如果設定長度,則會自動映射成tinyblob,blob,longblob. Oracle則對於爲raw,以及long raw.
Java類Dummy的image則爲byte[].
下面是保存圖片的java代碼:
dummy=new Dummy();
			dummy.setId(4);
			dummy.setUsername("176");
			dummy.setPassword("p4");
			dummy.setAge(21);
			File f=new File("src/176.png");
			InputStream is=new FileInputStream(f);
			int l=(int)f.length();
			byte[] buff=new byte[l];
			is.read(buff,0,l);
			is.close();
			dummy.setImage(buff);
			session.saveOrUpdate(dummy);

由於hibernate的binary對應的類型是BinaryType,在實現上是採用PreparedStatement.setBytes(index,buff)方法,所以在MYSQL與ORACLE是通用的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章