事件背景:
之前我手里的一個項目需要升級weblogic從11g到12c悴晰,要將Toplink轉(zhuǎn)化為Eclipselink,然后當(dāng)時遇到一個方法:databaseLogin.setSequencePreallocationSize(1);這個方法已經(jīng)無法使用庸论,就先將他注釋掉了薇溃。然后在測試中發(fā)現(xiàn)啟動后運行一段時間搜锰,DB就會報出主鍵沖突的錯誤。經(jīng)過排查發(fā)現(xiàn)每次重新啟動Server身坐,各個數(shù)據(jù)庫表的插入數(shù)據(jù)的id幾乎都會是原來最大id減去50的值囊陡,使得再插入新的數(shù)據(jù)會有id相同的情況。而且這種情況是周期性的掀亥,每增加50個id撞反,就會將id減去50重新處理。
相關(guān)知識:
首先搪花,什么是Sequence:Sequence是數(shù)據(jù)中一個特殊存放等差數(shù)列的表遏片,該表受數(shù)據(jù)庫系統(tǒng)控制,任何時候數(shù)據(jù)庫系統(tǒng)都可以根據(jù)當(dāng)前記錄數(shù)大小加上步長來獲取到該表下一條記錄應(yīng)該是多少撮竿,這個表沒有實際意義吮便,常常用來做主鍵用。以下是他的表結(jié)構(gòu):
increment_by(增長間隔),max_value (最大值),min_value (最小值),cache (制定存入緩存序列值的個數(shù)) ,last_number (當(dāng)前可取的最后一個number)
其中重點說一下increment_by幢踏,每當(dāng)表將已有的Sequence用完之后髓需,將會預(yù)加載的sequence大小。然后Last_number = last_number + increment_by房蝉。之后Sequence就會從預(yù)加載的這一塊Sequence的第一個開始讀僚匆。
開發(fā)中用到的相關(guān)方法:
先說明一下為什么會產(chǎn)生背景中介紹的那樣的問題。首先看一下EclipseLink在試圖取得下一段可使用Sequence所使用的方法:
protected Vector createVector(Number sequence, String seqName, int size) {
? ? ? ? long nextSequence = sequence.longValue();
? ? ? ? Vector sequencesForName = new Vector(size);
? ? ? ? nextSequence -= (long)size;
? ? ? ? if(nextSequence < -1L) {
? ? ? ? ? ? throw ValidationException.sequenceSetupIncorrectly(seqName);
? ? ? ? } else {
? ? ? ? ? ? for(int index = size; index > 0; --index) {
? ? ? ? ? ? ? ? ++nextSequence;
? ? ? ? ? ? ? ? sequencesForName.add(Long.valueOf(nextSequence));
? ? ? ? ? ? }
? ? ? ? ? ? return sequencesForName;
? ? ? ? }
? ? }
SequenceForName是接下來所能使用的Sequence列表搭幻,從代碼中可以看出咧擂,取得的sequence區(qū)間是 nextSequence - size ~?nextSequence。sequece得到的值和DB中sequence table相關(guān)檀蹋,如上圖我們的設(shè)置松申,下一個要取的sequence id是最后一個id + 1,而size = 50 (之后會說明為什么=50),所以新的sequence區(qū)間相當(dāng)于后退了49個id贸桶。
然后我們看一下之前被我們注釋的方法的官方說明:
setSequencePreallocationSize
public voidsetSequencePreallocationSize(int?size)
Deprecated.use getDefaultSequence().setPreallocationSize(int) instead
OBSOLETE:
TopLink supports sequence number preallocation.
This improves the performance and concurrency of inserts by selecting new object IDs in batches and caching them on the client.
The preallocation size can be configured, and the default is 50.
By default a sequence table is used. Using a sequence table is recommended as it supports preallocation.
This MUST be 1 if native sequencing is used on Sybase, Informix, or SQL Server.
This MUST match the SEQUENCE increment if Oracle native sequencing is used.
注意最后兩行舅逸,由于我們使用了Nactive sequence(Many databases have built in support for sequencing. This can be a SEQUENCE object such as in Oracle,or a auto-incrementing column such as the IDENTITY field in Sybase. For an auto-incrementing column the preallocation size is always 1.For a SEQUENCE object the preallocation size must match the SEQUENCE objects "increment by".)所以是要主動設(shè)置這個增量size的(50 -> 1)Squence table不用做改動 (因為本來就是1)
這里有一點比較奇怪,就是如果直接使用databaseLogin.useNativeSequencing();還是會去取this.getPlatform().getDefaultSequence().getPreallocationSize() 作為Pre-allocation皇筛,就是還是會賦默認(rèn)值50... 所以我們采取的解決方案是用默認(rèn)的Sequecing并修改Pre-allocation的值:databaseLogin.setDefaultSequence(new NativeSequence(login.getPlatform().getDefaultSequence().getName(),1, login.getPlatform().getDefaultSequence().getInitialValue())); 這樣問題就解決了堡赔。
還有一點就是為什么其他的application升級eclipseLink時候沒有問題,可能是最初就用的DefaultSequencing或者使用getDefaultSequence().setPreallocationSize(int)設(shè)置的Pre-allocation吧设联,因為setSequencePreallocationSize(1) 在Toplink的時候已經(jīng)廢棄了善已。
以上就是這個問題的解決過程。其實也不用特別在升級的時候留意离例,因為EclipseLink中如果有setSequencePreallocationSize(1)這個方法會標(biāo)紅换团,這樣自然而然會用新的方法設(shè)置default值,就不會有問題了宫蛆。