Hibernate, @SequenceGenerator và phân bổSize


117

Tất cả chúng ta đều biết hành vi mặc định của Hibernate khi sử dụng @SequenceGenerator- nó tăng chuỗi cơ sở dữ liệu thực lên một , nhân giá trị này lên 50 ( allocationSizegiá trị mặc định ) - và sau đó sử dụng giá trị này làm ID thực thể.

Đây là hành vi không chính xác và xung đột với thông số kỹ thuật cho biết:

DeliverySize - (Tùy chọn) Số lượng tăng lên khi phân bổ số thứ tự từ trình tự.

Nói rõ hơn: Tôi không bận tâm về khoảng cách giữa các ID được tạo.

Tôi quan tâm đến các ID không nhất quán với chuỗi cơ sở dữ liệu bên dưới. Ví dụ: bất kỳ ứng dụng nào khác (ví dụ: sử dụng JDBC thuần túy) có thể muốn chèn các hàng mới dưới ID thu được từ trình tự - nhưng tất cả các giá trị đó có thể đã được Hibernate sử dụng! Điên cuồng.

Có ai biết giải pháp nào cho vấn đề này (mà không cần thiết lập allocationSize=1và do đó làm giảm hiệu suất) không?

CHỈNH SỬA:
Để làm cho mọi thứ rõ ràng. Nếu bản ghi được chèn lần cuối có ID = 1, thì HB sử dụng các giá trị 51, 52, 53...cho các thực thể mới của nó NHƯNG đồng thời: giá trị của chuỗi trong cơ sở dữ liệu sẽ được đặt thành 2. Điều này có thể dễ dàng dẫn đến lỗi khi các ứng dụng khác đang sử dụng trình tự đó.

Mặt khác: đặc tả cho biết (theo hiểu biết của tôi) rằng chuỗi cơ sở dữ liệu nên được đặt thành 51và trong thời gian chờ đợi HB nên sử dụng các giá trị từ phạm vi 2, 3 ... 50


CẬP NHẬT:
Như Steve Ebersole đã đề cập bên dưới: hành vi được mô tả bởi tôi (và cũng là trực quan nhất đối với nhiều người) có thể được kích hoạt bằng cách cài đặt hibernate.id.new_generator_mappings=true.

Cảm ơn tất cả các bạn.

CẬP NHẬT 2:
Đối với những độc giả trong tương lai, bạn có thể tìm thấy một ví dụ hoạt động bên dưới.

@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;
}

Persence.xml

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

2
"mà không đặt phân bổ sung kích thước = 1 và do đó làm giảm hiệu suất" tại sao nó làm giảm hiệu suất là bạn đặt nó thành 1?
sheidaei

3
@sheidaei thấy có thể nhận xét bên dưới :-) Điều này là do mọi savenhu cầu truy vấn cơ sở dữ liệu cho giá trị tiếp theo của chuỗi.
G. Demecki

Cảm ơn bạn đã đối mặt với cùng một vấn đề. Lúc đầu, tôi đã thêm phân bổ kích thước = 1 tại mỗi @SequenceGenerator. Sử dụng hibernate.id.new_generator_mappings = true ngăn chặn điều đó. Mặc dù JPA vẫn truy vấn cơ sở dữ liệu để lấy id cho mỗi lần chèn ...
TheBakker

1
Với SequenceGeneratorHibernate sẽ chỉ truy vấn cơ sở dữ liệu khi hết số lượng ID được chỉ định bởi allocationsize. Nếu bạn thiết lập allocationSize = 1thì đó là lý do tại sao Hibernate truy vấn DB cho mỗi lần chèn. Thay đổi giá trị này, và bạn đã hoàn tất.
G. Demecki

1
Cảm ơn! các hibernate.id.new_generator_mappingsthiết lập thực sự quan trọng. Tôi hy vọng đó là cài đặt mặc định mà tôi không phải mất quá nhiều thời gian để nghiên cứu lý do tại sao số id trở nên lung tung.
LeOn - Han Li

Câu trả lời:


43

Để hoàn toàn rõ ràng ... những gì bạn mô tả không mâu thuẫn với thông số kỹ thuật theo bất kỳ cách nào. Thông số kỹ thuật nói về các giá trị mà Hibernate gán cho các thực thể của bạn, không phải các giá trị thực sự được lưu trữ trong chuỗi cơ sở dữ liệu.

