<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊聊spring事務失效的12種場景,太坑了

          共 12545字,需瀏覽 26分鐘

           ·

          2021-12-23 03:54

          前言

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

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

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

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

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

          一 事務不生效

          1.訪問權(quán)限問題

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

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

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

          我們可以看到add方法的訪問權(quán)限被定義成了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;
          ??}

          也就是說,如果我們自定義的事務方法(即目標方法),它的訪問權(quán)限不是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動態(tài)代理或者cglib,幫我們生成了代理類,在代理類中實現(xiàn)的事務功能。

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

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

          3.方法內(nèi)部調(diào)用

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

          @Service
          public?class?UserService?{

          ????@Autowired
          ????private?UserMapper?userMapper;

          ??
          ????public?void?add(UserModel?userModel)?{
          ????????userMapper.insertUser(userModel);
          ????????updateStatus(userModel);
          ????}

          ????@Transactional
          ????public?void?updateStatus(UserModel?userModel)?{
          ????????doSameThing();
          ????}
          }

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

          由此可見,在同一個類中的方法直接內(nèi)部調(diào)用,會導致事務失效。

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

          3.1 新加一個Service方法

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

          @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();
          ????}
          ?}

          可能有些人可能會有這樣的疑問:這種做法會不會出現(xiàn)循環(huán)依賴問題?

          答案:不會。

          其實spring ioc內(nèi)部的三級緩存保證了它,不會出現(xiàn)循環(huán)依賴問題。但有些坑,如果你想進一步了解循環(huán)依賴問題,可以看看我之前文章《spring:我是如何解決循環(huán)依賴的?》。

          3.3 通過AopContent類

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

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

          @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管理

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

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

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

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

          //@Service
          public?class?UserService?{

          ????@Transactional
          ????public?void?add(UserModel?userModel)?{
          ?????????saveData(userModel);
          ?????????updateData(userModel);
          ????}????
          }

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

          5.多線程調(diào)用

          在實際項目開發(fā)中,多線程的使用場景還是挺多的。如果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表數(shù)據(jù)");
          ????}
          }

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

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

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

          private?static?final?ThreadLocal>?resources?=

          ??new?NamedThreadLocal<>("Transactional?resources");

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

          6.表不支持事務

          周所周知,在mysql5之前,默認的數(shù)據(jù)庫引擎是myisam

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

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

          在創(chuàng)建表的時候,只需要把ENGINE參數(shù)設置成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好用,但有個很致命的問題是:不支持事務

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

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

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

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

          7.未開啟事務

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

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

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

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

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

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

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

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

          你所要做的事情很簡單,只需要配置spring.datasource相關(guān)參數(shù)即可。

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

          具體配置如下信息:

          ???
          ?
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"?id="transactionManager">?
          ????"dataSource"?ref="dataSource">?
          ?
          "advice"?transaction-manager="transactionManager">?
          ?????
          ????????"*"?propagation="REQUIRED"/>
          ????
          ?
          ?
          ?
          ?
          ????"execution(*?com.susan.*.*(..))"?id="pointcut"/>?
          ????"advice"?pointcut-ref="pointcut"/>?
          ?

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

          二 事務不回滾

          1.錯誤的傳播特性

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

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

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

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

          @Service
          public?class?UserService?{

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

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

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

          2.自己吞了異常

          事務不會回滾,最常見的問題是:開發(fā)者在代碼中手動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事務當然不會回滾,因為開發(fā)者自己捕獲了異常,又沒有手動拋出,換句話說就是把異常吞掉了。

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

          3.手動拋了別的異常

          即使開發(fā)者沒有手動捕獲異常,但如果拋的異常不正確,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);
          ????????}
          ????}
          }

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

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

          4.自定義了回滾異常

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

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

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

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

          即使rollbackFor有默認值,但阿里巴巴開發(fā)者規(guī)范中,還是要求開發(fā)者重新指定該參數(shù)。

          這是為什么呢?

          因為如果使用默認值,一旦程序拋出了Exception,事務不會回滾,這會出現(xiàn)很大的bug。所以,建議一般情況下,將該參數(shù)設置成: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表數(shù)據(jù)");
          ????}
          }

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

          why?

          因為doOtherThing方法出現(xiàn)了異常,沒有手動捕獲,會繼續(xù)往上拋,到外層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);
          ????????}
          ????}
          }

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

          三 其他

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

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

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

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

          2.編程式事務

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

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


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

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

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

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

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

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人777777 | 日韩AV免费在线观看 | 蜜桃av成人网站 蜜桃无码久久久久 | 草久热| 国产1234区 |