国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 一篇文章帶你了解spring事務失效的多種場景

一篇文章帶你了解spring事務失效的多種場景

2021-12-20 13:56蘇三說技術 Java教程

在日常編碼過程中常常涉及到事務,在前兩天看到一篇文章提到了Spring事務,那么在此總結下在Spring環境下事務失效的幾種原因.

前言

對于從事java開發工作的同學來說,spring的事務肯定再熟悉不過了。

在某些業務場景下,如果一個請求中,需要同時寫入多張表的數據。為了保證操作的原子性(要么同時成功,要么同時失敗),避免數據不一致的情況,我們一般都會用到spring事務。

確實,spring事務用起來賊爽,就用一個簡單的注解:@Transactional,就能輕松搞定事務。我猜大部分小伙伴也是這樣用的,而且一直用一直爽。

但如果你使用不當,它也會坑你于無形。

今天我們就一起聊聊,事務失效的一些場景,說不定你已經中招了。不信,讓我們一起看看。

一篇文章帶你了解spring事務失效的多種場景

最近無意間獲得一份BAT大廠大佬寫的刷題筆記,一下子打通了我的任督二脈,越來越覺得算法沒有想象中那么難了。

BAT大佬寫的刷題筆記,讓我offer拿到手軟

 

一 事務不生效

1.訪問權限問題

眾所周知,java的訪問權限主要有四種:private、default、protected、public,它們的權限從左到右,依次變大。

但如果我們在開發過程中,把有某些事務方法,定義了錯誤的訪問權限,就會導致事務功能出問題,例如:

@Service
public class UserService {
  @Transactional
  private void add(UserModel userModel) {
       saveData(userModel);
       updateData(userModel);
  }
}

我們可以看到add方法的訪問權限被定義成了private,這樣會導致事務失效,spring要求被代理方法必須是public的。

說白了,在AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則TransactionAttribute返回null,即不支持事務。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
  // Don't allow no-public methods as required.
  if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    return null;
  }
  // The method may be on an interface, but we need attributes from the target class.
  // If the target class is null, the method will be unchanged.
  Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
  // First try is the method in the target class.
  TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
  if (txAttr != null) {
    return txAttr;
  }
  // Second try is the transaction attribute on the target class.
  txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
  if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
    return txAttr;
  }
  if (specificMethod != method) {
    // Fallback is to look at the original method.
    txAttr = findTransactionAttribute(method);
    if (txAttr != null) {
      return txAttr;
    }
    // Last fallback is the class of the original method.
    txAttr = findTransactionAttribute(method.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
      return txAttr;
    }
  }
  return null;
}

也就是說,如果我們自定義的事務方法(即目標方法),它的訪問權限不是public,而是private、default或protected的話,spring則不會提供事務功能。

2. 方法用final修飾

有時候,某個方法不想被子類重新,這時可以將該方法定義成final的。普通方法這樣定義是沒問題的,但如果將事務方法定義成final,例如:

@Service
public class UserService {
  @Transactional
  public final void add(UserModel userModel){
      saveData(userModel);
      updateData(userModel);
  }
}

我們可以看到add方法被定義成了final的,這樣會導致事務失效。

為什么?

如果你看過spring事務的源碼,可能會知道spring事務底層使用了aop,也就是通過jdk動態代理或者cglib,幫我們生成了代理類,在代理類中實現的事務功能。

但如果某個方法用final修飾了,那么在它的代理類中,就無法重寫該方法,而添加事務功能。

注意:如果某個方法是static的,同樣無法通過動態代理,變成事務方法。

3.方法內部調用

有時候我們需要在某個Service類的某個方法中,調用另外一個事務方法,比如:

@Service
public class UserService {
  @Autowired
  private UserMapper userMapper;
  @Transactional
  public void add(UserModel userModel) {
      userMapper.insertUser(userModel);
      updateStatus(userModel);
  }
  @Transactional
  public void updateStatus(UserModel userModel) {
      doSameThing();
  }
}