Tuy nhiên, có tùy chọn để có được hành vi mà bạn đang tìm kiếm. Trước tiên, hãy xem câu trả lời của tôi về Có cách nào để chọn động chiến lược @GeneratedValue bằng cách sử dụng chú thích JPA và Hibernate không? Điều đó sẽ cung cấp cho bạn những điều cơ bản. Miễn là bạn được thiết lập để sử dụng SequenceStyleGenerator đó, Hibernate sẽ thông dịch allocationSizebằng cách sử dụng "trình tối ưu hóa tổng hợp" trong SequenceStyleGenerator. "Trình tối ưu hóa tổng hợp" được sử dụng với cơ sở dữ liệu cho phép tùy chọn "tăng dần" khi tạo chuỗi (không phải tất cả cơ sở dữ liệu hỗ trợ chuỗi đều hỗ trợ tăng). Dù sao, hãy đọc về các chiến lược trình tối ưu hóa khác nhau ở đó.


Cảm ơn Steve! Câu trả lời tốt nhất. Ngoài ra bài viết khác của bạn cũng hữu ích.
G. Demecki

4
Tôi cũng nhận thấy rằng bạn là đồng tác giả của org.hibernate.id.enhanced.SequenceStyleGenerator. Bạn đã làm tôi ngạc nhiên.
G. Demecki

22
Làm bạn ngạc nhiên như thế nào? Tôi là nhà phát triển chính của Hibernate. Tôi đã viết / đồng tác giả nhiều lớp Hibernate;)
Steve Ebersole

Chỉ để cho bản ghi âm thôi. Trình tự DB Tăng dần nên được tránh để ngăn chặn khoảng trống lớn. DB chuỗi được nhân với allocationSize khi ID bộ nhớ cache chạy out.More chi tiết stackoverflow.com/questions/5346147/...
Olcay Tarazan

1
Một cách để thay đổi "trình tối ưu hóa" được sử dụng trên toàn cầu là thêm một thứ như thế này vào tùy chọn ngủ đông của bạn: serviceBuilder.applySetting ("hibernate.id.optimizer.pooled.preferred", LegacyHiLoAlgorithmOptimizer.class.getName ()); Thay vì LegacyHiLoAlgorithOptimizer, bạn có thể chọn bất kỳ lớp trình tối ưu hóa nào và nó sẽ trở thành mặc định. Điều này sẽ giúp dễ dàng giữ hành vi bạn muốn làm mặc định mà không thay đổi tất cả các chú thích. Ngoài ra, hãy cẩn thận với các trình tối ưu hóa "gộp" và "hilo": những công cụ này cho kết quả kỳ lạ khi giá trị trình tự của bạn bắt đầu từ 0 gây ra ID âm.
fjalvingh

17

allocationSize=1Đó là một tối ưu hóa vi mô trước khi nhận được truy vấn Hibernate cố gắng chỉ định giá trị trong phạm vi phân bổ Kích thước và do đó cố gắng tránh truy vấn cơ sở dữ liệu cho trình tự. Nhưng truy vấn này sẽ được thực thi mọi lúc nếu bạn đặt nó thành 1. Điều này hầu như không tạo ra bất kỳ sự khác biệt nào vì nếu cơ sở dữ liệu của bạn được một số ứng dụng khác truy cập thì nó sẽ tạo ra vấn đề nếu cùng một id được sử dụng bởi ứng dụng khác trong thời gian chờ đợi.

Thế hệ tiếp theo của Sequence Id dựa trên phân bổ Kích thước.

Bằng cách defualt, nó được giữ như 50là quá nhiều. Nó cũng sẽ chỉ hữu ích nếu bạn sắp có 50các bản ghi trong một phiên không được duy trì và sẽ được tiếp tục sử dụng phiên và chuyển tiếp cụ thể này.

Vì vậy, bạn nên luôn sử dụng allocationSize=1trong khi sử dụng SequenceGenerator. Đối với hầu hết các cơ sở dữ liệu, trình tự cơ sở dữ liệu luôn tăng lên 1.


12
Không có gì để làm với hiệu suất? Bạn có thực sự chắc chắn? Tôi đã được dạy rằng với allocationSize=1Hibernate trên mọi savethao tác cần thực hiện chuyến đi đến cơ sở dữ liệu để lấy giá trị ID mới.
G. Demecki

2
Đó là một tối ưu hóa vi mô trước khi nhận được truy vấn Hibernate cố gắng gán giá trị trong phạm vi allocationSizevà vì vậy hãy cố gắng tránh truy vấn cơ sở dữ liệu cho chuỗi. Nhưng truy vấn này sẽ được thực thi mọi lúc nếu bạn đặt nó thành 1. Điều này hầu như không tạo ra bất kỳ sự khác biệt nào vì nếu cơ sở dữ liệu của bạn được một số ứng dụng khác truy cập thì nó sẽ tạo ra vấn đề nếu cùng một id được sử dụng bởi một ứng dụng khác
Amit Deshpande

