3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

使用Spring JDBC进行数据访问 (JdbcTemplate/NamedParameterJdbcTemplate/SimpleJdbcTemplate/SimpleJdbcCall/Stor)

發布時間:2024/4/17 javascript 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Spring JDBC进行数据访问 (JdbcTemplate/NamedParameterJdbcTemplate/SimpleJdbcTemplate/SimpleJdbcCall/Stor) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://www.cnblogs.com/webcc/archive/2012/04/11/2442680.html

使用Spring JDBC進行數據訪問

11.1.?簡介

Spring JDBC抽象框架所帶來的價值將在以下幾個方面得以體現:(注:使用了Spring JDBC抽象框架之后,應用開發人員只需要完成斜體字部分的編碼工作。)

  • 定義數據庫連接參數

  • 打開數據庫連接

  • 聲明SQL語句

  • 預編譯并執行SQL語句

  • 遍歷查詢結果(如果需要的話)

  • 處理每一次遍歷操作

  • 處理拋出的任何異常

  • 處理事務

  • 關閉數據庫連接

  • Spring將替我們完成所有使用JDBC API進行開發的單調乏味的、底層細節處理工作。

    11.1.1.?選擇一種工作模式

    使用Spring進行基本的JDBC訪問數據庫有多種選擇。Spring至少提供了三種不同的工作模式:JdbcTemplate, 一個在Spring2.5中新提供的SimpleJdbc類能夠更好的處理數據庫元數據; 還有一種稱之為RDBMS Object的風格的面向對象封裝方式, 有點類似于JDO的查詢設計。 我們在這里簡要列舉你采取某一種工作方式的主要理由. 不過請注意, 即使你選擇了其中的一種工作模式, 你依然可以在你的代碼中混用其他任何一種模式以獲取其帶來的好處和優勢。 所有的工作模式都必須要求JDBC 2.0以上的數據庫驅動的支持, 其中一些高級的功能可能需要JDBC 3.0以上的數據庫驅動支持。

    • JdbcTemplate - 這是經典的也是最常用的Spring對于JDBC訪問的方案。這也是最低級別的封裝, 其他的工作模式事實上在底層使用了JdbcTemplate作為其底層的實現基礎。JdbcTemplate在JDK 1.4以上的環境上工作得很好。

    • NamedParameterJdbcTemplate - 對JdbcTemplate做了封裝,提供了更加便捷的基于命名參數的使用方式而不是傳統的JDBC所使用的“?”作為參數的占位符。這種方式在你需要為某個SQL指定許多個參數時,顯得更加直觀而易用。該特性必須工作在JDK 1.4以上。

    • SimpleJdbcTemplate - 這個類結合了JdbcTemplate和NamedParameterJdbcTemplate的最常用的功能,同時它也利用了一些Java 5的特性所帶來的優勢,例如泛型、varargs和autoboxing等,從而提供了更加簡便的API訪問方式。需要工作在Java 5以上的環境中。

    • SimpleJdbcInsert 和 SimpleJdbcCall - 這兩個類可以充分利用數據庫元數據的特性來簡化配置。通過使用這兩個類進行編程,你可以僅僅提供數據庫表名或者存儲過程的名稱以及一個Map作為參數。其中Map的key需要與數據庫表中的字段保持一致。這兩個類通常和SimpleJdbcTemplate配合使用。這兩個類需要工作在JDK 5以上,同時數據庫需要提供足夠的元數據信息。

    • RDBMS 對象包括MappingSqlQuery, SqlUpdate and StoredProcedure - 這種方式允許你在初始化你的數據訪問層時創建可重用并且線程安全的對象。該對象在你定義了你的查詢語句,聲明查詢參數并編譯相應的Query之后被模型化。一旦模型化完成,任何執行函數就可以傳入不同的參數對之進行多次調用。這種方式需要工作在JDK 1.4以上。

    11.1.2.?Spring JDBC包結構

    Spring Framework的JDBC抽象框架由四個包構成:core、 dataSource、object以及support。

    org.springframework.jdbc.core包由JdbcTemplate類以及相關的回調接口(callback interface)和類組成。 org.springframework.jdbc.core.simple 子包則包含了 SimpleJdbcTemplate 類以及相關的SimpleJdbcInsert 類和SimpleJdbcCall 類。 org.springframework.jdbc.core.namedparam 子包則包含了NamedParameterJdbcTemplate 類以及其相關的支持類。

    org.springframework.jdbc.datasource包提供了一些工具類來簡化對DataSource的訪問。同時提供了多種簡單的DataSource實現。這些實現可以脫離J2EE容器進行獨立測試和運行。 這些工具類提供了一些靜態方法,允許你通過JNDI來獲取數據庫連接和關閉連接。同時支持綁定到當前線程的數據庫連接。例如使用DataSourceTransactionManager。

    接著org.springframework.jdbc.object包包含了一些類,用于將RDBMS查詢、更新以及存儲過程表述為一些可重用的、線程安全的對象。這種方式通過JDO進行模型化,不過這些通過查詢返回的對象是與數據庫脫離的對象。 這種對于JDBC的高層次的封裝是基于org.springframework.jdbc.core包對JDBC的低層次封裝之上的。

    最后,org.springframework.jdbc.support包定義了SQLException轉化類以及一些其他的工具類。

    在JDBC調用過程中所拋出的異常都會被轉化為在org.springframework.dao包中定義的異常。也就是說,凡是使用Spring的JDBC封裝層的代碼無需實現任何JDBC或者RDBMS相關的異常處理。所有的這些被轉化的異常都是unchecked異常,因而也給了你一種額外的選擇,你可以抓住這些異常,從而轉化成其他類型的異常被允許調用者傳播。

    11.2.?利用JDBC核心類控制JDBC的基本操作和錯誤處理

    11.2.1.?JdbcTemplate類

    JdbcTemplate是core包的核心類。它替我們完成了資源的創建以及釋放工作,從而簡化了我們對JDBC的使用。 它還可以幫助我們避免一些常見的錯誤,比如忘記關閉數據庫連接。 JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創建、執行,而把SQL語句的生成以及查詢結果的提取工作留給我們的應用代碼。 它可以完成SQL查詢、更新以及調用存儲過程,可以對ResultSet進行遍歷并加以提取。 它還可以捕獲JDBC異常并將其轉換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。

    使用JdbcTemplate進行編碼只需要根據明確定義的一組契約來實現回調接口。 PreparedStatementCreator回調接口通過給定的Connection創建一個PreparedStatement,包含SQL和任何相關的參數。 CallableStatementCreateor實現同樣的處理,只不過它創建的是CallableStatement。 RowCallbackHandler接口則從數據集的每一行中提取值。

    我們可以在DAO實現類中通過傳遞一個DataSource引用來完成JdbcTemplate的實例化,也可以在Spring的IoC容器中配置一個JdbcTemplate的bean并賦予DAO實現類作為一個實例。 需要注意的是DataSource在Spring的IoC容器中總是配制成一個bean,第一種情況下,DataSource bean將傳遞給service,第二種情況下DataSource bean傳遞給JdbcTemplate bean。

    最后,JdbcTemplate中使用的所有SQL將會以“DEBUG”級別記入日志(一般情況下日志的category是JdbcTemplate相應的全限定類名,不過如果需要對JdbcTemplate進行定制的話,可能是它的子類名)。

    11.2.1.1.?一些示例

    下面是一些使用JdbcTemplate類的示例。(這些示例并不是完整展示所有的JdbcTemplate所暴露出來的功能。請查看與之相關的Javadoc)。

    11.2.1.1.1.?查詢(SELECT)

    一個簡單的例子用于展示如何獲取一個表中的所有行數。

    int rowCount = this.jdbcTemplate.queryForInt("select count(0) from t_accrual");

    一個簡單的例子展示如何進行參數綁定。

    int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

    查詢一個String。

    String surname = (String) this.jdbcTemplate.queryForObject("select surname from t_actor where id = ?", new Object[]{new Long(1212)}, String.class);

    查詢并將結果記錄為一個簡單的數據模型。

    Actor actor = (Actor) this.jdbcTemplate.queryForObject("select first_name, surname from t_actor where id = ?",new Object[]{new Long(1212)},new RowMapper() {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setFirstName(rs.getString("first_name"));actor.setSurname(rs.getString("surname"));return actor;}});

    查詢并組裝多個數據模型。

    Collection actors = this.jdbcTemplate.query("select first_name, surname from t_actor",new RowMapper() {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setFirstName(rs.getString("first_name"));actor.setSurname(rs.getString("surname"));return actor;}});

    如果最后2個示例中的代碼出現在同一段程序中,我們有必要去掉這些重復的RowMapper匿名類代碼,將這些代碼抽取到一個單獨的類中(通常是一個靜態的內部類)。 這樣,這個內部類就可以在DAO的方法中被共享。因而,最后2個示例寫成如下的形式將更加好:

    public Collection findAllActors() {return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper()); }private static final class ActorMapper implements RowMapper {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setFirstName(rs.getString("first_name"));actor.setSurname(rs.getString("surname"));return actor;} }
    11.2.1.1.2.?更新(INSERT/UPDATE/DELETE)
    this.jdbcTemplate.update("insert into t_actor (first_name, surname) values (?, ?)", new Object[] {"Leonor", "Watling"}); this.jdbcTemplate.update("update t_actor set weapon = ? where id = ?", new Object[] {"Banjo", new Long(5276)}); this.jdbcTemplate.update("delete from actor where id = ?",new Object[] {new Long.valueOf(actorId)});
    11.2.1.1.3.?其他操作

    execute(..)方法可以被用作執行任何類型的SQL,甚至是DDL語句。 這個方法的實現需要傳入一個回調接口、需要綁定的參數數組等作為參數。

    this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

    調用一個簡單的存儲過程(更多復雜的存儲過程支持請參見存儲過程支持)。

    this.jdbcTemplate.update("call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", new Object[]{Long.valueOf(unionId)});

    11.2.1.2.?JdbcTemplate 的最佳實踐

    JdbcTemplate類的實例是線程安全的實例。這一點非常重要,正因為如此,你可以配置一個簡單的JdbcTemplate實例,并將這個“共享的”、“安全的”實例注入到不同的DAO類中去。 另外, JdbcTemplate 是有狀態的,因為他所維護的DataSource 實例是有狀態的,但是這種狀態是無法變化的。

    使用JdbcTemplate的一個常見的最佳實踐(同時也是SimpleJdbcTemplate和NamedParameterJdbcTemplate 類的最佳實踐)就是在Spring配置文件中配置一個DataSource實例,然后將這個共享的DataSource實例助于到你的DAO中去。 而JdbcTemplate的實例將在DataSource的setter方法中被創建。這樣的話,DAO可能看上去像這樣:

    public class JdbcCorporateEventDao implements CorporateEventDao {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}// JDBC-backed implementations of the methods on the CorporateEventDao follow... }

    相關的配置看上去就像這樣。

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"><bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"><property name="dataSource" ref="dataSource"/></bean><!-- the DataSource (parameterized for configuration via a PropertyPlaceHolderConfigurer) --><bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean></beans>

    如果你使用Spring提供的JdbcDaoSupport類,并且你的那些基于JDBC的DAO都繼承自這個類,那么你會自動地從JdbcDaoSupport類中繼承了setDataSource(..)方法。 是否將你的DAO類繼承自這些類完全取決于你自己的決定,事實上這并不是必須的,如果你看一下JdbcDaoSupport類你會發現,這里只是提供了一個簡便的方式而已。

    無論你是否使用上述這種初始化方式,都無需在執行某些SQL操作時多次創建一個JdbcTemplate實例。記住,一旦JdbcTemplate被創建,他是一個線程安全的對象。 一個你需要創建多次JdbcTemplate實例的理由可能在于,你的應用需要訪問多個不同的數據庫,從而需要不同的DataSources來創建不同的JdbcTemplates實例。

    11.2.2.?NamedParameterJdbcTemplate類

    NamedParameterJdbcTemplate類為JDBC操作增加了命名參數的特性支持,而不是傳統的使用('?')作為參數的占位符。NamedParameterJdbcTemplate類對JdbcTemplate類進行了封裝, 在底層,JdbcTemplate完成了多數的工作。這一個章節將主要描述NamedParameterJdbcTemplate類與JdbcTemplate類的一些區別,也就是使用命名參數進行JDBC操作。

    // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate;public void setDataSource(DataSource dataSource) {this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); }public int countOfActorsByFirstName(String firstName) {String sql = "select count(0) from T_ACTOR where first_name = :first_name";SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);return namedParameterJdbcTemplate.queryForInt(sql, namedParameters); }

    注意這里在'sql'中使用了命名參數作為變量,而這個名稱所對應的值被定義在傳入的'namedParameters' 中作為參數(也可以傳入到MapSqlParameterSource中作為參數)。

    你也可以傳入許多命名參數以及他們所對應的值,以Map的方式,作為鍵值對傳入到NamedParameterJdbcTemplate中。 (其余的被NamedParameterJdbcOperations所暴露的接口以及NamedParameterJdbcTemplate實現類遵循了類似的方式,此處不包含相關內容)。

    // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate;public void setDataSource(DataSource dataSource) {this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); }public int countOfActorsByFirstName(String firstName) {String sql = "select count(0) from T_ACTOR where first_name = :first_name";Map namedParameters = Collections.singletonMap("first_name", firstName);return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); }

    NamedParameterJdbcTemplate類所具備的另外一個比較好的特性就是可以接收SqlParameterSource作為傳入參數 (這個類位于相同的包定義中)。 你已經在先前的一個例子中看到了這個接口的一個具體實現類。( MapSqlParameterSource類)。而SqlParameterSource 這個接口對于NamedParameterJdbcTemplate類的操作而言是一個傳入的參數。MapSqlParameterSource只是一個非常簡單的實現,使用了java.util.Map作為轉接器, 其中,Map中的Key表示參數名稱,而Map中的Value表示參數值。

    另外一個SqlParameterSource 的實現類是BeanPropertySqlParameterSource。這個類對傳統的Java進行了封裝(也就是那些符合JavaBean標準的類), 并且使用了JavaBean的屬性作為參數的名稱和值。

    public class Actor {private Long id;private String firstName;private String lastName;public String getFirstName() {return this.firstName;}public String getLastName() {return this.lastName;}public Long getId() {return this.id;}// setters omitted...} // some JDBC-backed DAO class... private NamedParameterJdbcTemplate namedParameterJdbcTemplate;public void setDataSource(DataSource dataSource) {this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); }public int countOfActors(Actor exampleActor) {// notice how the named parameters match the properties of the above 'Actor' classString sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); }

    注意,NamedParameterJdbcTemplate類只是封裝了JdbcTemplate模板; 因而如果你需要訪問相應被封裝的JdbcTemplate類,并訪問一些只有在JdbcTemplate中擁有的功能,你需要使用getJdbcOperations()方法來進行訪問。

    請參照Section?11.2.1.2, “JdbcTemplate 的最佳實踐”來獲取一些使用NamedParameterJdbcTemplate的最佳實踐。

    11.2.3.?SimpleJdbcTemplate類

    Note

    SimpleJdbcTemplate所提供的一些特性必須工作在Java 5及以上版本。

    SimpleJdbcTemplate類是對JdbcTemplate類進行的封裝,從而可以充分利用Java 5所帶來的varargs和autoboxing等特性。 SimpleJdbcTemplate類完全利用了Java 5語法所帶來的蜜糖效應。凡是使用過Java 5的程序員們如果要從Java 5遷移回之前的JDK版本,無疑會發現這些特性所帶來的蜜糖效應。

    “before and after”示例可以成為SimpleJdbcTemplate類所帶來的蜜糖效應的最佳詮釋。 下面的代碼示例首先展示了使用傳統的JdbcTemplate進行JDBC訪問的代碼,接著是使用SimpleJdbcTemplate類做同樣的事情。

    // classic JdbcTemplate-style... private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource); }public Actor findActor(long id) {String sql = "select id, first_name, last_name from T_ACTOR where id = ?";RowMapper mapper = new RowMapper() {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setId(rs.getLong("id"));actor.setFirstName(rs.getString("first_name"));actor.setLastName(rs.getString("last_name"));return actor;}};// notice the cast, the wrapping up of the 'id' argument// in an array, and the boxing of the 'id' argument as a reference typereturn (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)}); }

    下面是同樣的邏輯,使用了SimpleJdbcTemplate;可以看到代碼“干凈”多了:

    // SimpleJdbcTemplate-style... private SimpleJdbcTemplate simpleJdbcTemplate;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); }public Actor findActor(long id) {String sql = "select id, first_name, last_name from T_ACTOR where id = ?";ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper<Actor>() {// notice the return type with respect to Java 5 covariant return typespublic Actor mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setId(rs.getLong("id"));actor.setFirstName(rs.getString("first_name"));actor.setLastName(rs.getString("last_name"));return actor;}};return this.simpleJdbcTemplate.queryForObject(sql, mapper, id); }

    請同樣參照Section?11.2.1.2, “JdbcTemplate 的最佳實踐”來獲取一些SimpleJdbcTemplate的最佳實踐

    Note

    SimpleJdbcTemplate只是提供了JdbcTemplate所提供的功能的子類。 如果你需要使用JdbcTemplate的方法,而這些方法又沒有在SimpleJdbcTemplate中定義,你需要調用getJdbcOperations()方法 獲取相應的方法調用。JdbcOperations接口中定義的方法需要在這邊做強制轉化才能使用。

    11.2.4.?DataSource接口

    為了從數據庫中取得數據,我們首先需要獲取一個數據庫連接。Spring通過DataSource對象來完成這個工作。 DataSource是JDBC規范的一部分,它被視為一個通用的數據庫連接工廠。通過使用DataSource, Container或Framework可以將連接池以及事務管理的細節從應用代碼中分離出來。 作為一個開發人員,在開發和測試產品的過程中,你可能需要知道連接數據庫的細節。但在產品實施時,你不需要知道這些細節。通常數據庫管理員會幫你設置好數據源。

    在使用Spring JDBC時,你既可以通過JNDI獲得數據源,也可以自行配置數據源(使用Spring提供的DataSource實現類)。使用后者可以更方便的脫離Web容器來進行單元測試。 這里我們將使用DriverManagerDataSource,不過DataSource有多種實現, 后面我們會講到。使用DriverManagerDataSource和你以前獲取一個JDBC連接 的做法沒什么兩樣。你首先必須指定JDBC驅動程序的全限定名,這樣DriverManager 才能加載JDBC驅動類,接著你必須提供一個url(因JDBC驅動而異,為了保證設置正確請參考相關JDBC驅動的文檔), 最后你必須提供一個用戶連接數據庫的用戶名和密碼。下面我們將通過一個例子來說明如何配置一個DriverManagerDataSource:

    DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); dataSource.setUsername("sa"); dataSource.setPassword("");

    11.2.5.?SQLExceptionTranslator接口

    SQLExceptionTranslator是一個接口,如果你需要在 SQLException和org.springframework.dao.DataAccessException之間作轉換,那么必須實現該接口。 轉換器類的實現可以采用一般通用的做法(比如使用JDBC的SQLState code),如果為了使轉換更準確,也可以進行定制(比如使用Oracle的error code)。

    SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的默認實現。 該實現使用指定數據庫廠商的error code,比采用SQLState更精確。轉換過程基于一個JavaBean(類型為SQLErrorCodes)中的error code。 這個JavaBean由SQLErrorCodesFactory工廠類創建,其中的內容來自于 “sql-error-codes.xml”配置文件。該文件中的數據庫廠商代碼基于 Database MetaData 信息中的DatabaseProductName,從而配合當前數據庫的使用。

    SQLErrorCodeSQLExceptionTranslator使用以下的匹配規則:

    • 首先檢查是否存在完成定制轉換的子類實現。通常SQLErrorCodeSQLExceptionTranslator 這個類可以作為一個具體類使用,不需要進行定制,那么這個規則將不適用。

    • 接著將SQLException的error code與錯誤代碼集中的error code進行匹配。 默認情況下錯誤代碼集將從SQLErrorCodesFactory取得。 錯誤代碼集來自classpath下的sql-error-codes.xml文件,它們將與數據庫metadata信息中的database name進行映射。

    • 使用fallback翻譯器。SQLStateSQLExceptionTranslator類是缺省的fallback翻譯器。

    ?

    SQLErrorCodeSQLExceptionTranslator可以采用下面的方式進行擴展:

    public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {if (sqlex.getErrorCode() == -12345) {return new DeadlockLoserDataAccessException(task, sqlex);}return null;} }

    在上面的這個例子中,error code為'-12345'的SQLException將采用該轉換器進行轉換,而其他的error code將由默認的轉換器進行轉換。 為了使用該轉換器,必須將其作為參數傳遞給JdbcTemplate類的setExceptionTranslator方法,并在需要使用這個轉換器器的數據 存取操作中使用該JdbcTemplate。下面的例子演示了如何使用該定制轉換器:

    // create a JdbcTemplate and set data source JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(dataSource); // create a custom translator and set the DataSource for the default translation lookup MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); tr.setDataSource(dataSource); jt.setExceptionTranslator(tr); // use the JdbcTemplate for this SqlUpdate SqlUpdate su = new SqlUpdate(); su.setJdbcTemplate(jt); su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); su.compile(); su.update();

    在上面的定制轉換器中,我們給它注入了一個數據源,因為我們仍然需要 使用默認的轉換器從sql-error-codes.xml中獲取錯誤代碼集。

    11.2.6.?執行SQL語句

    我們僅需要非常少的代碼就可以達到執行SQL語句的目的,一旦獲得一個 DataSource和一個JdbcTemplate, 我們就可以使用JdbcTemplate提供的豐富功能實現我們的操作。下面的例子使用了極少的代碼完成創建一張表的工作。

    import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate;public class ExecuteAStatement {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}public void doExecute() {this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");} }

    11.2.7.?執行查詢

    除了execute方法之外,JdbcTemplate還提供了大量的查詢方法。 在這些查詢方法中,有很大一部分是用來查詢單值的。比如返回一個匯總(count)結果 或者從返回行結果中取得指定列的值。這時我們可以使用queryForInt(..)、 queryForLong(..)或者queryForObject(..)方法。 queryForObject方法用來將返回的JDBC類型對象轉換成指定的Java對象,如果類型轉換失敗將拋出 InvalidDataAccessApiUsageException異常。 下面的例子演示了兩個查詢的用法,一個返回int值,另一個返回String。

    import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate;public class RunAQuery {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}public int getCount() {return this.jdbcTemplate.queryForInt("select count(*) from mytable");}public String getName() {return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class);}public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;} }

    除了返回單值的查詢方法,JdbcTemplate還提供了一組返回List結果 的方法。List中的每一項對應查詢返回結果中的一行。其中最簡單的是queryForList方法, 該方法將返回一個List,該List中的每一條 記錄是一個Map對象,對應應數據庫中某一行;而該Map 中的每一項對應該數據庫行中的某一列值。下面的代碼片斷接著上面的例子演示了如何用該方法返回表中所有記錄:

    private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource); }public List getList() {return this.jdbcTemplate.queryForList("select * from mytable"); }

    返回的結果集類似下面這種形式:

    [{name=Bob, id=1}, {name=Mary, id=2}]

    11.2.8.?更新數據庫

    JdbcTemplate還提供了一些更新數據庫的方法。 在下面的例子中,我們根據給定的主鍵值對指定的列進行更新。 例子中的SQL語句中使用了“?”占位符來接受參數(這種做法在更新和查詢SQL語句中很常見)。 傳遞的參數值位于一個對象數組中(基本類型需要被包裝成其對應的對象類型)。

    import javax.sql.DataSource;import org.springframework.jdbc.core.JdbcTemplate;public class ExecuteAnUpdate {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}public void setName(int id, String name) {this.jdbcTemplate.update("update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)});} }

    11.2.9.?獲取自動生成的主鍵

    JdbcTemplate中有一個update方法,可以方便地從數據庫中獲取數據庫自動創建的主鍵。(這是JDBC 3.0的標準 - 可以參見13.6節獲取詳細信息)。 這個方法使用了PreparedStatementCreator接口作為第一個參數, 可以通過這個接口的實現類來定義相應的Insert語句。另外一個參數是KeyHolder, 一旦update方法成功,這個參數將包含生成的主鍵。這里對于創建合適的PreparedStatement并沒有一個統一的標準。(這也解釋了函數簽名如此定義的原因)。下面是一個在Oracle上運行良好的示例,它可能在其他平臺上無法工作:

    final String INSERT_SQL = "insert into my_test (name) values(?)"; final String name = "Rob";KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() {public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {PreparedStatement ps =connection.prepareStatement(INSERT_SQL, new String[] {"id"});ps.setString(1, name);return ps;}},keyHolder);// keyHolder.getKey() now contains the generated key

    11.3.?控制數據庫連接

    11.3.1.?DataSourceUtils類

    DataSourceUtils作為一個幫助類提供易用且強大的數據庫訪問能力, 我們可以使用該類提供的靜態方法從JNDI獲取數據庫連接以及在必要的時候關閉之。 它提供支持線程綁定的數據庫連接(比如使用DataSourceTransactionManager的時候,將把數據庫連接綁定到當前的線程上)。

    11.3.2.?SmartDataSource接口

    SmartDataSource是DataSource 接口的一個擴展,用來提供數據庫連接。使用該接口的類在指定的操作之后可以檢查是否需要關閉連接。該接口在某些情況下非常有用,比如有些情況需要重用數據庫連接。

    11.3.3.?AbstractDataSource類

    AbstractDataSource是一個實現了DataSource 接口的abstract基類。它實現了DataSource接口的 一些無關痛癢的方法,如果你需要實現自己的DataSource,那么可以繼承該類。

    11.3.4.?SingleConnectionDataSource類

    SingleConnectionDataSource是SmartDataSource接口 的一個實現,其內部包裝了一個單連接。該連接在使用之后將不會關閉,很顯然它不能在多線程的環境下使用。

    當客戶端代碼調用close方法的時候,如果它總是假設數據庫連接來自連接池(就像使用持久化工具時一樣), 你應該將suppressClose設置為true。這樣,通過該類獲取的將是代理連接(禁止關閉)而不是原有的物理連接。 需要注意的是,我們不能把使用該類獲取的數據庫連接造型(cast)為Oracle Connection之類的本地數據庫連接。

    SingleConnectionDataSource主要在測試的時候使用。它使得測試代碼很容易脫離應用服務器而在一個簡單的JNDI環境下運行。 與DriverManagerDataSource不同的是,它始終只會使用同一個數據庫連接,從而避免每次建立物理連接的開銷。

    11.3.5.?DriverManagerDataSource類

    DriverManagerDataSource類實現了 SmartDataSource接口。可以使用bean properties來設置JDBC Driver屬性,該類每次返回的都是一個新的連接。

    該類主要在測試以及脫離J2EE容器的獨立環境中使用。它既可以用來在application context中作為一個DataSource bean,也可以在簡單的JNDI環境下使用。 由于Connection.close()僅僅只是簡單的關閉數據庫連接,因此任何能夠獲取DataSource的持久化代碼都能很好的工作。不過使用JavaBean風格的連接池 (比如commons-dbcp)也并非難事。即使是在測試環境下,使用連接池也是一種比使用DriverManagerDataSource更好的做法。

    11.3.6.?TransactionAwareDataSourceProxy類

    TransactionAwareDataSourceProxy作為目標DataSource的一個代理, 在對目標DataSource包裝的同時,還增加了Spring的事務管理能力, 在這一點上,這個類的功能非常像J2EE服務器所提供的事務化的JNDI DataSource。

    Note

    該類幾乎很少被用到,除非現有代碼在被調用的時候需要一個標準的 JDBC DataSource接口實現作為參數。 這種情況下,這個類可以使現有代碼參與Spring的事務管理。通常最好的做法是使用更高層的抽象 來對數據源進行管理,比如JdbcTemplate和DataSourceUtils等等。

    如果需要更詳細的資料,請參考 TransactionAwareDataSourceProxy JavaDocs。

    11.3.7.?DataSourceTransactionManager類

    DataSourceTransactionManager類是 PlatformTransactionManager接口的一個實現,用于處理單JDBC數據源。 它將從指定DataSource取得的JDBC連接綁定到當前線程,因此它也支持了每個數據源對應到一個線程。

    我們推薦在應用代碼中使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,而不是使用J2EE標準的DataSource.getConnection。因為前者將拋出 unchecked的org.springframework.dao異常,而不是checked的 SQLException異常。Spring Framework中所有的類(比如 JdbcTemplate)都采用這種做法。如果不需要和這個 DataSourceTransactionManager類一起使用,DataSourceUtils 提供的功能跟一般的數據庫連接策略沒有什么兩樣,因此它可以在任何場景下使用。

    DataSourceTransactionManager類支持定制隔離級別,以及對SQL語句查詢超時的設定。 為了支持后者,應用代碼必須使用JdbcTemplate或者在每次創建SQL語句時調用DataSourceUtils.applyTransactionTimeout(..)方法。

    在使用單個數據源的情形下,你可以用DataSourceTransactionManager來替代JtaTransactionManager, 因為DataSourceTransactionManager不需要容器支持JTA。如果你使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,二者之間的切換只需要更改一些配置。最后需要注意的一點就是JtaTransactionManager不支持隔離級別的定制!

    11.3.8.?NativeJdbcExtractor

    有時我們需要執行特殊的,由特定廠商提供的與標準JDBC的API不同的JDBC方法。此時,當我們在某個應用服務器上運行包裝了這些廠商各自實現的Connection, Statement和ResultSet對象的DataSource 時,可能會遇到一些問題。如果你要訪問這些對象,你可以配置一個包含NativeJdbcExtractor的JdbcTemplate或者OracleLobHandler。

    NativeJdbcExtractor根據執行環境的不同,會有不同的風格的實現:

    • SimpleNativeJdbcExtractor

    • C3P0NativeJdbcExtractor

    • CommonsDbcpNativeJdbcExtractor

    • JBossNativeJdbcExtractor

    • WebLogicNativeJdbcExtractor

    • WebSphereNativeJdbcExtractor

    • XAPoolNativeJdbcExtractor

    通常來說SimpleNativeJdbcExtractor類對于絕大多數環境,已經足以屏蔽 Connection 對象。可以參見Java Docs獲取詳細信息。

    11.4.?JDBC批量操作

    絕大多數JDBC驅動針對批量調用相同的prepared statement對象提供了性能提升。通過將這些更新操作封裝到一個批量操作中,可以大量減少與數據庫的操作頻繁度。 本章節將詳細描述使用JdbcTemplate或者SimpleJdbcTemplate進行批量操作的流程。

    11.4.1.?使用JdbcTemplate進行批量操作

    JdbcTemplate的批量操作特性需要實現特定的接口BatchPreparedStatementSetter來進行的, 通過實現這個接口,并將其傳入batchUpdate方法進行調用。 這個接口有兩個方法需要實現。一個叫做getBatchSize來提供當前需要批量操作的數量。另外一個方法是setValues 允許你為prepared statement設置參數。這個方法將在整個過程中被調用的次數,則取決于你在getBatchSize中所指定的大小。 下面的示例展示了根據傳入的list參數更新actor表,而傳入的list同時作為批量操作的參數。

    ?

    public class JdbcActorDao implements ActorDao {private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}public int[] batchUpdate(final List actors) {int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, last_name = ? where id = ?",new BatchPreparedStatementSetter() {public void setValues(PreparedStatement ps, int i) throws SQLException {ps.setString(1, ((Actor)actors.get(i)).getFirstName());ps.setString(2, ((Actor)actors.get(i)).getLastName());ps.setLong(3, ((Actor)actors.get(i)).getId().longValue());}public int getBatchSize() {return actors.size();}} );return updateCounts;}// ... additional methods }

    如果你是通過讀取文件進行批量操作,那么你可能需要一個特定的批量操作的數量,不過最后一次的批量操作,你可能沒有那么多數量的記錄。 在這種情況下,你可以實現InterruptibleBatchPreparedStatementSetter接口,從而允許你在某些情況中斷批量操作,isBatchExhausted 方法允許你指定一個終止批量操作的信號量。

    11.4.2.?使用SimpleJdbcTemplate進行批量操作

    SimpleJdbcTemplate類提供了另外一種批量操作的方式。無需實現一個特定的接口,你只需要提供所有在調用過程中要用到的參數,框架會遍歷這些參數值,并使用內置的prepared statement類進行批量操作。API將根據你是否使用命名參數而有所不同。對于使用命名參數的情況,你需要提供一個SqlParameterSource的數組, 其中的每個元素將將作為批量操作的參數。 你可以使用SqlParameterSource.createBatch方法,通過傳入一個JavaBean的數組或者一個包含了參數鍵值對的Map數組來創建這個數組。

    下面的示例展示了使用命名參數進行批量更新的方法:

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);}public int[] batchUpdate(final List<Actor> actors) {SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray());int[] updateCounts = simpleJdbcTemplate.batchUpdate("update t_actor set first_name = :firstName, last_name = :lastName where id = :id",batch);return updateCounts;}// ... additional methods }

    對于使用傳統的“?”作為參數占位符的情況,你可以傳入一個List,包含了所有需要進行批量更新的對象。這樣的對象數組必須與每個SQL Statement的占位符以及他們在SQL Statement中出現的位置一一對應。

    下面是同樣的例子,使用的傳統的“?”作為參數占位符:

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);}public int[] batchUpdate(final List<Actor> actors) {List<Object[]> batch = new ArrayList<Object[]>();for (Actor actor : actors) {Object[] values = new Object[] {actor.getFirstName(),actor.getLastName(),actor.getId()};batch.add(values);}int[] updateCounts = simpleJdbcTemplate.batchUpdate("update t_actor set first_name = ?, last_name = ? where id = ?",batch);return updateCounts;}// ... additional methods }

    所有的批量更新的方法都會返回一組int的數組,表示在整個操作過程中有多少記錄被批量更新。 這個數量是由JDBC驅動所返回的,有時這個返回并不可靠,尤其對于某些JDBC驅動只是簡單的返回-2作為返回值。

    11.5.?通過使用SimpleJdbc類簡化JDBC操作

    SimpleJdbcInsert類和SimpleJdbcCall類主要利用了JDBC驅動所提供的數據庫元數據的一些特性來簡化數據庫操作配置。 這意味著你可以盡可能的簡化你的數據庫操作配置。當然,你可以可以將元數據處理的特性關閉,從而在你的代碼中詳細指定這些特性。

    11.5.1.?使用SimpleJdbcInsert插入數據

    讓我們從SimpleJdbcInsert類開始。我們將盡可能使用最少量的配置。SimpleJdbcInsert類必須在數據訪問層的初始化方法中被初始化。 在這個例子中,初始化方法為setDataSource方法。你無需繼承自SimpleJdbcInsert 類,只需要創建一個新的實例,并通過withTableName方法設置table名稱。 這個類使用了“fluid”模式返回SimpleJdbcInsert,從而你可以訪問到所有的可以進行配置的方法。在這個例子中,我們只使用了一個方法,稍后我們會看到更多的配置方法。

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcInsert insertActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");}public void add(Actor actor) {Map<String, Object> parameters = new HashMap<String, Object>(3);parameters.put("id", actor.getId());parameters.put("first_name", actor.getFirstName());parameters.put("last_name", actor.getLastName());insertActor.execute(parameters);}// ... additional methods }

    這個方法通過接收 java.utils.Map 作為它唯一的參數。 在這里需要重點注意的是,在Map中的所有的key,必須和數據庫中定義的列名完全匹配。這是因為我們是通過讀取元數據來構造實際的Insert語句的。

    11.5.2.?使用SimpleJdbcInsert來獲取自動生成的主鍵

    接下來,我們對于同樣的插入語句,我們并不傳入id,而是通過數據庫自動獲取主鍵的方式來創建新的Actor對象并插入數據庫。 當我們創建SimpleJdbcInsert實例時, 我們不僅需要指定表名,同時我們通過usingGeneratedKeyColumns方法指定需要數據庫自動生成主鍵的列名。

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcInsert insertActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.insertActor =new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingGeneratedKeyColumns("id");}public void add(Actor actor) {Map<String, Object> parameters = new HashMap<String, Object>(2);parameters.put("first_name", actor.getFirstName());parameters.put("last_name", actor.getLastName());Number newId = insertActor.executeAndReturnKey(parameters);actor.setId(newId.longValue());}// ... additional methods }

    這樣我們可以看到與之前執行的insert操作的不同之處在于,我們無需添加id到參數的Map中去,只需要調用executeReturningKey方法。 這個方法將返回一個java.lang.Number對象,我們可以使用這個對象來創建一個字符類型的實例作為我們的域模型的屬性。 有一點很重要的地方需要注意,我們無法依賴所有的數據庫來返回我們指定的Java類型,因為我們只知道這些對象的基類是java.lang.Number。 如果你有聯合主鍵或者那些非數字類型的主鍵需要生成,那么你可以使用executeReturningKeyHolder方法返回的KeyHolder對象。

    11.5.3.?指定SimpleJdbcInsert所使用的字段

    通過指定所使用的字段名稱,可以使SimpleJdbcInsert僅使用這些字段作為insert語句所使用的字段。這可以通過usingColumns方法進行配置。

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcInsert insertActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.insertActor =new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingColumns("first_name", "last_name").usingGeneratedKeyColumns("id");}public void add(Actor actor) {Map<String, Object> parameters = new HashMap<String, Object>(2);parameters.put("first_name", actor.getFirstName());parameters.put("last_name", actor.getLastName());Number newId = insertActor.executeAndReturnKey(parameters);actor.setId(newId.longValue());}// ... additional methods }

    執行這樣的insert語句所使用的字段,與之前我們所依賴的數據庫元數據是一致的。

    11.5.4.?使用SqlParameterSource提供參數值

    使用Map來指定參數值有時候工作得非常好,但是這并不是最簡單的使用方式。Spring提供了一些其他的SqlParameterSource實現類來指定參數值。 我們首先可以看看BeanPropertySqlParameterSource類,這是一個非常簡便的指定參數的實現類,只要你有一個符合JavaBean規范的類就行了。它將使用其中的getter方法來獲取參數值。 下面是一個例子:

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcInsert insertActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.insertActor =new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingGeneratedKeyColumns("id");}public void add(Actor actor) {SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);Number newId = insertActor.executeAndReturnKey(parameters);actor.setId(newId.longValue());}// ... additional methods }

    另外一個實現類:MapSqlParameterSource也使用Map來指定參數,但是他另外提供了一個非常簡便的addValue方法,可以被連續調用,來增加參數。

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcInsert insertActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.insertActor =new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingGeneratedKeyColumns("id");}public void add(Actor actor) {SqlParameterSource parameters = new MapSqlParameterSource().addValue("first_name", actor.getFirstName()).addValue("last_name", actor.getLastName());Number newId = insertActor.executeAndReturnKey(parameters);actor.setId(newId.longValue());}// ... additional methods }

    正如你看到的,配置是一樣的,區別只是切換了不同的提供參數的實現方式來執行調用。

    11.5.5.?使用SimpleJdbcCall調用存儲過程

    接下來我們把我們的關注點放在使用 SimpleJdbcCall 來進行存儲過程的調用上。 設計這個類的目的在于使得調用存儲過程盡可能簡單。它同樣利用的數據庫元數據的特性來查找傳入的參數和返回值。 這意味著你無需明確聲明那些參數。當然,如果你喜歡,可以依然聲明這些參數,尤其對于某些參數,你無法直接將他們映射到Java類上,例如ARRAY類型和STRUCT類型的參數。 在我們的第一個示例中,我們可以看到一個簡單的存儲過程調用,它僅僅返回VARCHAR和DATE類型。 這里,我特地為Actor類增加了一個birthDate的屬性,從而可以使得返回值擁有不同的數據類型。 這個存儲過程讀取actor的主鍵,并返回first_name,last_name,和birth_date字段作為返回值。 下面是這個存儲過程的源碼,它可以工作在MySQL數據庫上:

    ?

    CREATE PROCEDURE read_actor ( IN in_id INTEGER, OUT out_first_name VARCHAR(100), OUT out_last_name VARCHAR(100), OUT out_birth_date DATE) BEGIN SELECT first_name, last_name, birth_date INTO out_first_name, out_last_name, out_birth_date FROM t_actor where id = in_id; END;

    正如你看到的,這里有四個參數,其中一個是傳入的參數“in_id”,表示了Actor的主鍵,剩下的參數是作為讀取數據庫表中的數據所返回的返回值。

    SimpleJdbcCall的聲明與SimpleJdbcInsert類似,你無需繼承這個類,而只需要在初始化方法中進行初始化。 在這里例子中,我們只需要指定存儲過程的名稱。

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcCall procReadActor;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);this.procReadActor =new SimpleJdbcCall(dataSource).withProcedureName("read_actor");}public Actor readActor(Long id) {SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id); Map out = procReadActor.execute(in);Actor actor = new Actor();actor.setId(id);actor.setFirstName((String) out.get("out_first_name"));actor.setLastName((String) out.get("out_last_name"));actor.setBirthDate((Date) out.get("out_birth_date"));return actor;}// ... additional methods }

    通過SimpleJdbcCall執行存儲過程需要創建一個SqlParameterSource的實現類來指定傳入的參數。 需要注意的是,傳入參數的名稱與存儲過程中定義的名稱必須保持一致。這里,我們無需保持一致,因為我們使用數據庫的元數據信息來決定我們需要什么樣的數據庫對象。 當然,你在源代碼中所指定的名稱可能和數據庫中完全不同,有的數據庫會把這些名稱全部轉化成大寫,而有些數據庫會把這些名稱轉化為小寫。

    execute方法接收傳入的參數,并返回一個Map作為返回值,這個Map包含所有在存儲過程中指定的參數名稱作為key。 在這個例子中,他們分別是out_first_name,out_last_name和 out_birth_date。

    execute方法的最后部分是使用存儲過程所返回的值創建一個新的Actor實例。 同樣的,這里我們將名稱與存儲過程中定義的名稱保持一致非常重要。在這個例子中,在返回的Map中所定義的key值和數據庫的存儲過程中定義的值一致。 你可能需要在這里指定Spring使用Jakarta Commons提供的CaseInsensitiveMap。這樣做,你需要在創建你自己的JdbcTemplate類時,設置setResultsMapCaseInsensitive屬性為True。 然后,你將這個自定義的JdbcTemplate傳入SimpleJdbcCall的構造函數。當然,你需要把commons-collections.jar加入到classpath中去。 下面是配置示例:

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcCall procReadActor;public void setDataSource(DataSource dataSource) {JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);jdbcTemplate.setResultsMapCaseInsensitive(true);this.procReadActor =new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_actor");}// ... additional methods }

    通過這樣的配置,你就可以無需擔心返回參數值的大小寫問題。

    11.5.6.?聲明SimpleJdbcCall使用的參數

    你已經看到如何通過元數據來簡化參數配置,但是你也可以明確地指定這些參數。可以在創建SimpleJdbcCall時,通過使用declareParameters方法來聲明參數。 這個方法接收一組SqlParameter對象作為參數。我們可以參照下一個章節,如何創建SqlParameter。

    我們可以有選擇性的顯示聲明一個、多個、甚至所有的參數。參數元數據在這里會被同時使用。 通過調用withoutProcedureColumnMetaDataAccess方法,我們可以指定數據庫忽略所有的元數據處理并使用顯示聲明的參數。 另外一種情況是,其中的某些參數值具有默認的返回值,我們需要在返回值中指定這些返回值。為了實現這個特性,我們可以使用useInParameterNames來指定一組需要被包含的參數名稱。

    這是一個完整的聲明存儲過程調用的例子:

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcCall procReadActor;public void setDataSource(DataSource dataSource) {JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);jdbcTemplate.setResultsMapCaseInsensitive(true);this.procReadActor =new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_actor").withoutProcedureColumnMetaDataAccess().useInParameterNames("in_id").declareParameters(new SqlParameter("in_id", Types.NUMERIC),new SqlOutParameter("out_first_name", Types.VARCHAR),new SqlOutParameter("out_last_name", Types.VARCHAR),new SqlOutParameter("out_birth_date", Types.DATE));}// ... additional methods }

    執行和最終的返回處理是相同的,我們在這里只是明確聲明了參數類型,而不是依賴數據庫元數據特性。 這一點很重要,尤其對于那些數據庫并不支持元數據的情況。當前,我們支持元數據的特性的數據包含:Apache Derby、DB2、MySQL、 Microsoft SQL Server、Oracle和Sybase。我們同時對某些數據庫內置函數支持元數據特性:MySQL、Microsoft SQL Server和Oracle。

    11.5.7.?如何定義SqlParameters

    為SimpleJdbc類或者后續章節提到的RDBMS操作指定參數,你需要使用SqlParameter或者他的子類。 你可以通過指定參數名稱以及對應的SQL類型并傳入構造函數作為參數來指定SqlParameter,其中,SQL類型是java.sql.Types中所定義的常量。 我們已經看到過類似的聲明:

    ?

    new SqlParameter("in_id", Types.NUMERIC),new SqlOutParameter("out_first_name", Types.VARCHAR),

    ?

    第一行的SqlParameter定義了一個傳入參數。傳入參數可以在所有的存儲過程中使用,也可以在稍后章節中提到的SqlQuery類及其子類中使用。

    第二行SqlOutParameter定義了一個返回值。它可以被存儲過程調用所使用。當然,還有一個SqlInOutParameter類,可以用于輸入輸出參數。 也就是說,它既是一個傳入參數,也是一個返回值。

    除了參數名稱和SQL類型,你還可以聲明一些其他額外的選項。對于傳入參數,你可以為numeric數據類型指定精度,或者對于特定的數據庫指定特殊類型。 對于返回值,你可以提供一個RowMapper接口來處理所有從REF cursor返回的列。另外一個選項是指定一個SqlReturnType類,從而可以定制返回值的處理方式。

    11.5.8.?使用SimpleJdbcCall調用內置函數

    內置函數的調用幾乎和存儲過程的調用是一樣的。唯一的不同在于,你需要聲明的是一個函數的名稱而不是存儲過程的名稱。 這可以通過withFunctionName方法來完成。使用這個方法,表明你的調用是一個函數。你所指定的這個函數名稱將被作為調用對象。 同時有一個叫做executeFunction的方法,將獲得特定的Java類型的函數調用的返回值。 此時,你無需通過返回的Map來獲取返回值。另外有一個類似的便捷方法executeObject用于存儲過程,但是他只能處理單個返回值的情況。 下面的示例展示了一個叫做get_actor_name 的函數調用,返回actor的完整的名稱。 這個函數將工作在MySQL數據庫上。

    ?

    CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200) READS SQL DATA BEGINDECLARE out_name VARCHAR(200);SELECT concat(first_name, ' ', last_name)INTO out_nameFROM t_actor where id = in_id;RETURN out_name; END;

    ?

    調用這個函數,我們依然在初始化方法中創建SimpleJdbcCall

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcCall funcGetActorName;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);jdbcTemplate.setResultsMapCaseInsensitive(true);this.funcGetActorName =new SimpleJdbcCall(jdbcTemplate).withFunctionName("get_actor_name");}public String getActorName(Long id) {SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id); String name = funcGetActorName.executeFunction(String.class, in);return name;}// ... additional methods }

    被調用的函數返回一個String類型。

    11.5.9.?使用SimpleJdbcCall返回的ResultSet/REF Cursor

    期望通過調用存儲過程或者函數來返回ResultSet一直是一個問題。一些數據庫在JDBC結果處理中返回結果集,而另外一些數據庫則需要明確指定返回值的類型。 無論哪種方法,都需要在循環遍歷結果集時,做出一些額外的工作,從而處理每一條記錄。 通過SimpleJdbcCall,你可以使用returningResultSet方法,并定義一個RowMapper的實現類來處理特定的返回值。 當結果集在返回結果處理過程中沒有被定義名稱時,返回的結果集必須與定義的RowMapper的實現類指定的順序保持一致。 而指定的名字也會被用作返回結果集中的名稱。

    在這個例子中,我們將使用一個存儲過程,它并不接收任何參數,返回t_actor表中的所有的行,下面是MySQL數據庫中的存儲過程源碼:

    CREATE PROCEDURE read_all_actors() BEGINSELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END;

    要調用這個存儲過程,我們需要定義一個RowMapper的實現類。我們所使用的類遵循JavaBean的規范,所以我們可以使用ParameterizedBeanPropertyRowMapper作為實現類。 通過將相應的class類作為參數傳入到newInstance方法中,我們可以創建這個實現類。

    ?

    public class JdbcActorDao implements ActorDao {private SimpleJdbcTemplate simpleJdbcTemplate;private SimpleJdbcCall procReadAllActors;public void setDataSource(DataSource dataSource) {this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);jdbcTemplate.setResultsMapCaseInsensitive(true);this.procReadAllActors =new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_all_actors").returningResultSet("actors",ParameterizedBeanPropertyRowMapper.newInstance(Actor.class));}public List getActorsList() {Map m = procReadAllActors.execute(new HashMap<String, Object>(0));return (List) m.get("actors");}// ... additional methods }

    這個函數調用傳入一個空的Map進入,因為這里不需要任何的參數傳入。而函數調用所返回的結果集將返回的是Actors列表。

    11.6.?用Java對象來表達JDBC操作

    org.springframework.jdbc.object包下的類允許用戶以更加 面向對象的方式去訪問數據庫。比如說,用戶可以執行查詢并返回一個list, 該list作為一個結果集將把從數據庫中取出的列數據映射到業務對象的屬性上。 用戶也可以執行存儲過程,以及運行更新、刪除以及插入SQL語句。

    Note

    在許多Spring開發人員中間存在有一種觀點,那就是下面將要提到的各種RDBMS操作類 (StoredProcedure類除外) 通常也可以直接使用JdbcTemplate相關的方法來替換。 相對于把一個查詢操作封裝成一個類而言,直接調用JdbcTemplate方法將更簡單而且更容易理解。

    必須強調的一點是,這僅僅只是一種觀點而已, 如果你認為你可以從直接使用RDBMS操作類中獲取一些額外的好處,你不妨根據自己的需要和喜好進行不同的選擇。

    11.6.1.?SqlQuery類

    SqlQuery是一個可重用、線程安全的類,它封裝了一個SQL查詢。 其子類必須實現newResultReader()方法,該方法用來在遍歷 ResultSet的時候能使用一個類來保存結果。 我們很少需要直接使用SqlQuery,因為其子類 MappingSqlQuery作為一個更加易用的實現能夠將結果集中的行映射為Java對象。 SqlQuery還有另外兩個擴展分別是 MappingSqlQueryWithParameters和UpdatableSqlQuery。

    11.6.2.?MappingSqlQuery類

    MappingSqlQuery是一個可重用的查詢抽象類,其具體類必須實現 mapRow(ResultSet, int)抽象方法來將結果集中的每一行轉換成Java對象。 下面這個例子演示了一個定制查詢,它將從客戶表中取得的數據映射到一個Customer類實例。

    private class CustomerMappingQuery extends MappingSqlQuery {public CustomerMappingQuery(DataSource ds) {super(ds, "SELECT id, name FROM customer WHERE id = ?");super.declareParameter(new SqlParameter("id", Types.INTEGER));compile();}public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {Customer cust = new Customer();cust.setId((Integer) rs.getObject("id"));cust.setName(rs.getString("name"));return cust;} }

    在上面的例子中,我們為用戶查詢提供了一個構造函數并為構造函數傳遞了一個 DataSource參數。在構造函數里面我們把 DataSource和一個用來返回查詢結果的SQL語句作為參數 調用父類的構造函數。SQL語句將被用于生成一個PreparedStatement對象, 因此它可以包含占位符來傳遞參數。而每一個SQL語句的參數必須通過調用 declareParameter方法來進行聲明,該方法需要一個 SqlParameter(封裝了一個字段名字和一個 java.sql.Types中定義的JDBC類型)對象作為參數。 所有參數定義完之后,我們調用compile()方法來對SQL語句進行預編譯。

    public Customer getCustomer(Integer id) {CustomerMappingQuery custQry = new CustomerMappingQuery(dataSource); Object[] parms = new Object[1];parms[0] = id;List customers = custQry.execute(parms);if (customers.size() > 0) {return (Customer) customers.get(0);}else {return null;} }

    在上面的例子中,getCustomer方法通過傳遞惟一參數id來返回一個客戶對象。 該方法內部在創建CustomerMappingQuery實例之后, 我們創建了一個對象數組用來包含要傳遞的查詢參數。這里我們只有唯一的一個 Integer參數。執行CustomerMappingQuery的 execute方法之后,我們得到了一個List,該List中包含一個 Customer對象,如果有對象滿足查詢條件的話。

    11.6.3.?SqlUpdate類

    SqlUpdate類封裝了一個可重復使用的SQL更新操作。 跟所有RdbmsOperation類一樣,SqlUpdate可以在SQL中定義參數。 該類提供了一系列update()方法,就像SqlQuery提供的一系列execute()方法一樣。 SqlUpdate是一個具體的類。通過在SQL語句中定義參數,這個類可以支持不同的更新方法,我們一般不需要通過繼承來實現定制。

    import java.sql.Types;import javax.sql.DataSource;import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.SqlUpdate;public class UpdateCreditRating extends SqlUpdate {public UpdateCreditRating(DataSource ds) {setDataSource(ds);setSql("update customer set credit_rating = ? where id = ?");declareParameter(new SqlParameter(Types.NUMERIC));declareParameter(new SqlParameter(Types.NUMERIC));compile();}/*** @param id for the Customer to be updated* @param rating the new value for credit rating* @return number of rows updated*/public int run(int id, int rating) {Object[] params =new Object[] {new Integer(rating),new Integer(id)};return update(params);} }

    11.6.4.?StoredProcedure類

    StoredProcedure類是一個抽象基類,它是對RDBMS存儲過程的一種抽象。 該類提供了多種execute(..)方法,不過這些方法的訪問類型都是protected的。

    從父類繼承的sql屬性用來指定RDBMS存儲過程的名字。 盡管該類提供了許多必須在JDBC3.0下使用的功能,但是我們更關注的是JDBC 3.0中引入的命名參數特性。

    下面的程序演示了如何調用Oracle中的sysdate()函數。 這里我們創建了一個繼承StoredProcedure的子類,雖然它沒有輸入參數, 但是我必須通過使用SqlOutParameter來聲明一個日期類型的輸出參數。 execute()方法將返回一個map,map中的每個entry是一個用參數名作key,以輸出參數為value的名值對。

    import java.sql.Types; import java.util.HashMap; import java.util.Iterator; import java.util.Map;import javax.sql.DataSource;import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.datasource.*; import org.springframework.jdbc.object.StoredProcedure;public class TestStoredProcedure {public static void main(String[] args) {TestStoredProcedure t = new TestStoredProcedure();t.test();System.out.println("Done!");}void test() {DriverManagerDataSource ds = new DriverManagerDataSource();ds.setDriverClassName("oracle.jdbc.OracleDriver");ds.setUrl("jdbc:oracle:thin:@localhost:1521:mydb");ds.setUsername("scott");ds.setPassword("tiger");MyStoredProcedure sproc = new MyStoredProcedure(ds);Map results = sproc.execute();printMap(results);}private class MyStoredProcedure extends StoredProcedure {private static final String SQL = "sysdate";public MyStoredProcedure(DataSource ds) {setDataSource(ds);setFunction(true);setSql(SQL);declareParameter(new SqlOutParameter("date", Types.DATE));compile();}public Map execute() {// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...return execute(new HashMap());}}private static void printMap(Map results) {for (Iterator it = results.entrySet().iterator(); it.hasNext(); ) {System.out.println(it.next()); }} }

    下面是StoredProcedure的另一個例子,它使用了兩個Oracle游標類型的輸出參數。

    import oracle.jdbc.driver.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure;import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;public class TitlesAndGenresStoredProcedure extends StoredProcedure {private static final String SPROC_NAME = "AllTitlesAndGenres";public TitlesAndGenresStoredProcedure(DataSource dataSource) {super(dataSource, SPROC_NAME);declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));compile();}public Map execute() {// again, this sproc has no input parameters, so an empty Map is supplied...return super.execute(new HashMap());} }

    值得注意的是TitlesAndGenresStoredProcedure構造函數中 declareParameter(..)的SqlOutParameter參數, 該參數使用了RowMapper接口的實現。這是一種非常方便而強大的重用方式。 下面我們來看一下RowMapper的兩個具體實現。

    首先是TitleMapper類,它簡單的把ResultSet中的每一行映射為一個TitleDomain Object。

    import com.foo.sprocs.domain.Title; import org.springframework.jdbc.core.RowMapper;import java.sql.ResultSet; import java.sql.SQLException;public final class TitleMapper implements RowMapper {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {Title title = new Title();title.setId(rs.getLong("id"));title.setName(rs.getString("name"));return title;} }

    另一個是GenreMapper類,也是非常簡單的將ResultSet中的每一行映射為一個GenreDomain Object。

    import org.springframework.jdbc.core.RowMapper;import java.sql.ResultSet; import java.sql.SQLException;import com.foo.domain.Genre;public final class GenreMapper implements RowMapper {public Object mapRow(ResultSet rs, int rowNum) throws SQLException {return new Genre(rs.getString("name"));} }

    如果你需要給存儲過程傳輸入參數(這些輸入參數是在RDBMS存儲過程中定義好了的), 則需要提供一個指定類型的execute(..)方法, 該方法將調用基類的protected execute(Map parameters)方法。例如:

    import oracle.jdbc.driver.OracleTypes; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.object.StoredProcedure;import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;public class TitlesAfterDateStoredProcedure extends StoredProcedure {private static final String SPROC_NAME = "TitlesAfterDate";private static final String CUTOFF_DATE_PARAM = "cutoffDate";public TitlesAfterDateStoredProcedure(DataSource dataSource) {super(dataSource, SPROC_NAME);declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE);declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));compile();}public Map execute(Date cutoffDate) {Map inputs = new HashMap();inputs.put(CUTOFF_DATE_PARAM, cutoffDate);return super.execute(inputs);} }

    11.6.5.?SqlFunction類

    SqlFunction RDBMS操作類封裝了一個SQL“函數”包裝器(wrapper), 該包裝器適用于查詢并返回一個單行結果集。默認返回的是一個int值, 不過我們可以采用類似JdbcTemplate中的queryForXxx 做法自己實現來返回其它類型。SqlFunction優勢在于我們不必創建 JdbcTemplate,這些它都在內部替我們做了。

    該類的主要用途是調用SQL函數來返回一個單值的結果集,比如類似“select user()”、 “select sysdate from dual”的查詢。如果需要調用更復雜的存儲函數, (可以為這種類型的處理使用StoredProcedure或SqlCall)。

    SqlFunction是一個具體類,通常我們不需要它的子類。 其用法是創建該類的實例,然后聲明SQL語句以及參數就可以調用相關的run方法去多次執行函數。 下面的例子用來返回指定表的記錄行數:

    public int countRows() {SqlFunction sf = new SqlFunction(dataSource, "select count(*) from mytable");sf.compile();return sf.run(); }

    11.7.?參數和數據處理的基本原則

    在Spring的JDBC框架的所有工作模式中貫徹了一些與參數和數據處理相關的基本原則。

    11.7.1.?為參數設置SQL類型信息

    多數情況下,Spring會根據傳入的參數值來設定相應的SQL類型。有時,我們有必要明確指定傳入參數所代表的SQL類型,這一點對于正確設置NULL值的時候可能比較有用。

    另外還有一些其他的不同方面的作用:

    • 多數JdbcTemplate的update或者query方法會接收一個額外的int數組構成的參數。 這個數組需要提供的是使用java.sql.Types中所定義的SQL類型。而這個數組中定義的類型需要與每個傳入的參數所對應。

    • 你可以使用SqlParameterValue對參數進行額外的封裝從而包裝更多的參數信息。通過傳入參數值和對應的SQL類型作為構造函數的參數,你可以創建這個類的一個實例。 你也可以為numeric的值提供一些額外的精度要求。

    • 對于那些使用命名參數的情況,你可以使用SqlParameterSource、BeanPropertySqlParameterSource或者MapSqlParameterSource類。 他們都具備了為命名參數注冊SQL類型的功能。

    11.7.2.?處理BLOB 和 CLOB對象

    你可以在數據庫中存儲圖像、二進制對象或者大文本等對象。這些較大的二進制對象被稱之為BLOB類型,而對應的大文本對象被稱之為CLOB對象。 Spring允許你使用JdbcTemplate、更高層次封裝的RDBMS對象和SimpleJdbc類對這些大對象進行操作。 所有的這些操作方式都實現了LobHandler接口來處理LOB類型的數據。 LobHandler接口提供了訪問LobCreator的方法,通過調用getLobCreator,你可以創建一個新的LOB類型的數據。

    LobCreator/LobHandler接口針對LOB類型的數據操作提供了下列支持:

    ?

    • 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

    ?

    現在我們通過一個示例來展示如何創建一個BLOB數據并插入數據庫。稍后的例子,我們將展示如何從數據庫中將BLOB數據讀取出來。

    這個例子使用JdbcTemplate和一個AbstractLobCreatingPreparedStatementCallback的實現類。 這里唯一需要實現的方法就是"setValues"。在這個方法中,將提供一個LobCreator接口,被用作在你的插入語句中設置LOB字段的值。

    我們假設有一個變量叫做“lobHandler”已經被設置到DefaultLobHandler的實例中。當然,這是由注入完成的。

    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();

    我們在這里使用的lobHandler實現類是一個普通的DefaultLobHandler

    使用setClobAsCharacterStream,我們傳入CLOB的內容

    使用setBlobAsBinartStream,我們傳入BLOB的內容

    現在我們來示范從數據庫中讀取LOB數據。我們這里再次使用JdbcTempate并使用相同的DefaultLobHandler實例。

    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;}});

    使用getClobAsString 獲取CLOB內容

    使用getBlobAsBytes獲取BLOB內容

    11.7.3.?在IN語句中傳入一組參數值

    SQL標準允許基于一個帶參數列表的表達式進行查詢。一個典型的例子可能像這樣:"select * from T_ACTOR where id in (1, 2, 3)"。 不過這種參數列表的方式并不能直接被JDBC標準所支持 - 因為并不存在這種聲明一個列表參數作為占位符的方式。 你不得不為此寫多個占位符來表示多個參數,或者當你知道占位符的數量時,你可以動態構建SQL字符串。 NamedParameterJdbcTemplate和SimpleJdbcTemplate中所提供的命名參數的特性,采用的是后面一種做法。 當你傳入參數時,你需要傳入一個java.util.List類型,支持基本類型。而這個list將會在SQL執行時替換占位符并傳入參數。

    Note

    在使用IN語句時,當你傳入大批量的值時要小心,JDBC標準并不確保超過100個元素在IN語句中。 有不少數據庫可以超出這個值的限制,但是不同的數據庫會有不同的數量限制,比如Oracle的限制數量是1000個。

    除了基本類型之外,你還可以創建一個java.util.List的對象數組,這可以讓你支持在IN表達式中編寫多重表達式,例如"select * from T_ACTOR where (id, last_name) in ((1, 'Johnson'), (2, 'Harrop'))". 當然,這樣做的前提是數據庫底層的語法支持。

    11.7.4.?處理復雜類型的存儲過程調用

    當調用存儲過程時,有時需要使用數據庫特定的復雜類型。為了適應這些類型,Spring提供了SqlReturnType類來處理存儲過程的返回值,而使用SqlTypeValue來處理傳入的參數。

    下面這個例子,將Oracle的STRUCT對象作為返回值,這是一個由用戶自定義的“ITEM_TYPE”。 SqlReturnType接口有唯一的方法“getTypeValue”需要被實現。而這個接口的實現將被用作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;}}));

    通過Java代碼調用存儲過程使用SqlTypeValue來傳入一個TestItem作為參數。 SqlTypeValue接口有一個方法"createTypeValue"需要被實現。 一個活動的數據庫連接也同時被傳入,它將被用作創建數據庫特定的對象,類似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 現在可以被加入到作為參數的Map中去,從而可以執行相應的存儲過程。

    ?

    總結

    以上是生活随笔為你收集整理的使用Spring JDBC进行数据访问 (JdbcTemplate/NamedParameterJdbcTemplate/SimpleJdbcTemplate/SimpleJdbcCall/Stor)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    欧美怡红院免费全部视频 | 日本免费一区二区三区最新 | 亚洲春色在线视频 | 麻豆蜜桃av蜜臀av色欲av | 精品久久综合1区2区3区激情 | 丰满人妻精品国产99aⅴ | 日韩少妇白浆无码系列 | 亚洲中文字幕乱码av波多ji | 国产精品久久久久久久影院 | 国内精品九九久久久精品 | 欧美日韩一区二区免费视频 | 人妻中文无码久热丝袜 | 午夜福利不卡在线视频 | 久久久精品成人免费观看 | 天下第一社区视频www日本 | 97夜夜澡人人双人人人喊 | 老熟女重囗味hdxx69 | 免费人成在线视频无码 | 国产性生大片免费观看性 | 377p欧洲日本亚洲大胆 | 欧美xxxx黑人又粗又长 | 国色天香社区在线视频 | 精品熟女少妇av免费观看 | 内射白嫩少妇超碰 | 性欧美videos高清精品 | 国产午夜福利100集发布 | 中文无码精品a∨在线观看不卡 | 免费无码午夜福利片69 | 久久精品国产精品国产精品污 | 中文字幕精品av一区二区五区 | 国产精品二区一区二区aⅴ污介绍 | 国产熟妇另类久久久久 | www国产精品内射老师 | 亚欧洲精品在线视频免费观看 | 国产xxx69麻豆国语对白 | 人妻aⅴ无码一区二区三区 | 免费看男女做好爽好硬视频 | 亚洲国产高清在线观看视频 | 午夜精品一区二区三区在线观看 | 国产 精品 自在自线 | 亚洲国产精品久久人人爱 | 国产一精品一av一免费 | 亚洲国产精品美女久久久久 | 熟妇人妻激情偷爽文 | 漂亮人妻洗澡被公强 日日躁 | 精品久久综合1区2区3区激情 | 免费人成网站视频在线观看 | 老熟女乱子伦 | 亚洲欧美国产精品专区久久 | 好爽又高潮了毛片免费下载 | 亚洲成av人片天堂网无码】 | 日韩精品乱码av一区二区 | 亚洲精品综合一区二区三区在线 | 久久久久久久女国产乱让韩 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产精品怡红院永久免费 | 亚洲精品国产精品乱码视色 | 精品亚洲成av人在线观看 | 欧美日韩人成综合在线播放 | 国产精品视频免费播放 | 宝宝好涨水快流出来免费视频 | 日本饥渴人妻欲求不满 | 草草网站影院白丝内射 | 国产手机在线αⅴ片无码观看 | 三上悠亚人妻中文字幕在线 | 永久黄网站色视频免费直播 | 亚洲精品成人av在线 | 国产黄在线观看免费观看不卡 | 精品国产一区二区三区av 性色 | 色综合久久久无码网中文 | 久久亚洲中文字幕无码 | 精品无码一区二区三区的天堂 | 欧美成人免费全部网站 | 国产亚洲精品久久久ai换 | 樱花草在线社区www | 亚洲色大成网站www | 亚洲 欧美 激情 小说 另类 | 国产肉丝袜在线观看 | 国产人成高清在线视频99最全资源 | 久久综合久久自在自线精品自 | 欧美老人巨大xxxx做受 | 亚洲国产av美女网站 | 免费观看又污又黄的网站 | 一本久久伊人热热精品中文字幕 | 老太婆性杂交欧美肥老太 | 亚洲国产午夜精品理论片 | 亚洲乱码日产精品bd | 超碰97人人做人人爱少妇 | 麻豆蜜桃av蜜臀av色欲av | 日本大乳高潮视频在线观看 | 少妇性荡欲午夜性开放视频剧场 | 最新版天堂资源中文官网 | 国产午夜无码精品免费看 | 中文无码精品a∨在线观看不卡 | 久久久精品欧美一区二区免费 | 国产精品高潮呻吟av久久 | 久久久久久亚洲精品a片成人 | 亚洲精品一区二区三区在线 | 日本精品人妻无码77777 天堂一区人妻无码 | 成人一区二区免费视频 | 久久天天躁夜夜躁狠狠 | 亚洲综合在线一区二区三区 | 久久精品视频在线看15 | 一本精品99久久精品77 | 免费播放一区二区三区 | 在线成人www免费观看视频 | 无码吃奶揉捏奶头高潮视频 | 牛和人交xxxx欧美 | 熟妇人妻无乱码中文字幕 | 国产色视频一区二区三区 | 永久免费观看国产裸体美女 | 蜜桃视频韩日免费播放 | 无码精品人妻一区二区三区av | 亚洲色大成网站www国产 | 免费人成网站视频在线观看 | 中文字幕精品av一区二区五区 | 少妇愉情理伦片bd | 蜜臀av无码人妻精品 | 亚洲日本va中文字幕 | 强开小婷嫩苞又嫩又紧视频 | 成熟女人特级毛片www免费 | 亚洲va欧美va天堂v国产综合 | 欧美放荡的少妇 | 亚洲最大成人网站 | 福利一区二区三区视频在线观看 | 日日天干夜夜狠狠爱 | 中文字幕 亚洲精品 第1页 | 精品久久8x国产免费观看 | 国产又粗又硬又大爽黄老大爷视 | 午夜熟女插插xx免费视频 | 久久成人a毛片免费观看网站 | 亚洲狠狠色丁香婷婷综合 | 精品无码成人片一区二区98 | 午夜免费福利小电影 | 蜜桃无码一区二区三区 | 久久人人爽人人爽人人片ⅴ | 一本久久伊人热热精品中文字幕 | 亚洲中文字幕无码中文字在线 | 日产精品高潮呻吟av久久 | 夜夜影院未满十八勿进 | 日本一卡2卡3卡四卡精品网站 | 国产成人综合色在线观看网站 | 精品久久8x国产免费观看 | 亚洲中文字幕无码一久久区 | 亚洲第一网站男人都懂 | 国产一区二区三区影院 | 久久国产精品二国产精品 | 亚洲色大成网站www国产 | 伊人久久大香线蕉午夜 | 天堂在线观看www | 亚洲精品国产精品乱码视色 | 国内揄拍国内精品人妻 | 亚洲成a人片在线观看无码 | 国产精品嫩草久久久久 | 国产午夜福利亚洲第一 | 无码人妻丰满熟妇区五十路百度 | 女人色极品影院 | 麻豆精品国产精华精华液好用吗 | 极品嫩模高潮叫床 | 麻豆国产人妻欲求不满 | 亚洲精品一区二区三区大桥未久 | 色一情一乱一伦一区二区三欧美 | 亚洲人成无码网www | 无码人妻丰满熟妇区毛片18 | 成人欧美一区二区三区 | 亚洲国产精品毛片av不卡在线 | 久久久久亚洲精品男人的天堂 | 中文字幕乱码中文乱码51精品 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 在线欧美精品一区二区三区 | 丝袜美腿亚洲一区二区 | 久久伊人色av天堂九九小黄鸭 | 日本精品人妻无码77777 天堂一区人妻无码 | 最近中文2019字幕第二页 | 熟女少妇人妻中文字幕 | 黑人粗大猛烈进出高潮视频 | 女人被爽到呻吟gif动态图视看 | 国产成人av免费观看 | 俺去俺来也www色官网 | 国产精品va在线观看无码 | 国产在热线精品视频 | 精品久久8x国产免费观看 | 男女爱爱好爽视频免费看 | 久久精品国产99久久6动漫 | 国产精品无码一区二区桃花视频 | 日韩精品无码一区二区中文字幕 | 色欲人妻aaaaaaa无码 | 九月婷婷人人澡人人添人人爽 | 久久熟妇人妻午夜寂寞影院 | 日本精品高清一区二区 | 色妞www精品免费视频 | 国产精品久久久久无码av色戒 | 亚洲色欲色欲天天天www | 久久久久99精品国产片 | 精品亚洲成av人在线观看 | 成人无码视频在线观看网站 | 狂野欧美激情性xxxx | 玩弄人妻少妇500系列视频 | 日本一区二区三区免费播放 | 欧美精品国产综合久久 | 日韩精品无码免费一区二区三区 | 亚洲成av人片天堂网无码】 | 色欲av亚洲一区无码少妇 | 宝宝好涨水快流出来免费视频 | 亚洲欧洲中文日韩av乱码 | 男女作爱免费网站 | 亚洲欧美精品伊人久久 | 正在播放东北夫妻内射 | 亚洲精品欧美二区三区中文字幕 | 日韩少妇内射免费播放 | 人人妻人人澡人人爽欧美一区 | 美女黄网站人色视频免费国产 | 最近中文2019字幕第二页 | 国产xxx69麻豆国语对白 | 国产乱人伦av在线无码 | 俺去俺来也www色官网 | v一区无码内射国产 | 国产精品丝袜黑色高跟鞋 | 漂亮人妻洗澡被公强 日日躁 | 亚洲精品一区二区三区在线 | 亚洲成a人一区二区三区 | 国产婷婷色一区二区三区在线 | 日韩人妻无码一区二区三区久久99 | 色婷婷综合激情综在线播放 | 国产人妻大战黑人第1集 | 午夜精品久久久内射近拍高清 | 麻豆av传媒蜜桃天美传媒 | 亚洲国产av精品一区二区蜜芽 | 少妇被黑人到高潮喷出白浆 | 色综合久久久无码网中文 | 国产熟妇高潮叫床视频播放 | 少妇无码一区二区二三区 | 成人无码视频免费播放 | 久久久久99精品成人片 | av无码电影一区二区三区 | 亚洲国精产品一二二线 | 日本乱人伦片中文三区 | 亚洲成熟女人毛毛耸耸多 | 亚洲国产精品一区二区第一页 | aa片在线观看视频在线播放 | 日日摸日日碰夜夜爽av | 无码乱肉视频免费大全合集 | 亚洲欧洲无卡二区视頻 | 欧美一区二区三区 | 亚洲人成网站免费播放 | 88国产精品欧美一区二区三区 | 亚洲天堂2017无码中文 | 日本熟妇浓毛 | 亚洲国产午夜精品理论片 | 国产精品久久久久无码av色戒 | 亚洲午夜久久久影院 | 久久人人爽人人人人片 | 欧美放荡的少妇 | 久久99精品国产麻豆蜜芽 | 一区二区传媒有限公司 | 亚洲综合在线一区二区三区 | 国产精品亚洲专区无码不卡 | 婷婷丁香五月天综合东京热 | 高潮毛片无遮挡高清免费视频 | 2020最新国产自产精品 | 欧美大屁股xxxxhd黑色 | 东京热男人av天堂 | 婷婷色婷婷开心五月四房播播 | 久久精品视频在线看15 | 欧美性生交活xxxxxdddd | 亚洲精品久久久久久一区二区 | 国产亚洲人成a在线v网站 | 国产成人无码av在线影院 | 亚洲乱亚洲乱妇50p | 天堂久久天堂av色综合 | 婷婷丁香五月天综合东京热 | 欧美日韩视频无码一区二区三 | 男女猛烈xx00免费视频试看 | 国产亲子乱弄免费视频 | 蜜桃av抽搐高潮一区二区 | 欧洲精品码一区二区三区免费看 | 人妻尝试又大又粗久久 | 沈阳熟女露脸对白视频 | 沈阳熟女露脸对白视频 | 国内精品一区二区三区不卡 | 国产 精品 自在自线 | 日本一卡二卡不卡视频查询 | 男女下面进入的视频免费午夜 | 欧美日韩在线亚洲综合国产人 | 无码人妻精品一区二区三区下载 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 久久亚洲日韩精品一区二区三区 | 久久亚洲中文字幕精品一区 | 亚洲欧洲中文日韩av乱码 | 人妻少妇精品视频专区 | 国产亚洲欧美日韩亚洲中文色 | 国产乱人无码伦av在线a | 人人妻人人澡人人爽人人精品浪潮 | 国产成人综合在线女婷五月99播放 | 天堂а√在线中文在线 | 精品久久综合1区2区3区激情 | 国产明星裸体无码xxxx视频 | 成人精品一区二区三区中文字幕 | 精品无码av一区二区三区 | 人人爽人人澡人人高潮 | 精品无码一区二区三区爱欲 | 国产真实夫妇视频 | 中文字幕亚洲情99在线 | 精品一区二区三区波多野结衣 | 免费看男女做好爽好硬视频 | 久久久久久a亚洲欧洲av冫 | 人人妻人人澡人人爽欧美一区九九 | 蜜桃无码一区二区三区 | 亚洲综合精品香蕉久久网 | 九九久久精品国产免费看小说 | 国产精品丝袜黑色高跟鞋 | 又湿又紧又大又爽a视频国产 | 东京一本一道一二三区 | 欧美精品无码一区二区三区 | 精品久久久久久人妻无码中文字幕 | 日本免费一区二区三区最新 | 国产精品亚洲一区二区三区喷水 | 国产精品久久久久久久9999 | 大色综合色综合网站 | 九月婷婷人人澡人人添人人爽 | 午夜肉伦伦影院 | 综合激情五月综合激情五月激情1 | 亚洲精品一区二区三区在线观看 | 99在线 | 亚洲 | 秋霞特色aa大片 | 天下第一社区视频www日本 | 久久精品无码一区二区三区 | 亚洲理论电影在线观看 | 波多野42部无码喷潮在线 | 国产精品内射视频免费 | 久久久婷婷五月亚洲97号色 | 久久久久成人精品免费播放动漫 | 国色天香社区在线视频 | 成人性做爰aaa片免费看不忠 | 成在人线av无码免观看麻豆 | 亚洲国产精品一区二区美利坚 | 亚洲中文字幕成人无码 | 亚洲国产精品无码一区二区三区 | 兔费看少妇性l交大片免费 | 中文字幕无码免费久久9一区9 | 一二三四在线观看免费视频 | 蜜桃视频插满18在线观看 | 久久久久亚洲精品中文字幕 | 亚洲欧美中文字幕5发布 | 国产免费观看黄av片 | 国产精品va在线播放 | 欧美性生交活xxxxxdddd | 亚洲精品一区二区三区在线观看 | 最新国产麻豆aⅴ精品无码 | 成人性做爰aaa片免费看 | 内射后入在线观看一区 | 97se亚洲精品一区 | 日欧一片内射va在线影院 | 国产suv精品一区二区五 | 国内少妇偷人精品视频免费 | 精品久久综合1区2区3区激情 | 久久99热只有频精品8 | 欧美日本免费一区二区三区 | 国产一区二区三区四区五区加勒比 | 少妇一晚三次一区二区三区 | 欧美性生交活xxxxxdddd | 麻豆av传媒蜜桃天美传媒 | 51国偷自产一区二区三区 | 无码乱肉视频免费大全合集 | 欧美黑人巨大xxxxx | 麻豆国产丝袜白领秘书在线观看 | 国产午夜无码视频在线观看 | 国产另类ts人妖一区二区 | 人人爽人人爽人人片av亚洲 | aⅴ亚洲 日韩 色 图网站 播放 | 欧美放荡的少妇 | 中文字幕无码免费久久9一区9 | 欧美 丝袜 自拍 制服 另类 | 夜夜影院未满十八勿进 | 欧美freesex黑人又粗又大 | 综合网日日天干夜夜久久 | 六月丁香婷婷色狠狠久久 | 久久国产精品精品国产色婷婷 | 亚洲一区二区三区国产精华液 | 欧美一区二区三区 | 成 人影片 免费观看 | 亚洲精品中文字幕乱码 | 国产成人一区二区三区在线观看 | 免费国产成人高清在线观看网站 | 丰满人妻翻云覆雨呻吟视频 | 久热国产vs视频在线观看 | 亚洲欧美日韩国产精品一区二区 | 国内精品九九久久久精品 | 男人和女人高潮免费网站 | 久久人人爽人人爽人人片ⅴ | 又大又硬又黄的免费视频 | 久久精品99久久香蕉国产色戒 | 国产成人无码区免费内射一片色欲 | 天天拍夜夜添久久精品 | 亚洲午夜福利在线观看 | 宝宝好涨水快流出来免费视频 | 亚洲综合无码久久精品综合 | 丰满人妻被黑人猛烈进入 | 日本一卡2卡3卡四卡精品网站 | 樱花草在线播放免费中文 | 成 人 网 站国产免费观看 | 婷婷丁香五月天综合东京热 | 成人试看120秒体验区 | 久久精品中文闷骚内射 | 丰满岳乱妇在线观看中字无码 | 日本丰满护士爆乳xxxx | 亚洲а∨天堂久久精品2021 | 无码国产色欲xxxxx视频 | 中文字幕人妻无码一夲道 | 亚洲欧洲日本综合aⅴ在线 | 日日碰狠狠丁香久燥 | 精品欧洲av无码一区二区三区 | 亚洲精品一区二区三区大桥未久 | 暴力强奷在线播放无码 | 丰满人妻一区二区三区免费视频 | 性做久久久久久久免费看 | 97久久国产亚洲精品超碰热 | 亚洲熟妇色xxxxx欧美老妇y | 青青青手机频在线观看 | 国产精品18久久久久久麻辣 | 亚洲成av人综合在线观看 | 久久久精品国产sm最大网站 | 亚洲欧美中文字幕5发布 | 欧美人与禽zoz0性伦交 | 少妇激情av一区二区 | 色综合天天综合狠狠爱 | 精品国产精品久久一区免费式 | 青草视频在线播放 | 国产另类ts人妖一区二区 | 国产在线无码精品电影网 | 爱做久久久久久 | 又大又硬又爽免费视频 | 男人和女人高潮免费网站 | 麻豆国产人妻欲求不满谁演的 | 人人妻人人澡人人爽欧美一区 | 国产激情无码一区二区app | 精品少妇爆乳无码av无码专区 | 一本久久a久久精品vr综合 | 国产一区二区三区四区五区加勒比 | 精品人妻人人做人人爽 | ass日本丰满熟妇pics | 十八禁真人啪啪免费网站 | 久久国产劲爆∧v内射 | 性史性农村dvd毛片 | 亚洲欧美精品伊人久久 | 亚洲精品中文字幕乱码 | 精品国产麻豆免费人成网站 | 免费看男女做好爽好硬视频 | 亚洲 日韩 欧美 成人 在线观看 | 国产精品嫩草久久久久 | 99精品无人区乱码1区2区3区 | 国产艳妇av在线观看果冻传媒 | 国产精品办公室沙发 | 丰腴饱满的极品熟妇 | 久久久久成人片免费观看蜜芽 | 国产精品成人av在线观看 | 麻豆果冻传媒2021精品传媒一区下载 | 日本一区二区更新不卡 | 中文字幕无码免费久久99 | 少妇人妻大乳在线视频 | 久久久久亚洲精品男人的天堂 | 无码精品国产va在线观看dvd | 人人妻在人人 | 成人免费视频视频在线观看 免费 | 欧美 亚洲 国产 另类 | 中文字幕人妻无码一夲道 | 人人澡人人妻人人爽人人蜜桃 | 人妻aⅴ无码一区二区三区 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产av无码专区亚洲a∨毛片 | 乱中年女人伦av三区 | 四十如虎的丰满熟妇啪啪 | 大地资源网第二页免费观看 | 日韩欧美成人免费观看 | 十八禁真人啪啪免费网站 | 亚洲精品成人福利网站 | 成人影院yy111111在线观看 | 日韩亚洲欧美精品综合 | 精品一区二区三区无码免费视频 | 性欧美疯狂xxxxbbbb | 国产午夜无码精品免费看 | 国产精品资源一区二区 | а√资源新版在线天堂 | 丰满肥臀大屁股熟妇激情视频 | 久久国产精品偷任你爽任你 | 欧美人与禽猛交狂配 | 天堂久久天堂av色综合 | 国产精品久久福利网站 | 日韩av无码一区二区三区不卡 | 久久精品人妻少妇一区二区三区 | 波多野结衣av一区二区全免费观看 | 极品嫩模高潮叫床 | 中文字幕色婷婷在线视频 | 日韩av无码一区二区三区 | 熟妇女人妻丰满少妇中文字幕 | 欧洲熟妇精品视频 | 亚洲人成网站在线播放942 | 亚洲一区二区三区 | 少妇无码av无码专区在线观看 | 少妇性l交大片欧洲热妇乱xxx | 熟妇人妻激情偷爽文 | 国产午夜亚洲精品不卡下载 | 久久99精品国产麻豆 | 久久久久久久久蜜桃 | 双乳奶水饱满少妇呻吟 | 亚洲热妇无码av在线播放 | 99久久久无码国产精品免费 | 丰腴饱满的极品熟妇 | 少妇性l交大片 | 人妻有码中文字幕在线 | 成人精品一区二区三区中文字幕 | 国产精品高潮呻吟av久久 | 亚洲男人av天堂午夜在 | 精品成人av一区二区三区 | 亚洲阿v天堂在线 | 99久久人妻精品免费一区 | 在线观看国产午夜福利片 | 国产成人综合色在线观看网站 | 无人区乱码一区二区三区 | 久久99精品国产麻豆蜜芽 | 国产激情无码一区二区 | 内射欧美老妇wbb | 性生交大片免费看l | 国产免费无码一区二区视频 | 国产又爽又黄又刺激的视频 | 人人妻人人澡人人爽人人精品 | 夜夜夜高潮夜夜爽夜夜爰爰 | 亚洲一区二区三区偷拍女厕 | 午夜精品久久久久久久久 | 亚洲日本va中文字幕 | 亚洲成a人片在线观看无码3d | 亚洲娇小与黑人巨大交 | 精品国产一区二区三区四区 | 奇米影视7777久久精品 | 亚洲日韩av一区二区三区四区 | 国产精品久久久久影院嫩草 | 成人亚洲精品久久久久软件 | 亚洲中文字幕无码中字 | 丰满少妇熟乱xxxxx视频 | 久久熟妇人妻午夜寂寞影院 | √8天堂资源地址中文在线 | 亚洲无人区午夜福利码高清完整版 | 亲嘴扒胸摸屁股激烈网站 | 无码av免费一区二区三区试看 | 奇米影视7777久久精品人人爽 | 四虎4hu永久免费 | 中文字幕无线码 | 成人无码视频在线观看网站 | 特黄特色大片免费播放器图片 | 欧美一区二区三区视频在线观看 | 亚洲成av人影院在线观看 | 成人无码视频免费播放 | 在线成人www免费观看视频 | 免费看男女做好爽好硬视频 | 性开放的女人aaa片 | а√天堂www在线天堂小说 | 国产色在线 | 国产 | 波多野结衣 黑人 | 少妇激情av一区二区 | 国产亚洲精品久久久久久国模美 | 国产亚洲视频中文字幕97精品 | 少妇无码av无码专区在线观看 | 日韩欧美群交p片內射中文 | √天堂资源地址中文在线 | 妺妺窝人体色www婷婷 | 国产精品第一区揄拍无码 | 亚洲中文字幕在线观看 | 天堂亚洲免费视频 | 无码人妻精品一区二区三区不卡 | 久久97精品久久久久久久不卡 | 亚洲色成人中文字幕网站 | 国产精品久久国产精品99 | 青青青手机频在线观看 | 国产精华av午夜在线观看 | 综合网日日天干夜夜久久 | 国产97在线 | 亚洲 | 小鲜肉自慰网站xnxx | 老子影院午夜精品无码 | 伊在人天堂亚洲香蕉精品区 | 亚洲 欧美 激情 小说 另类 | 精品人妻人人做人人爽夜夜爽 | 色综合天天综合狠狠爱 | 国产黄在线观看免费观看不卡 | 夜精品a片一区二区三区无码白浆 | 国产特级毛片aaaaaaa高清 | 日本va欧美va欧美va精品 | 精品久久久中文字幕人妻 | 欧美xxxx黑人又粗又长 | 亚洲国产精品美女久久久久 | 亚洲精品一区二区三区大桥未久 | 亚洲精品午夜国产va久久成人 | 久久人人爽人人爽人人片av高清 | 国产精品怡红院永久免费 | 亲嘴扒胸摸屁股激烈网站 | 国产性生交xxxxx无码 | 欧美日韩久久久精品a片 | 欧美熟妇另类久久久久久多毛 | 人妻熟女一区 | 亚洲中文字幕成人无码 | 男人扒开女人内裤强吻桶进去 | 亚洲男人av天堂午夜在 | 国产莉萝无码av在线播放 | 狠狠色噜噜狠狠狠狠7777米奇 | 黑人巨大精品欧美一区二区 | 性色av无码免费一区二区三区 | 久久久久免费精品国产 | 久久久精品456亚洲影院 | 在线视频网站www色 | 中文字幕 亚洲精品 第1页 | 国产激情精品一区二区三区 | 国产av一区二区三区最新精品 | 沈阳熟女露脸对白视频 | 亚洲 a v无 码免 费 成 人 a v | 国产av人人夜夜澡人人爽麻豆 | 亚洲精品久久久久久一区二区 | 久久久久久亚洲精品a片成人 | 精品少妇爆乳无码av无码专区 | 婷婷色婷婷开心五月四房播播 | 欧美日韩人成综合在线播放 | 亚洲精品综合五月久久小说 | 日本一区二区三区免费播放 | 亚洲精品午夜国产va久久成人 | 午夜性刺激在线视频免费 | 国产97在线 | 亚洲 | а√天堂www在线天堂小说 | 精品水蜜桃久久久久久久 | 免费视频欧美无人区码 | 国产特级毛片aaaaaaa高清 | 99久久婷婷国产综合精品青草免费 | 久久精品视频在线看15 | 久久人人爽人人爽人人片ⅴ | 亚洲欧美国产精品久久 | 国产三级精品三级男人的天堂 | 国内老熟妇对白xxxxhd | 国产av久久久久精东av | 沈阳熟女露脸对白视频 | 丰满少妇熟乱xxxxx视频 | 国产激情无码一区二区 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 久久综合久久自在自线精品自 | 成在人线av无码免费 | 日本护士xxxxhd少妇 | 荫蒂添的好舒服视频囗交 | 天海翼激烈高潮到腰振不止 | 性色av无码免费一区二区三区 | 欧美阿v高清资源不卡在线播放 | 国产美女极度色诱视频www | 中文字幕色婷婷在线视频 | 国语精品一区二区三区 | 男人扒开女人内裤强吻桶进去 | 国精产品一品二品国精品69xx | 亚洲精品综合五月久久小说 | 露脸叫床粗话东北少妇 | 天天躁夜夜躁狠狠是什么心态 | 日本一区二区三区免费高清 | 日本va欧美va欧美va精品 | 老熟妇乱子伦牲交视频 | 99久久人妻精品免费一区 | 亚洲中文字幕av在天堂 | 精品国产av色一区二区深夜久久 | 成人一区二区免费视频 | 国内精品久久毛片一区二区 | 中文无码精品a∨在线观看不卡 | 67194成是人免费无码 | 精品国产成人一区二区三区 | 亚洲娇小与黑人巨大交 | 夜夜高潮次次欢爽av女 | 丝袜美腿亚洲一区二区 | 色欲人妻aaaaaaa无码 | 男女性色大片免费网站 | 一本大道久久东京热无码av | 丰满人妻精品国产99aⅴ | 亚洲 另类 在线 欧美 制服 | 精品一区二区三区波多野结衣 | 特黄特色大片免费播放器图片 | 高潮喷水的毛片 | 51国偷自产一区二区三区 | 国产精品久久久av久久久 | 女人被爽到呻吟gif动态图视看 | 久久午夜无码鲁丝片午夜精品 | 亚洲爆乳精品无码一区二区三区 | 水蜜桃av无码 | 国产午夜亚洲精品不卡 | 久久国产精品精品国产色婷婷 | 自拍偷自拍亚洲精品10p | 亚洲成av人在线观看网址 | 人妻尝试又大又粗久久 | 精品久久久无码中文字幕 | 亚洲国产精品无码久久久久高潮 | 偷窥日本少妇撒尿chinese | 亚洲精品国偷拍自产在线观看蜜桃 | 在线观看国产一区二区三区 | 色欲av亚洲一区无码少妇 | 久久精品国产99精品亚洲 | 97久久精品无码一区二区 | 亚洲综合久久一区二区 | 国产精品理论片在线观看 | 亚洲精品美女久久久久久久 | 精品无码一区二区三区爱欲 | 少女韩国电视剧在线观看完整 | 日本爽爽爽爽爽爽在线观看免 | 亚洲欧美日韩综合久久久 | 亚洲精品无码国产 | 国产av无码专区亚洲awww | √天堂中文官网8在线 | 亚洲日韩乱码中文无码蜜桃臀网站 | 欧美亚洲日韩国产人成在线播放 | 久久亚洲a片com人成 | 国产艳妇av在线观看果冻传媒 | 老熟妇仑乱视频一区二区 | 成人欧美一区二区三区 | 国内精品久久久久久中文字幕 | 熟妇人妻无码xxx视频 | 国产绳艺sm调教室论坛 | 中文字幕色婷婷在线视频 | 亚洲精品成a人在线观看 | 亚洲精品中文字幕久久久久 | 中文精品无码中文字幕无码专区 | 亚洲日韩一区二区三区 | 人妻夜夜爽天天爽三区 | 国内揄拍国内精品人妻 | 国内揄拍国内精品少妇国语 | 国产婷婷色一区二区三区在线 | 麻豆蜜桃av蜜臀av色欲av | 熟妇人妻无乱码中文字幕 | 亚洲阿v天堂在线 | 国产精品亚洲一区二区三区喷水 | 色婷婷综合激情综在线播放 | 乱人伦人妻中文字幕无码久久网 | 蜜臀av无码人妻精品 | 欧洲精品码一区二区三区免费看 | 国产三级精品三级男人的天堂 | 四虎影视成人永久免费观看视频 | 精品国产一区二区三区av 性色 | 亚洲人成网站色7799 | 麻豆av传媒蜜桃天美传媒 | 国内精品久久久久久中文字幕 | 樱花草在线播放免费中文 | 亚洲а∨天堂久久精品2021 | 久久人妻内射无码一区三区 | 免费人成网站视频在线观看 | 老司机亚洲精品影院无码 | 亚洲人成影院在线无码按摩店 | 任你躁国产自任一区二区三区 | 国产精品国产三级国产专播 | 学生妹亚洲一区二区 | 真人与拘做受免费视频 | 男女作爱免费网站 | 麻豆蜜桃av蜜臀av色欲av | 亚洲 另类 在线 欧美 制服 | 老司机亚洲精品影院 | 中文字幕乱码亚洲无线三区 | 成人三级无码视频在线观看 | 国产乡下妇女做爰 | 久热国产vs视频在线观看 | 亚洲一区二区三区偷拍女厕 | 欧美人与物videos另类 | 中文字幕人妻无码一区二区三区 | 国产乱人偷精品人妻a片 | 成年美女黄网站色大免费全看 | 久久视频在线观看精品 | 2019午夜福利不卡片在线 | 国产在线一区二区三区四区五区 | 国产精品久久精品三级 | 久久99久久99精品中文字幕 | 天堂а√在线地址中文在线 | 欧美日韩一区二区免费视频 | 亚洲精品一区二区三区在线观看 | www国产亚洲精品久久久日本 | 无码人妻精品一区二区三区不卡 | 熟女体下毛毛黑森林 | 2019午夜福利不卡片在线 | 国产明星裸体无码xxxx视频 | 日韩精品久久久肉伦网站 | 精品国产麻豆免费人成网站 | 国产女主播喷水视频在线观看 | 中文字幕无码av波多野吉衣 | 国语自产偷拍精品视频偷 | 无码人妻av免费一区二区三区 | 中文精品久久久久人妻不卡 | 欧美 丝袜 自拍 制服 另类 | 成人欧美一区二区三区黑人免费 | 日本又色又爽又黄的a片18禁 | 亚洲成色在线综合网站 | 亚洲中文字幕无码中字 | 最近免费中文字幕中文高清百度 | 无遮挡啪啪摇乳动态图 | 亚洲国产欧美日韩精品一区二区三区 | 人人妻人人澡人人爽人人精品 | 久久亚洲精品中文字幕无男同 | 超碰97人人做人人爱少妇 | 精品人人妻人人澡人人爽人人 | 永久免费精品精品永久-夜色 | 好爽又高潮了毛片免费下载 | 亚洲精品成人福利网站 | 好屌草这里只有精品 | 成熟女人特级毛片www免费 | www国产亚洲精品久久久日本 | 久久精品国产99精品亚洲 | av人摸人人人澡人人超碰下载 | 99久久精品日本一区二区免费 | 99riav国产精品视频 | 天堂无码人妻精品一区二区三区 | 精品偷自拍另类在线观看 | 奇米影视7777久久精品人人爽 | 国产尤物精品视频 | 一区二区三区高清视频一 | 亚洲中文字幕av在天堂 | 东京热无码av男人的天堂 | 青青久在线视频免费观看 | 日日天干夜夜狠狠爱 | 国产精品永久免费视频 | 人妻与老人中文字幕 | 99久久99久久免费精品蜜桃 | 亚洲欧洲中文日韩av乱码 | 成在人线av无码免费 | 国产人妻久久精品二区三区老狼 | 久久这里只有精品视频9 | 任你躁国产自任一区二区三区 | 久青草影院在线观看国产 | 老司机亚洲精品影院无码 | 国产精品办公室沙发 | 狠狠色丁香久久婷婷综合五月 | 国产精品久久国产精品99 | 久久精品中文闷骚内射 | 乌克兰少妇xxxx做受 | 最新国产麻豆aⅴ精品无码 | 久久久久亚洲精品男人的天堂 | 老熟妇仑乱视频一区二区 | 久久99精品国产.久久久久 | 青春草在线视频免费观看 | 特黄特色大片免费播放器图片 | 国内少妇偷人精品视频 | 夜夜高潮次次欢爽av女 | 疯狂三人交性欧美 | 日日碰狠狠丁香久燥 | av香港经典三级级 在线 | 粗大的内捧猛烈进出视频 | 国产成人精品优优av | 日韩精品无码一本二本三本色 | 澳门永久av免费网站 | 久久精品国产99久久6动漫 | 桃花色综合影院 | 国产人妻精品一区二区三区不卡 | 麻豆精品国产精华精华液好用吗 | 久久精品无码一区二区三区 | 欧美高清在线精品一区 | 成人无码精品1区2区3区免费看 | 男人的天堂av网站 | 无码帝国www无码专区色综合 | 欧美黑人性暴力猛交喷水 | 亚洲国产综合无码一区 | 亚欧洲精品在线视频免费观看 | 亚洲欧美中文字幕5发布 | 国产午夜手机精彩视频 | 蜜桃视频插满18在线观看 | 久久精品丝袜高跟鞋 | 婷婷综合久久中文字幕蜜桃三电影 | a片在线免费观看 | 性欧美疯狂xxxxbbbb | 久久精品国产一区二区三区肥胖 | 国产一区二区三区影院 | 国内丰满熟女出轨videos | 在线观看国产一区二区三区 | 无码国产色欲xxxxx视频 | 亚洲色www成人永久网址 | 丰满人妻翻云覆雨呻吟视频 | 国产精品久久久久7777 | 任你躁国产自任一区二区三区 | 精品人妻中文字幕有码在线 | 精品国产乱码久久久久乱码 | 55夜色66夜色国产精品视频 | 久久精品国产日本波多野结衣 | 精品国产一区二区三区四区 | 在线天堂新版最新版在线8 | 欧美熟妇另类久久久久久多毛 | 2020久久超碰国产精品最新 | 国产在线无码精品电影网 | 377p欧洲日本亚洲大胆 | 国产午夜视频在线观看 | 国产亚洲美女精品久久久2020 | 成人欧美一区二区三区黑人免费 | 少妇性俱乐部纵欲狂欢电影 | 午夜精品一区二区三区的区别 | 国产精品久久久久影院嫩草 | 两性色午夜视频免费播放 | 亚洲高清偷拍一区二区三区 | 东京热男人av天堂 | 激情国产av做激情国产爱 | 欧美性猛交内射兽交老熟妇 | 国内精品久久毛片一区二区 | 亚洲精品国产a久久久久久 | 精品无码av一区二区三区 | 中文字幕中文有码在线 | 天天躁日日躁狠狠躁免费麻豆 | 装睡被陌生人摸出水好爽 | 成熟妇人a片免费看网站 | 天堂а√在线地址中文在线 | 亚洲日韩av一区二区三区四区 | 免费人成在线视频无码 | 日日碰狠狠丁香久燥 | 日韩av无码一区二区三区不卡 | 亚洲午夜福利在线观看 | 欧美人与禽zoz0性伦交 | 亚洲精品久久久久中文第一幕 | 亚洲国产欧美国产综合一区 | 少妇被粗大的猛进出69影院 | ass日本丰满熟妇pics | 免费观看激色视频网站 | 中文精品无码中文字幕无码专区 | 亚洲热妇无码av在线播放 | 国产午夜手机精彩视频 | 国产成人一区二区三区在线观看 | 丝袜美腿亚洲一区二区 | 亚洲乱码国产乱码精品精 | 少妇无套内谢久久久久 | 小泽玛莉亚一区二区视频在线 | 夜夜夜高潮夜夜爽夜夜爰爰 | 人人妻人人澡人人爽欧美一区九九 | 丰满护士巨好爽好大乳 | 图片区 小说区 区 亚洲五月 | 欧美成人高清在线播放 | 精品水蜜桃久久久久久久 | 2019nv天堂香蕉在线观看 | 婷婷五月综合缴情在线视频 | 波多野结衣乳巨码无在线观看 | 精品厕所偷拍各类美女tp嘘嘘 | 日韩精品无码免费一区二区三区 | 成人aaa片一区国产精品 | 97久久精品无码一区二区 | 亚洲精品国产精品乱码视色 | 精品无码成人片一区二区98 | 亚洲综合伊人久久大杳蕉 | 好男人社区资源 | 亚洲精品国产第一综合99久久 | 国产69精品久久久久app下载 | 欧美国产日韩亚洲中文 | 波多野结衣乳巨码无在线观看 | 男女性色大片免费网站 | 国产婷婷色一区二区三区在线 | 女人高潮内射99精品 | 亚洲色成人中文字幕网站 | 99riav国产精品视频 | 色 综合 欧美 亚洲 国产 | 欧美人与物videos另类 | 欧美人与牲动交xxxx | 日本乱人伦片中文三区 | 99国产精品白浆在线观看免费 | 成在人线av无码免费 | 亚洲熟妇色xxxxx欧美老妇 | 大乳丰满人妻中文字幕日本 | 玩弄人妻少妇500系列视频 | 久久人人爽人人爽人人片ⅴ | 激情内射日本一区二区三区 | 99精品国产综合久久久久五月天 | 永久免费观看美女裸体的网站 | 乱人伦中文视频在线观看 | 国产色视频一区二区三区 | 在线а√天堂中文官网 | 无码国产乱人伦偷精品视频 | 天天综合网天天综合色 | 色欲综合久久中文字幕网 | 国产另类ts人妖一区二区 | 午夜肉伦伦影院 | 无遮挡啪啪摇乳动态图 | 免费看男女做好爽好硬视频 | 久久精品无码一区二区三区 | 国产精品99久久精品爆乳 | 天海翼激烈高潮到腰振不止 | 精品国偷自产在线视频 | 国产三级久久久精品麻豆三级 | 网友自拍区视频精品 | 国产后入清纯学生妹 | 国产欧美亚洲精品a | 妺妺窝人体色www婷婷 | 国产精品18久久久久久麻辣 | 亚洲熟妇色xxxxx欧美老妇 | 牲交欧美兽交欧美 | 精品人妻人人做人人爽 | 久久精品女人天堂av免费观看 | 少妇人妻av毛片在线看 | 日本饥渴人妻欲求不满 | 人妻人人添人妻人人爱 | 国模大胆一区二区三区 | 成 人 网 站国产免费观看 | 欧美日韩人成综合在线播放 | 综合激情五月综合激情五月激情1 | 精品人人妻人人澡人人爽人人 | 青青草原综合久久大伊人精品 | 久久人人97超碰a片精品 | 国产亚洲日韩欧美另类第八页 | 一本加勒比波多野结衣 | 国产精品多人p群无码 | 老太婆性杂交欧美肥老太 | 水蜜桃色314在线观看 | 综合人妻久久一区二区精品 | 色婷婷欧美在线播放内射 | 中文字幕+乱码+中文字幕一区 | 久久综合狠狠综合久久综合88 | 成人性做爰aaa片免费看不忠 | 久久精品国产大片免费观看 | 精品久久久无码中文字幕 | 天天做天天爱天天爽综合网 | 六十路熟妇乱子伦 | 99久久精品日本一区二区免费 | 男女作爱免费网站 | 超碰97人人做人人爱少妇 | 亚洲精品无码国产 | 88国产精品欧美一区二区三区 | 国产成人精品久久亚洲高清不卡 | 中文字幕 亚洲精品 第1页 | 大肉大捧一进一出好爽视频 | 欧美日韩综合一区二区三区 | 久在线观看福利视频 | 亚洲区欧美区综合区自拍区 | 亚洲人成无码网www | 亚洲国产欧美日韩精品一区二区三区 | 在线看片无码永久免费视频 | 亚洲男人av香蕉爽爽爽爽 | 欧美三级a做爰在线观看 | 中文字幕无码av波多野吉衣 | 六月丁香婷婷色狠狠久久 | 亚洲一区av无码专区在线观看 | 久久久国产一区二区三区 | 国产精品无码一区二区桃花视频 | 成 人 网 站国产免费观看 | 大地资源网第二页免费观看 | 亚洲精品中文字幕久久久久 | 夜夜躁日日躁狠狠久久av | 人人妻人人澡人人爽欧美精品 | 2019nv天堂香蕉在线观看 | 午夜精品一区二区三区的区别 | 熟女体下毛毛黑森林 | 天天爽夜夜爽夜夜爽 | 特级做a爰片毛片免费69 | 久久国产精品萌白酱免费 | 精品国精品国产自在久国产87 | 波多野42部无码喷潮在线 | 丰满人妻精品国产99aⅴ | 国产乱人伦av在线无码 | 欧美丰满熟妇xxxx | 中文亚洲成a人片在线观看 | 久久精品国产日本波多野结衣 | 亚洲精品一区二区三区婷婷月 | 日韩精品无码一本二本三本色 | 国产又爽又猛又粗的视频a片 | 奇米影视7777久久精品 | 色一情一乱一伦一区二区三欧美 | 人人爽人人澡人人高潮 | 国产精品久久久久久无码 | 亚洲精品美女久久久久久久 | 欧美丰满熟妇xxxx | 久久成人a毛片免费观看网站 | 成人免费无码大片a毛片 | 丰满人妻翻云覆雨呻吟视频 | 亚洲a无码综合a国产av中文 | 又大又硬又爽免费视频 | 中文毛片无遮挡高清免费 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产精品久久久久久亚洲毛片 | 国产精品成人av在线观看 | av在线亚洲欧洲日产一区二区 | 国产成人av免费观看 | 成人无码影片精品久久久 | 久精品国产欧美亚洲色aⅴ大片 | 亚洲一区二区三区香蕉 | 国产亚洲精品久久久久久大师 | 久久精品中文字幕一区 | 青青草原综合久久大伊人精品 | 国产日产欧产精品精品app | 久久精品中文闷骚内射 | 日本乱偷人妻中文字幕 | 青青青手机频在线观看 | 日本精品少妇一区二区三区 | 牲交欧美兽交欧美 | 九九在线中文字幕无码 | 乱码午夜-极国产极内射 | 中文毛片无遮挡高清免费 | 国产精品久久久一区二区三区 | 国产人妖乱国产精品人妖 | 色婷婷综合激情综在线播放 | 台湾无码一区二区 | 无人区乱码一区二区三区 | 丰满人妻一区二区三区免费视频 | 欧洲熟妇精品视频 | 国产9 9在线 | 中文 | 精品一区二区三区无码免费视频 | 永久免费观看美女裸体的网站 | 老熟妇仑乱视频一区二区 | 国产激情一区二区三区 | 无码国产激情在线观看 | 夜夜夜高潮夜夜爽夜夜爰爰 | 天天拍夜夜添久久精品 | 精品一区二区三区波多野结衣 | 亚洲成av人片天堂网无码】 | 男女下面进入的视频免费午夜 | 18禁止看的免费污网站 | 日韩精品无码一区二区中文字幕 | 激情综合激情五月俺也去 | av无码久久久久不卡免费网站 | 久热国产vs视频在线观看 | 国产三级精品三级男人的天堂 | 荫蒂添的好舒服视频囗交 | 日日摸天天摸爽爽狠狠97 | 欧美一区二区三区视频在线观看 | 97久久精品无码一区二区 | 国产乱人偷精品人妻a片 | 亚洲国产精品成人久久蜜臀 | 久久熟妇人妻午夜寂寞影院 | 亚洲aⅴ无码成人网站国产app | 欧美国产日产一区二区 | 久久久精品456亚洲影院 | 纯爱无遮挡h肉动漫在线播放 | 特黄特色大片免费播放器图片 | 青青青爽视频在线观看 | 国内精品一区二区三区不卡 | √天堂中文官网8在线 | 超碰97人人做人人爱少妇 | 国产精品怡红院永久免费 | 久久国产自偷自偷免费一区调 | 一本久道久久综合狠狠爱 | 国产无套粉嫩白浆在线 | 男人的天堂2018无码 | 少妇的肉体aa片免费 | 欧美亚洲日韩国产人成在线播放 | 丝袜 中出 制服 人妻 美腿 | 亚洲欧美精品aaaaaa片 | 国产精品久免费的黄网站 | 国产精品毛片一区二区 | 久久精品国产大片免费观看 | 99riav国产精品视频 | 美女毛片一区二区三区四区 | 99精品国产综合久久久久五月天 | 亚洲经典千人经典日产 | 人妻人人添人妻人人爱 | 国产激情无码一区二区 | 草草网站影院白丝内射 | 午夜理论片yy44880影院 | 无码纯肉视频在线观看 | 久久国语露脸国产精品电影 | 欧美精品无码一区二区三区 | 国产在线aaa片一区二区99 | 久久人人爽人人人人片 | av无码久久久久不卡免费网站 | 大肉大捧一进一出视频出来呀 | 欧美精品无码一区二区三区 | 亚洲精品国产品国语在线观看 | 亚洲色大成网站www国产 | 国产成人久久精品流白浆 | 久久久久99精品国产片 | 巨爆乳无码视频在线观看 | 精品成在人线av无码免费看 | 男女超爽视频免费播放 | 国内揄拍国内精品少妇国语 | 午夜福利电影 | 亚洲一区av无码专区在线观看 | 国产人妻精品午夜福利免费 | 国内综合精品午夜久久资源 | 水蜜桃色314在线观看 | 国产内射老熟女aaaa | 国产精品久免费的黄网站 | 亚洲の无码国产の无码影院 | 人妻有码中文字幕在线 | 女人被爽到呻吟gif动态图视看 | 中文字幕无线码免费人妻 | 久久久婷婷五月亚洲97号色 | 自拍偷自拍亚洲精品10p | 欧美老熟妇乱xxxxx | 性色欲情网站iwww九文堂 | a在线观看免费网站大全 | 国产精品无套呻吟在线 | 日本精品久久久久中文字幕 | 色狠狠av一区二区三区 | 综合人妻久久一区二区精品 | 无码人妻丰满熟妇区五十路百度 | 欧美 日韩 人妻 高清 中文 | 狠狠躁日日躁夜夜躁2020 | 在线视频网站www色 | 97资源共享在线视频 | 小鲜肉自慰网站xnxx | 国产亚洲精品久久久ai换 | 一本久道久久综合狠狠爱 | 国产成人精品一区二区在线小狼 | 美女极度色诱视频国产 | 青青久在线视频免费观看 | 亚洲中文字幕无码一久久区 | 国产乱子伦视频在线播放 | 双乳奶水饱满少妇呻吟 | 亚洲va欧美va天堂v国产综合 | 亚洲综合无码一区二区三区 | 欧美国产日产一区二区 | 色综合久久中文娱乐网 | 亚洲国产成人a精品不卡在线 | 亚洲日本在线电影 | 美女毛片一区二区三区四区 | 国产偷自视频区视频 | 午夜福利电影 | 国产97色在线 | 免 | 99精品视频在线观看免费 | 精品国产福利一区二区 | 最新国产乱人伦偷精品免费网站 | 人妻少妇精品无码专区二区 | 高潮毛片无遮挡高清免费 | 国语自产偷拍精品视频偷 | 久久久久久久女国产乱让韩 | 国产激情无码一区二区app | 人妻插b视频一区二区三区 | 天堂在线观看www | 日韩精品一区二区av在线 | 领导边摸边吃奶边做爽在线观看 | 国产综合久久久久鬼色 | 国内丰满熟女出轨videos | 亚洲理论电影在线观看 | 综合激情五月综合激情五月激情1 | 久久国产精品萌白酱免费 | 青春草在线视频免费观看 | 人人爽人人爽人人片av亚洲 | 精品亚洲成av人在线观看 | 久久精品无码一区二区三区 | 国产精品久久精品三级 | 精品人妻人人做人人爽夜夜爽 | 国产精品va在线观看无码 | 精品人人妻人人澡人人爽人人 | 77777熟女视频在线观看 а天堂中文在线官网 | 免费乱码人妻系列无码专区 | 色综合久久久无码中文字幕 | 蜜桃视频韩日免费播放 | 亚洲国产精品一区二区第一页 | 中文字幕无码av激情不卡 | 久久久久久久久888 | 在线播放无码字幕亚洲 | 欧洲精品码一区二区三区免费看 | 国内少妇偷人精品视频 | 午夜精品一区二区三区的区别 | 亲嘴扒胸摸屁股激烈网站 | 亚洲国产精品久久久天堂 | 又大又硬又黄的免费视频 | 亚洲国产一区二区三区在线观看 | 亚洲天堂2017无码中文 | 人妻少妇精品久久 | 久久综合久久自在自线精品自 | 国产亚洲精品精品国产亚洲综合 | 久久久国产精品无码免费专区 | 1000部啪啪未满十八勿入下载 | 国产特级毛片aaaaaaa高清 | 精品国产精品久久一区免费式 | 日本精品少妇一区二区三区 | 免费观看的无遮挡av | 3d动漫精品啪啪一区二区中 | 成人精品一区二区三区中文字幕 | a在线观看免费网站大全 | 少妇性荡欲午夜性开放视频剧场 | 午夜福利试看120秒体验区 | 美女张开腿让人桶 | 国产农村妇女高潮大叫 | 国产手机在线αⅴ片无码观看 | 久久综合狠狠综合久久综合88 | 国产性生大片免费观看性 | 国产一区二区三区日韩精品 | 国产av一区二区三区最新精品 | 亚洲欧美日韩国产精品一区二区 | 久久人人爽人人爽人人片av高清 | 亚洲成熟女人毛毛耸耸多 | 任你躁国产自任一区二区三区 | 黑人巨大精品欧美黑寡妇 | 久久精品国产一区二区三区 | 欧美黑人性暴力猛交喷水 | 国产情侣作爱视频免费观看 | 亚洲成av人片天堂网无码】 | 精品国产乱码久久久久乱码 | 超碰97人人做人人爱少妇 | 久久天天躁狠狠躁夜夜免费观看 | 无码国模国产在线观看 | 国产精品久免费的黄网站 | 图片区 小说区 区 亚洲五月 | 久久人人爽人人人人片 | 日韩精品一区二区av在线 | 国产亚洲美女精品久久久2020 | 中文字幕无码人妻少妇免费 | 天堂亚洲免费视频 | 人人妻人人藻人人爽欧美一区 | 久久成人a毛片免费观看网站 | 99国产欧美久久久精品 | 啦啦啦www在线观看免费视频 | 久久人人爽人人人人片 | 中文字幕+乱码+中文字幕一区 | 亚洲国产精品一区二区美利坚 | 日韩av无码中文无码电影 | 岛国片人妻三上悠亚 | 国产suv精品一区二区五 | 大肉大捧一进一出好爽视频 | 亚洲精品综合一区二区三区在线 | 人人妻人人澡人人爽欧美一区九九 | 亚洲呦女专区 | 精品亚洲韩国一区二区三区 | 日日麻批免费40分钟无码 | 波多野结衣av一区二区全免费观看 | 在线天堂新版最新版在线8 | 精品一区二区三区无码免费视频 | 国内少妇偷人精品视频免费 | 少妇性荡欲午夜性开放视频剧场 | 人妻天天爽夜夜爽一区二区 | 国内精品九九久久久精品 | 欧美人与善在线com | 亚洲熟妇自偷自拍另类 | 亚洲午夜福利在线观看 | 99久久精品无码一区二区毛片 | 俺去俺来也www色官网 | 天堂久久天堂av色综合 | 少妇的肉体aa片免费 | 久久精品成人欧美大片 | 99久久无码一区人妻 | 国产国语老龄妇女a片 | 国产人妻精品一区二区三区不卡 | 熟女俱乐部五十路六十路av | 久久亚洲a片com人成 | 亚洲欧美精品伊人久久 | 亚洲第一网站男人都懂 | 丰满妇女强制高潮18xxxx | 国产色视频一区二区三区 | 国产激情无码一区二区 | 精品久久久久久人妻无码中文字幕 | 亚洲春色在线视频 | 久久这里只有精品视频9 | 中文无码成人免费视频在线观看 | 久久久久免费看成人影片 | 亚洲va中文字幕无码久久不卡 | 国产色视频一区二区三区 | 久久综合给合久久狠狠狠97色 | 蜜桃无码一区二区三区 | 日韩人妻系列无码专区 | 国产人妖乱国产精品人妖 | 三级4级全黄60分钟 | 亚洲精品国产第一综合99久久 | 暴力强奷在线播放无码 | 在线天堂新版最新版在线8 | 无码av免费一区二区三区试看 | 精品一区二区三区波多野结衣 | 中文字幕无码av波多野吉衣 | 女高中生第一次破苞av | 中文字幕av日韩精品一区二区 | 内射后入在线观看一区 | 中文字幕av日韩精品一区二区 | 理论片87福利理论电影 | 久久亚洲国产成人精品性色 | 欧美猛少妇色xxxxx | 人人妻人人澡人人爽人人精品浪潮 | 全球成人中文在线 | 精品久久综合1区2区3区激情 | 性欧美熟妇videofreesex | 影音先锋中文字幕无码 | 色情久久久av熟女人妻网站 | 精品无码av一区二区三区 | 国产精品国产三级国产专播 | 国产乡下妇女做爰 | 亚洲中文字幕成人无码 | 天天做天天爱天天爽综合网 | 国产精品视频免费播放 | 国内老熟妇对白xxxxhd | 四十如虎的丰满熟妇啪啪 | 图片区 小说区 区 亚洲五月 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 精品夜夜澡人妻无码av蜜桃 | 国产又爽又猛又粗的视频a片 | 狠狠躁日日躁夜夜躁2020 | 中文字幕+乱码+中文字幕一区 | 粉嫩少妇内射浓精videos | 日本精品少妇一区二区三区 | 国产亚洲精品久久久久久久久动漫 | 精品一区二区三区无码免费视频 | 久久久久久国产精品无码下载 | 最近免费中文字幕中文高清百度 | 国产国语老龄妇女a片 | 中文字幕人妻无码一夲道 | 爽爽影院免费观看 | 亚洲综合无码一区二区三区 | 中文字幕亚洲情99在线 | 性做久久久久久久免费看 | 人妻人人添人妻人人爱 | 久久午夜无码鲁丝片 | 亚洲男女内射在线播放 | 日韩人妻少妇一区二区三区 | 成年美女黄网站色大免费视频 | 国产午夜亚洲精品不卡下载 | 波多野结衣aⅴ在线 | 国语自产偷拍精品视频偷 | 99久久精品日本一区二区免费 | 永久免费精品精品永久-夜色 | 内射巨臀欧美在线视频 | 97久久超碰中文字幕 | 国产人妖乱国产精品人妖 | 精品无人区无码乱码毛片国产 | 国产激情一区二区三区 | www国产亚洲精品久久网站 | 无码毛片视频一区二区本码 | 丰满少妇高潮惨叫视频 | 中文字幕av日韩精品一区二区 | 国产精品99爱免费视频 | 波多野结衣高清一区二区三区 | 内射爽无广熟女亚洲 | 免费人成网站视频在线观看 | 亚洲精品一区国产 | aⅴ在线视频男人的天堂 | 亚洲日韩中文字幕在线播放 | 中文字幕乱妇无码av在线 | 六月丁香婷婷色狠狠久久 | 麻豆国产人妻欲求不满谁演的 | 久久精品国产大片免费观看 | 中文字幕av无码一区二区三区电影 | www国产亚洲精品久久久日本 | 日韩精品乱码av一区二区 | 在线欧美精品一区二区三区 | 久久久久se色偷偷亚洲精品av | 精品aⅴ一区二区三区 | 色一情一乱一伦一区二区三欧美 | 精品久久8x国产免费观看 | www国产精品内射老师 | 国产农村妇女高潮大叫 | 97人妻精品一区二区三区 | 国产精品久久久久久亚洲影视内衣 | 国产激情综合五月久久 | 又大又硬又爽免费视频 | 日韩少妇内射免费播放 | 成人精品一区二区三区中文字幕 | 婷婷五月综合缴情在线视频 | 国产情侣作爱视频免费观看 | 久久久精品成人免费观看 | 鲁鲁鲁爽爽爽在线视频观看 | 国产精品福利视频导航 | 亚洲自偷自拍另类第1页 | 国产成人精品一区二区在线小狼 | 久久五月精品中文字幕 | 久久99精品久久久久久动态图 | 男女性色大片免费网站 | 强辱丰满人妻hd中文字幕 | 亚洲精品一区二区三区在线 | 欧洲熟妇色 欧美 | 老熟女重囗味hdxx69 | 久久精品成人欧美大片 | 大肉大捧一进一出好爽视频 | 少妇厨房愉情理9仑片视频 | 久久国产精品精品国产色婷婷 | 久久99精品国产.久久久久 | 久久久久久久人妻无码中文字幕爆 | 亚洲国产午夜精品理论片 | 亚洲日韩av片在线观看 | 久久人人爽人人爽人人片av高清 | 色婷婷av一区二区三区之红樱桃 | 双乳奶水饱满少妇呻吟 | 无码精品人妻一区二区三区av | 亚洲精品成a人在线观看 | 亚洲国产成人a精品不卡在线 | 精品无码国产一区二区三区av | 麻豆国产丝袜白领秘书在线观看 | 国产人妻人伦精品1国产丝袜 | 99视频精品全部免费免费观看 | 丰满少妇高潮惨叫视频 | a国产一区二区免费入口 | 亚洲成色www久久网站 | 又大又硬又爽免费视频 | 欧美性生交活xxxxxdddd | 欧美亚洲日韩国产人成在线播放 | 无码午夜成人1000部免费视频 | 黑人粗大猛烈进出高潮视频 | 欧美性猛交内射兽交老熟妇 | 骚片av蜜桃精品一区 | 久久精品中文闷骚内射 | 成人精品一区二区三区中文字幕 | 国产激情一区二区三区 | 亚洲无人区一区二区三区 | 欧洲熟妇精品视频 | 日本www一道久久久免费榴莲 | 天天综合网天天综合色 | 国产精品18久久久久久麻辣 | 人妻与老人中文字幕 | 无码精品人妻一区二区三区av | 日日天日日夜日日摸 | 国产精品视频免费播放 | 精品无码一区二区三区爱欲 | 日本高清一区免费中文视频 | 高潮毛片无遮挡高清免费 | 美女扒开屁股让男人桶 | 装睡被陌生人摸出水好爽 | 国产精品无码一区二区桃花视频 | 波多野结衣乳巨码无在线观看 | 日本精品人妻无码免费大全 | 成人女人看片免费视频放人 | 亚洲男女内射在线播放 | 亚洲精品中文字幕久久久久 | 男人和女人高潮免费网站 | 日韩精品无码免费一区二区三区 | 中文字幕 亚洲精品 第1页 | 成人精品天堂一区二区三区 | 精品久久久中文字幕人妻 | 亚洲精品久久久久avwww潮水 | 国内少妇偷人精品视频免费 | 日韩在线不卡免费视频一区 | 偷窥日本少妇撒尿chinese | 婷婷综合久久中文字幕蜜桃三电影 | 国内综合精品午夜久久资源 | 色噜噜亚洲男人的天堂 | 久久久婷婷五月亚洲97号色 | 野外少妇愉情中文字幕 | 黑人玩弄人妻中文在线 | 国产精品亚洲五月天高清 | 女人被男人爽到呻吟的视频 | 国内精品人妻无码久久久影院蜜桃 | 亚洲成av人片在线观看无码不卡 | 日日摸日日碰夜夜爽av | 精品久久久久久亚洲精品 | 亚洲热妇无码av在线播放 | 中文精品久久久久人妻不卡 | 最近的中文字幕在线看视频 | 无码乱肉视频免费大全合集 | 国产精品久久久午夜夜伦鲁鲁 | 玩弄中年熟妇正在播放 | 亚洲国产精品一区二区美利坚 | 久久无码中文字幕免费影院蜜桃 | 国产在线精品一区二区三区直播 | 国产特级毛片aaaaaaa高清 | 大肉大捧一进一出视频出来呀 | 国产内射老熟女aaaa | 国产av剧情md精品麻豆 | 日韩精品成人一区二区三区 | 无人区乱码一区二区三区 | 久久99久久99精品中文字幕 | 国产精品va在线观看无码 | 一本色道久久综合狠狠躁 | 久久99久久99精品中文字幕 | 日本一卡2卡3卡四卡精品网站 | 一个人看的www免费视频在线观看 | 国产av人人夜夜澡人人爽麻豆 | 久久久久久久人妻无码中文字幕爆 | 久久亚洲日韩精品一区二区三区 | 成人精品天堂一区二区三区 | 色综合视频一区二区三区 | 精品久久久无码人妻字幂 | 国产精品高潮呻吟av久久 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产97色在线 | 免 | 内射欧美老妇wbb | 成熟女人特级毛片www免费 | 色婷婷综合中文久久一本 | 欧洲vodafone精品性 | 无码人妻少妇伦在线电影 | 欧美国产亚洲日韩在线二区 | 天下第一社区视频www日本 | 午夜成人1000部免费视频 | 正在播放东北夫妻内射 | 中文字幕人妻丝袜二区 | 中文字幕+乱码+中文字幕一区 | 亚洲综合色区中文字幕 | 在线天堂新版最新版在线8 | 色综合久久中文娱乐网 | 国产精华av午夜在线观看 | 色 综合 欧美 亚洲 国产 | 精品午夜福利在线观看 | 高潮喷水的毛片 | 欧美日韩一区二区综合 | 亚洲色在线无码国产精品不卡 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 午夜无码区在线观看 | 中文字幕无码热在线视频 | 日日干夜夜干 | 天天燥日日燥 | 激情综合激情五月俺也去 | 日本大香伊一区二区三区 | 亚洲精品中文字幕乱码 | 国产乱码精品一品二品 | 婷婷色婷婷开心五月四房播播 | 精品厕所偷拍各类美女tp嘘嘘 | 久久久久99精品成人片 | 国产深夜福利视频在线 | 婷婷丁香六月激情综合啪 | 日韩亚洲欧美中文高清在线 | 扒开双腿疯狂进出爽爽爽视频 | 成人欧美一区二区三区黑人免费 | 牲欲强的熟妇农村老妇女 | 未满成年国产在线观看 | 亚洲国产精华液网站w | 未满小14洗澡无码视频网站 | 亚洲欧美日韩国产精品一区二区 | 亚洲色欲久久久综合网东京热 | 在线a亚洲视频播放在线观看 | 高潮毛片无遮挡高清免费视频 | 国产人妻精品一区二区三区不卡 | 国产乱人偷精品人妻a片 | 亚洲自偷精品视频自拍 | 天天拍夜夜添久久精品 | 亚洲一区二区观看播放 | 国产口爆吞精在线视频 | 草草网站影院白丝内射 | 亚洲爆乳精品无码一区二区三区 | 成人免费视频在线观看 | 亚洲午夜无码久久 | 精品少妇爆乳无码av无码专区 | 国产热a欧美热a在线视频 | 撕开奶罩揉吮奶头视频 | 天天拍夜夜添久久精品大 | 国产亚洲欧美日韩亚洲中文色 | 国产精品无码成人午夜电影 | 一本精品99久久精品77 | 377p欧洲日本亚洲大胆 | 97精品人妻一区二区三区香蕉 | 亚洲精品一区三区三区在线观看 | 亚无码乱人伦一区二区 | 国产人妻人伦精品 | aⅴ亚洲 日韩 色 图网站 播放 | 日本一区二区三区免费播放 | 亚洲自偷精品视频自拍 | 玩弄人妻少妇500系列视频 | 亚洲成熟女人毛毛耸耸多 | 国产97色在线 | 免 | 国产激情艳情在线看视频 | 亚洲区欧美区综合区自拍区 | 亚洲精品一区三区三区在线观看 | 亚洲精品www久久久 | 亚洲中文字幕无码中文字在线 | 在线成人www免费观看视频 | 欧美变态另类xxxx | 色欲综合久久中文字幕网 | 中国女人内谢69xxxxxa片 | 日日橹狠狠爱欧美视频 | 丰满妇女强制高潮18xxxx | 鲁一鲁av2019在线 | 中文无码精品a∨在线观看不卡 | 国产精品对白交换视频 | 台湾无码一区二区 | 亚洲国产综合无码一区 | 人人澡人人妻人人爽人人蜜桃 | 女人色极品影院 | 日本爽爽爽爽爽爽在线观看免 | 免费观看黄网站 | 沈阳熟女露脸对白视频 | 亚洲精品午夜国产va久久成人 | 中文字幕无码av波多野吉衣 | 人妻无码久久精品人妻 | 国产人成高清在线视频99最全资源 | 一本加勒比波多野结衣 | 亚洲成在人网站无码天堂 | 欧美自拍另类欧美综合图片区 | 中文字幕av无码一区二区三区电影 | 亚洲色在线无码国产精品不卡 | 少妇一晚三次一区二区三区 | 1000部啪啪未满十八勿入下载 | 国产午夜亚洲精品不卡下载 | 欧美性生交xxxxx久久久 | 欧美老妇与禽交 | 狠狠综合久久久久综合网 | 玩弄少妇高潮ⅹxxxyw | 国内精品一区二区三区不卡 | 乱码av麻豆丝袜熟女系列 | 中文字幕日产无线码一区 | 国产成人午夜福利在线播放 | 少妇被黑人到高潮喷出白浆 | 蜜桃臀无码内射一区二区三区 | 亚洲精品欧美二区三区中文字幕 | 国产亚洲精品久久久久久久久动漫 | 宝宝好涨水快流出来免费视频 | 久久无码中文字幕免费影院蜜桃 | 久久综合激激的五月天 | 精品乱码久久久久久久 | 麻豆国产丝袜白领秘书在线观看 | 亚洲精品国产第一综合99久久 | 欧美性生交xxxxx久久久 | 日本肉体xxxx裸交 | 爱做久久久久久 | 久久精品中文字幕一区 | 久久五月精品中文字幕 | 亚洲中文字幕在线无码一区二区 | 丰腴饱满的极品熟妇 | 麻豆果冻传媒2021精品传媒一区下载 | 麻豆国产97在线 | 欧洲 | 四虎国产精品一区二区 | 激情亚洲一区国产精品 | 国产人成高清在线视频99最全资源 |