我們看到在事務方法add中,直接調用事務方法updateStatus。從前面介紹的內容可以知道,updateStatus方法擁有事務的能力是因為spring aop生成代理了對象,但是這種方法直接調用了this對象的方法,所以updateStatus方法不會生成事務。

由此可見,在同一個類中的方法直接內部調用,會導致事務失效。

那么問題來了,如果有些場景,確實想在同一個類的某個方法中,調用它自己的另外一個方法,該怎么辦呢?

3.1 新加一個Service方法

這個方法非常簡單,只需要新加一個Service方法,把@Transactional注解加到新Service方法上,把需要事務執行的代碼移到新方法中。具體代碼如下:

@Servcie
public class ServiceA {
 @Autowired
 prvate ServiceB serviceB;
 public void save(User user) {
       queryData1();
       queryData2();
       serviceB.doSave(user);
 }
}
@Servcie
public class ServiceB {
  @Transactional(rollbackFor=Exception.class)
  public void doSave(User user) {
     addData1();
     updateData2();
  }
}

3.2 在該Service類中注入自己

如果不想再新加一個Service類,在該Service類中注入自己也是一種選擇。具體代碼如下:

@Servcie
public class ServiceA {
 @Autowired
 prvate ServiceA serviceA;
 public void save(User user) {
       queryData1();
       queryData2();
       serviceA.doSave(user);
 }
 @Transactional(rollbackFor=Exception.class)
 public void doSave(User user) {
     addData1();
     updateData2();
  }
}

可能有些人可能會有這樣的疑問:這種做法會不會出現循環依賴問題?

答案:不會。

3.3 通過AopContent類

在該Service類中使用AopContext.currentProxy()獲取代理對象

上面的方法2確實可以解決問題,但是代碼看起來并不直觀,還可以通過在該Service類中使用AOPProxy獲取代理對象,實現相同的功能。具體代碼如下:

@Servcie
public class ServiceA {
 public void save(User user) {
       queryData1();
       queryData2();
       ((ServiceA)AopContext.currentProxy()).doSave(user);
 }
 @Transactional(rollbackFor=Exception.class)
 public void doSave(User user) {
     addData1();
     updateData2();
  }
}

4.未被spring管理

在我們平時開發過程中,有個細節很容易被忽略。即使用spring事務的前提是:對象要被spring管理,需要創建bean實例。

通常情況下,我們通過@Controller、@Service、@Component、@Repository等注解,可以自動實現bean實例化和依賴注入的功能。

當然創建bean實例的方法還有很多,有興趣的小伙伴可以看看我之前寫的另一篇文章《@Autowired的這些騷操作,你都知道嗎?》

如果有一天,你匆匆忙忙的開發了一個Service類,但忘了加@Service注解,比如:

//@Service
public class UserService {
  @Transactional
  public void add(UserModel userModel) {
       saveData(userModel);
       updateData(userModel);
  }    
}

從上面的例子,我們可以看到UserService類沒有加@Service注解,那么該類不會交給spring管理,所以它的add方法也不會生成事務。

5.多線程調用

在實際項目開發中,多線程的使用場景還是挺多的。如果spring事務用在多線程場景中,會有問題嗎?

@Slf4j
@Service
public class UserService {
  @Autowired
  private UserMapper userMapper;
  @Autowired
  private RoleService roleService;
  @Transactional
  public void add(UserModel userModel) throws Exception {
      userMapper.insertUser(userModel);
      new Thread(() -> {
          roleService.doOtherThing();
      }).start();
  }
}
@Service
public class RoleService {
  @Transactional
  public void doOtherThing() {
      System.out.println("保存role表數據");
  }
}

從上面的例子中,我們可以看到事務方法add中,調用了事務方法doOtherThing,但是事務方法doOtherThing是在另外一個線程中調用的。

這樣會導致兩個方法不在同一個線程中,獲取到的數據庫連接不一樣,從而是兩個不同的事務。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。

如果看過spring事務源碼的朋友,可能會知道spring的事務是通過數據庫連接來實現的。當前線程中保存了一個map,key是數據源,value是數據庫連接。

private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");