Và vâng, nó hoàn toàn là ứng dụng cụ thể cho dù kích thước phân bổ là 1 có bất kỳ tác động thực sự nào đến hiệu suất hay không. Tất nhiên, trong một tiêu chuẩn vi mô, nó sẽ luôn thể hiện là một tác động rất lớn; đó là vấn đề với hầu hết các điểm chuẩn (vi mô hoặc cách khác), chúng chỉ đơn giản là không thực tế. Và ngay cả khi chúng phức tạp đến mức hơi thực tế, bạn vẫn phải xem điểm chuẩn gần với ứng dụng thực tế của mình như thế nào để hiểu mức độ áp dụng của kết quả điểm chuẩn với kết quả bạn sẽ thấy trong ứng dụng của mình. Ngắn câu chuyện dài .. thử nghiệm nó cho chính mình
Steve Ebersole

2
ĐỒNG Ý. Mọi thứ đều là ứng dụng cụ thể, phải không! Trong trường hợp ứng dụng của bạn là ứng dụng chỉ đọc thì tác động của việc sử dụng kích thước phân bổ 1000 hoặc 1 là hoàn toàn bằng 0. Mặt khác, những thứ như thế này là phương pháp hay nhất. Nếu bạn không tôn trọng các phương pháp hay nhất mà họ thu thập được và tác động tổng hợp sẽ là ứng dụng của bạn trở nên chậm chạp. Một ví dụ khác là bắt đầu một giao dịch khi bạn hoàn toàn không cần đến.
Hasan Ceylan

1

Steve Ebersole và các thành viên khác,
Bạn có vui lòng giải thích lý do id có khoảng cách lớn hơn (theo mặc định là 50) không? Tôi đang sử dụng Hibernate 4.2.15 và tìm thấy mã sau trong băng 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++ );

Bất cứ khi nào nó chạm vào bên trong câu lệnh if, giá trị hi sẽ lớn hơn nhiều. Vì vậy, id của tôi trong quá trình thử nghiệm với khởi động lại máy chủ thường xuyên tạo ra các id trình tự sau:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.

Tôi biết bạn đã nói rằng nó không mâu thuẫn với thông số kỹ thuật, nhưng tôi tin rằng đây sẽ là tình huống rất bất ngờ đối với hầu hết các nhà phát triển.

Đầu vào của bất kỳ ai cũng sẽ hữu ích.

Jihwan

CẬP NHẬT: ne1410s: Cảm ơn bạn đã chỉnh sửa.
cfrick: Được rồi. Tôi sẽ làm điều đó. Đây là bài viết đầu tiên của tôi ở đây và tôi không chắc chắn về cách sử dụng nó.

Bây giờ, tôi đã hiểu rõ hơn tại sao maxLo được sử dụng cho hai mục đích: Vì hibernate gọi chuỗi DB một lần, hãy tiếp tục tăng id ở cấp Java và lưu nó vào DB, giá trị id cấp Java nên xem xét số lượng đã được thay đổi mà không cần gọi trình tự DB khi nó gọi trình tự lần sau.

Ví dụ: id trình tự là 1 tại một điểm và chế độ ngủ đông được nhập vào 5, 6, 7, 8, 9 (với phân bổ Kích thước = 5). Lần tới, khi chúng ta nhận được số thứ tự tiếp theo, DB trả về 2, nhưng chế độ ngủ đông cần sử dụng 10, 11, 12 ... Vì vậy, đó là lý do tại sao "hi = lastSourceValue.copy (). KernelBy (maxLo + 1)" là được sử dụng để lấy id 10 tiếp theo từ 2 được trả về từ chuỗi DB. Có vẻ như điều đáng lo ngại duy nhất là trong quá trình khởi động lại máy chủ thường xuyên và đây là vấn đề của tôi với khoảng cách lớn hơn.

Vì vậy, khi chúng ta sử dụng SEQUENCE ID, id được chèn vào bảng sẽ không khớp với số SEQUENCE trong DB.


1

Sau khi đào sâu vào mã nguồn ngủ đông và cấu hình dưới đây sẽ chuyển đến Oracle db cho giá trị tiếp theo sau 50 lần chèn. Vì vậy, hãy tăng INST_PK_SEQ của bạn lên 50 mỗi khi nó được gọi.

Hibernate 5 được sử dụng cho chiến lược bên dưới

