久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > 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)的全部內容,希望文章能夠幫你解決所遇到的問題。

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

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