Spring事務(wù)

事務(wù)的嵌套概念

所謂事務(wù)的嵌套就是兩個事務(wù)方法之間相互調(diào)用。spring事務(wù)開啟 ,或者是基于接口的或者是基于類的代理被創(chuàng)建(注意一定要是代理,不能手動new 一個對象,并且此類(有無接口都行)一定要被代理——spring中的bean只要納入了IOC管理都是被代理的)。所以在同一個類中一個方法調(diào)用另一個方法有事務(wù)的方法,事務(wù)是不會起作用的

事務(wù)異常回滾

Spring默認(rèn)情況下會對運(yùn)行期例外(RunTimeException),即uncheck異常,進(jìn)行事務(wù)回滾。
如果遇到checked異常就不回滾。

如何改變默認(rèn)規(guī)則

  1. 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)
  1. 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
  1. 不需要事務(wù)管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
    上面三種方式也可在xml配置

spring事務(wù)傳播屬性

在 spring的 TransactionDefinition接口中一共定義了六種事務(wù)傳播屬性:

  • PROPAGATION_REQUIRED -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。

  • PROPAGATION_SUPPORTS -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。

  • PROPAGATION_MANDATORY -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。

  • PROPAGATION_REQUIRES_NEW -- 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。

  • PROPAGATION_NOT_SUPPORTED -- 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。

  • PROPAGATION_NEVER -- 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。

  • PROPAGATION_NESTED -- 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

前六個策略類似于EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。

它要求事務(wù)管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務(wù)行為(如Spring的DataSourceTransactionManager)

舉例淺析Spring嵌套事務(wù)

ServiceA {  
         
     void methodA() {  
         ServiceB.methodB();  
     }  
    
}  
    
ServiceB {  
         
     void methodB() {  
     }  
         
}  
1、PROPAGATION_REQUIRED

假如當(dāng)前正要執(zhí)行的事務(wù)不在另外一個事務(wù)里,那么就起一個新的事務(wù)
比如說,ServiceB.methodB的事務(wù)級別定義為PROPAGATION_REQUIRED, 那么由于執(zhí)行ServiceA.methodA的時候
(1)、如果ServiceA.methodA已經(jīng)起了事務(wù),這時調(diào)用ServiceB.methodB,ServiceB.methodB看到自己已經(jīng)運(yùn)行在ServiceA.methodA的事務(wù)內(nèi)部,就不再起新的事務(wù)。這時只有外部事務(wù)并且他們是共用的,所以這時ServiceA.methodA或者ServiceB.methodB無論哪個發(fā)生異常methodA和methodB作為一個整體都將一起回滾
(2)、如果ServiceA.methodA沒有事務(wù),ServiceB.methodB就會為自己分配一個事務(wù)。這樣,在ServiceA.methodA中是沒有事務(wù)控制的。只是在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常,ServiceB.methodB將會被回滾,不會引起ServiceA.methodA的回滾

2、PROPAGATION_SUPPORTS

如果當(dāng)前在事務(wù)中,即以事務(wù)的形式運(yùn)行,如果當(dāng)前不再一個事務(wù)中,那么就以非事務(wù)的形式運(yùn)行 。

3、PROPAGATION_MANDATORY

必須在一個事務(wù)中運(yùn)行。也就是說,他只能被一個父事務(wù)調(diào)用。否則,他就要拋出異常

4、PROPAGATION_REQUIRES_NEW

啟動一個新的, 不依賴于環(huán)境的 "內(nèi)部" 事務(wù). 這個事務(wù)將被完全 commited 或 rolled back 而不依賴于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開始執(zhí)行時, 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時, 外部事務(wù)將繼續(xù)執(zhí)行.
比如我們設(shè)計ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_REQUIRES_NEW,那么當(dāng)執(zhí)行到ServiceB.methodB的時候,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的事務(wù),等待ServiceB.methodB的事務(wù)完成以后,他才繼續(xù)執(zhí)行。他與PROPAGATION_REQUIRED 的事務(wù)區(qū)別在于事務(wù)的回滾程度了。因?yàn)镾erviceB.methodB是新起一個事務(wù),那么就是存在兩個不同的事務(wù)。
(1)、如果ServiceB.methodB已經(jīng)提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。
(2)、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲并處理,ServiceA.methodA事務(wù)仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務(wù)將回滾。

