Spring的7種事務(wù)傳播機(jī)制

1.REQUIRED

REQUIRED(Spring默認(rèn)的事務(wù)傳播類型 required):如果當(dāng)前沒有事務(wù),則自己新建一個事務(wù)偷遗,如果當(dāng)前存在事務(wù)則加入這個事務(wù)驶忌。

當(dāng)A調(diào)用B的時候:如果A中沒有事務(wù),B中有事務(wù)粥航,那么B會新建一個事務(wù);如果A中也有事務(wù)生百、B中也有事務(wù)递雀,那么B會加入到A中去,變成一個事務(wù)蚀浆,這時缀程,要么都成功,要么都失敗蜡坊。

class ServiceA {
    @Autowired 
    ServiceB serviceB杠输;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional
    void b() {
        ......
    }
}
  1. 線程執(zhí)行到serviceA.a() 方法時,其實(shí)是執(zhí)行的 代理serviceA對象的a方法秕衙。
  2. 執(zhí)行代理serviceA對象的a方法
    2.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng))
    2.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    2.3: 檢查當(dāng)前線程有沒有綁定 conn 數(shù)據(jù)庫連接 資源蠢甲? 發(fā)現(xiàn)當(dāng)前線程未綁定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,檢查key:datasource 有沒有數(shù)據(jù))
    2.4: 因?yàn)槲唇壎╟onn資源据忘,所以線程下一步就是 到 datasource.getConnection() 獲取一個conn資源
    2.5: 因?yàn)樾芦@取的conn資源的autocommit是true鹦牛,所以這一步 修改 autocommit 為false,表示手動提交事務(wù)勇吊,這一步也表示 開啟事務(wù)(修改conn其它 屬性..)
    2.6: 綁定conn資源到 TransactionSync...Manager#resources曼追,key:datasource
  3. 執(zhí)行事務(wù)增強(qiáng)器后面的增強(qiáng)器..
  4. 最后一個advice調(diào)用 target的目標(biāo)方法 a() 方法
    4.1: 假設(shè)target a方法 需要訪問數(shù)據(jù)庫 執(zhí)行SQL 的話,程序需要獲取一個 conn 資源汉规,到哪拿礼殊? DataSourceUtils.getConnection(datasource) 這一步最終會拿到 事務(wù)增強(qiáng)器 前置增強(qiáng)邏輯 存放在 TransactionSync..Manager#resources 內(nèi)的
    conn 資源
    4.2: 執(zhí)行方法a邏輯...可能會執(zhí)行一些 SQL 語句...
  5. 線程執(zhí)行到這樣一行代碼:serviceB.b()
  6. serviceB 它是一個代理對象驹吮,因?yàn)樗彩褂昧?@Transactional 注解了,Spring 會為它創(chuàng)建代理的晶伦。
  7. 執(zhí)行代理serviceB對象的b方法
    7.1: 執(zhí)行b方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器(環(huán)繞增強(qiáng))
    7.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    7.3: 檢查當(dāng)前線程有沒有綁定 conn 數(shù)據(jù)庫連接 資源碟狞?發(fā)現(xiàn)當(dāng)前線程已經(jīng)綁定了 conn 數(shù)據(jù)庫連接資源了
    7.4: 檢查事務(wù)注解屬性,發(fā)現(xiàn)自己打的propagation == REQUIRED婚陪,所以繼續(xù)共享 conn 數(shù)據(jù)庫鏈接資源
  8. 執(zhí)行事務(wù)增強(qiáng)器后面的增強(qiáng)器..
  9. 最后一個device調(diào)用 target (serviceB)的目標(biāo)方法 b() 方法
    9.1: 假設(shè)target b方法 需要訪問數(shù)據(jù)庫 執(zhí)行SQL 的話族沃,程序需要獲取一個 conn 資源,到哪拿泌参? DataSourceUtils.getConnection(datasource) 這一步最終會拿到 代理serviceA對象存放在 TransactionSync..Manager#resources 內(nèi)的
    conn 資源
    9.2: 執(zhí)行方法b邏輯...可能會執(zhí)行一些 SQL 語句...
  10. 線程繼續(xù)執(zhí)行 事務(wù)增強(qiáng)器 環(huán)繞增強(qiáng)的后置邏輯 (代理serviceB.b() 方法的 后置增強(qiáng))
    10.1: 檢查發(fā)現(xiàn)脆淹,serviceB.b() 事務(wù)并不是 當(dāng)前 b方法開啟的,所以 基本不做什么事情..
  11. 線程繼續(xù)回到 目標(biāo) serviceA.a() 方法內(nèi)沽一,繼續(xù)執(zhí)行
    11.1: 執(zhí)行方法a邏輯...可能會執(zhí)行一些 SQL 語句...
  12. 線程繼續(xù)回到 代理 serviceA.a() 方法內(nèi)盖溺,繼續(xù)執(zhí)行
    12.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng)-后置增強(qiáng)邏輯)
    12.2: 提交事務(wù)/回滾事務(wù)
    12.3: 恢復(fù)連接狀態(tài) (將conn的autocommit 設(shè)置回 true...等等)
    12.4: 清理工作(將綁定的conn資源從TransactionSync...Manager#resources移除)
    12.5: conn 連接關(guān)閉 (歸還連接到datasource)

2.SUPPORTS

SUPPORTS:當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù)锯玛,如果當(dāng)前沒有事務(wù)咐柜,就以非事務(wù)方法執(zhí)行兼蜈。
如果A中有事務(wù)攘残,則B方法的事務(wù)加入A事務(wù)中,成為一個事務(wù)(一起成功为狸,一起失敿吖),如果A中沒有事務(wù)辐棒,那么B就以非事務(wù)方式運(yùn)行(執(zhí)行完直接提交)病曾。