Kiểm tra thêm bên dưới 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
Xin lỗi, nhưng đây là một cách cực kỳ dài dòng để thiết lập một cái gì đó, có thể được diễn đạt dễ dàng với hai tham số cho toàn bộ Hibernate và do đó cho tất cả các thực thể.
G. Demecki

đúng nhưng khi tôi cố gắng với những cách khác không ai trong số họ làm việc nếu bạn có nó làm việc có thể gửi cho tôi làm thế nào bạn đã cấu hình
Fatih Tekin

Tôi đã cập nhật câu trả lời của mình - bây giờ nó cũng bao gồm một ví dụ hoạt động. Mặc dù nhận xét của tôi ở trên là sai một phần: thật không may, bạn không thể đặt không allocationSizehoặc initialValuetoàn cục cho tất cả các thực thể (trừ khi chỉ sử dụng một trình tạo, nhưng IMHO nó không dễ đọc).
G. Demecki

1
Cảm ơn vì lời giải thích nhưng những gì bạn viết ở trên, tôi đã thử và nó không hoạt động với hibernate 5.0.7. Phiên bản cuối cùng sau đó tôi đã đào sâu vào mã nguồn để có thể đạt được mục tiêu này và đó là cách triển khai mà tôi có thể tìm thấy trong mã nguồn ngủ đông. Cấu hình có thể trông xấu nhưng điều đó là không may ngủ đông api và tôi đang sử dụng thực hiện EntityManager tiêu chuẩn ngủ đông của
Fatih Tekin

1

Tôi cũng phải đối mặt với vấn đề này trong Hibernate 5:

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

Có một cảnh báo như sau:

Đã tìm thấy việc sử dụng trình tạo id dựa trên trình tự [org.hibernate.id.SequenceHiLoGenerator] không dùng nữa; thay vào đó hãy sử dụng org.hibernate.id.enhanced.SequenceStyleGenerator. Xem Hướng dẫn lập bản đồ mô hình miền ngủ đông để biết chi tiết.

Sau đó, thay đổi mã của tôi thành 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;

Điều này đã giải quyết được hai vấn đề của tôi:

  1. Cảnh báo không dùng nữa đã được khắc phục
  2. Bây giờ id được tạo theo chuỗi oracle.

0

Tôi sẽ kiểm tra DDL cho trình tự trong lược đồ. Việc thực hiện JPA chỉ chịu trách nhiệm tạo chuỗi với kích thước phân bổ chính xác. Do đó, nếu kích thước phân bổ là 50 thì chuỗi của bạn phải có số gia là 50 trong DDL của nó.

Trường hợp này thường có thể xảy ra với việc tạo một chuỗi với kích thước phân bổ 1, sau đó được định cấu hình thành kích thước phân bổ 50 (hoặc mặc định) nhưng chuỗi DDL không được cập nhật.


Bạn đang hiểu sai quan điểm của tôi. ALTER SEQUENCE ... INCREMENTY BY 50;sẽ không giải quyết được bất cứ điều gì, bởi vì vấn đề vẫn như cũ. Giá trị chuỗi vẫn không phản ánh ID thực thể thực.
G. Demecki

Hãy chia sẻ một test case để chúng ta hiểu rõ hơn về vấn đề tại đây.
Hasan Ceylan

1
Trường hợp thử nghiệm? Tại sao? Câu hỏi do tôi đăng không quá phức tạp và đã được trả lời. Có vẻ như bạn chưa biết cách hoạt động của máy phát điện HiLo. Dù sao: cảm ơn bạn đã hy sinh thời gian và công sức của bạn.
G. Demecki

1
Gregory, Thực ra tôi biết tôi đang nói gì, tôi đã viết Batoo JPA là Triển khai JPA 100% hiện đang được ấp ủ và đánh bại Hibernate về tốc độ - nhanh hơn 15 lần. Mặt khác, tôi có thể đã hiểu sai câu hỏi của bạn và không nghĩ rằng việc sử dụng Hibernate với các chuỗi sẽ tạo ra bất kỳ vấn đề nào vì tôi đã sử dụng Hibernate từ năm 2003 trong nhiều dự án trên nhiều cơ sở dữ liệu. Điều quan trọng là bạn có giải pháp cho câu hỏi, xin lỗi tôi đã bỏ lỡ câu trả lời đánh dấu là đúng ...
Hasan Ceylan

Xin lỗi, tôi không cố ý xúc phạm bạn. Cảm ơn một lần nữa vì sự giúp đỡ của bạn, câu hỏi đã được trả lời.
G. Demecki
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.