場景: 不管業(yè)務(wù)邏輯的service是否有異常,Log Service都應(yīng)該能夠記錄成功,所以Log Service的傳播屬性可以配為此屬性。最下面將會貼出配置代碼。

5、PROPAGATION_NOT_SUPPORTED

當(dāng)前不支持事務(wù)。比如ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務(wù)級別是PROPAGATION_NOT_SUPPORTED ,那么當(dāng)執(zhí)行到ServiceB.methodB時,ServiceA.methodA的事務(wù)掛起,而他以非事務(wù)的狀態(tài)運(yùn)行完,再繼續(xù)ServiceA.methodA的事務(wù)。

6、PROPAGATION_NEVER

不能在事務(wù)中運(yùn)行。假設(shè)ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務(wù)級別是PROPAGATION_NEVER ,那么ServiceB.methodB就要拋出異常了。

7、PROPAGATION_NESTED (特殊)

開始一個 "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個真正的子事務(wù). 潛套事務(wù)開始執(zhí)行時, 它將取得一個 savepoint. 如果這個嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 潛套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會被提交.

比如我們設(shè)計ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_NESTED,那么當(dāng)執(zhí)行到ServiceB.methodB的時候,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的子事務(wù)并設(shè)置savepoint,等待ServiceB.methodB的事務(wù)完成以后,他才繼續(xù)執(zhí)行。。因?yàn)镾erviceB.methodB是外部事務(wù)的子事務(wù),那么

1、如果ServiceB.methodB已經(jīng)提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB也將回滾。

2、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲并處理,ServiceA.methodA事務(wù)仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務(wù)將回滾。

理解Nested的關(guān)鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區(qū)別是:

PROPAGATION_REQUIRES_NEW 完全是一個新的事務(wù),它與外部事務(wù)相互獨(dú)立; 而 PROPAGATION_NESTED 則是外部事務(wù)的子事務(wù), 如果外部事務(wù) commit, 嵌套事務(wù)也會被 commit, 這個規(guī)則同樣適用于 roll back.

 在 spring 中使用 PROPAGATION_NESTED的前提:

 1. 我們要設(shè)置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認(rèn)為 false!!! 

 2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+ 

 3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0 

確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了.


隔離級別(Isolation Level)

1、Serializable:最嚴(yán)格的級別,事務(wù)串行執(zhí)行,資源消耗最大;

2、REPEATABLE READ:保證了一個事務(wù)不會修改已經(jīng)由另一個事務(wù)讀取但未提交(回滾)的數(shù)據(jù)。避免了“臟讀取”和“不可重復(fù)讀取”的情況,但是帶來了更多的性能損失。

3、READ COMMITTED:大多數(shù)主流數(shù)據(jù)庫的默認(rèn)事務(wù)等級,保證了一個事務(wù)不會讀到另一個并行事務(wù)已修改但未提交的數(shù)據(jù),避免了“臟讀取”。該級別適用于大多數(shù)系統(tǒng)。

4、Read Uncommitted:保證了讀取過程中不會讀取到非法數(shù)據(jù)。隔離級別在于處理多事務(wù)的并發(fā)問題。

我們知道并行可以提高數(shù)據(jù)庫的吞吐量和效率,但是并不是所有的并發(fā)事務(wù)都可以并發(fā)運(yùn)行,這需要查看數(shù)據(jù)庫教材的可串行化條件判斷了。

我們首先說并發(fā)中可能發(fā)生的3中不討人喜歡的事情

1: Dirty reads--讀臟數(shù)據(jù)。也就是說,比如事務(wù)A的未提交(還依然緩存)的數(shù)據(jù)被事務(wù)B讀走,如果事務(wù)A失敗回滾,會導(dǎo)致事務(wù)B所讀取的的數(shù)據(jù)是錯誤的。

2: non-repeatable reads--數(shù)據(jù)不可重復(fù)讀。比如事務(wù)A中兩處讀取數(shù)據(jù)-total-的值。在第一讀的時候,total是100,然后事務(wù)B就把total的數(shù)據(jù)改成 200,事務(wù)A再讀一次,結(jié)果就發(fā)現(xiàn),total竟然就變成200了,造成事務(wù)A數(shù)據(jù)混亂。