class ServiceA {
    @Autowired 
    ServiceB serviceB;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = SUPPORTS)   
    void b() {
        ......
    }
}

邏輯和上面 完全一致漾根。

class ServiceA {
    @Transactional(Propagation = SUPPORTS)
    void a() {
        ....

        ....
    }
}

線程在未綁定事務(wù)的情況下泰涂,去調(diào)用serviceA.a() 方法會發(fā)生什么呢?

  1. 線程執(zhí)行到serviceA.a() 方法時辐怕,其實(shí)是執(zhí)行的 代理serviceA對象的a方法逼蒙。
  2. 執(zhí)行代理serviceA對象的a方法
    2.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng))
    2.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    2.3: 檢查當(dāng)前線程有沒有綁定 conn 數(shù)據(jù)庫連接 資源? 發(fā)現(xiàn)當(dāng)前線程未綁定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>寄疏,檢查key:datasource 有沒有數(shù)據(jù))
    2.4: 啥也不用做..
  3. 執(zhí)行事務(wù)增強(qiáng)器后面的增強(qiáng)器..
  4. 最后一個advice調(diào)用 target的目標(biāo)方法 a() 方法
    4.1: 假設(shè)target a方法 需要訪問數(shù)據(jù)庫 執(zhí)行SQL 的話是牢,程序需要獲取一個 conn 資源陕截,到哪拿? DataSourceUtils.getConnection(datasource) 农曲,因?yàn)槭聞?wù)增強(qiáng)器前置增強(qiáng)邏輯 并沒有 向TransactionSync..Manager#resources 內(nèi)綁定conn資源
    4.2: 因?yàn)?上一步未拿到 conn資源,所以 DataSourceUtils 通過 datasource.getConnection() 獲取了一個全新的 conn 資源(注意:conn.autocommit == true,執(zhí)行的每一條sql 都是一個 獨(dú)立事務(wù)!H春骸)
    4.3: 執(zhí)行方法a邏輯...可能會執(zhí)行一些 SQL 語句...
  5. 線程繼續(xù)執(zhí)行到代理serviceA對象的a方法 (事務(wù)增強(qiáng)器-后置增強(qiáng)邏輯)
    5.1: 檢查發(fā)現(xiàn) TrasactionSync..Manager#resources 并未綁定任何 conn 資源,所以 這一步啥也不做了...

