Hibernate, @SequenceGenerator และการจัดสรรขนาด


117

เราทุกคนรู้ถึงพฤติกรรมเริ่มต้นของ Hibernate เมื่อใช้@SequenceGenerator- มันจะเพิ่มลำดับฐานข้อมูลจริงทีละค่านี้คูณด้วย 50 (ค่าเริ่มต้นallocationSize) - จากนั้นใช้ค่านี้เป็น ID เอนทิตี

นี่เป็นพฤติกรรมที่ไม่ถูกต้องและขัดแย้งกับข้อกำหนดที่ระบุว่า:

จัดสรรขนาด - (ไม่บังคับ) จำนวนเงินที่จะเพิ่มขึ้นเมื่อจัดสรรหมายเลขลำดับจากลำดับ

เพื่อความชัดเจน: ฉันไม่กังวลเกี่ยวกับช่องว่างระหว่าง ID ที่สร้างขึ้น

ฉันสนใจ ID ที่ไม่สอดคล้องกับลำดับฐานข้อมูลพื้นฐาน ตัวอย่างเช่น: แอปพลิเคชันอื่น ๆ (เช่นใช้ JDBC ธรรมดา) อาจต้องการแทรกแถวใหม่ภายใต้ ID ที่ได้รับจากลำดับ - แต่ค่าเหล่านั้นทั้งหมดอาจถูกใช้โดย Hibernate! ความบ้าคลั่ง

มีใครทราบวิธีแก้ปัญหานี้allocationSize=1บ้างไหม(โดยไม่ต้องตั้งค่าและทำให้ประสิทธิภาพลดลง)

แก้ไข:
เพื่อให้ทุกอย่างชัดเจน ถ้าระเบียนสุดท้ายแทรกมี ID = 1ค่าใช้แล้ว HB 51, 52, 53...สำหรับองค์กรใหม่ แต่ในเวลาเดียวกัน: 2ค่าลำดับในฐานข้อมูลของจะถูกตั้งค่า ซึ่งสามารถนำไปสู่ข้อผิดพลาดได้อย่างง่ายดายเมื่อแอปพลิเคชันอื่นกำลังใช้ลำดับนั้น

ในทางกลับกัน: ข้อกำหนดระบุว่า (ในความเข้าใจของฉัน) ลำดับฐานข้อมูลควรถูกตั้งค่าเป็น51และในระหว่างนี้ HB ควรใช้ค่าจากช่วง 2, 3 ... 50


UPDATE:
สตีฟในฐานะ Ebersole ระบุไว้ด้านล่าง: พฤติกรรมที่อธิบายไว้โดยฉัน (และยังใช้งานง่ายที่สุดสำหรับอีกหลายคน) hibernate.id.new_generator_mappings=trueสามารถเปิดใช้งานโดยการตั้งค่า

ขอบคุณทุกท่าน.

อัปเดต 2:
สำหรับผู้อ่านในอนาคตคุณจะพบตัวอย่างการใช้งานด้านล่าง

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
    @SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
    private Long id;
}

persistence.xml

<persistence-unit name="testPU">
  <properties>
    <property name="hibernate.id.new_generator_mappings" value="true" />
  </properties>
</persistence-unit>

2
"ไม่มีการตั้งค่าการจัดสรรขนาด = 1 และทำให้ประสิทธิภาพลดลง" เหตุใดจึงลดประสิทธิภาพการทำงานคุณจึงตั้งค่าเป็น 1
sheidaei

3
@sheidaei ดูอาจแสดงความคิดเห็นด้านล่าง :-) เนื่องจากทุกคนsaveต้องการค้นหาฐานข้อมูลสำหรับค่าถัดไปของลำดับ
G. Demecki