3: phantom reads--幻象讀數(shù)據(jù),這個和non-repeatable reads相似,也是同一個事務(wù)中多次讀不一致的問題。但是non-repeatable reads的不一致是因?yàn)樗〉臄?shù)據(jù)集被改變了(比如total的數(shù)據(jù)),但是phantom reads所要讀的數(shù)據(jù)的不一致卻不是他所要讀的數(shù)據(jù)集改變,而是他的條件數(shù)據(jù)集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候,由于事務(wù)b把一個帳號的名字由"dd"改成"ppgogo1",結(jié)果取出來了7個數(shù)據(jù)。

數(shù)據(jù)庫提供了四種事務(wù)隔離級別, 不同的隔離級別采用不同的鎖類開來實(shí)現(xiàn).

在四種隔離級別中, Serializable的級別最高, Read Uncommited級別最低.

大多數(shù)數(shù)據(jù)庫的默認(rèn)隔離級別為: Read Commited,如Sql Server , Oracle.

少數(shù)數(shù)據(jù)庫默認(rèn)的隔離級別為Repeatable Read, 如MySQL InnoDB存儲引擎

3、Transactional注意點(diǎn)

Transactional特性

  • service實(shí)現(xiàn)類標(biāo)簽
    在service實(shí)現(xiàn)類 類頭(一般不建議在接口上)上添加@Transactional,可以將整個類納入spring事務(wù)管理,在每個業(yè)務(wù)方法執(zhí)行時都會開啟一個事務(wù),不過這些事務(wù)采用相同的管理方式。
  • 可見度
    @Transactional 注解只能應(yīng)用到 public 可見度的方法上。 如果應(yīng)用在protected、private或者 package可見度的方法上,也不會報錯,不過事務(wù)設(shè)置不會起作用。
  • 回滾
    默認(rèn)情況下,spring會對unchecked異常進(jìn)行事務(wù)回滾;如果是checked異常則不回滾。
    通俗一點(diǎn):你寫代碼出現(xiàn)的空指針等異常,會被回滾,文件讀寫,網(wǎng)絡(luò)出問題,spring就沒法回滾了。

java里面將派生于Error或者RuntimeException(比如空指針,1/0)的異常稱為unchecked異常,其他繼承自java.lang.Exception得異常統(tǒng)稱為Checked Exception,如IOException、TimeoutException

  • 只讀事務(wù)
    只讀標(biāo)志只在事務(wù)啟動時應(yīng)用,否則即使配置也會被忽略。
    啟動事務(wù)會增加線程開銷,數(shù)據(jù)庫因共享讀取而鎖定(具體跟數(shù)據(jù)庫類型和事務(wù)隔離級別有關(guān))。通常情況下,僅是讀取數(shù)據(jù)時,不必設(shè)置只讀事務(wù)而增加額外的系統(tǒng)開銷。

解決Transactional不回滾

1、檢查方法是否是public的。
2、異常類型是否是unchecked異常

如果想check異常也想回滾怎么辦,注解上面寫明異常類型即可

 @Transactional(rollbackFor=Exception.class)

3、spring是否開啟對注解的解析

  • @EnableTransactionManagement
  • 還有例如SpringDataJPA 事務(wù)容器聲明:
    transactionManager(JpaTransactionManager) -> entityManagerFactory(EntityManagerFactory) -> dataSource
    4、spring是否掃描到包
    5、數(shù)據(jù)庫引擎是否支持事務(wù)

事務(wù)綁定事件@TransactionalEventListener

1、 使用DEMO

@Service
public class TransactionEventTestService {
    @Resource
    private TestMapper mapper;
    @Resource
    private ApplicationEventPublisher publisher;
    @Transactional
    public void addTestModel() {
        TestModel model = new TestModel();
        model.setName("haogrgr");
        mapper.insert(model);
        //如果model沒有繼承ApplicationEvent, 則內(nèi)部會包裝為PayloadApplicationEvent
        //對于@TransactionalEventListener, 會在事務(wù)提交后才執(zhí)行Listener處理邏輯.
        
        //發(fā)布事件, 事務(wù)提交后, 記錄日志, 或發(fā)送消息等操作
        publisher.publishEvent(model);
    }
    //當(dāng)事務(wù)提交后, 才會真正的執(zhí)行@TransactionalEventListener配置的Listener, 如果Listener拋異常, 方法返回失敗, 但事務(wù)不會回滾.
}
@Component
public class TransactionEventListener {
    @TransactionalEventListener
    public void handle(PayloadApplicationEvent<TestModel> event) {
        System.out.println(event.getPayload().getName());
        //這里可以記錄日志, 發(fā)送消息等操作.
        //這里拋出異常, 會導(dǎo)致addTestModel方法異常, 但不會回滾事務(wù).
        //注意, ApplicationEventPublisher不能使用線程池, 否則不會執(zhí)行到這里
        //因?yàn)? 包裝類是通過ThreadLocal來判斷當(dāng)前是否有活動的事務(wù)信息.
        //TransactionalEventListener.fallbackExecution就是為了決定當(dāng)當(dāng)前線程沒有事務(wù)上下文時,
        //是否還調(diào)用 handle 方法, 默認(rèn)不調(diào)用.
    }
}

