黄a在线观看-黄a在线-黄a大片-黄色片在线看-黄色毛片免费-黄色大片网站

您的位置:首頁技術文章
文章詳情頁

Mybatis #foreach中相同的變量名導致值覆蓋的問題解決

瀏覽:58日期:2023-10-18 13:51:16
目錄背景問題原因(簡略版)Mybatis流程源碼解析(長文警告,按需自取)一、獲取SqlSessionFactory二、獲取SqlSession三、執行SQL背景

使用Mybatis中執行如下查詢:

單元測試

@Testpublic void test1() { String resource = 'mybatis-config.xml'; InputStream inputStream = null; try {inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) {e.printStackTrace(); } SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); try (SqlSession sqlSession = sqlSessionFactory.openSession()) {CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);QueryCondition queryCondition = new QueryCondition();List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);queryCondition.setWidthList(list);System.out.println(mapper.findByCondition(queryCondition)); }}

XML

<select parameterType='cn.liupjie.pojo.QueryCondition' resultType='cn.liupjie.pojo.Test'> select * from test <where><if test='id != null'> and id = #{id,jdbcType=INTEGER}</if><if test='widthList != null and widthList.size > 0'> <foreach collection='widthList' open='and width in (' close=')' item='width' separator=','>#{width,jdbcType=INTEGER} </foreach></if><if test='width != null'> and width = #{width,jdbcType=INTEGER}</if> </where></select>

打印的SQL:DEBUG [main] - ==> Preparing: select * from test WHERE width in ( ? , ? , ? ) and width = ? DEBUG [main] - ==> Parameters: 1(Integer), 2(Integer), 3(Integer), 3(Integer)

Mybatis版本

<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version></dependency>

這是公司的老項目,在迭代的過程中遇到了此問題,以此記錄!PS: 此bug在mybatis-3.4.5版本中已經解決。并且Mybatis維護者也建議不要在item/index中使用重復的變量名。

Mybatis #foreach中相同的變量名導致值覆蓋的問題解決

Mybatis #foreach中相同的變量名導致值覆蓋的問題解決

問題原因(簡略版) 在獲取到DefaultSqlSession之后,會獲取到Mapper接口的代理類,通過調用代理類的方法來執行查詢 真正執行數據庫查詢之前,需要將可執行的SQL拼接好,此操作在DynamicSqlSource#getBoundSql方法中執行 當解析到foreach標簽時,每次循環都會緩存一個item屬性值與變量值之間的映射(如:width:1),當foreach標簽解析完成后,緩存的參數映射關系中就保留了一個(width:3) 當解析到最后一個if標簽時,由于width變量有值,因此if判斷為true,正常執行拼接,導致出錯 3.4.5版本中,在foreach標簽解析完成后,增加了兩行代碼來解決這個問題。

//foreach標簽解析完成后,從bindings中移除item context.getBindings().remove(item); context.getBindings().remove(index);Mybatis流程源碼解析(長文警告,按需自取)一、獲取SqlSessionFactory

入口,跟著build方法走

//獲取SqlSessionFactory, 解析完成后,將XML中的內容封裝到一個Configuration對象中,//使用此對象構造一個DefaultSqlSessionFactory對象,并返回SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

來到SqlSessionFactoryBuilder#build方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //獲取XMLConfigBuilder,在XMLConfigBuilder的構造方法中,會創建XPathParser對象 //在創建XPathParser對象時,會將mybatis-config.xml文件轉換成Document對象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //調用XMLConfigBuilder#parse方法開始解析Mybatis的配置文件 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException('Error building SqlSession.', e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } }}

跟著parse方法走,來到XMLConfigBuilder#parseConfiguration方法

