日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

MyBatis攔截器的原理與使用

瀏覽:98日期:2023-10-19 09:07:07
目錄一、攔截對(duì)象和接口實(shí)現(xiàn)示例二、攔截器注冊(cè)的三種方式1.XML注冊(cè)2.配置類注冊(cè)3.注解方式三、ParameterHandler參數(shù)改寫(xiě)-修改時(shí)間和修改人統(tǒng)一插入四、通過(guò)StatementHandler改寫(xiě)SQL一、攔截對(duì)象和接口實(shí)現(xiàn)示例

MyBatis攔截器的作用是在于Dao到DB中間進(jìn)行額外的處理。大部分情況下通過(guò)mybatis的xml配置sql都可以達(dá)到想要的DB操作效果,然而存在一些類似或者相同的查詢條件或者查詢要求,這些可以通過(guò)攔截器的實(shí)現(xiàn)可以提升開(kāi)發(fā)效率,比如:分頁(yè)、插入和更新時(shí)間/人、數(shù)據(jù)權(quán)限、SQL監(jiān)控日志等。

Mybatis支持四種對(duì)象攔截Executor、StatementHandler、PameterHandler和ResultSetHandler Executor:攔截執(zhí)行器的方法。 StatementHandler:攔截Sql語(yǔ)法構(gòu)建的處理。 ParameterHandler:攔截參數(shù)的處理。 ResultHandler:攔截結(jié)果集的處理。

public interface Executor { ResultHandler NO_RESULT_HANDLER = null; int update(MappedStatement var1, Object var2) throws SQLException; <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException; <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException; <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException; List<BatchResult> flushStatements() throws SQLException; void commit(boolean var1) throws SQLException; void rollback(boolean var1) throws SQLException; CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4); boolean isCached(MappedStatement var1, CacheKey var2); void clearLocalCache(); void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5); Transaction getTransaction(); void close(boolean var1); boolean isClosed(); void setExecutorWrapper(Executor var1);}public interface StatementHandler { Statement prepare(Connection var1, Integer var2) throws SQLException; void parameterize(Statement var1) throws SQLException; void batch(Statement var1) throws SQLException; int update(Statement var1) throws SQLException; <E> List<E> query(Statement var1, ResultHandler var2) throws SQLException; <E> Cursor<E> queryCursor(Statement var1) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler();}public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement var1) throws SQLException;}public interface ResultHandler<T> { void handleResult(ResultContext<? extends T> var1);}

攔截的執(zhí)行順序是Executor->StatementHandler->ParameterHandler->ResultHandler

MyBatis提供的攔截器接口:

public interface Interceptor { Object intercept(Invocation var1) throws Throwable; default Object plugin(Object target) {return Plugin.wrap(target, this); } default void setProperties(Properties properties) {}}

Object intercept方法用于攔截器的實(shí)現(xiàn);

Object plugin方法用于判斷執(zhí)行攔截器的類型;

void setProperties方法用于獲取配置項(xiàng)的屬性。

攔截對(duì)象和攔截器接口的結(jié)合,自定義的攔截器類需要實(shí)現(xiàn)攔截器接口,并通過(guò)注解@Intercepts和參數(shù)@Signature來(lái)聲明要攔截的對(duì)象。

@Signature參數(shù)type是攔截對(duì)象,method是攔截的方法,即上面的四個(gè)類對(duì)應(yīng)的方法,args是攔截方法對(duì)應(yīng)的參數(shù)(方法存在重載因此需要指明參數(shù)個(gè)數(shù)和類型)

@Intercepts可以有多個(gè)@Signature,即一個(gè)攔截器實(shí)現(xiàn)類可以同時(shí)攔截多個(gè)對(duì)象及方法,示例如下:

Executor->intercept StatementHandler->intercept ParameterHandler->intercept ResultHandler->intercept