我們說的同一個事務,其實是指同一個數據庫連接,只有擁有同一個數據庫連接才能同時提交和回滾。如果在不同的線程,拿到的數據庫連接肯定是不一樣的,所以是不同的事務。

6.表不支持事務

周所周知,在mysql5之前,默認的數據庫引擎是myisam

它的好處就不用多說了:索引文件和數據文件是分開存儲的,對于查多寫少的單表操作,性能比innodb更好。

有些老項目中,可能還在用它。

在創建表的時候,只需要把ENGINE參數設置成MyISAM即可:

CREATE TABLE `category` (
`id` bigint NOT NULL AUTO_INCREMENT,
`one_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`two_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`three_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`four_category` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

myisam好用,但有個很致命的問題是:不支持事務。

如果只是單表操作還好,不會出現太大的問題。但如果需要跨多張表操作,由于其不支持事務,數據極有可能會出現不完整的情況。

此外,myisam還不支持行鎖和外鍵。

所以在實際業務場景中,myisam使用的并不多。在mysql5以后,myisam已經逐漸退出了歷史的舞臺,取而代之的是innodb。

有時候我們在開發的過程中,發現某張表的事務一直都沒有生效,那不一定是spring事務的鍋,最好確認一下你使用的那張表,是否支持事務。

7.未開啟事務

有時候,事務沒有生效的根本原因是沒有開啟事務。

你看到這句話可能會覺得好笑。

開啟事務不是一個項目中,最最最基本的功能嗎?

為什么還會沒有開啟事務?

沒錯,如果項目已經搭建好了,事務功能肯定是有的。

但如果你是在搭建項目demo的時候,只有一張表,而這張表的事務沒有生效。那么會是什么原因造成的呢?

當然原因有很多,但沒有開啟事務,這個原因極其容易被忽略。

如果你使用的是springboot項目,那么你很幸運。因為springboot通過DataSourceTransactionManagerAutoConfiguration類,已經默默的幫你開啟了事務。

你所要做的事情很簡單,只需要配置spring.datasource相關參數即可。

但如果你使用的還是傳統的spring項目,則需要在applicationContext.xml文件中,手動配置事務相關參數。如果忘了配置,事務肯定是不會生效的。

具體配置如下信息:

<!-- 配置事務管理器 --> 
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> 
  <property name="dataSource" ref="dataSource"></property> 
</bean> 
<tx:advice id="advice" transaction-manager="transactionManager"> 
  <tx:attributes> 
      <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes> 
</tx:advice> 
<!-- 用切點把事務切進去 --> 
<aop:config> 
  <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> 
  <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
</aop:config> 

默默的說一句,如果在pointcut標簽中的切入點匹配規則,配錯了的話,有些類的事務也不會生效。

 

二 事務不回滾

1.錯誤的傳播特性

其實,我們在使用@Transactional注解時,是可以指定propagation參數的。

該參數的作用是指定事務的傳播特性,spring目前支持7種傳播特性:

  • REQUIRED 如果當前上下文中存在事務,那么加入該事務,如果不存在事務,創建一個事務,這是默認的傳播屬性值。
  • SUPPORTS 如果當前上下文存在事務,則支持事務加入事務,如果不存在事務,則使用非事務的方式執行。
  • MANDATORY 如果當前上下文中存在事務,否則拋出異常。
  • REQUIRES_NEW 每次都會新建一個事務,并且同時將上下文中的事務掛起,執行當前新建事務完成以后,上下文事務恢復再執行。
  • NOT_SUPPORTED 如果當前上下文中存在事務,則掛起當前事務,然后新的方法在沒有事務的環境中執行。
  • NEVER 如果當前上下文中存在事務,則拋出異常,否則在無事務環境上執行代碼。
  • NESTED 如果當前上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務。

如果我們在手動設置propagation參數的時候,把傳播特性設置錯了,比如:

@Service
public class UserService {
  @Transactional(propagation = Propagation.NEVER)
  public void add(UserModel userModel) {
      saveData(userModel);
      updateData(userModel);
  }
}

我們可以看到add方法的事務傳播特性定義成了Propagation.NEVER,這種類型的傳播特性不支持事務,如果有事務則會拋異常。

目前只有這三種傳播特性才會創建新事務:REQUIRED,REQUIRES_NEW,NESTED。

2.自己吞了異常

事務不會回滾,最常見的問題是:開發者在代碼中手動try…catch了異常。比如:

@Slf4j
@Service
public class UserService {
  @Transactional
  public void add(UserModel userModel) {
      try {
          saveData(userModel);
          updateData(userModel);
      } catch (Exception e) {
          log.error(e.getMessage(), e);
      }
  }
}

這種情況下spring事務當然不會回滾,因為開發者自己捕獲了異常,又沒有手動拋出,換句話說就是把異常吞掉了。

如果想要spring事務能夠正常回滾,必須拋出它能夠處理的異常。如果沒有拋異常,則spring認為程序是正常的。

3.手動拋了別的異常

即使開發者沒有手動捕獲異常,但如果拋的異常不正確,spring事務也不會回滾。

@Slf4j
@Service
public class UserService {
  @Transactional
  public void add(UserModel userModel) throws Exception {
      try {
           saveData(userModel);
           updateData(userModel);
      } catch (Exception e) {
          log.error(e.getMessage(), e);
          throw new Exception(e);
      }
  }
}

上面的這種情況,開發人員自己捕獲了異常,又手動拋出了異常:Exception,事務同樣不會回滾。

因為spring事務,默認情況下只會回滾RuntimeException(運行時異常)和Error(錯誤),對于普通的Exception(非運行時異常),它不會回滾。

4.自定義了回滾異常

在使用@Transactional注解聲明事務時,有時我們想自定義回滾的異常,spring也是支持的。可以通過設置rollbackFor參數,來完成這個功能。

但如果這個參數的值設置錯了,就會引出一些莫名其妙的問題,例如:

@Slf4j
@Service
public class UserService {
  @Transactional(rollbackFor = BusinessException.class)
  public void add(UserModel userModel) throws Exception {
     saveData(userModel);
     updateData(userModel);
  }
}

如果在執行上面這段代碼,保存和更新數據時,程序報錯了,拋了SqlException、DuplicateKeyException等異常。而BusinessException是我們自定義的異常,報錯的異常不屬于BusinessException,所以事務也不會回滾。

即使rollbackFor有默認值,但阿里巴巴開發者規范中,還是要求開發者重新指定該參數。

這是為什么呢?

因為如果使用默認值,一旦程序拋出了Exception,事務不會回滾,這會出現很大的bug。所以,建議一般情況下,將該參數設置成:Exception或Throwable。

5.嵌套事務回滾多了

public class UserService {
  @Autowired
  private UserMapper userMapper;
  @Autowired
  private RoleService roleService;
  @Transactional
  public void add(UserModel userModel) throws Exception {
      userMapper.insertUser(userModel);
      roleService.doOtherThing();
  }
}
@Service
public class RoleService {
  @Transactional(propagation = Propagation.NESTED)
  public void doOtherThing() {
      System.out.println("保存role表數據");
  }
}

這種情況使用了嵌套的內部事務,原本是希望調用roleService.doOtherThing方法時,如果出現了異常,只回滾doOtherThing方法里的內容,不回滾 userMapper.insertUser里的內容,即回滾保存點。。但事實是,insertUser也回滾了。

why?

因為doOtherThing方法出現了異常,沒有手動捕獲,會繼續往上拋,到外層add方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個事務,不只回滾單個保存點。

怎么樣才能只回滾保存點呢?

@Slf4j
@Service
public class UserService {
  @Autowired
  private UserMapper userMapper;
  @Autowired
  private RoleService roleService;
  @Transactional
  public void add(UserModel userModel) throws Exception {
      userMapper.insertUser(userModel);
      try {
          roleService.doOtherThing();
      } catch (Exception e) {
          log.error(e.getMessage(), e);
      }
  }
}

可以將內部嵌套事務放在try/catch中,并且不繼續往上拋異常。這樣就能保證,如果內部嵌套事務中出現異常,只回滾內部事務,而不影響外部事務。

 

三 其他

1 大事務問題

在使用spring事務時,有個讓人非常頭疼的問題,就是大事務問題。

通常情況下,我們會在方法上@Transactional注解,填加事務功能,比如:

@Service
public class UserService {
  @Autowired 
  private RoleService roleService;
  @Transactional
  public void add(UserModel userModel) throws Exception {
     query1();
     query2();
     query3();
     roleService.save(userModel);
     update(userModel);
  }
}

@Service
public class RoleService {
  @Autowired 
  private RoleService roleService;
  @Transactional
  public void save(UserModel userModel) throws Exception {
     query4();
     query5();
     query6();
     saveData(userModel);
  }
}

@Transactional注解,如果被加到方法上,有個缺點就是整個方法都包含在事務當中了。

上面的這個例子中,在UserService類中,其實只有這兩行才需要事務:

roleService.save(userModel);
update(userModel);

在RoleService類中,只有這一行需要事務:

saveData(userModel);

現在的這種寫法,會導致所有的query方法也被包含在同一個事務當中。

如果query方法非常多,調用層級很深,而且有部分查詢方法比較耗時的話,會造成整個事務非常耗時,而從造成大事務問題。

關于大事務問題的危害,可以閱讀一下我的另一篇文章《讓人頭痛的大事務問題到底要如何解決?》,上面有詳細的講解。

一篇文章帶你了解spring事務失效的多種場景

2.編程式事務

上面聊的這些內容都是基于@Transactional注解的,主要說的是它的事務問題,我們把這種事務叫做:聲明式事務。

其實,spring還提供了另外一種創建事務的方式,即通過手動編寫代碼實現的事務,我們把這種事務叫做:編程式事務。例如:

 @Autowired
 private TransactionTemplate transactionTemplate;
 ...
 public void save(final User user) {
       queryData1();
       queryData2();
       transactionTemplate.execute((status) => {
          addData1();
          updateData2();
          return Boolean.TRUE;
       })
 }

在spring中為了支持編程式事務,專門提供了一個類:TransactionTemplate,在它的execute方法中,就實現了事務的功能。

相較于@Transactional注解聲明式事務,我更建議大家使用,基于TransactionTemplate的編程式事務。主要原因如下:

避免由于spring aop問題,導致事務失效的問題。能夠更小粒度的控制事務的范圍,更直觀。

建議在項目中少使用@Transactional注解開啟事務。但并不是說一定不能用它,如果項目中有些業務邏輯比較簡單,而且不經常變動,使用@Transactional注解開啟事務開啟事務也無妨,因為它更簡單,開發效率更高,但是千萬要小心事務失效的問題。

 

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://blog.csdn.net/lisu061714112/article/details/120098743

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 深夜视频在线 | 国产精品九九久久99视频 | 中文字幕av网站 | 久久免费精品国产 | 久久艹精品 | 欧美日韩在线播放 | 亚洲精品视频网 | 中文字幕1区2区3区 日韩免费高清视频 | 亚洲精品一区二区三区在线 | 久久99精品久久久久久久青青日本 | 久久精品一区二区三区不卡牛牛 | 国产精品久久久久久久久久新婚 | 天天拍天天干天天操 | 欧美日韩一区二区三区在线观看 | www.avtt天堂网 | 日韩国产欧美一区 | 亚洲精品无码专区在线播放 | 日韩在线精品视频 | 精品一区二区久久 | 欧美一二三 | 亚洲综合视频在线 | 亚洲国产精品视频 | 日韩欧美国产精品综合嫩v 在线视频 中文字幕 | 中文字幕一区二区三区四区不卡 | 欧美操穴| 午夜精品久久久久久久久 | 免费av在线网站 | 最近免费中文字幕大全免费版视频 | 美女久久久 | 国产精品久久久久久久久福交 | 久久伊人国产 | 亚洲天堂一区在线 | 黄色免费电影网站 | 中文字幕国产日韩 | 伊人网网站 | 成人一区二区三区 | 偷拍自拍第一页 | 色在线影院 | 国产99精品| 搞黄视频在线观看 | 国产黄色一级毛片 |