JPA實體中數(shù)據(jù)庫生成ID的最終指南1

根據(jù)JPA規(guī)范写妥,Entity是滿足以下要求的Java類:

  1. 帶注釋@Entity注記
  2. 沒有args構(gòu)造函數(shù)啄糙。
  3. 不是最終的
  4. 具有帶注釋的ID字段(或多個字段)@Id

如您所見扎拣,需要ID啥容。那是為什么骚亿?

我們?yōu)槭裁匆贘PA實體中使用ID呢曲秉?

JDBC和關(guān)系數(shù)據(jù)庫不需要表的主鍵或唯一鍵采蚀。在使用JDBC時,我們使用自己的語言--原生SQL查詢與數(shù)據(jù)庫進(jìn)行通信岸浑。若要獲取數(shù)據(jù)集搏存,開發(fā)人員將運行SELECT語句,該語句返回相應(yīng)的元組矢洲。要保存或更新數(shù)據(jù)璧眠,我們需要編寫另一個INSERTUPDATE聲明。在應(yīng)用程序到數(shù)據(jù)庫級的通信中读虏,應(yīng)用程序中的對象與存儲在數(shù)據(jù)庫中的記錄之間沒有直接聯(lián)系责静。通常,這種映射是作為業(yè)務(wù)邏輯的一部分手動管理的盖桥。

JPA采取了不同的方法灾螃。它引入了實體-Java對象,這些對象嚴(yán)格地與數(shù)據(jù)庫中的記錄綁定在一起揩徊。因此腰鬼,JPA規(guī)范要求開發(fā)人員定義字段或一組字段,以在實體實例和特定DB記錄之間建立一對一的關(guān)聯(lián)塑荒。這樣熄赡,開發(fā)人員就可以從數(shù)據(jù)庫中獲取JPA實體,使用它們并在以后保存它們齿税,而無需調(diào)用任何JPA實體彼硫。INSERTUPDATE陳述。這是允許開發(fā)人員主要關(guān)注業(yè)務(wù)邏輯的關(guān)鍵概念之一,而大多數(shù)樣板操作是由JPA實現(xiàn)本身處理的拧篮,ID是此過程的重要部分词渤。

注: ID不必映射到定義為表主鍵的列。我們需要將ID映射到唯一標(biāo)識每一行的列串绩。但是對于本文缺虐,我們將繼續(xù)交替使用術(shù)語ID和主鍵。

ID類型:我們有什么

我們需要在實體中定義ID赏参。我們有什么選擇志笼?

首先,我們可以定義一個“簡單”或“復(fù)合”結(jié)構(gòu)的ID把篓∪依#“簡單”ID由實體中的單個字段表示,復(fù)合字段由一個單獨的類表示韧掩,該類包含一組標(biāo)識實體的字段紊浩。

通常,我們對JPA實體使用簡單的ID疗锐》凰可以自動生成簡單ID(代理ID),這是處理ID值的最常用方法滑臊。生成可以發(fā)生在數(shù)據(jù)庫端(服務(wù)器端生成)或應(yīng)用程序中(客戶端生成)口芍。這兩種方法各有優(yōu)缺點。

在本文中雇卷,我們將重點討論服務(wù)器端生成的ID鬓椭。為了簡單起見,我們將使用Hibernate ORM作為所有示例的默認(rèn)JPA實現(xiàn)关划,除非我們明確提到另一個ORM小染。

生成的ID-為什么我們要關(guān)心?

ID生成事件通常只發(fā)生一次--當(dāng)我們將新實體保存到數(shù)據(jù)庫時贮折。因此裤翩,假設(shè)我們有一個不經(jīng)常創(chuàng)建多個實體的應(yīng)用程序(經(jīng)驗法則--假設(shè)每秒不超過100個實體),并且不與其他應(yīng)用程序共享數(shù)據(jù)庫调榄。在這種情況下踊赠,理論上,我們可以使用任何ID生成策略每庆。管理國家清單的應(yīng)用程序就是一個很好的例子--我們并不經(jīng)常創(chuàng)建新的國家臼疫。但是電能計量呢?如果我們有100米扣孟,每小時發(fā)送數(shù)據(jù),我們必須每小時保存100個測量數(shù)據(jù)荣赶》锛郏基本上鸽斟,我們每36秒就可以節(jié)省一次測量±担看起來不太像富蓄。幾千米呢?數(shù)以萬計慢逾?如果我們決定每10分鐘做一次測量呢立倍?一個企業(yè)要花費多少錢來停止信息系統(tǒng)來改變ID生成策略?