ขอบคุณที่ประสบปัญหาเดียวกัน ตอนแรกฉันเพิ่มการจัดสรรขนาด = 1 ที่ทุก @SequenceGenerator การใช้ hibernate.id.new_generator_mappings = true ป้องกันสิ่งนั้น แม้ว่า JPA จะยังคงค้นหาฐานข้อมูลเพื่อรับรหัสสำหรับการแทรกแต่ละครั้ง ...
TheBakker

1
ด้วยSequenceGeneratorHibernate จะค้นหาฐานข้อมูลก็ต่อเมื่อจำนวน ID ที่ระบุโดยallocationsizeหมด หากคุณตั้งค่าไว้allocationSize = 1นั่นคือเหตุผลที่ไฮเบอร์เนตสอบถามฐานข้อมูลสำหรับการแทรกแต่ละครั้ง เปลี่ยนค่านี้และคุณทำเสร็จแล้ว
G. Demecki

1
ขอบคุณ! การhibernate.id.new_generator_mappingsตั้งค่ามีความสำคัญมาก ฉันหวังว่าจะเป็นการตั้งค่าเริ่มต้นที่ฉันไม่ต้องเสียเวลามากนักในการค้นคว้าว่าทำไมหมายเลขประจำตัวถึงผิดปกติ
LeOn - Han Li

คำตอบ:


44

เพื่อความชัดเจนอย่างแท้จริง ... สิ่งที่คุณอธิบายไม่ได้ขัดแย้งกับสเป็ค แต่อย่างใด ข้อมูลจำเพาะพูดถึงค่าที่ไฮเบอร์เนตกำหนดให้กับเอนทิตีของคุณไม่ใช่ค่าที่เก็บไว้ในลำดับฐานข้อมูล

อย่างไรก็ตามมีตัวเลือกในการรับพฤติกรรมที่คุณกำลังมองหา อันดับแรกดูคำตอบของฉันเกี่ยวกับมีวิธีเลือกกลยุทธ์ @GeneratedValue แบบไดนามิกโดยใช้คำอธิบายประกอบ JPA และไฮเบอร์เนตหรือไม่ นั่นจะทำให้คุณมีพื้นฐาน ตราบเท่าที่คุณได้รับการตั้งค่าให้ใช้ SequenceStyleGenerator นั้น Hibernate จะตีความallocationSizeโดยใช้ "pooled optimizer" ใน SequenceStyleGenerator "pooled optimizer" มีไว้สำหรับใช้กับฐานข้อมูลที่อนุญาตให้มีตัวเลือก "Increment" ในการสร้างลำดับ (ไม่ใช่ฐานข้อมูลทั้งหมดที่สนับสนุนลำดับที่สนับสนุนการเพิ่ม) อย่างไรก็ตามโปรดอ่านเกี่ยวกับกลยุทธ์เครื่องมือเพิ่มประสิทธิภาพต่างๆที่นั่น


ขอบคุณสตีฟ! คำตอบที่ดีที่สุด. นอกจากนี้โพสต์อื่น ๆ ของคุณก็มีประโยชน์
G. Demecki

4
org.hibernate.id.enhanced.SequenceStyleGeneratorฉันยังพบว่าคุณเป็นผู้เขียนร่วมของ คุณทำให้ฉันประหลาดใจ
G. Demecki

22
ทำให้คุณประหลาดใจได้อย่างไร? ฉันเป็นหัวหน้าผู้พัฒนา Hibernate ฉันเขียน / ร่วมเขียนคลาส Hibernate มาแล้วมากมาย;)
Steve Ebersole

สำหรับบันทึก ควรหลีกเลี่ยงการเพิ่มลำดับ DB เพื่อป้องกันช่องว่างขนาดใหญ่ ลำดับ DB ถูกคูณด้วยการจัดสรรขนาดเมื่อ ID แคชหมดรายละเอียดเพิ่มเติมstackoverflow.com/questions/5346147/…
Olcay Tarazan