@Intercepts({@Signature(type = Executor.class,method = 'query',args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class SelectPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof Executor) { System.out.println('SelectPlugin');}return invocation.proceed(); } @Override public Object plugin(Object target) {if (target instanceof Executor) { return Plugin.wrap(target, this);}return target; } @Override public void setProperties(Properties properties) {}}@Intercepts({@Signature(type = StatementHandler.class, method = 'prepare', args = {Connection.class, Integer.class})})public class StatementPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof StatementHandler) { System.out.println('StatementPlugin');}return invocation.proceed(); } @Override public Object plugin(Object target) {if (target instanceof StatementHandler) { return Plugin.wrap(target, this);}return target; } @Override public void setProperties(Properties properties) {}}@Intercepts({@Signature(type = ParameterHandler.class,method = 'setParameters',args = {PreparedStatement.class})})public class ParameterPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof ParameterHandler) { System.out.println('ParameterPlugin');}return invocation.proceed(); } @Override public Object plugin(Object target) {if (target instanceof ParameterHandler) { return Plugin.wrap(target, this);}return target; } @Override public void setProperties(Properties properties) {}}@Intercepts({@Signature(type = ResultHandler.class,method = 'handleResult',args = {ResultContext.class})})public class ResultPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof ResultHandler) { System.out.println('ResultPlugin');}return invocation.proceed(); } @Override public Object plugin(Object target) {if (target instanceof ResultHandler) { return Plugin.wrap(target, this);}return target; } @Override public void setProperties(Properties properties) {}}二、攔截器注冊(cè)的三種方式

前面介紹了Mybatis的攔截對(duì)象及其接口的實(shí)現(xiàn)方式,那么在項(xiàng)目中如何注冊(cè)攔截器呢?本文中給出三種注冊(cè)方式。

1.XML注冊(cè)

xml注冊(cè)是最基本的方式,是通過(guò)在Mybatis配置文件中plugins元素來(lái)進(jìn)行注冊(cè)的。一個(gè)plugin對(duì)應(yīng)著一個(gè)攔截器,在plugin元素可以指定property子元素,在注冊(cè)定義攔截器時(shí)把對(duì)應(yīng)攔截器的所有property通過(guò)Interceptor的setProperties方法注入給攔截器。因此攔截器注冊(cè)xml方式如下:

<?xml version='1.0' encoding='UTF-8' ?><!DOCTYPE configuration PUBLIC '-//mybatis.org//DTD Config 3.0//EN' 'http://mybatis.org/dtd/mybatis-3-config.dtd'><configuration> <!-- ...... --> <plugins> <plugin interceptor='com.tiantian.mybatis.interceptor.MyInterceptor'> <property name='prop1' value='prop1'/> <property name='prop2' value='prop2'/> </plugin> </plugins> <!-- ...... --></configuration>2.配置類注冊(cè)

配置類注冊(cè)是指通過(guò)Mybatis的配置類中聲明注冊(cè)攔截器,配置類注冊(cè)也可以通過(guò)Properties類給Interceptor的setProperties方法注入?yún)?shù)。具體參考如下:

@Configurationpublic class MyBatisConfig { @Bean public String MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) {UpdatePlugin executorInterceptor = new UpdatePlugin();Properties properties = new Properties();properties.setProperty('prop1', 'value1');// 給攔截器添加自定義參數(shù)executorInterceptor.setProperties(properties);sqlSessionFactory.getConfiguration().addInterceptor(executorInterceptor);sqlSessionFactory.getConfiguration().addInterceptor(new StatementPlugin());sqlSessionFactory.getConfiguration().addInterceptor(new ResultPlugin());sqlSessionFactory.getConfiguration().addInterceptor(new ParameterPlugin());// sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());return 'interceptor'; } // 與sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());效果一致 @Bean public SelectPlugin SelectInterceptor() {SelectPlugin interceptor = new SelectPlugin();Properties properties = new Properties();// 調(diào)用properties.setProperty方法給攔截器設(shè)置自定義參數(shù)interceptor.setProperties(properties);return interceptor; }}3.注解方式

通過(guò)@Component注解方式是最簡(jiǎn)單的方式,在不需要轉(zhuǎn)遞自定義參數(shù)時(shí)可以使用,方便快捷。

@Component@Intercepts({@Signature(type = Executor.class,method = 'query',args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class SelectPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof Executor) { System.out.println('SelectPlugin');}return invocation.proceed(); } @Override public Object plugin(Object target) {if (target instanceof Executor) { return Plugin.wrap(target, this);}return target; } @Override public void setProperties(Properties properties) { }}三、ParameterHandler參數(shù)改寫(xiě)-修改時(shí)間和修改人統(tǒng)一插入

針對(duì)具體的攔截器實(shí)現(xiàn)進(jìn)行描述。日常編碼需求中會(huì)碰到修改時(shí)需要插入修改的時(shí)間和人員,如果要用xml的方式去寫(xiě)非常麻煩,而通過(guò)攔截器的方式可以快速實(shí)現(xiàn)全局的插入修改時(shí)間和人員。先看代碼:

@Component@Intercepts({@Signature(type = ParameterHandler.class, method = 'setParameters', args = {PreparedStatement.class}),})public class MyBatisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {// 參數(shù)代理if (invocation.getTarget() instanceof ParameterHandler) { System.out.println('ParameterHandler'); // 自動(dòng)添加操作員信息 autoAddOperatorInfo(invocation);}return invocation.proceed(); } @Override public Object plugin(Object target) {return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } /** * 自動(dòng)添加操作員信息 * * @param invocation 代理對(duì)象 * @throws Throwable 異常 */ private void autoAddOperatorInfo(Invocation invocation) throws Throwable {System.out.println('autoInsertCreatorInfo');// 獲取代理的參數(shù)對(duì)象ParameterHandlerParameterHandler ph = (ParameterHandler) invocation.getTarget();// 通過(guò)MetaObject獲取ParameterHandler的反射內(nèi)容MetaObject metaObject = MetaObject.forObject(ph,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());// 通過(guò)MetaObject反射的內(nèi)容獲取MappedStatementMappedStatement mappedStatement = (MappedStatement) metaObject.getValue('mappedStatement');// 當(dāng)sql類型為INSERT或UPDATE時(shí),自動(dòng)插入操作員信息if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT ||mappedStatement.getSqlCommandType() == SqlCommandType.UPDATE) { // 獲取參數(shù)對(duì)象 Object obj = ph.getParameterObject(); if (null != obj) {// 通過(guò)反射獲取參數(shù)對(duì)象的屬性Field[] fields = obj.getClass().getDeclaredFields();// 遍歷參數(shù)對(duì)象的屬性for (Field f : fields) { // 如果sql是INSERT,且存在createdAt屬性 if ('createdAt'.equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {// 設(shè)置允許訪問(wèn)反射屬性f.setAccessible(true);// 如果沒(méi)有設(shè)置createdAt屬性,則自動(dòng)為createdAt屬性添加當(dāng)前的時(shí)間if (null == f.get(obj)) { // 設(shè)置createdAt屬性為當(dāng)前時(shí)間 f.set(obj, LocalDateTime.now());} } // 如果sql是INSERT,且存在createdBy屬性 if ('createdBy'.equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {// 設(shè)置允許訪問(wèn)反射屬性f.setAccessible(true);// 如果沒(méi)有設(shè)置createdBy屬性,則自動(dòng)為createdBy屬性添加當(dāng)前登錄的人員if (null == f.get(obj)) { // 設(shè)置createdBy屬性為當(dāng)前登錄的人員 f.set(obj, 0);} } // sql為INSERT或UPDATE時(shí)均需要設(shè)置updatedAt屬性 if ('updatedAt'.equals(f.getName())) {f.setAccessible(true);if (null == f.get(obj)) { f.set(obj, LocalDateTime.now());} } // sql為INSERT或UPDATE時(shí)均需要設(shè)置updatedBy屬性 if ('updatedBy'.equals(f.getName())) {f.setAccessible(true);if (null == f.get(obj)) { f.set(obj, 0);} }}// 通過(guò)反射獲取ParameterHandler的parameterObject屬性Field parameterObject = ph.getClass().getDeclaredField('parameterObject');// 設(shè)置允許訪問(wèn)parameterObject屬性parameterObject.setAccessible(true);// 將上面設(shè)置的新參數(shù)對(duì)象設(shè)置到ParameterHandler的parameterObject屬性parameterObject.set(ph, obj); }} }}

攔截器的接口實(shí)現(xiàn)參考前文,這里著重介紹autoAddOperatorInfo方法里的相關(guān)類。

1.ParameterHandler

接口源碼:

public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement var1) throws SQLException; }