在實踐中侣滩,應(yīng)用程序和業(yè)務(wù)一樣趨向于增長和變化口注,這就是為什么必須選擇適當(dāng)?shù)腎D生成策略,以避免將來發(fā)生痛苦的遷移君珠。我們將在這篇文章中提到很多性能寝志,甚至我們的應(yīng)用程序也不是新的Facebook或Twitter,它們還沒有每秒節(jié)省數(shù)百萬實體策添,我們應(yīng)該提前考慮最合適的ID生成策略材部,以避免將來出現(xiàn)問題。

默認(rèn)情況下世代是如何工作的

最簡單的方法是在JPA實體中定義生成的ID唯竹,用@Id@GeneratedValue注釋乐导。我們甚至不需要為@GeneratedValue。默認(rèn)情況下浸颓,您將得到一個正確生成的ID字段物臂。

@Table(name = "pet")
@Entity
public class Pet {
    @Id
    @GeneratedValue
    @Column(name = "id", nullable = false)
    private Long id;
}

有兩種類型的默認(rèn)值:從一開始就不應(yīng)更改的默認(rèn)值和應(yīng)該更改的默認(rèn)值。默認(rèn)值不會破壞應(yīng)用程序猾愿,但是在生成ID的情況下鹦聪,它們工作得好嗎?讓我們看看@GeneratedValue默認(rèn)參數(shù)值:

public @interface GeneratedValue {

    GenerationType strategy() default AUTO;

    String generator() default "";
}

正如我們所看到的蒂秘,我們將生成策略參數(shù)設(shè)置為AUTO泽本。這意味著JPA提供者決定如何為ID生成一個唯一的值锰悼。讓我們從我們可以使用的策略列表開始梧税。

JPA標(biāo)準(zhǔn)除了描述AUTO:

  • IDENTITY-特定于數(shù)據(jù)庫的內(nèi)置用途identity用于生成ID的列類型。
  • SEQUENCE-使用序列生成唯一的ID值也榄。
  • TABLE-使用模擬序列的單獨表撇贺。當(dāng)應(yīng)用程序需要ID時赌莺,JPA提供程序鎖定表行,更新存儲的ID值松嘶,并將其返回給應(yīng)用程序艘狭。與前兩種策略相比,這種策略提供了最差的性能,如果可能的話巢音,應(yīng)該避免遵倦。您可以閱讀更多有關(guān)此策略的內(nèi)容。在文件中.

開發(fā)人員手冊官撼,如果我們使用與UUID不同的ID類型(如Long梧躺、Integer等)并將策略設(shè)置為AUTO,Hibernate將執(zhí)行以下操作(自5.0版起):

  • 嘗試使用SEQUENCEID生成策略
  • 如果序列不受支持(即我們使用MySQL)傲绣,它將使用TABLE(或IDENTITY掠哥,在Hibernate 5.0之前)生成ID的策略

為什么Hibernate試圖使用SEQUENCE作為默認(rèn)策略?這里的關(guān)鍵指標(biāo)是性能秃诵。這個TABLE就業(yè)績而言续搀,戰(zhàn)略是最差的。在本文中顷链,作者使用不同的策略進(jìn)行了一些測試目代。通過更改ID生成策略,他能夠?qū)?0K實體的時間從185秒減少到4.3秒嗤练。IDENTITYSEQUENCE并啟用Hibernate的一些優(yōu)化榛了。所以,這兩種退步策略(IDENTITYTABLE)不會破壞應(yīng)用程序煞抬,但性能不會很好霜大。

這里的問題是,即使是SEQUENCE表現(xiàn)不佳革答;表演將接近IDENTITY战坤。之所以發(fā)生這種情況,是因為所有實體都使用單個數(shù)據(jù)庫序列残拐,而且序列參數(shù)不允許Hibernate應(yīng)用ID池優(yōu)化途茫。我們將查看默認(rèn)的SEQUENCE下一節(jié)將詳細(xì)介紹行為。

結(jié)語::為ID生成策略保留默認(rèn)值可能會對我們的應(yīng)用程序性能造成負(fù)面影響溪食。對于生產(chǎn)應(yīng)用程序囊卜,我們需要將默認(rèn)值更改為更合適的東西。

順序:如何正確定義错沃?