2、TransactionalEventListener詳解

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {

    // 指定當(dāng)前標(biāo)注方法處理事務(wù)的類型
    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;

    // 用于指定當(dāng)前方法如果沒有事務(wù),是否執(zhí)行相應(yīng)的事務(wù)事件監(jiān)聽器
    boolean fallbackExecution() default false;

    // 與classes屬性一樣,指定了當(dāng)前事件傳入的參數(shù)類型,指定了這個參數(shù)之后就可以在監(jiān)聽方法上
    // 直接什么一個這個參數(shù)了
    @AliasFor(annotation = EventListener.class, attribute = "classes")
    Class<?>[] value() default {};

    // 作用于value屬性一樣,用于指定當(dāng)前監(jiān)聽方法的參數(shù)類型
    @AliasFor(annotation = EventListener.class, attribute = "classes")
    Class<?>[] classes() default {};

    // 這個屬性使用Spring Expression Language對目標(biāo)類和方法進(jìn)行匹配,對于不匹配的方法將會過濾掉
    String condition() default "";

}

關(guān)于這里的classes屬性需要說明一下,如果指定了classes屬性,那么當(dāng)前監(jiān)聽方法的參數(shù)類型就可以直接使用所發(fā)布的事件的參數(shù)類型,如果沒有指定,那么這里監(jiān)聽的參數(shù)類型可以使用兩種:ApplicationEvent和PayloadApplicationEvent。對于ApplicationEvent類型的參數(shù),可以通過其getSource()方法獲取發(fā)布的事件參數(shù),只不過其返回值是一個Object類型的,如果想獲取具體的類型還需要進(jìn)行強(qiáng)轉(zhuǎn);對于PayloadApplicationEvent類型,其可以指定一個泛型參數(shù),該泛型參數(shù)必須與發(fā)布的事件的參數(shù)類型一致,這樣就可以通過其getPayload()方法獲取事務(wù)事件發(fā)布的數(shù)據(jù)了。關(guān)于上述屬性中的TransactionPhase,其可以取如下幾個類型的值:

public enum TransactionPhase {
    // 指定目標(biāo)方法在事務(wù)commit之前執(zhí)行
    BEFORE_COMMIT,

    // 指定目標(biāo)方法在事務(wù)commit之后執(zhí)行
    AFTER_COMMIT,

    // 指定目標(biāo)方法在事務(wù)rollback之后執(zhí)行
    AFTER_ROLLBACK,
    
    // 指定目標(biāo)方法在事務(wù)完成時執(zhí)行,這里的完成是指無論事務(wù)是成功提交還是事務(wù)回滾了
    AFTER_COMPLETION
}

如何通過程序判斷是否存在事務(wù)?

boolean flag = TransactionSynchronizationManager.isActualTransactionActive();

在同一類中一個調(diào)用本類中另一個有事務(wù)的方法,事務(wù)是無效的 解決辦法

1、 將這部分業(yè)務(wù)代碼寫到另一個service中,然后注入調(diào)用
2、要調(diào)用代理類才會被切進(jìn)去

  • ((TestService) SpringContextUtils.getBean("testService")).testTransactional2();

applicationContext 如何獲取:
https://blog.csdn.net/u010784959/article/details/78892020

@Autowired
private ApplicationContext applicationContext;
applicationContext.getBean(TestService.class);

3、(推薦)通過ThreadLocal暴露代理對象

  • 第一步:開啟cglib代理。spring.aop.proxy-target-class:true
  • 第二步:@EnableAspectJAutoProxy(exposeProxy = true)
  • 第三步:方法一中調(diào)用方法兒如:((TestService)AopContext.currentProxy()).testTransactional2();

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,205評論 2 375

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