1
วิธีหนึ่งในการเปลี่ยน "เครื่องมือเพิ่มประสิทธิภาพ" ที่ใช้ทั่วโลกคือการเพิ่มสิ่งนี้ลงในตัวเลือกไฮเบอร์เนตของคุณ: serviceBuilder.applySetting ("hibernate.id.optimizer.pooled.preferred", LegacyHiLoAlgorithmOptimizer.class.getName ()); แทนที่จะเป็น LegacyHiLoAlgorithOptimizer คุณสามารถเลือกคลาสเครื่องมือเพิ่มประสิทธิภาพใดก็ได้และจะกลายเป็นค่าเริ่มต้น สิ่งนี้จะทำให้ง่ายต่อการรักษาพฤติกรรมที่คุณต้องการเป็นค่าเริ่มต้นโดยไม่ต้องเปลี่ยนคำอธิบายประกอบทั้งหมด นอกจากนี้โปรดระวังด้วยเครื่องมือเพิ่มประสิทธิภาพ "pooled" และ "hilo" สิ่งเหล่านี้จะให้ผลลัพธ์ที่แปลกเมื่อค่าลำดับของคุณเริ่มต้นที่ 0 ทำให้เกิด ID เชิงลบ
fjalvingh

17

allocationSize=1เป็นการเพิ่มประสิทธิภาพแบบไมโครก่อนที่จะได้รับการสืบค้น Hibernate พยายามกำหนดค่าในช่วงของการจัดสรรขนาดและพยายามหลีกเลี่ยงการสืบค้นฐานข้อมูลสำหรับลำดับ แต่แบบสอบถามนี้จะดำเนินการทุกครั้งหากคุณตั้งค่าเป็น 1 สิ่งนี้แทบจะไม่สร้างความแตกต่างใด ๆ เนื่องจากหากแอปพลิเคชันอื่นเข้าถึงฐานข้อมูลของคุณมันจะสร้างปัญหาหากแอปพลิเคชันอื่นใช้ id เดียวกัน

ลำดับถัดไปของรหัสลำดับขึ้นอยู่กับการจัดสรรขนาด

โดยค่าเริ่มต้นจะถูกเก็บไว้ใน50ที่ที่มากเกินไป นอกจากนี้ยังจะช่วยได้ก็ต่อเมื่อคุณจะมีเรก50คอร์ดใกล้ ๆในหนึ่งเซสชันซึ่งไม่คงอยู่และจะยังคงอยู่โดยใช้เซสชันและการเปลี่ยนเฉพาะนี้

ดังนั้นคุณควรใช้ในขณะที่ใช้allocationSize=1 SequenceGeneratorสำหรับลำดับฐานข้อมูลพื้นฐานส่วนใหญ่จะเพิ่มขึ้นด้วย1เสมอ


12
ไม่มีอะไรเกี่ยวข้องกับประสิทธิภาพ? คุณเป็นจริงๆแน่ใจหรือไม่? ฉันได้รับการสอนว่าallocationSize=1เมื่อใช้ไฮเบอร์เนตในทุกsaveการดำเนินการจำเป็นต้องเดินทางไปยังฐานข้อมูลเพื่อรับค่า ID ใหม่
G. Demecki

2
เป็นการเพิ่มประสิทธิภาพแบบไมโครก่อนที่จะได้รับแบบสอบถามไฮเบอร์เนตพยายามกำหนดค่าในช่วงของallocationSizeดังนั้นพยายามหลีกเลี่ยงการสืบค้นฐานข้อมูลสำหรับลำดับ แต่การสืบค้นนี้จะดำเนินการทุกครั้งหากคุณตั้งค่าเป็น 1 สิ่งนี้แทบจะไม่สร้างความแตกต่างใด ๆ เนื่องจากหากแอปพลิเคชันอื่นเข้าถึงฐานข้อมูลของคุณมันจะสร้างปัญหาหากแอปพลิเคชันอื่นใช้รหัสเดียวกันในขณะเดียวกัน
Amit Deshpande

