?
Ce document utilise Manuel du site Web PHP chinois Libérer
在Spring的JDBC框架的所有工作模式中貫徹了一些與參數(shù)和數(shù)據(jù)處理相關(guān)的基本原則。
多數(shù)情況下,Spring會(huì)根據(jù)傳入的參數(shù)值來(lái)設(shè)定相應(yīng)的SQL類(lèi)型。有時(shí),我們有必要明確指定傳入?yún)?shù)所代表的SQL類(lèi)型,這一點(diǎn)對(duì)于正確設(shè)置NULL值的時(shí)候可能比較有用。
另外還有一些其他的不同方面的作用:
多數(shù)JdbcTemplate
的update或者query方法會(huì)接收一個(gè)額外的int數(shù)組構(gòu)成的參數(shù)。
這個(gè)數(shù)組需要提供的是使用java.sql.Types
中所定義的SQL類(lèi)型。而這個(gè)數(shù)組中定義的類(lèi)型需要與每個(gè)傳入的參數(shù)所對(duì)應(yīng)。
你可以使用SqlParameterValue
對(duì)參數(shù)進(jìn)行額外的封裝從而包裝更多的參數(shù)信息。通過(guò)傳入?yún)?shù)值和對(duì)應(yīng)的SQL類(lèi)型作為構(gòu)造函數(shù)的參數(shù),你可以創(chuàng)建這個(gè)類(lèi)的一個(gè)實(shí)例。
你也可以為numeric的值提供一些額外的精度要求。
對(duì)于那些使用命名參數(shù)的情況,你可以使用SqlParameterSource
、BeanPropertySqlParameterSource
或者MapSqlParameterSource
類(lèi)。
他們都具備了為命名參數(shù)注冊(cè)SQL類(lèi)型的功能。
你可以在數(shù)據(jù)庫(kù)中存儲(chǔ)圖像、二進(jìn)制對(duì)象或者大文本等對(duì)象。這些較大的二進(jìn)制對(duì)象被稱(chēng)之為BLOB類(lèi)型,而對(duì)應(yīng)的大文本對(duì)象被稱(chēng)之為CLOB對(duì)象。
Spring允許你使用JdbcTemplate、更高層次封裝的RDBMS對(duì)象和SimpleJdbc類(lèi)對(duì)這些大對(duì)象進(jìn)行操作。 所有的這些操作方式都實(shí)現(xiàn)了LobHandler
接口來(lái)處理LOB類(lèi)型的數(shù)據(jù)。
LobHandler
接口提供了訪問(wèn)LobCreator
的方法,通過(guò)調(diào)用getLobCreator
,你可以創(chuàng)建一個(gè)新的LOB類(lèi)型的數(shù)據(jù)。
LobCreator/LobHandler
接口針對(duì)LOB類(lèi)型的數(shù)據(jù)操作提供了下列支持:
BLOB
byte[] – getBlobAsBytes and setBlobAsBytes
byte[] – getBlobAsBytes 和 setBlobAsBytes
InputStream – getBlobAsBinaryStream and setBlobAsBinaryStream
InputStream – getBlobAsBinaryStream和setBlobAsBinaryStream
CLOB
String – getClobAsString and setClobAsString
String – getClobAsString和setClobAsString
InputStream – getClobAsAsciiStream and setClobAsAsciiStream
InputStream – getClobAsAsciiStream和setClobAsAsciiStream
Reader – getClobAsCharacterStream and setClobAsCharacterStream
Reader – getClobAsCharacterStream和setClobAsCharacterStream
現(xiàn)在我們通過(guò)一個(gè)示例來(lái)展示如何創(chuàng)建一個(gè)BLOB數(shù)據(jù)并插入數(shù)據(jù)庫(kù)。稍后的例子,我們將展示如何從數(shù)據(jù)庫(kù)中將BLOB數(shù)據(jù)讀取出來(lái)。
這個(gè)例子使用JdbcTemplate和一個(gè)AbstractLobCreatingPreparedStatementCallback的實(shí)現(xiàn)類(lèi)。 這里唯一需要實(shí)現(xiàn)的方法就是"setValues"。在這個(gè)方法中,將提供一個(gè)LobCreator接口,被用作在你的插入語(yǔ)句中設(shè)置LOB字段的值。
我們假設(shè)有一個(gè)變量叫做“l(fā)obHandler”已經(jīng)被設(shè)置到DefaultLobHandler
的實(shí)例中。當(dāng)然,這是由注入完成的。
final File blobIn = new File("spring2004.jpg"); final InputStream blobIs = new FileInputStream(blobIn); final File clobIn = new File("large.txt"); final InputStream clobIs = new FileInputStream(clobIn); final InputStreamReader clobReader = new InputStreamReader(clobIs); jdbcTemplate.execute( "INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)", new AbstractLobCreatingPreparedStatementCallback(lobhandler) { protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { ps.setLong(1, 1L); lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length()); lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length()); } } ); blobIs.close(); clobReader.close();
![]() |
我們?cè)谶@里使用的lobHandler實(shí)現(xiàn)類(lèi)是一個(gè)普通的 |
![]() |
使用 |
![]() |
使用 |
現(xiàn)在我們來(lái)示范從數(shù)據(jù)庫(kù)中讀取LOB數(shù)據(jù)。我們這里再次使用JdbcTempate并使用相同的DefaultLobHandler
實(shí)例。
List l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table", new RowMapper() { public Object mapRow(ResultSet rs, int i) throws SQLException { Map results = new HashMap(); String clobText = lobHandler.getClobAsString(rs, "a_clob"); results.put("CLOB", clobText); byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob"); results.put("BLOB", blobBytes); return results; } });
![]() |
使用 |
![]() |
使用 |
SQL標(biāo)準(zhǔn)允許基于一個(gè)帶參數(shù)列表的表達(dá)式進(jìn)行查詢(xún)。一個(gè)典型的例子可能像這樣:"select * from T_ACTOR where id in (1, 2, 3)"。
不過(guò)這種參數(shù)列表的方式并不能直接被JDBC標(biāo)準(zhǔn)所支持 - 因?yàn)椴⒉淮嬖谶@種聲明一個(gè)列表參數(shù)作為占位符的方式。
你不得不為此寫(xiě)多個(gè)占位符來(lái)表示多個(gè)參數(shù),或者當(dāng)你知道占位符的數(shù)量時(shí),你可以動(dòng)態(tài)構(gòu)建SQL字符串。
NamedParameterJdbcTemplate
和SimpleJdbcTemplate
中所提供的命名參數(shù)的特性,采用的是后面一種做法。
當(dāng)你傳入?yún)?shù)時(shí),你需要傳入一個(gè)java.util.List
類(lèi)型,支持基本類(lèi)型。而這個(gè)list將會(huì)在SQL執(zhí)行時(shí)替換占位符并傳入?yún)?shù)。
在使用IN語(yǔ)句時(shí),當(dāng)你傳入大批量的值時(shí)要小心,JDBC標(biāo)準(zhǔn)并不確保超過(guò)100個(gè)元素在IN語(yǔ)句中。 有不少數(shù)據(jù)庫(kù)可以超出這個(gè)值的限制,但是不同的數(shù)據(jù)庫(kù)會(huì)有不同的數(shù)量限制,比如Oracle的限制數(shù)量是1000個(gè)。
除了基本類(lèi)型之外,你還可以創(chuàng)建一個(gè)java.util.List
的對(duì)象數(shù)組,這可以讓你支持在IN表達(dá)式中編寫(xiě)多重表達(dá)式,例如"select * from T_ACTOR where (id, last_name) in ((1,
'Johnson'), (2, 'Harrop'))". 當(dāng)然,這樣做的前提是數(shù)據(jù)庫(kù)底層的語(yǔ)法支持。
當(dāng)調(diào)用存儲(chǔ)過(guò)程時(shí),有時(shí)需要使用數(shù)據(jù)庫(kù)特定的復(fù)雜類(lèi)型。為了適應(yīng)這些類(lèi)型,Spring提供了SqlReturnType
類(lèi)來(lái)處理存儲(chǔ)過(guò)程的返回值,而使用SqlTypeValue
來(lái)處理傳入的參數(shù)。
下面這個(gè)例子,將Oracle的STRUCT對(duì)象作為返回值,這是一個(gè)由用戶(hù)自定義的“ITEM_TYPE”。
SqlReturnType
接口有唯一的方法“getTypeValue
”需要被實(shí)現(xiàn)。而這個(gè)接口的實(shí)現(xiàn)將被用作SqlOutParameter
聲明的一部分。
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE", new SqlReturnType() { public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName) throws SQLException { STRUCT struct = (STRUCT)cs.getObject(colIndx); Object[] attr = struct.getAttributes(); TestItem item = new TestItem(); item.setId(((Number) attr[0]).longValue()); item.setDescription((String)attr[1]); item.setExpirationDate((java.util.Date)attr[2]); return item; } }));
通過(guò)Java代碼調(diào)用存儲(chǔ)過(guò)程使用SqlTypeValue
來(lái)傳入一個(gè)TestItem
作為參數(shù)。
SqlTypeValue
接口有一個(gè)方法"createTypeValue
"需要被實(shí)現(xiàn)。
一個(gè)活動(dòng)的數(shù)據(jù)庫(kù)連接也同時(shí)被傳入,它將被用作創(chuàng)建數(shù)據(jù)庫(kù)特定的對(duì)象,類(lèi)似StructDescriptor
和ArrayDescriptor
SqlTypeValue value = new AbstractSqlTypeValue() { protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException { StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn); Struct item = new STRUCT(itemDescriptor, conn, new Object[] { testItem.getId(), testItem.getDescription(), new java.sql.Date(testItem.getExpirationDate().getTime()) }); return item; } };
這里的SqlTypeValue
現(xiàn)在可以被加入到作為參數(shù)的Map中去,從而可以執(zhí)行相應(yīng)的存儲(chǔ)過(guò)程。