提供兩個(gè)方法:

getParameterObject是獲取參數(shù)對(duì)象,可能存在null,需要注意null指針。

setParameters是控制如何設(shè)置SQL參數(shù),即sql語(yǔ)句中配置的java對(duì)象和jdbc類型對(duì)應(yīng)的關(guān)系,例如#{id,jdbcType=INTEGER},id默認(rèn)類型是javaType=class java.lang.Integer。

該接口有一個(gè)默認(rèn)的實(shí)現(xiàn)類,源碼如下:

public class DefaultParameterHandler implements ParameterHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private final BoundSql boundSql; private final Configuration configuration; public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {this.mappedStatement = mappedStatement;this.configuration = mappedStatement.getConfiguration();this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();this.parameterObject = parameterObject;this.boundSql = boundSql; } public Object getParameterObject() {return this.parameterObject; } public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity('setting parameters').object(this.mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();if (parameterMappings != null) { for(int i = 0; i < parameterMappings.size(); ++i) {ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) { String propertyName = parameterMapping.getProperty(); Object value; if (this.boundSql.hasAdditionalParameter(propertyName)) {value = this.boundSql.getAdditionalParameter(propertyName); } else if (this.parameterObject == null) {value = null; } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {value = this.parameterObject; } else {MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) {jdbcType = this.configuration.getJdbcTypeForNull(); } try {typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (SQLException | TypeException var10) {throw new TypeException('Could not set parameters for mapping: ' + parameterMapping + '. Cause: ' + var10, var10); }} }} }}

通過(guò)DefaultParameterHandler實(shí)現(xiàn)類我們知道通過(guò)ParameterHandler可以獲取到哪些屬性和方法,其中包括我們下面一個(gè)重要的類MappedStatement。

2.MappedStatement

MyBatis的mapper文件中的每個(gè)select/update/insert/delete標(biāo)簽會(huì)被解析器解析成一個(gè)對(duì)應(yīng)的MappedStatement對(duì)象,也就是一個(gè)MappedStatement對(duì)象描述一條SQL語(yǔ)句。MappedStatement對(duì)象屬性如下:

// mapper配置文件名 private String resource; // mybatis的全局信息,如jdbc private Configuration configuration; // 節(jié)點(diǎn)的id屬性加命名空間,如:com.example.mybatis.dao.UserMapper.selectByExample private String id; private Integer fetchSize; private Integer timeout; private StatementType statementType; private ResultSetType resultSetType; private SqlSource sqlSource; private Cache cache; private ParameterMap parameterMap; private List<ResultMap> resultMaps; private boolean flushCacheRequired; private boolean useCache; private boolean resultOrdered; // sql語(yǔ)句的類型:select、update、delete、insert private SqlCommandType sqlCommandType; private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; private String databaseId; private Log statementLog; private LanguageDriver lang; private String[] resultSets;

在本例中通過(guò)MappedStatement對(duì)象的sqlCommandType來(lái)判斷當(dāng)前的sql類型是insert、update來(lái)進(jìn)行下一步的操作。

四、通過(guò)StatementHandler改寫(xiě)SQL

StatementHandler是用于封裝JDBC Statement操作,負(fù)責(zé)對(duì)JDBC Statement的操作,如設(shè)置參數(shù),并將Statement結(jié)果集轉(zhuǎn)換成List集合。

實(shí)現(xiàn)代碼如下:

刪除注解標(biāo)記

@Target({ElementType.METHOD}) //表示注解的使用范圍@Retention(RetentionPolicy.RUNTIME) //注解的保存時(shí)間@Documented //文檔顯示public @interface DeletedAt { boolean has() default true;}

Dao層添加刪除注解,為false時(shí)不添加刪除標(biāo)志

@Mapper public interface AdminProjectDao { @DeletedAt(has = false) List<AdminProjectPo> selectProjects(AdminProjectPo po); }

攔截器通過(guò)刪除注解標(biāo)記判斷是否添加刪除標(biāo)志