และใช่มันเป็นแอปพลิเคชั่นที่เฉพาะเจาะจงว่าขนาดการจัดสรร 1 มีผลกระทบต่อประสิทธิภาพจริงหรือไม่ แน่นอนว่าในเกณฑ์มาตรฐานระดับจุลภาคนั้นมักจะแสดงผลกระทบอย่างมาก นั่นคือปัญหากับเกณฑ์มาตรฐานส่วนใหญ่ (ไมโครหรืออย่างอื่น) มันไม่เป็นจริง และแม้ว่าจะมีความซับซ้อนพอที่จะเป็นจริงได้บ้าง แต่คุณยังต้องดูว่าเกณฑ์มาตรฐานนั้นใกล้เคียงกับแอปพลิเคชันจริงของคุณมากเพียงใดเพื่อให้เข้าใจว่าผลการเปรียบเทียบมีผลกับผลลัพธ์ที่คุณจะเห็นในแอปของคุณอย่างไร เรื่องสั้นสั้น ๆ .. ทดสอบด้วยตัวคุณเอง
Steve Ebersole

2
ตกลง. ทุกอย่างเป็นแอปพลิเคชั่นเฉพาะไม่ใช่เหรอ! ในกรณีที่แอปพลิเคชันของคุณเป็นแอปพลิเคชันแบบอ่านอย่างเดียวผลกระทบของการใช้ขนาดการจัดสรร 1000 หรือ 1 จะเป็น 0 อย่างแน่นอนในทางกลับกันสิ่งเหล่านี้เป็นแนวทางปฏิบัติที่ดีที่สุด หากคุณไม่เคารพแนวทางปฏิบัติที่ดีที่สุดที่พวกเขารวบรวมขึ้นและผลกระทบร่วมกันจะทำให้แอปพลิเคชันของคุณซบเซา อีกตัวอย่างหนึ่งคือการเริ่มต้นธุรกรรมเมื่อคุณไม่ต้องการอย่างแท้จริง
Hasan Ceylan

1

Steve Ebersole และสมาชิกคนอื่น ๆ
กรุณาอธิบายเหตุผลของ id ที่มีช่องว่างมากกว่า (โดยค่าเริ่มต้นคือ 50)? ฉันใช้ Hibernate 4.2.15 และพบรหัสต่อไปนี้ในคาสต์ org.hibernate.id.enhanced.OptimizerFactory

if ( lo > maxLo ) {
   lastSourceValue = callback.getNextValue();
   lo = lastSourceValue.eq( 0 ) ? 1 : 0;
   hi = lastSourceValue.copy().multiplyBy( maxLo+1 ); 
}  
value = hi.copy().add( lo++ );

เมื่อใดก็ตามที่เข้าสู่ภายในของคำสั่ง if ค่า hi จะมีขนาดใหญ่ขึ้นมาก ดังนั้น id ของฉันในระหว่างการทดสอบด้วยการรีสตาร์ทเซิร์ฟเวอร์บ่อยๆจะสร้างรหัสลำดับต่อไปนี้:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150

ฉันรู้ว่าคุณบอกแล้วว่ามันไม่ได้ขัดแย้งกับสเป็ค แต่ฉันเชื่อว่านี่จะเป็นสถานการณ์ที่ไม่คาดคิดสำหรับนักพัฒนาส่วนใหญ่

ข้อมูลของทุกคนจะเป็นประโยชน์มาก

จีฮวาน

UPDATE: ne1410s: ขอบคุณสำหรับการแก้ไข
cfrick: ตกลง ฉันจะทำอย่างนั้น. นี่เป็นกระทู้แรกของฉันและไม่แน่ใจว่าจะใช้อย่างไร

ตอนนี้ฉันเข้าใจดีขึ้นว่าทำไม maxLo จึงใช้เพื่อวัตถุประสงค์สองประการ: เนื่องจาก hibernate เรียกลำดับ DB หนึ่งครั้งให้เพิ่ม id ในระดับ Java และบันทึกลงใน DB ค่า id ระดับ Java ควรพิจารณาว่ามีการเปลี่ยนแปลงเท่าใดโดยไม่ต้องเรียก ลำดับ DB เมื่อเรียกลำดับในครั้งต่อไป