這個SEQUENCE策略使用一個單獨的DB對象--序列--在將數(shù)據(jù)插入數(shù)據(jù)庫之前獲取和分配一個唯一的ID值栅组。這提供了批處理。INSERT操作支持枢析,因為JPA提供程序不需要在每個提供程序之后獲取生成的ID玉掸。INSERT與標(biāo)識列、觸發(fā)器生成的ID等類似醒叁。

缺省值:它們足夠好嗎司浪?

對象的默認(rèn)定義泊业。SEQUENCE策略,我們需要寫下面的代碼断傲。實際上脱吱,這就是默認(rèn)情況下的結(jié)果。AUTO如果我們的數(shù)據(jù)庫支持序列认罩,策略。

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id", nullable = false)
private Long id;

為該定義生成的序列(如果我們在Hibernate設(shè)置中啟用自動數(shù)據(jù)庫創(chuàng)建)的SQL如下所示:

create sequence hibernate_sequence start 1 increment 1;

JPA提供程序?qū)⒅粚λ杏脩羰褂么藬?shù)據(jù)庫序列续捂。INSERT語句垦垂,如果我們?yōu)樗?code>SEQUENCE我們應(yīng)用的策略。這可能會引起一些問題牙瓢。

首先劫拗,我們可能會耗盡序列。在大多數(shù)數(shù)據(jù)庫中矾克,最大序列值為2^63-1页慷,因此很難達(dá)到這一極限。但是胁附,產(chǎn)生大量新數(shù)據(jù)的應(yīng)用程序仍然有可能酒繁,例如物聯(lián)網(wǎng)系統(tǒng)或橫幅網(wǎng)絡(luò),每天產(chǎn)生數(shù)十億個事件控妻。

2^63-1是一個很大的數(shù)字州袒。如果我們每秒保存10.000個實體,我們需要大約2900萬年來耗盡這個序列弓候。這意味著郎哭,在大多數(shù)情況下,我們可能不會擔(dān)心序列結(jié)束菇存,但我們?nèi)匀恍枰庾R到序列是有限的夸研。

第二,演出會受到影響依鸥。默認(rèn)的序列增量設(shè)置為1亥至,這將禁用Hibernate對序列的ID池生成優(yōu)化。JPA提供程序?qū)拿總€INSERT序列中的語句毕籽。例如抬闯,如果我們試圖保存兩個實體并查看Hibernate SQL日志,我們將看到如下內(nèi)容:

select nextval ('hibernate_sequence')
insert into pet (name, id) values (?, ?)
select nextval ('hibernate_sequence')
insert into pet (name, id) values (?, ?)

因此关筒,我們通過執(zhí)行兩個ID值來選擇兩個ID值溶握。SELECT語句,將這些ID分配給實體蒸播,然后保存它們睡榆。這給了我們一個額外的開銷SELECT每一個INSERT萍肆。這是對應(yīng)用程序性能的負(fù)面影響。

結(jié)語::SEQUENCE對于非數(shù)據(jù)密集型應(yīng)用程序胀屿,ID生成策略是一種很好的方法塘揣。如果我們計劃做更大的事情,為了避免性能和順序耗盡的問題宿崭,我們需要改變默認(rèn)策略的設(shè)置亲铡。

序列:我們可以改變什么?

讓我們首先指定一個實體ID生成的專用序列葡兑。

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pet_seq")
@Column(name = "id", nullable = false)
private Long id;

對于這個定義奖蔓,我們將看到執(zhí)行以下SQL:

create sequence pet_seq start 1 increment 50

Hibernate對非默認(rèn)序列使用ID生成池優(yōu)化。這樣做的目的是為一個會話分配一系列的值讹堤,并將這些值用作ID吆鹤。默認(rèn)情況下,分配的ID數(shù)等于50個洲守。