3.MANDATORY

MANDATORY(mandatory:強(qiáng)制性的):當(dāng)前存在事務(wù)合砂,則加入當(dāng)前事務(wù),如果當(dāng)前事務(wù)不存在源织,則拋出異常。
如果A中有事務(wù)谈息,則B方法的事務(wù)加入A事務(wù)中,成為一個事務(wù)(一起成功侠仇,一起失敗)逻炊;如果A中沒有事務(wù),B中有事務(wù)余素,那么B就直接拋異常了,意思是B必須要支持回滾的事務(wù)中運(yùn)行桨吊。

class ServiceA {
    @Autowired 
    ServiceB serviceB威根;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = MANDATORY)
    void b() {
        ......
    }
}   

如果是這樣的話,情況和 PROPAGATION_REQUIRED 案例分析 完全一致视乐。

class ServiceA {
    @Transactional(Propagation = MANDATORY)
    void a() {
        ....

        ....
    }
}

線程在未綁定事務(wù)的情況下洛搀,去調(diào)用serviceA.a() 方法會發(fā)生什么呢?

  1. 線程執(zhí)行到serviceA.a() 方法時佑淀,其實(shí)是執(zhí)行的 代理serviceA對象的a方法留美。
  2. 執(zhí)行代理serviceA對象的a方法
    2.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng))
    2.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    2.3: 檢查當(dāng)前線程有沒有綁定 conn 數(shù)據(jù)庫連接 資源? 發(fā)現(xiàn)當(dāng)前線程未綁定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>渣聚,檢查key:datasource 有沒有數(shù)據(jù))
    2.4: 直接拋出異常...

4.REQUIRES_NEW

REQUIRES_NEW:創(chuàng)建一個新事務(wù)独榴,如果存在當(dāng)前事務(wù),則掛起該事務(wù)奕枝。
B會新建一個事務(wù)棺榔,A和B事務(wù)互不干擾,他們出現(xiàn)問題回滾的時候隘道,也都只回滾自己的事務(wù)症歇;A方法調(diào)用B方法郎笆;不管A方法有沒有事務(wù),B方法都新建一個自己的事務(wù)忘晤。

class ServiceA {
    @Autowired 
    ServiceB serviceB宛蚓;
    
    @Transactional
    void a() {
        ....
        serviceB.b();
        ....
    }
}