ตัวอย่างเช่นรหัสลำดับคือ 1 ณ จุดหนึ่งและไฮเบอร์เนตป้อน 5, 6, 7, 8, 9 (ด้วยการจัดสรรขนาด = 5) ครั้งต่อไปเมื่อเราได้หมายเลขลำดับถัดไป DB จะส่งกลับ 2 แต่ hibernate จำเป็นต้องใช้ 10, 11, 12 ... ดังนั้นจึงเป็นเหตุให้ "hi = lastSourceValue.copy (). multiplyBy (maxLo + 1)" คือ ใช้เพื่อรับ id 10 ถัดไปจาก 2 ที่ส่งคืนจากลำดับ DB ดูเหมือนว่าสิ่งเดียวที่น่ารำคาญคือระหว่างการรีสตาร์ทเซิร์ฟเวอร์บ่อยครั้งและนี่คือปัญหาของฉันกับช่องว่างที่ใหญ่ขึ้น

ดังนั้นเมื่อเราใช้ SEQUENCE ID รหัสที่แทรกในตารางจะไม่ตรงกับหมายเลข SEQUENCE ใน DB


1

หลังจากขุดลงในซอร์สโค้ดจำศีลและการกำหนดค่าด้านล่างจะไปที่ Oracle db สำหรับค่าถัดไปหลังจากแทรก 50 ดังนั้นทำให้ INST_PK_SEQ ของคุณเพิ่มขึ้น 50 ทุกครั้งที่เรียก

Hibernate 5 ใช้สำหรับกลยุทธ์ด้านล่าง

ตรวจสอบด้านล่าง http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence

@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ", 
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
        @org.hibernate.annotations.Parameter(
                name = "optimizer", value = "pooled-lo"),
        @org.hibernate.annotations.Parameter(
                name = "initial_value", value = "1"),
        @org.hibernate.annotations.Parameter(
                name = "increment_size", value = "50"),
        @org.hibernate.annotations.Parameter(
                name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
    }
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;

3
ขออภัยนี่เป็นวิธีการตั้งค่าที่ละเอียดมากซึ่งสามารถแสดงได้อย่างง่ายดายด้วยพารามิเตอร์สองตัวสำหรับ Hibernate ทั้งหมดและสำหรับเอนทิตีทั้งหมด
G. Demecki

จริง แต่เมื่อฉันลองด้วยวิธีอื่นไม่มีวิธีใดได้ผลหากคุณใช้งานได้สามารถส่งวิธีการกำหนดค่าให้ฉันได้
fatih tekin

ฉันได้อัปเดตคำตอบแล้ว - ตอนนี้มีตัวอย่างการทำงานด้วย แม้ว่าความคิดเห็นของฉันข้างต้นจะผิดบางส่วน: น่าเสียดายที่คุณไม่สามารถตั้งค่าallocationSizeหรือinitialValueทั่วโลกสำหรับเอนทิตีทั้งหมดได้ (เว้นแต่จะใช้เครื่องกำเนิดไฟฟ้าเพียงเครื่องเดียว แต่ IMHO ไม่สามารถอ่านได้มากนัก)
G. Demecki

1
ขอบคุณสำหรับคำอธิบาย แต่สิ่งที่คุณเขียนข้างต้นฉันได้ลองแล้วและมันใช้ไม่ได้กับ hibernate 5.0.7 เวอร์ชันสุดท้ายฉันได้ขุดลงไปในซอร์สโค้ดเพื่อให้สามารถบรรลุเป้าหมายนี้และนั่นคือการใช้งานที่ฉันสามารถหาได้ ในซอร์สโค้ดจำศีล การกำหนดค่าอาจดูไม่ดี แต่น่าเสียดายที่จำศีล api และฉันกำลังใช้การใช้งาน EntityManager มาตรฐานของ hibernate
fatih tekin