優(yōu)化的工作方式如下:

  • 步驟1:Hibernate執(zhí)行一個SELECT從序列中獲取ID疑务。
  • 步驟2如果所選值等于序列初始值,Hibernate將從序列中選擇下一個ID作為高值梗醇,將初始值設(shè)置為范圍低值知允。否則,它將進(jìn)入第4步婴削。
  • 步驟3:Hibernates插入數(shù)據(jù)分配IDlow到``high`‘范圍廊镜。
  • 步驟4:一旦Hibernate需要下一個批處理,它就會從序列中選擇下一個ID值(大于初始值)唉俗。Hibernate根據(jù)allocationSize參數(shù)嗤朴。Low值=ID – allocationSize, high = ID。然后Hibernate進(jìn)入步驟3虫溜。

我們只多表演兩場SELECTS用于前50個保存的實體雹姊,用于默認(rèn)設(shè)置。對于以下50個實體衡楞,我們只執(zhí)行一個額外的選擇吱雏。例如,如果啟用Hibernate SQL日志瘾境,我們可以看到如下內(nèi)容:

select nextval ('pet_seq'); //selects 1 – got initial value, need to select next value
select nextval ('pet_seq'); //selects 51 as range high value
insert into pet (name, id) values (?, ?);// id=1
insert into pet (name, id) values (?, ?);//id=2
//insert other 48 entities
select nextval ('pet_seq'); //selects 101 as range next high value, calculates 101 – 50 = 51 as the low
insert into pet (name, id) values (?, ?);//id=51
//etc. 

有一個缺點:如果關(guān)閉數(shù)據(jù)庫會話(即重新啟動應(yīng)用程序或重新打開實體管理器)歧杏,將丟失未使用的ID。這樣一個短命的應(yīng)用程序的一個很好的例子可能是一個無服務(wù)器的lambda函數(shù)迷守。如果每個會話只保存一個實體犬绒,然后退出應(yīng)用程序,我們將永遠(yuǎn)失去49個ID兑凿。這種行為可能導(dǎo)致序列耗盡凯力,因此對于處理少量實例的短會話茵瘾,我們需要設(shè)置更小的ID分配大小,以避免浪費大量ID咐鹤。

要調(diào)整ID生成參數(shù)拗秘,即減少分配大小,可以使用@SequenceGenerator注釋序列生成器允許我們使用現(xiàn)有序列或創(chuàng)建具有所需參數(shù)的新序列祈惶。例如雕旨,在下面的代碼中,我們提供完整的序列定義奸腺,并將ID分配大小指定為20。

@Id
@SequenceGenerator(name = "pet_seq", 
        sequenceName = "pet_sequence", 
        initialValue = 1, allocationSize = 20)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pet_seq")
@Column(name = "id", nullable = false)
private Long id;

如果序列不存在血久,Hibernate將為該定義生成以下SQL:

create sequence pet_sequence start 1 increment 20

定義序列生成器時,需要記住以下內(nèi)容:如果指定現(xiàn)有序列名稱帮非,并且啟用Hibernate模式驗證氧吐,則allocationSize參數(shù)必須與increment參數(shù),否則應(yīng)用程序?qū)o法啟動末盔。

如果要更改Hibernate中的序列驗證行為筑舅,可以禁用模式驗證或設(shè)置參數(shù)的hibernate.id.sequence.increment_size_mismatch_strategy價值對價值LOGFIX.

LOG參數(shù)值時,Hibernate將忽略不匹配陨舱。這可能會導(dǎo)致PK唯一性沖突翠拣,因為ID分配范圍計算將與實際序列不匹配。increment值游盲,我們可以得到重復(fù)的ID值误墓。例如,對于allocationSize等于20和序列increment是1益缎,我們會得到這樣的東西:

select nextval ('pet_seq'); // selects 1 initial value, need to select next value
select nextval ('pet_seq'); //selects 2 as range high value
insert into pet (name, id) values (?, ?);// id=1
insert into pet (name, id) values (?, ?);//id=2
//Now we’ve exceeded high value, need to select the next batch
select nextval ('pet_seq'); //selects 3 as range high value, calculates 3 – 20 = -17 as the low
insert into pet (name, id) values (?, ?);//id=-17
insert into pet (name, id) values (?, ?);//id=-16
//Restarting the application
select nextval ('pet_seq'); //selects 4 as range high value, calculates 4 – 20 = -16 as the low
insert into pet (name, id) values (?, ?);//id=-16 getting unique constraint violation

假設(shè)我們將參數(shù)設(shè)置為FIX谜慌。在這種情況下,allocationSize將自動調(diào)整jpa序列生成器中的參數(shù)以匹配DB序列莺奔。increment參數(shù)欣范,例如,對于上述情況令哟,參數(shù)為1恼琼。

的另一個特性@SequenceGenerator定義是,通過在不同的序列生成器中指定相同的“序列名稱”屏富,我們可以為不同的實體重用相同的序列晴竞。

//ID Definition for ‘Pet’ entity
@Id
@SequenceGenerator(name = "pet_seq", sequenceName = "common_sequence")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pet_seq")
@Column(name = "id", nullable = false)
private Long id;

//ID Definition for ‘Owner’ entity
@Id
@SequenceGenerator(name = "owner_seq", sequenceName = " common_sequence ")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "owner_seq")
@Column(name = "id", nullable = false)
private Long id;

結(jié)語*定義序列生成器使我們能夠:

  1. 使用ID獲取優(yōu)化以獲得更好的應(yīng)用程序性能。
  2. 根據(jù)應(yīng)用程序工作負(fù)載優(yōu)化獲取大小役听,以便在頻繁獲取ID和由于會話關(guān)閉而浪費一些ID之間保持平衡颓鲜。
  3. 在不同實體之間共享相同的序列表窘。

這使得SEQUENCEID生成幾乎是一個理想的選擇。在這個策略中有什么需要注意的地方嗎甜滨?

DB的多個客戶端:這里有什么問題嗎乐严?

即使SEQUENCE生成策略使用數(shù)據(jù)庫中的序列,它在應(yīng)用程序代碼中分配ID值衣摩。這意味著使用相同數(shù)據(jù)庫的其他應(yīng)用程序可能不知道序列的存在昂验,因此出現(xiàn)了ID生成策略。

在數(shù)據(jù)庫中使用多個客戶端可能導(dǎo)致其他DB客戶端直接分配ID而不使用序列的情況艾扮。這些ID值可能與為應(yīng)用程序中未保存的實體保留的ID值相同既琴。當(dāng)我們的應(yīng)用程序開始保存實體時,它可能會導(dǎo)致PK唯一性沖突泡嘴,并且數(shù)據(jù)不會被存儲甫恩。

結(jié)語: SEQUENCE如果多個客戶端更新數(shù)據(jù)庫,ID的生成策略可能無法正常工作酌予。在這種情況下磺箕,ID生成應(yīng)該由數(shù)據(jù)庫控制。IDENTITY這里的策略效果更好抛虫。

身份:利弊

IDENTITY是用于開發(fā)人員使用MySQL數(shù)據(jù)庫生成ID的“默認(rèn)”策略松靡。由于許多RDBMS(除了MySQL)都支持用于列定義的標(biāo)識數(shù)據(jù)類型,所以我們可以在許多應(yīng)用程序中看到這種策略建椰。有時開發(fā)人員選擇它是因為“它在我以前的項目中奏效了”雕欺,而且沒有人愿意改變這個習(xí)慣,如果它成功的話棉姐。通過指定如下代碼中的策略屠列,我們獲得了一個可靠的ID生成過程,該流程管理在一個地方--數(shù)據(jù)庫谅海。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

為每一個INSERT語句脸哀,數(shù)據(jù)庫將自動為相應(yīng)的@Id場。這與SEQUENCE策略行為扭吁,如果我們定義allocationSize等于“1”撞蜂。對于這兩種情況,我們都需要為每個INSERT聲明侥袜。然而蝌诡,有一個不同之處。重要的是要理解使用identity列意味著必須在已知標(biāo)識符值之前物理插入實體行枫吧。由于數(shù)據(jù)庫生成ID的值浦旱,JPA提供程序應(yīng)該在插入數(shù)據(jù)后將其返回給應(yīng)用程序。

問題是:JPA提供程序如何在插入記錄后獲取ID九杂?如果數(shù)據(jù)庫驅(qū)動程序支持JDBC3API(大多數(shù)現(xiàn)代數(shù)據(jù)庫都支持)颁湖,則會自動完成宣蠕。JPA提供程序隱式調(diào)用Statement.getGeneratedValues()方法,該方法返回生成的值甥捺。在遮罩下抢蚀,JPA提供程序生成如下所示的SQL語句:

insert into pet (name) values (‘Buddy’) RETURNING *

假設(shè)我們使用一個舊版本的數(shù)據(jù)庫驅(qū)動程序。在這種情況下镰禾,將執(zhí)行額外的SELECT(通常由JPA提供程序執(zhí)行皿曲,但有時我們需要手動執(zhí)行)來獲取生成的值,類似于下面的代碼吴侦。這是舊PostgreSQL版本的日志屋休,它模擬IDENTITY使用DB序列的數(shù)據(jù)類型。對于其他RDBMS备韧,SQL將類似劫樟。

insert into pet (name) values (?)
select currval('pet_id_seq')
insert into pet (name) values (?)
select currval('pet_id_seq')

此行為不允許JPA提供程序執(zhí)行批處理插入。因為提供程序需要在每個INSERT织堂,它將批處理操作拆分為單個操作毅哗。INSERT運算符,并在每次執(zhí)行后獲取生成的ID值捧挺。我們不能送一批INSERT語句并獲得一批生成的ID,因為我們無法將生成的ID可靠地關(guān)聯(lián)到JPA對象尿瞭。原因是數(shù)據(jù)庫不能保證生成的id的順序與INSERT此外闽烙,INSERT語句不能以與批處理相同的順序執(zhí)行。因此声搁,獲得插入記錄的ID的唯一可靠方法是拆分批處理黑竞。

結(jié)語: IDENTITY策略簡單易用,保證了可靠的應(yīng)用獨立主鍵值的生成.

從另一方面來說疏旨,這種策略在規(guī)則中提供了次優(yōu)的性能很魂。INSERT操作和批處理INSERT根本不支持操作。因此檐涝,建議使用IDENTITY對于我們保存少量新數(shù)據(jù)或幾個獨立客戶端應(yīng)用程序更改數(shù)據(jù)庫的情況遏匆。

結(jié)論:序列與序列的一致性

那么,我們應(yīng)該為我們的JPA實體選擇哪種ID生成策略呢谁榜?以下是一些建議:

  1. SEQUENCE與其他策略相比幅聘,它提供了更好的整體性能。此外窃植,我們需要考慮以下幾點:
    1. 為每個JPA實體定義一個單獨的序列是一個很好的實踐帝蒿。避免默認(rèn)的序列生成器參數(shù)。
    2. 我們應(yīng)該用@SequenceGenerator對微調(diào)序列參數(shù)的注釋巷怜。
    3. 我們需要根據(jù)應(yīng)用程序工作負(fù)載模式來定義批處理大小葛超。
  1. 我們可能更喜歡IDENTITY下列案件的戰(zhàn)略:
    1. 如果數(shù)據(jù)庫不支持序列暴氏。
    2. 用于不經(jīng)常創(chuàng)建和保存的實體。
    3. 如果我們的數(shù)據(jù)庫被其他應(yīng)用程序修改绣张。
  1. TABLEAUTO如果可能的話答渔,生成策略。他們的表現(xiàn)最差胖替。

ID列表不限于簡單的服務(wù)器生成的ID研儒。在下面的文章中,我們將討論客戶端生成的ID独令,特別是UUID端朵。此外,盡管不太流行燃箭,復(fù)合ID也有一些需要學(xué)習(xí)的東西冲呢,所以我們也將討論它們。

db-generated-ids-in-jpa.png

小伙伴們?nèi)绻X得我寫的不錯招狸,不妨幫個忙敬拓,給我點個贊唄,可以讓更多的人看到這篇文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末裙戏,一起剝皮案震驚了整個濱河市乘凸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌累榜,老刑警劉巖营勤,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異壹罚,居然都是意外死亡葛作,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門猖凛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赂蠢,“玉大人,你說我怎么就攤上這事辨泳∈瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵菠红,是天一觀的道長量瓜。 經(jīng)常有香客問我,道長途乃,這世上最難降的妖魔是什么绍傲? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上烫饼,老公的妹妹穿的比我還像新娘猎塞。我一直安慰自己,他們只是感情好杠纵,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布荠耽。 她就那樣靜靜地躺著,像睡著了一般比藻。 火紅的嫁衣襯著肌膚如雪铝量。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天银亲,我揣著相機與錄音慢叨,去河邊找鬼。 笑死务蝠,一個胖子當(dāng)著我的面吹牛拍谐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馏段,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼轩拨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了院喜?” 一聲冷哼從身側(cè)響起亡蓉,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喷舀,沒想到半個月后寸宵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡元咙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了巫员。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庶香。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖简识,靈堂內(nèi)的尸體忽然破棺而出赶掖,到底是詐尸還是另有隱情,我是刑警寧澤七扰,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布奢赂,位于F島的核電站,受9級特大地震影響颈走,放射性物質(zhì)發(fā)生泄漏膳灶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轧钓。 院中可真熱鬧序厉,春花似錦、人聲如沸毕箍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽而柑。三九已至文捶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間媒咳,已是汗流浹背粹排。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伟葫,地道東北人恨搓。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像筏养,于是被迫代替她去往敵國和親斧抱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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