class ServiceB {
    @Transactional(propagation = REQUIRES_NEW)
    void b() {
        ......
    }
}
  1. 線程執(zhí)行到serviceA.a() 方法時,其實(shí)是執(zhí)行的 代理serviceA對象的a方法设塔。

  2. 執(zhí)行代理serviceA對象的a方法
    2.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng))
    2.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    2.3: 檢查當(dāng)前線程有沒有綁定 conn 數(shù)據(jù)庫連接 資源凄吏? 發(fā)現(xiàn)當(dāng)前線程未綁定(TransactionSync...Manager#resources 是 ThreadLocal<Map<obj,obj>>,檢查key:datasource 有沒有數(shù)據(jù))
    2.4: 因?yàn)槲唇壎╟onn資源闰蛔,所以線程下一步就是 到 datasource.getConnection() 獲取一個conn資源
    2.5: 因?yàn)樾芦@取的conn資源的autocommit是true痕钢,所以這一步 修改 autocommit 為false,表示手動提交事務(wù)序六,這一步也表示 開啟事務(wù)(修改conn其它 屬性..)
    2.6: 綁定conn資源到 TransactionSync...Manager#resources任连,key:datasource

  3. 執(zhí)行事務(wù)增強(qiáng)器后面的增強(qiáng)器..

  4. 最后一個advice調(diào)用 target的目標(biāo)方法 a() 方法
    4.1: 假設(shè)target a方法 需要訪問數(shù)據(jù)庫 執(zhí)行SQL 的話,程序需要獲取一個 conn 資源例诀,到哪拿随抠? DataSourceUtils.getConnection(datasource) 這一步最終會拿到 事務(wù)增強(qiáng)器 前置增強(qiáng)邏輯 存放在 TransactionSync..Manager#resources 內(nèi)的
    conn 資源
    4.2: 執(zhí)行方法a邏輯...可能會執(zhí)行一些 SQL 語句...

  5. 線程執(zhí)行到這樣一行代碼:serviceB.b()

  6. serviceB 它是一個代理對象,因?yàn)樗彩褂昧?@Transactional 注解了繁涂,Spring 會為它創(chuàng)建代理的拱她。

  7. 執(zhí)行代理serviceB對象的b方法
    7.1: 執(zhí)行b方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器(環(huán)繞增強(qiáng))
    7.2: 事務(wù)增強(qiáng)器會做什么事? 提取事務(wù)標(biāo)簽屬性
    7.3: 檢查發(fā)現(xiàn)當(dāng)前線程已經(jīng)綁定了conn資源(并且手動開啟了事務(wù)..),又發(fā)現(xiàn) 當(dāng)前方法的 傳播行為:REQUIRES_NEW 爆土,需要開啟一個新的事務(wù)..
    7.4: 將已經(jīng)綁定的conn資源 保存到 suspand 變量內(nèi)
    7.5: 因?yàn)?REQUIRES_NEW 不會和上層共享同一個事務(wù)椭懊,所以這一步 又到 datasource.getConnection() 獲取了一個全新的 conn 數(shù)據(jù)庫連接資源
    7.6: 因?yàn)樾芦@取的conn資源的autocommit是true,所以這一步 修改 autocommit 為false,表示手動提交事務(wù)背犯,這一步也表示 開啟事務(wù)(修改conn其它 屬性..)
    7.7: 綁定conn資源到 TransactionSync...Manager#resources,key:datasource

  8. 執(zhí)行事務(wù)增強(qiáng)器后面的增強(qiáng)器..

  9. 最后一個advice調(diào)用 target (serviceB)的目標(biāo)方法 b() 方法
    9.1: 假設(shè)target b方法 需要訪問數(shù)據(jù)庫 執(zhí)行SQL 的話倔矾,程序需要獲取一個 conn 資源柱锹,到哪拿? DataSourceUtils.getConnection(datasource) 這一步最終會拿到 事務(wù)增強(qiáng)器 前置增強(qiáng)邏輯 存放在 TransactionSync..Manager#resources 內(nèi)的
    conn 資源
    9.2: 執(zhí)行方法a邏輯...可能會執(zhí)行一些 SQL 語句...

  10. 線程繼續(xù)執(zhí)行 事務(wù)增強(qiáng)器 環(huán)繞增強(qiáng)的后置邏輯 (代理serviceB.b() 方法的 后置增強(qiáng))
    10.1: 檢查發(fā)現(xiàn)壤巷,serviceB.b() 事務(wù)是 b方法開啟的瞧毙,所以 需要做一些事情了
    10.1: 執(zhí)行b方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng)-后置增強(qiáng)邏輯)
    10.2: 提交事務(wù)/回滾事務(wù)
    10.3: 恢復(fù)連接狀態(tài) (將conn的autocommit 設(shè)置回 true...等等)
    10.4: 清理工作(將綁定的conn資源從TransactionSync...Manager#resources移除)
    10.5: conn 連接關(guān)閉 (歸還連接到datasource)
    10.6: 檢查suspand 發(fā)現(xiàn) 該變量有值,需要執(zhí)行 恢復(fù)現(xiàn)場的工作 resume()

  11. 恢復(fù)現(xiàn)場
    11.1: 將suspand 掛起的 conn 資源再次 綁定到 TransactionSync...Manager#resources 內(nèi)矩动,方便 serviceA 繼續(xù)使用它的conn資源 (它自己的事務(wù))

  12. 線程繼續(xù)回到 serviceA.a() 方法內(nèi)
    12.1: 繼續(xù)執(zhí)行一些sql ...注意 這里它使用的 conn 是 serviceA 申請的 conn

  13. 線程繼續(xù)執(zhí)行 事務(wù)增強(qiáng)器 環(huán)繞增強(qiáng)的后置邏輯 (代理serviceA.a() 方法的 后置增強(qiáng))
    10.1: 檢查發(fā)現(xiàn)悲没,serviceA.a() 事務(wù)是 a方法開啟的,所以 需要做一些事情了
    10.1: 執(zhí)行a方法的增強(qiáng)邏輯-> 事務(wù)增強(qiáng)器 (環(huán)繞增強(qiáng)-后置增強(qiáng)邏輯)
    10.2: 提交事務(wù)/回滾事務(wù)
    10.3: 恢復(fù)連接狀態(tài) (將conn的autocommit 設(shè)置回 true...等等)
    10.4: 清理工作(將綁定的conn資源從TransactionSync...Manager#resources移除)
    10.5: conn 連接關(guān)閉 (歸還連接到datasource)

5.NOT_SUPPORTED

NOT_SUPPORTED: 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù)柑潦,則掛起當(dāng)前事務(wù)
被調(diào)用者B會以非事務(wù)方式運(yùn)行(直接提交)峻凫,如果當(dāng)前有事務(wù),也就是A中有事務(wù)譬胎,A會被掛起(不執(zhí)行堰乔,等待B執(zhí)行完脐恩,返回);A和B出現(xiàn)異常需要回滾苟翻,互不影響

6.NEVER

NEVER: 如果當(dāng)前沒有事務(wù)存在骗污,就以非事務(wù)方式執(zhí)行需忿;如果有,就拋出異常涕烧。就是B從不以事務(wù)方式運(yùn)行
A中不能有事務(wù)汗洒,如果沒有,B就以非事務(wù)方式執(zhí)行痹扇,如果A存在事務(wù),那么直接拋異常

7.NESTED

NESTED: 嵌套事務(wù):如果當(dāng)前事務(wù)存在浓恶,則在嵌套事務(wù)中執(zhí)行结笨,否則REQUIRED的操作一樣(開啟一個事務(wù))
如果A中沒有事務(wù),那么B創(chuàng)建一個事務(wù)執(zhí)行伐憾,如果A中也有事務(wù)树肃,那么B會會把事務(wù)嵌套在里面瀑罗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斩祭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耳奕,更是在濱河造成了極大的恐慌诬像,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谓晌,死亡現(xiàn)場離奇詭異,居然都是意外死亡溺欧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門芥牌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壁拉,“玉大人,你說我怎么就攤上這事弃理。” “怎么了钥勋?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵算灸,是天一觀的道長驻啤。 經(jīng)常有香客問我,道長骑冗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任森逮,我火速辦了婚禮褒侧,結(jié)果婚禮上闷供,老公的妹妹穿的比我還像新娘歪脏。我一直安慰自己粮呢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布豪硅。 她就那樣靜靜地躺著懒浮,像睡著了一般识藤。 火紅的嫁衣襯著肌膚如雪次伶。 梳的紋絲不亂的頭發(fā)上冠王,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天版确,我揣著相機(jī)與錄音乎折,去河邊找鬼。 笑死吓蘑,一個胖子當(dāng)著我的面吹牛坟冲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播琳猫,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼脐嫂,長吁一口氣:“原來是場噩夢啊……” “哼紊遵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匀奏,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娃善,失蹤者是張志新(化名)和其女友劉穎会放,沒想到半個月后钉凌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體御雕,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年捣鲸,在試婚紗的時候發(fā)現(xiàn)自己被綠了闽坡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡外厂,死狀恐怖汁蝶,靈堂內(nèi)的尸體忽然破棺而出论悴,到底是詐尸還是另有隱情,我是刑警寧澤幔亥,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布帕棉,位于F島的核電站捐寥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瞒窒。R本人自食惡果不足惜乡洼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拔稳。 院中可真熱鬧锹雏,春花似錦、人聲如沸轻绞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽既棺。三九已至懒叛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晨仑,已是汗流浹背拆檬。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留答捕,地道東北人屑那。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓持际,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜘欲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容