@Component@Intercepts({@Signature(type = StatementHandler.class, method = 'prepare', args = {Connection.class, Integer.class}),})public class MyBatisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof StatementHandler) { System.out.println('StatementHandler'); checkHasDeletedAtField(invocation);}return invocation.proceed(); } @Override public Object plugin(Object target) {return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } /** * 檢查查詢是否需要添加刪除標(biāo)志字段 * * @param invocation 代理對(duì)象 * @throws Throwable 異常 */ private void checkHasDeletedAtField(Invocation invocation) throws Throwable {System.out.println('checkHasDeletedAtField');StatementHandler statementHandler = (StatementHandler) invocation.getTarget();// 通過(guò)MetaObject訪問(wèn)對(duì)象的屬性MetaObject metaObject = MetaObject.forObject(statementHandler,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());// 獲取成員變量mappedStatementMappedStatement mappedStatement = (MappedStatement) metaObject.getValue('delegate.mappedStatement');// 如果sql類型是查詢if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) { // 獲取刪除注解標(biāo)志 DeletedAt annotation = null; String id = mappedStatement.getId(); String className = id.substring(0, id.lastIndexOf('.')); String methodName = id.substring(id.lastIndexOf('.') + 1); Class<?> aClass = Class.forName(className); Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) {declaredMethod.setAccessible(true);//方法名相同,并且注解是DeletedAtif (methodName.equals(declaredMethod.getName()) && declaredMethod.isAnnotationPresent(DeletedAt.class)) { annotation = declaredMethod.getAnnotation(DeletedAt.class);} } // 如果注解不存在或者注解為true(默認(rèn)為true) 則為mysql語(yǔ)句增加刪除標(biāo)志 if (annotation == null || annotation.has()) {BoundSql boundSql = statementHandler.getBoundSql();//獲取到原始sql語(yǔ)句String sql = boundSql.getSql();//通過(guò)反射修改sql語(yǔ)句Field field = boundSql.getClass().getDeclaredField('sql');field.setAccessible(true);String newSql = sql.replaceAll('9=9', '9=9 and deleted_at is null ');field.set(boundSql, newSql); }} }}

在SQL語(yǔ)句替換上需要能識(shí)別到要被替換的內(nèi)容,因此在xml的sql語(yǔ)句中加入特殊標(biāo)志'9=9',該標(biāo)志不影響原來(lái)SQL的執(zhí)行結(jié)果,不同的過(guò)濾條件可以設(shè)置不同的標(biāo)志,是一個(gè)比較巧妙的替換方式。

以上就是MyBatis攔截器的原理與使用的詳細(xì)內(nèi)容,更多關(guān)于MyBatis攔截器的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国内激情久久| 国产伦精品一区二区三区视频 | 久久精品在线| 吉吉日韩欧美| 国产一区二区三区国产精品| 国精品产品一区| 成人午夜网址| 捆绑调教美女网站视频一区| 国产精品最新| 久久中文欧美| 在线一区av| 欧美中文一区二区| 黄色日韩在线| 综合五月婷婷| 国产情侣久久| 日韩黄色大片| 亚洲一区二区免费看| 人人爽香蕉精品| 日本欧美一区二区| 国产高清精品二区| 美女av在线免费看| 尹人成人综合网| 亚洲精品在线二区| 无码日韩精品一区二区免费| 国产日产精品_国产精品毛片| 欧美激情 亚洲a∨综合| av免费不卡国产观看| 欧美日韩国产免费观看视频| 国产日韩三级| 日韩一区精品视频| 免费在线观看成人| 六月丁香综合在线视频| 九九综合在线| 日本精品另类| 亚洲理论在线| 精品视频亚洲| 国产精品香蕉| 青青草伊人久久| 国产精品一区二区美女视频免费看| 欧美三级第一页| 国产黄色一区| 国产一区二区三区日韩精品 | 一本一本久久| 色偷偷色偷偷色偷偷在线视频| 国产精品一区二区三区www| 亚洲一区二区毛片| 久久99久久久精品欧美| 日韩精品久久久久久久电影99爱| 欧美日韩亚洲一区三区| 丝袜a∨在线一区二区三区不卡| 天堂va在线高清一区| 999国产精品永久免费视频app| 午夜久久av| 欧美偷窥清纯综合图区| 日韩高清在线观看一区二区| 亚洲va在线| 麻豆国产精品| 99视频精品全部免费在线视频| 日韩和欧美一区二区三区| 免费不卡在线视频| 欧美日一区二区三区在线观看国产免| 久久a爱视频| 麻豆网站免费在线观看| 国产午夜一区| 国产欧美成人| 99视频精品全部免费在线视频| 亚洲精品在线观看91| 久久精品国产网站| 91久久中文| 日韩精品免费视频人成| 99精品电影| 国产在线观看www| 麻豆视频在线看| 欧美一级精品| 樱桃成人精品视频在线播放| 日韩一区二区三区四区五区| 成人羞羞视频在线看网址| 国产一区91| 国产午夜久久av| 国产精品第一| 蜜臀久久99精品久久久久久9| 欧美激情99| 香蕉久久国产| 免费人成黄页网站在线一区二区| 欧美日韩尤物久久| 日韩中文字幕无砖| 欧美日一区二区在线观看| 热久久免费视频| 97精品在线| 国产成人精品一区二区免费看京| 伊人久久大香伊蕉在人线观看热v| 激情久久婷婷| 国产人成精品一区二区三| 亚洲成人不卡| 日韩午夜在线| 欧美一区三区| 激情不卡一区二区三区视频在线| 99riav国产精品| 欧美69视频| 久久xxxx| 丝袜诱惑制服诱惑色一区在线观看| 麻豆亚洲精品| 精品视频一区二区三区四区五区| 欧美日韩国产观看视频| 国产一区导航| 天堂av在线一区| 欧美成人精品三级网站| 日韩三区在线| 久久99精品久久久久久园产越南| 日本中文字幕不卡| 日韩一级精品| 亚洲精品日本| 久久精品国语| 成人福利av| 影院欧美亚洲| 国产精品欧美日韩一区| 日韩在线不卡| 日韩国产91| 亚洲夜间福利| 国产精选一区| 午夜影院欧美| 美女国产精品久久久| 在线看片不卡| 久久的色偷偷| 日韩专区欧美专区| 97精品在线| 亚洲精品动态| 在线免费观看亚洲| 日韩国产一区二区三区| 四虎成人av| 国产一区久久| 天海翼亚洲一区二区三区| 国产亚洲精品美女久久| 成人午夜网址| 日韩精品一级中文字幕精品视频免费观看| 综合一区在线| 国产欧美自拍| 美女高潮久久久| 国产成人免费| www在线观看黄色| 伊人成人在线视频| 亚洲欧洲另类| 91午夜精品| 九一精品国产| 国产亚洲一区二区三区啪| 国产精品天天看天天狠| 国产精品sm| 日本国产精品| 欧美综合国产| 日韩综合一区| 亚洲色图国产| 欧美极品一区二区三区| 好看不卡的中文字幕| 最新国产精品久久久| 国产伦精品一区二区三区千人斩 | 亚洲制服一区| 日韩精品高清不卡| 97精品一区| 日韩成人av影视| 视频小说一区二区| 日本国产欧美| 欧美/亚洲一区| 欧美日韩18| 在线亚洲激情| 国产高清不卡| 亚洲欧美在线综合| 亚洲三级网站| 日本欧美国产| 日本不卡一区二区| 久久久夜夜夜| 国产成人精品一区二区三区免费| 视频在线在亚洲| 久久精品理论片| 亚洲精品高潮| 欧美~级网站不卡| 日韩精品一区二区三区免费观影| 欧美亚洲综合视频| 免费不卡在线观看| 久久久久国产精品一区二区| 国产精品超碰| 四虎精品一区二区免费| 国产一区二区久久久久| 在线亚洲一区| 亚洲成人免费| 精品成人免费一区二区在线播放| 日韩欧美久久| 欧美亚洲专区| 国产亚洲高清一区| 日本午夜精品视频在线观看| 国产精品视区| 欧美中文日韩| 日本天堂一区| 久久99影视| 精品亚洲自拍| caoporn视频在线| 精品精品久久| 亚洲人妖在线| 日韩avvvv在线播放| 日本一区二区三区中文字幕| 精品久久一区|