private void parseConfiguration(XNode root) { try { Properties settings = settingsAsPropertiess(root.evalNode('settings')); //issue #117 read properties first propertiesElement(root.evalNode('properties')); loadCustomVfs(settings); typeAliasesElement(root.evalNode('typeAliases')); pluginElement(root.evalNode('plugins')); objectFactoryElement(root.evalNode('objectFactory')); objectWrapperFactoryElement(root.evalNode('objectWrapperFactory')); reflectorFactoryElement(root.evalNode('reflectorFactory')); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode('environments')); databaseIdProviderElement(root.evalNode('databaseIdProvider')); typeHandlerElement(root.evalNode('typeHandlers')); //這里解析mapper mapperElement(root.evalNode('mappers')); } catch (Exception e) { throw new BuilderException('Error parsing SQL Mapper Configuration. Cause: ' + e, e); }}

來到mapperElement方法

//本次mappers配置:<mapper resource='xml/CommomMapper.xml'/>private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ('package'.equals(child.getName())) {String mapperPackage = child.getStringAttribute('name');configuration.addMappers(mapperPackage); } else {String resource = child.getStringAttribute('resource');String url = child.getStringAttribute('url');String mapperClass = child.getStringAttribute('class');if (resource != null && url == null && mapperClass == null) { //因此走這里,讀取xml文件,并開始解析 ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); //這里同上文創建XMLConfigBuilder對象一樣,在內部構造時,也將xml文件轉換為了一個Document對象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); //解析 mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface);} else { throw new BuilderException('A mapper element may only specify a url, resource or class, but not more than one.');} } } }}

XMLMapperBuilder類,負責解析SQL語句所在XML中的內容

//parse方法public void parse() { if (!configuration.isResourceLoaded(resource)) { //解析mapper標簽 configurationElement(parser.evalNode('/mapper')); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements();}//configurationElement方法private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute('namespace'); if (namespace == null || namespace.equals('')) { throw new BuilderException('Mapper’s namespace cannot be empty'); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode('cache-ref')); cacheElement(context.evalNode('cache')); parameterMapElement(context.evalNodes('/mapper/parameterMap')); resultMapElements(context.evalNodes('/mapper/resultMap')); sqlElement(context.evalNodes('/mapper/sql')); //解析各種類型的SQL語句:select|insert|update|delete buildStatementFromContext(context.evalNodes('select|insert|update|delete')); } catch (Exception e) { throw new BuilderException('Error parsing Mapper XML. Cause: ' + e, e); }}private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { //創建XMLStatementBuilder對象 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { //解析 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } }}

XMLStatementBuilder負責解析單個select|insert|update|delete節點

public void parseStatementNode() { String id = context.getStringAttribute('id'); String databaseId = context.getStringAttribute('databaseId'); //判斷databaseId是否匹配,將namespace+’.’+id拼接,判斷是否已經存在此id if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } Integer fetchSize = context.getIntAttribute('fetchSize'); Integer timeout = context.getIntAttribute('timeout'); String parameterMap = context.getStringAttribute('parameterMap'); //獲取參數類型 String parameterType = context.getStringAttribute('parameterType'); //獲取參數類型的class對象 Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute('resultMap'); String resultType = context.getStringAttribute('resultType'); String lang = context.getStringAttribute('lang'); LanguageDriver langDriver = getLanguageDriver(lang); //獲取resultType的class對象 Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute('resultSetType'); StatementType statementType = StatementType.valueOf(context.getStringAttribute('statementType', StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); //獲取select|insert|update|delete類型 String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute('flushCache', !isSelect); boolean useCache = context.getBooleanAttribute('useCache', isSelect); boolean resultOrdered = context.getBooleanAttribute('resultOrdered', false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) //獲取SqlSource對象,langDriver為默認的XMLLanguageDriver,在new Configuration時設置 //若sql中包含元素節點或$,則返回DynamicSqlSource,否則返回RawSqlSource SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute('resultSets'); String keyProperty = context.getStringAttribute('keyProperty'); String keyColumn = context.getStringAttribute('keyColumn'); KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute('useGeneratedKeys',configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))? new Jdbc3KeyGenerator() : new NoKeyGenerator(); } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}二、獲取SqlSession

由上文可知,此處的SqlSessionFactory使用的是DefaultSqlSessionFactory

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //創建執行器,默認是SimpleExecutor //如果在配置文件中開啟了緩存(默認開啟),則是CachingExecutor final Executor executor = configuration.newExecutor(tx, execType); //返回DefaultSqlSession對象 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException('Error opening session. Cause: ' + e, e); } finally { ErrorContext.instance().reset(); }}

這里獲取到了一個DefaultSqlSession對象

三、執行SQL

獲取CommonMapper的對象,這里CommonMapper是一個接口,因此是一個代理對象,代理類是MapperProxy

org.apache.ibatis.binding.MapperProxy@72cde7cc

執行Query方法,來到MapperProxy的invoke方法

@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //緩存 final MapperMethod mapperMethod = cachedMapperMethod(method); //執行操作:select|insert|update|delete return mapperMethod.execute(sqlSession, args);}

執行操作時,根據SELECT操作,以及返回值類型(反射方法獲取)確定executeForMany方法

caseSELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break;

來到executeForMany方法中,就可以看到執行查詢的操作,由于這里沒有進行分頁查詢,因此走else

if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds);} else { result = sqlSession.<E>selectList(command.getName(), param);}

來到DefaultSqlSession#selectList方法中

@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //根據key(namespace+'.'+id)來獲取MappedStatement對象 //MappedStatement對象中封裝了解析好的SQL信息 MappedStatement ms = configuration.getMappedStatement(statement); //通過CachingExecutor#query執行查詢 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException('Error querying database. Cause: ' + e, e); } finally { ErrorContext.instance().reset(); }}

CachingExecutor#query

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //解析SQL為可執行的SQL BoundSql boundSql = ms.getBoundSql(parameter); //獲取緩存的key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //執行查詢 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

MappedStatement#getBoundSql

public BoundSql getBoundSql(Object parameterObject) { //解析SQL BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } //檢查是否有嵌套的ResultMap // check for nested result maps in parameter mappings (issue #30) for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) {hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql;}

由上文,此次語句由于SQL中包含元素節點,因此是DynamicSqlSource。由此來到DynamicSqlSource#getBoundSql。rootSqlNode.apply(context);這段代碼便是在執行SQL解析。

@Overridepublic BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); //執行SQL解析 rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql;}

打上斷點,跟著解析流程,來到解析foreach標簽的代碼,ForEachSqlNode#apply

@Overridepublic boolean apply(DynamicContext context) { Map<String, Object> bindings = context.getBindings(); final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings); if (!iterable.iterator().hasNext()) { return true; } boolean first = true; //解析open屬性 applyOpen(context); int i = 0; for (Object o : iterable) { DynamicContext oldContext = context; if (first) { context = new PrefixedContext(context, ''); } else if (separator != null) { context = new PrefixedContext(context, separator); } else {context = new PrefixedContext(context, ''); } int uniqueNumber = context.getUniqueNumber(); // Issue #709 //集合中的元素是Integer,走else if (o instanceof Map.Entry) { @SuppressWarnings('unchecked') Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o; applyIndex(context, mapEntry.getKey(), uniqueNumber); applyItem(context, mapEntry.getValue(), uniqueNumber); } else { //使用index屬性 applyIndex(context, i, uniqueNumber); //使用item屬性 applyItem(context, o, uniqueNumber); } //當foreach中使用#號時,會將變量替換為占位符(類似__frch_width_0)(StaticTextSqlNode) //當使用$符號時,會將值直接拼接到SQL中(TextSqlNode) contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber)); if (first) { first = !((PrefixedContext) context).isPrefixApplied(); } context = oldContext; i++; } applyClose(context); return true;}private void applyItem(DynamicContext context, Object o, int i) { if (item != null) {//在參數映射中綁定item屬性值與集合值的關系//第一次:(width:1)//第二次:(width:2)//第三次:(width:3)context.bind(item, o);//在參數映射中綁定處理后的item屬性值與集合值的關系//第一次:(__frch_width_0:1)//第二次:(__frch_width_1:2)//第三次:(__frch_width_2:3)context.bind(itemizeItem(item, i), o); } }

到這里,結果就清晰了,在解析foreach標簽時,每次循環都會將item屬性值與參數集合中的值進行綁定,到最后就會保留(width:3)的映射關系,而在解析完foreach標簽后,會解析最后一個if標簽,此時在判斷if標簽是否成立時,答案是true,因此最終拼接出來一個錯誤的SQL。

在3.4.5版本中,代碼中增加了context.getBindings().remove(item);在foreach標簽解析完成后移除bindings中的參數映射。以下是源碼:

@Overridepublic boolean apply(DynamicContext context) { Map<String, Object> bindings = context.getBindings(); final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings); if (!iterable.iterator().hasNext()) { return true; } boolean first = true; applyOpen(context); int i = 0; for (Object o : iterable) { DynamicContext oldContext = context; if (first || separator == null) { context = new PrefixedContext(context, ''); } else { context = new PrefixedContext(context, separator); } int uniqueNumber = context.getUniqueNumber(); // Issue #709 if (o instanceof Map.Entry) { @SuppressWarnings('unchecked') Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o; applyIndex(context, mapEntry.getKey(), uniqueNumber); applyItem(context, mapEntry.getValue(), uniqueNumber); } else { applyIndex(context, i, uniqueNumber); applyItem(context, o, uniqueNumber); } contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber)); if (first) { first = !((PrefixedContext) context).isPrefixApplied(); } context = oldContext; i++; } applyClose(context); //foreach標簽解析完成后,從bindings中移除item context.getBindings().remove(item); context.getBindings().remove(index); return true;}

到此這篇關于Mybatis #foreach中相同的變量名導致值覆蓋的問題解決的文章就介紹到這了,更多相關Mybatis #foreach相同變量名覆蓋內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Mybatis 數據庫
相關文章:
主站蜘蛛池模板: 国产亚洲精久久久久久叶玉卿 | 日本免费黄色网 | 一本色综合亚洲精品蜜桃冫 | 日本伦奷在线播放 | 国产精品 视频一区 二区三区 | 欧美亚洲视频 | 肥臀熟女一区二区三区 | 精品国产片一区二区三区 | 欧美一乱一性一交一视频 | 日韩欧美亚洲精品 | 天天搞夜夜爽 | 国产成人久久av免费高清密臂 | 欧美日韩精品一区二区天天拍小说 | 欧美成网 | 色婷婷97| 天天鲁一鲁摸一摸爽一爽视频 | 国产欧美日韩在线观看 | 亚洲欧美日韩中文在线制服 | 春色校园综合人妻av | 古装一级淫片a免费播放口 寡妇av | 日韩欧美高清dvd碟片 | 无套内射蜜桃小视频 | 国产嫩草影院久久久 | 欧美日韩高清一区 | 国产日韩一区二区三区在线观看 | 国产精品99久久免费观看 | 羞羞网站在线看 | 青青草国产成人99久久 | 香蕉视频在线观看黄 | 成人做爰高潮片免费视频韩国 | 夜夜夜夜夜夜av夜夜夜夜 | 美日韩在线 | 亚洲视频精品在线观看 | 丝瓜色版| 麻豆视频国产精品 | 国产精品福利视频 | 377人体粉嫩噜噜噜 精品久久久久久无码中文野结衣 | 风间由美一区二区 | 一区二区高清视频 | 国产精品久久av一区二区三区 | 中文幕无线码中文字夫妻 | 日韩第四页 | jizz欧美 | 亚洲s码欧洲m码吹潮 | 国产日韩亚洲欧美 | 九九re6热在线视频精品66 | 亚洲精品无圣光一区二区 | 国产美女激情 | 国产亚洲婷婷香蕉久久精品 | 青青草原精品99久久精品66 | 免费观看日本 | 亚洲精品国产91 | 在线看毛片网站 | 日本大乳免费观看久久99 | 52avaⅴ我爱haose免费视频 | 一本加勒比hezyo日本变态 | 色婷婷综合久久久久中文字幕 | xvideos成人免费看视频 | 欧美伊人网| 麻豆精品a∨在线观看 | 一性一交一伦一色一按—摩 | 不卡的av在线免费观看 | 欧洲久久久 | 成人在线视频网站 | 香蕉视频黄版 | 人妻 丝袜美腿 中文字幕 | 精品国产乱码久久久久夜 | 亚洲香蕉在线视频 | 国产三级黄色 | 国产熟妇高潮呻吟喷水 | 国产精品-区区久久久狼 | 伊人操| 日产欧产美韩系列久久99 | 免费av在线网址 | 久久久亚洲麻豆日韩精品一区三区 | 国产人成视频在线视频 | 日韩网站视频 | 国产婷婷成人久久av免费高清 | 国产视频亚洲 | 成人91视频 | 日本做爰全过程免费的叫床 | 日本亚洲精品一区二区三 | 欧美乱论视频 | 欧美自拍另类欧美综合图片区 | 婷婷丁香狼人久久大香线蕉 | 亚洲大乳av成人天堂精品 | 国产一级二级 | 少妇又紧又深又湿又爽视频 | 国产区在线看 | 野花香社区在线视频观看播放 | 操操网站 | 无码人妻精品一区二区三18禁 | 国产精品久久久久婷婷二区次 | 少妇吹潮 | 亚洲人a成www在线影院 | 亚洲国产精品美女久久久久 | 精产国品一区二区三区四区 | 99精品无人区乱码在线观看 | 亚洲永久 | 扒开双腿疯狂进出爽爽爽 | 成人在线视频一区二区三区 | 99国产精品久久不卡毛片 | 成人18夜夜网深夜福利网 | 日韩专区在线观看 | 精品国产一区二区三区小蝌蚪 | 天天躁日日躁狠狠躁av | 国产清纯白嫩初黑人高生在线观看 | www.成人网 | 快色网站 | 亚洲区另类春色综合小说 | 国产情侣久久 | 啪啪网站大全 | 欧美色第一页 | 亚洲视频在线观看免费 | 精品国产区 | 国产精品久久二区二区 | 久久久精品久 | 一区二区三区无码高清视频 | 色36cccwww在线播放 | 伊人艹| 亚洲av禁18成人毛片一级在线 | 精品国产乱码久久久久久浪潮 | 婷婷丁香六月 | 国产极品jizzhd欧美 | 日本精品视频一区 | 中文字幕第315页 | 亚洲性久久9久久爽 | 亚洲天堂免费在线观看视频 | 肉丝袜脚交视频一区二区 | av大片免费观看 | 亚洲综合av在线在线播放 | 免费精品一区二区三区视频日产 | 成色网 | 亚洲va一区二区 | 国产精品热久久高潮av袁孑怡 | xxxxxl19成人免费视频 | 成人免费xxxxx在线观看 | 少妇放荡的呻吟干柴烈火动漫 | 综合人妻久久一区二区精品 | 精品视频久久久久 | 国产原创中文av | 爱久久视频| 特黄视频免费看 | 影音先锋男人的天堂 | 欧美性猛交ⅹxxx乱大交3 | 女教师~淫辱のavhd101 | 久久久久久亚洲精品 | 日韩欧美国产网站 | 蜜臀av在线播放一区二区三区 | 免费韩国羞羞网站视频 | 2021精品亚洲中文字幕 | 亚洲日韩成人无码 | 全国男人天堂网 | 欧美亚洲国产一区二区三区 | 欧美在线 | 亚洲 | 色涩久久| 久久精品一区二区三区中文字幕 | 成人一级视频 | 成人午夜又粗又硬又长 | 在线观看二区 | 成熟少妇一区二区三区 | 国产精品入口麻豆原神 | 日韩在线 | 中文 | 毛茸茸熟妇张开腿呻吟 | 中国久久 | 日av一区| 精品一区不卡 | 欧美日韩精品乱国产 | 大肉大捧一进一出好爽app | 亚洲最大毛片 | 亚洲欧美不卡 | 黄色小视频链接 | 插b内射18免费视频 亚洲欧美另类激情综合区 很黄很色60分钟在线观看 | 亚洲精品久久久蜜桃 | 日韩午夜网站 | 久久久久久逼 | 日本人dh亚洲人ⅹxx | 免费看av在线 | 丝袜诱惑一区 | 亲子乱一区二区三区 | 91蝌蚪91porny国语 | 欧美顶级少妇作爱 | 日产mv免费观看 | 男女啪祼交视频 | 欧美视频一区在线观看 | 青娱乐极品在线 | 成年入口无限观看免费完整大片 | 妖精色av无码国产在线看 | 蜜桃狠狠色伊人亚洲综合网站 | 天天摸天天看 | 国产性猛交普通话对白 | 91看片淫黄大片一级在线观看 | 成人在线视频中文字幕 | 伊人中文字幕在线观看 | 国产黄色小说 | 9色porny自拍视频一区二区 | 成年人在线播放视频 | 天堂精品 | 国产aⅴ爽av久久久久久久 | 国产伦子真实事例对白 | 亚洲国产成人精品无码一区二区 | 一本色道久久综合亚洲精品小说 | 精品国产污污免费网站 | 蜜桃视频成人在线观看 | 99久久久久久国产精品 | 天天看黄色 | 国精产品一区一区三区mba下载 | 日韩人妻无码精品-专区 | 成人免费一级伦理片在线播放 | 免费性网站| 久久男人av久久久久久男 | 亚洲精品国产suv | 五月深爱网 | 精品无码一区二区三区在线 | 久久久久久久久久免费视频 | 五月天综合视频 | 国产欧美日韩精品一区二区三区 | 国产免费不卡 | 亚洲视频自拍偷拍 | 超碰天堂 | 91av视频免费观看 | 亚洲第一免费 | 黄色中文字幕在线观看 | 双性调教总裁失禁尿出来 | 日日爱669 | 18禁裸乳无遮挡啪啪无码免费 | 日本理论片在线 | 国产精品免费久久久久影院仙踪林 | xoxo国产三区精品欧美 | 成人午夜精品一区二区三区 | 91调教打屁股xxxx网站 | 丰满少妇aaaaaa爰片毛片 | 日本又白又嫩水又多毛片 | 少妇又紧又色又爽又刺激视频网站 | 欧美日韩国 | 亚洲欧美日韩国产成人精品影院 | 干美女视频 | 欧美一级大黄 | 性色av蜜臀av浪潮av老女人 | 成人乱淫av日日摸夜夜爽节目 | 婷婷久久网 | 亚洲一区二区三区 | 八区精品色欲人妻综合网 | 久草色香蕉 | 亚洲一区二区三区影院 | 四虎在线看片 | 欧美韩一区二区三区 | 欧美一级不卡 | 一区在线不卡 | 国产aⅴ夜夜欢一区二区三区 | 亚洲精品手机在线观看 | 韩国美女vip内部1101福利 | 欧美国产在线一区 | 国产97在线 | 中文 | 国产精品婷婷 | 99综合视频 | 欧美人与动交tv | 日本中文字幕视频在线 | 欧美性猛交xxxx乱大交 | 国产午夜精品一区理论片飘花 | 加勒比成人在线 | 国产精品不卡一区二区三区 | 国偷自产视频一区二区久 | 黄色一级免费片 | av一二三四区 | 午夜dv内射一区区 | 久久超碰97人人做人人爱 | 色欲综合久久中文字幕网 | 午夜精品久久久久久久久久久久 | 全部免费的毛片在线看 | 成人动漫在线免费观看 | 噼里啪啦动漫 | 国产精品女教师 | 国产精品 欧美精品 | 亚洲免费大全 | 午夜精品一区二区三区在线播放 | 八区精品色欲人妻综合网 | 国产一区二区三精品久久久无广告 | 久久久久久免费免费精品软件 | 欧美大波乳人伦免费视频 | 成品片a免费入口麻豆 | 欧美日韩精品一区二区在线视频 | 一级特黄aaa大片 | 国产人交视频xxxcom | 一区二区三区高清在线观看 | 欧美人与zoxxxx另类 | 91精品啪在线观看国产手机 | 奇米影视第四狠狠777 | 少妇羞涩呻吟乳沟偷拍视频 | 永久免费54看片 | 国产裸体永久免费视频网站 | 小荡货奶真大水多好紧视频 | 日本动漫做毛片一区二区 | 亚洲女同ⅹxx女同tv | 国产精品suv一区二区69 | 一级黄av| 欧美精品videossex少妇 | 亚洲欧美日韩精品久久 | 天天爽天天爽天天爽 | 亚洲青春草 | 国产av一区二区三区最新精品 | 福利视频免费观看 | 亚洲视频久久久 | 久久久久久国产精品免费播放 | 秋霞7777鲁丝伊人久久影院 | 欧洲美女tickling免费网站 | 熟妇人妻中文字幕 | 亚洲日本va中文字幕 | 图片区 小说区 区 亚洲五月 | 9人人澡人人爽人人精品 | 欧美日韩在线中文字幕 | 国产精品毛片a∨一区二区三区 | 国产精品久久久久久一区二区三区 | 国产精品新婚之夜泄露女同 | 国产精品视频久久久久久久 | 免费少妇荡乳情欲视频 | 欧美一级免费 | 国产乱对白刺激在线视频 | 草久久久| 精品国产三级 | 国产精华av午夜在线观看 | 在线日韩成人 | 香蕉视频性| 国产精品久久久久精k8 | 麻豆高清免费国产一区 | 国产亚洲福利 | 九色porny蝌蚪视频 | 91久久精品人人做人人爽综合 | 免费视频毛片 | 成年人黄色大片大全 | 日本五十肥熟交尾 | 96av在线| 亚洲综合图片区 | 黄色片免费在线观看 | 亚洲毛片在线观看 | 国产97色在线 | 日韩 | 91福利在线观看视频 | 久久精品专区 | 中文无码人妻有码人妻中文字幕 | 农村村妇真实偷人视频 | 国产精品爱久久久久久久 | 免费一级肉体全黄毛片 | 天天摸天天操天天射 | 乳孔很大能进去的av番号 | 性色av无码免费一区二区三区 | 丁香花免费高清完整在线播放 | 任你躁国产自任一区二区三区 | 人妻巨大乳hd免费看 | 情一色一乱一欲一区二区 | 香蕉视频一级片 | 国产肉体xxx裸体312大胆 | 久久久精品久 | 91成人免费网站 | 国产精品九九九九九 | 亚洲精品伊人 | 在线观看中文字幕dvd播放 | 国产88久久久国产精品免费二区 | 拍真实国产伦偷精品 | 久久98精品久久久久久久性 | 亚洲成人a√ | 麻豆精品自拍 | 久久久精品中文字幕麻豆发布 | 中文字幕亚洲无线码在线一区 | 国产大尺度做爰床 | 亚洲国产欧美在线观看 | 亚洲无线看 | 亚洲精品国偷拍自产在线麻豆 | 英语老师丝袜娇喘好爽视频 | 天堂网在线最新版www | 日本一级黄色毛片 | 国产高清在线一区 | 国产亚洲真人做受在线观看 | 日韩免费网址 | 国产的av| 亚洲欧美v国产一区二区 | 日韩精品在线免费观看 | 亚洲一区二区三区四区五区xx | 国产精品国产三级国产专播精品人 | 欧洲精品一区 | 一区二区在线视频播放 | 一级免费黄色大片 | 国产suv精品一区二区69 | 国产污视频在线 | 精品久久久久一区二区国产 | 欧美疯狂xxxxbbbb喷潮 | 91精品福利 | 中国一级簧色带免费看 | 欧美www在线观看 | 久久99精品久久久久久久清纯 | 九九热视频免费 | 黄色爱爱视频 | 成人在线视频网址 | 亚洲第七页 | h文日记高h污肉1v1 | 草碰在线 | 色噜噜视频 | 五月婷婷婷婷 | 国产色播 | 亚洲精品视频国产 | 亚洲精品一区二三区不卡 | 高清人人天天夜夜曰狠狠狠狠 | 538在线精品视频 | 亚洲国产精品99久久久久久久久 | 日韩人妻精品一区二区三区视频 | 星铁乱淫h侵犯h文 | 国产情侣作爱视频免费观看 | 国产农村妇女毛片精品久久 | 婷婷亚洲五月 | 日韩av片在线播放 | 在线视频午夜 | 久久福利免费视频 | 香港台湾日本三级大全 | 日韩区欧美久久久无人区 | 欧美日韩福利 | 黄色91 | 亚洲第一页夜 | 超碰免费在线播放 | 天天鲁一鲁摸一摸爽一爽 | 性视频黄色| 亚洲一| 男女性杂交内射妇女bbwxz | av天堂久久天堂色综合 | 国产九九久久 | 九九热在线观看视频 | 亚洲国产精品无码av | 久久婷婷网| 亚洲国产精品无码久久一区二区 | 黄色大片aaa| 自拍偷拍第 | 蜜臀久久精品久久久久久酒店 | 黄色福利网 | 亚洲欧美一区二区成人片 | 成人福利免费视频 | snis839痴汉明日花キララ | 亚洲精品一品区二品区三品区 | 国产丰满大乳奶水 | 少妇毛片久久久久久久久竹菊影院 | 性欧美videos另类艳妇3d | 色综合天 | 韩日午夜在线资源一区二区 | 国产精品国产a | 精品av无码国产一区二区 | 国产寡妇亲子伦一区二区 | 青娱乐欧美 | 国产精品极品白嫩在线 | 狼人伊人久久 | 狼人色综合| 亚洲高清视频一区 | 日韩特黄一级欧美毛片特黄 | 国产对白刺激视频 | 亚洲爆爽av| 少妇系列之白嫩人妻 | 欧美日韩在线观看成人 | 97国产免费| 麻豆av在线看 | 国产在线精品一区二区 | ass亚洲日本嫩体私拍ass | 成人激情在线视频 | 亚洲日韩av一区二区三区中文 | 欧美不卡高清 | 日韩免费成人av | 18禁无遮挡羞羞污污污污网站 | 无码人妻毛片丰满熟妇区毛片 | 日本成人一二三区 | 国产精品人人爱一区二区白浆 | 国产福利小视频在线观看 | 日本久久黄色 | 韩国三级中文字幕 | 一级又爽又黄的免费毛片视频 | 91丨porny在线| 亚洲精品国产a久久久久久 51国偷自产一区二区三区 | 狠狠色96视频 | 国产码在线播放 | 野花香社区在线视频观看播放 | 高h肉放荡爽全文寂寞少妇 高h肉各种姿势g短篇np视频 | 波多野结衣在线观看视频 | 亚洲欧美综合网 | 先锋影音播放不卡资源 | 欧美在线观看一区 | 91免费播放 | 国产成人三级视频在线播放 | 亚洲精品岛国片在线观看 | 国产麻豆md传媒视频 | 亚洲素人在线 | jizz一区| 99久久久99久久国产片鸭王 | 久久99精品久久久久久秒播放器 | 国产精品久久久久久久久久白浆 | 亚洲综合成人av | 五月天激情综合网 | 91久久极品少妇xxxxⅹ软件 | 久久久中文久久久无码 | 一级做性色α爱片久久毛片色 | 免费人成年激情视频在线观看 | 国产精品成人国产乱一区 | 久久国产精品嫩草影院的使用方法 | 久久人人爽人人 | 国产精品va在线观看无码 | 欧美一级免费在线观看 | 国产精品久久久久久亚洲毛片 | 久久久精品日韩 | 大胸奶汁乳流奶水出来h | 国产精品一区二区久久乐夜夜嗨 | 婷婷四房综合激情五月 | 久久国产成人午夜av影院武则天 | k8经典少妇在线观看 | 亚州黄色网址 | 午夜视频在线网站 | 一级全黄裸体免费观看视频 | 一区精品在线 | 欧美bbbbb | 西西午夜视频 | 亚洲婷婷网 | 国产高跟黑色丝袜在线 | 日本欧美精91品成人久久久 | 中文字幕日本人妻久久久免费 | 麻豆av一区二区三区久久 | 國产一二三内射在线看片 | 一二三区av| 国产理论一区 | 免费美女视频网站 | 我们2018在线观看免费版高清 | 日韩性色 | 婷婷久久综合网 | 午夜精品射精入后重之免费观看 | av在线免费播放网址 | 蜜桃av噜噜一区二区三区 | 久久爱99 | 中文字幕日产无码 | 91久久国产综合久久 | 亚洲综合网在线 | sese婷婷 | 最新中文字幕在线 | 国产精品一区二区在线看 | av福利在线| 女人被男人爽到呻吟的视频 | 国产91精品ai换脸 | 成人无码精品1区2区3区免费看 | 中出中文字幕 | 国产a一级片 | 国产精品zjzjzj在线观看 | juliaann风流的主妇hd | 小柔好湿好紧太爽了国产网址 | 黑人性受xxxx黑人xyx性爽 | 国产成人精品一二三区 | 国产午夜精品一区理论片飘花 | 印度午夜性春猛xxx交 | 国产三级观看 | 非洲黄色一级片 | 骚虎视频在线观看 | 国产性夜夜春夜夜爽1a片 | 熟女人妇 成熟妇女系列视频 | 亚洲影院丰满少妇中文字幕无码 | 久久久久久网站 | 国产九九在线 | 中文字幕一区二区视频 | 一本久久综合 | 久草剧场 | 91大神福利视频 | 国产免费网址 | 日韩中文字幕精品 | 日韩视频一区二区三区在线播放免费观看 | 国产小视频在线观看免费 | 国产精品久久久久av | 婷婷色在线播放 | 国产性猛交粗暴力xxxx | 少妇三级全黄 | 国产午夜激情 | 亚洲三级在线视频 | 亚洲天堂欧美 | 啪啪免费视频网站 | 国产视频色 | 欧美黄色一级 | 国产精品9x捆绑调教视频 | 成人18视频日本 | 做爰aa女r高潮 | 三级福利片 | 国产综合视频在线观看 | 国产成人精品一区二区秒拍 | 在线天堂中文字幕 | 乡村美女户外勾搭av | 日韩毛片精品 | 国产精品一区二区久久乐夜夜嗨 | 午夜精品免费视频 | 少妇高潮惨叫喷水在线观看 | 国产亚洲人成a在线v网站 | 久久亚洲国产精品成人av秋霞 | 国产精成人品日日拍夜夜免费 | 久久久综合九色合综 | 欧美成人四级hd版 | 国产亚洲精品岁国产微拍精品 | 午夜精品极品粉嫩国产尤物 | 国产精品一区二区三区在线 | 99国产精品久久久 | 搡女人真爽免费午夜网站 | 欧美老肥妇做.爰bbww视频 | 国产精品视频久久久久 | 另类中文字幕 | 爱爱网视频 | 日韩三级欧美 | 色爱情人网站 | 久久不见久久见免费视频4 国产真人做爰毛片视频直播 | 高清成人免费视频 | 亚洲玖玖玖| 国产成人涩涩涩视频在线观看 | 日韩免费福利视频 | 国内自拍视频在线播放 | 天天操操操操 | 成人三级视频 | 我和房东少妇激情 | 国产精品久久久久久亚洲毛片 | 日韩在线激情 | 99国内精品久久久久久久软件 | 日本黄色一级网站 | 亚洲色视频 |