1

ฉันประสบปัญหานี้เช่นกันใน Hibernate 5:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;

ได้รับคำเตือนดังนี้ด้านล่าง:

พบการใช้ตัวสร้าง id ตามลำดับ [org.hibernate.id.SequenceHiLoGenerator] ที่เลิกใช้แล้ว ใช้ org.hibernate.id.enhanced.SequenceStyleGenerator แทน ดูคู่มือการทำแผนที่แบบจำลองโดเมนไฮเบอร์เนตสำหรับรายละเอียด

จากนั้นเปลี่ยนรหัสของฉันเป็น SequenceStyleGenerator:

@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                @Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;

สิ่งนี้ช่วยแก้ปัญหาสองประการของฉัน:

  1. คำเตือนที่เลิกใช้งานได้รับการแก้ไขแล้ว
  2. ตอนนี้ id ถูกสร้างขึ้นตามลำดับ oracle

0

ฉันจะตรวจสอบ DDL สำหรับลำดับในสคีมา JPA Implementation มีหน้าที่สร้างลำดับที่มีขนาดการจัดสรรที่ถูกต้องเท่านั้น ดังนั้นหากขนาดการจัดสรรคือ 50 ลำดับของคุณจะต้องเพิ่มขึ้นเป็น 50 ใน DDL

โดยทั่วไปกรณีนี้อาจเกิดขึ้นกับการสร้างลำดับที่มีขนาดการจัดสรร 1 จากนั้นกำหนดค่าในภายหลังเป็นขนาดการจัดสรร 50 (หรือค่าเริ่มต้น) แต่ลำดับ DDL ไม่ได้รับการอัพเดต


คุณเข้าใจผิดประเด็นของฉัน ALTER SEQUENCE ... INCREMENTY BY 50;จะไม่แก้ปัญหาอะไรเพราะปัญหายังคงเหมือนเดิม ค่าลำดับยังคงไม่สะท้อนถึงรหัสเอนทิตีจริง
G. Demecki

โปรดแบ่งปันกรณีทดสอบเพื่อให้เราเข้าใจปัญหาที่นี่ได้ดีขึ้น
Hasan Ceylan

1
กรณีทดสอบ? ทำไม? คำถามที่โพสต์โดยฉันไม่ได้ซับซ้อนมากนักและได้รับคำตอบแล้ว ดูเหมือนว่าคุณไม่ทราบว่าเครื่องกำเนิดไฟฟ้า HiLo ทำงานอย่างไร อย่างไรก็ตาม: ขอบคุณสำหรับการเสียสละเวลาและความพยายาม
G. Demecki

1
Gregory ที่จริงฉันรู้ว่าฉันกำลังพูดถึงอะไรฉันได้เขียน Batoo JPA ซึ่งเป็นการนำไปใช้งาน 100 JPA ที่กำลังอยู่ในช่วงฟักตัวและเต้น Hibernate ในแง่ของความเร็ว - เร็วขึ้น 15 เท่า ในทางกลับกันฉันอาจเข้าใจคำถามของคุณผิดและไม่คิดว่าการใช้ Hibernate กับลำดับจะสร้างปัญหาใด ๆ เลยเนื่องจากฉันใช้ Hibernate มาตั้งแต่ปี 2546 ในหลายโครงการในหลาย ๆ ฐานข้อมูล สิ่งสำคัญคือคุณมีวิธีแก้ปัญหาขออภัยฉันพลาดคำตอบที่ทำเครื่องหมายว่าถูกต้อง ...
Hasan Ceylan

ขอโทษฉันไม่ได้ตั้งใจจะทำให้คุณขุ่นเคือง ขอขอบคุณอีกครั้งสำหรับความช่วยเหลือของคุณคำถามได้รับคำตอบแล้ว
G. Demecki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.