Hibernate - Cập nhật hàng loạt trả về số hàng bất ngờ từ bản cập nhật: 0 số hàng thực tế: 0 dự kiến: 1


140

Tôi nhận được sau lỗi ngủ đông. Tôi có thể xác định chức năng gây ra vấn đề. Thật không may, có một số cuộc gọi DB trong chức năng. Tôi không thể tìm thấy dòng gây ra sự cố kể từ khi ngủ đông tuôn ra phiên vào cuối giao dịch. Các lỗi ngủ đông được đề cập dưới đây trông giống như một lỗi chung. Nó thậm chí không đề cập đến Bean nào gây ra vấn đề. Bất cứ ai cũng quen với lỗi ngủ đông này?

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
        at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransacti
onManager.java:500)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManag
er.java:473)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(Transaction
AspectSupport.java:267)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)

Cảm ơn bạn @Peter Mortensen. Tôi đã cập nhật email của tôi.
Sujee

Tôi có cùng một vấn đề. Đây không phải là một vấn đề lớn vì nó rất hiếm khi xảy ra. Sử dụng show_sql là không thực tế vì việc tái tạo hành vi này đòi hỏi hàng triệu giao dịch. Tuy nhiên, vì điều này xảy ra liên tục trong một thử nghiệm hệ thống mà tôi chạy (có ánh mắt giao dịch) tôi nghi ngờ rằng có một lý do cụ thể.
daniel_or_else

Tôi gặp phải vấn đề này trong khi tôi cố gắng cập nhật các hàng KHÔNG có thay đổi. Không cập nhật một cái gì đó nếu không có sự khác biệt.
fifman

Câu trả lời:


62

Nếu không có mã và ánh xạ cho các giao dịch của bạn, sẽ không thể điều tra được vấn đề.

Tuy nhiên, để có cách xử lý tốt hơn về nguyên nhân gây ra sự cố, hãy thử các cách sau:

  • Trong cấu hình ngủ đông của bạn, đặt hibernate.show_sql thành true. Điều này sẽ cho bạn thấy SQL được thực thi và gây ra vấn đề.
  • Đặt mức ghi nhật ký cho Spring và Hibernate thành DEBUG, một lần nữa điều này sẽ cho bạn ý tưởng tốt hơn về việc dòng nào gây ra sự cố.
  • Tạo một bài kiểm tra đơn vị sao chép vấn đề mà không cần cấu hình trình quản lý giao dịch trong Spring. Điều này sẽ cung cấp cho bạn một ý tưởng tốt hơn về dòng mã vi phạm.

Mong rằng sẽ giúp.


21
hibernate.show_sql> tôi muốn khuyên bạn nên đặt loại nhật ký org.hibernate.SQL ở mức DEBUG. Bằng cách này, bạn không cần phải sửa đổi cấu hình ngủ đông chỉ để đăng nhập.
Thierry

Việc sử dụng "show_sql" có thể rất dài dòng và không thể thực hiện được trong sản xuất. Để làm điều này, tôi đã sửa đổi mệnh đề bắt ở dòng 74 của lớp BatchingBatcher để in câu lệnh với ps.toString () để chỉ có câu lệnh có vấn đề.
Orden

71

Tôi đã có cùng một ngoại lệ trong khi xóa một bản ghi bằng Id hoàn toàn không tồn tại. Vì vậy, hãy kiểm tra bản ghi mà bạn đang cập nhật / Xóa thực sự tồn tại trong DB


12
Tôi gặp vấn đề này khi tôi đưa một đứa trẻ ra khỏi mối quan hệ cha-con, đã cứu cha mẹ (nó xóa đứa trẻ) và sau đó cố gắng xóa đứa trẻ bằng tay.
dave thieben

Điều này đã giải quyết vấn đề của tôi. Bản ghi không tồn tại và dịch vụ của tôi đã gọi phương thức updateAll () trong khi thực sự cần thiết để gọi phương thức createdOrUpdateAll (). Cảm ơn.
Mital Pritmani

3
Vậy làm thế nào để giải quyết vấn đề? Nếu tôi nhận được một bản ghi, và sau đó xóa nó; nhưng nếu hệ thống đã xóa nó trước khi tôi xóa, một ứng dụng khác, ứng dụng của tôi sẽ ném ngoại lệ.
Stony

@davethieben, đây chính xác là thông tin tôi cần. Tôi đã không nhận ra mối quan hệ cha-con vẫn tồn tại sau khi xóa.
tuyết rơi

Giải pháp là sử dụng select để cập nhật khi tìm nạp bản ghi để khóa hàng, vì vậy không có gì khác có thể xóa nó trước khi bạn làm.
Matthew đọc

54

Giải pháp: Trong tệp ánh xạ Hibernate cho thuộc tính id, nếu bạn sử dụng bất kỳ lớp trình tạo nào, cho thuộc tính đó, bạn không nên đặt giá trị rõ ràng bằng cách sử dụng phương thức setter.

Nếu bạn đặt giá trị của thuộc tính Id một cách rõ ràng, nó sẽ dẫn đến lỗi ở trên. Kiểm tra điều này để tránh lỗi này. hoặc Lỗi hiển thị khi bạn đề cập trong tệp ánh xạ, trình tạo trường = "gốc" hoặc "gia tăng" và trong DATABASE của bạn, bảng được ánh xạ không phải là auto_incremented Giải pháp: Chuyển đến DATABASE của bạn và cập nhật bảng của bạn để đặt auto_increment


2
Hoàn toàn đúng. Đây phải là câu trả lời đúng. Cảm ơn @ Rda Biramanē
Kuldeep Verma

1
Tuy nhiên , bạn CÓ THỂ đặt giá trị ID thành '0' và sau đó Hibernate sẽ thoải mái ghi đè lên nó
forresthopkinsa

1
Cảm ơn! Không chắc chắn tại sao đây không phải là câu trả lời được xếp hạng cao nhất.
Stuart McIntyre

18

Điều này đã xảy ra với tôi một lần tình cờ khi tôi đang gán ID cụ thể cho một số đối tượng (thử nghiệm) và sau đó tôi đang cố lưu chúng trong cơ sở dữ liệu. Vấn đề là trong cơ sở dữ liệu có một chính sách cụ thể để thiết lập ID của các đối tượng. Chỉ không chỉ định ID nếu bạn có chính sách ở cấp Hibernate.


15

Trong trường hợp của tôi, tôi đã đến ngoại lệ này trong hai trường hợp tương tự:

  • Trong một phương thức được chú thích với @Transactionaltôi đã có một cuộc gọi đến một dịch vụ khác (với thời gian đáp ứng lâu). Phương thức cập nhật một số thuộc tính của thực thể (sau phương thức, thực thể vẫn tồn tại trong cơ sở dữ liệu). Nếu người dùng yêu cầu hai lần phương thức (vì anh ta nghĩ rằng nó không hoạt động lần đầu tiên) khi thoát khỏi phương thức giao dịch lần thứ hai, Hibernate cố gắng cập nhật một thực thể đã thay đổi trạng thái từ đầu giao dịch. Khi Hibernate tìm kiếm một thực thể ở trạng thái và tìm thấy cùng một thực thể nhưng đã bị thay đổi bởi yêu cầu đầu tiên, nó sẽ đưa ra một ngoại lệ vì nó không thể cập nhật thực thể đó. Nó giống như một cuộc xung đột trong GIT.
  • Tôi đã có các yêu cầu tự động (để theo dõi nền tảng) cập nhật một thực thể (và khôi phục thủ công một vài giây sau đó). Nhưng nền tảng này đã được sử dụng bởi một nhóm thử nghiệm. Khi người kiểm tra thực hiện kiểm tra trong cùng một thực thể với các yêu cầu tự động, (trong cùng một phần trăm mili giây), tôi nhận được ngoại lệ. Như trong trường hợp trước, khi thoát khỏi giao dịch thứ hai, thực thể được tìm nạp trước đó đã thay đổi.

Kết luận: trong trường hợp của tôi, đó không phải là vấn đề có thể tìm thấy trong mã. Ngoại lệ này được đưa ra khi Hibernate phát hiện ra rằng thực thể được tìm nạp lần đầu tiên từ cơ sở dữ liệu đã thay đổi trong giao dịch hiện tại , do đó, nó không thể xóa nó vào cơ sở dữ liệu vì Hibernate không biết đó là phiên bản chính xác của thực thể: phiên bản hiện tại tìm nạp giao dịch khi bắt đầu; hoặc cái đã được lưu trữ trong cơ sở dữ liệu.

Giải pháp: để giải quyết vấn đề, bạn sẽ phải chơi với Hibernate LockMode để tìm ra cái phù hợp nhất với yêu cầu của bạn.


Xin chào, cảm ơn câu trả lời hữu ích của bạn. Bạn có thể vui lòng thông báo, LockMode mà bạn đã đi cuối cùng, để thoát khỏi lỗi. Tôi đang đối mặt với vấn đề tương tự, khi một API bị tấn công liên tiếp trong một nửa giây khác nhau, do người dùng vô tình nhấp hai lần. Khóa bi quan làm tổn thương hiệu suất; Vì vậy, sẽ được quan tâm để biết những gì bạn cuối cùng đã đi cho?
Madhur Bhaiya

1
Đó là một thời gian dài kể từ khi tôi gặp vấn đề và tôi không nhớ rõ giải pháp chính xác mà tôi đã đưa ra, nhưng đó là LockMode.READhoặc LockMode.OPTIMISTIC.
Sergio Lema

13

Tôi vừa gặp phải vấn đề này và phát hiện ra rằng tôi đang xóa một bản ghi và cố gắng cập nhật nó sau đó trong một giao dịch Hibernate.


9

Điều này có thể xảy ra khi (các) trình kích hoạt thực hiện các truy vấn DML (sửa đổi dữ liệu) bổ sung ảnh hưởng đến số lượng hàng. Giải pháp của tôi là thêm phần sau vào đầu kích hoạt của tôi:

SET NOCOUNT ON;

1
Câu trả lời này đã gửi cho tôi đi đúng hướng. Tôi đã làm việc với một cơ sở dữ liệu kế thừa có một trình kích hoạt trên đó - may mắn thay tôi đã thay thế những gì trình kích hoạt đã làm với mã, vì vậy tôi chỉ có thể xóa nó.
S. Baggy

2
+1 Đó cũng là vấn đề của tôi. Tôi đã sử dụng postgresql, vì vậy cần thiết để sử dụng các chú thích @SQLInsert để tắt kiểm tra số lượng hàng: technology-ebay.de/the-teams/mobile-de/blog/...
s1mm0t

TẮT NOCOUNT *
G. Ciardini 27/12/18

7

Tôi đã phải đối mặt với vấn đề tương tự. Mã đã làm việc trong môi trường thử nghiệm. Nhưng nó không hoạt động trong môi trường dàn dựng.

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 3; expected: 1

Vấn đề là bảng có một mục nhập duy nhất cho mỗi khóa chính trong việc kiểm tra bảng DB. Nhưng trong dàn DB có nhiều mục nhập cho cùng một khóa chính. (Vấn đề là trong việc sắp xếp DB, bảng không có bất kỳ ràng buộc khóa chính nào cũng có nhiều mục nhập.)

Vì vậy, mỗi lần thao tác cập nhật đều bị lỗi. Nó cố gắng cập nhật một bản ghi và mong muốn có được số cập nhật là 1. Nhưng vì có 3 bản ghi trong cùng một khóa chính, nên số lượng cập nhật kết quả tìm thấy 3. Vì số lượng cập nhật dự kiến ​​và số lượng cập nhật kết quả thực tế không khớp , Nó ném ngoại lệ và cuộn lại.

Sau khi tôi loại bỏ tất cả các bản ghi có khóa chính trùng lặp và thêm các ràng buộc khóa chính. Nó đang hoạt động tốt.

Hibernate - Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

số hàng thực tế: 0 // có nghĩa là không tìm thấy bản ghi để cập
nhật cập nhật: 0 // có nghĩa là không tìm thấy bản ghi nên không có gì cập nhật
mong đợi: 1 // có nghĩa là dự kiến ​​ít nhất 1 bản ghi có khóa trong bảng db.

Vấn đề ở đây là truy vấn đang cố cập nhật một bản ghi cho một số khóa, nhưng ngủ đông không tìm thấy bất kỳ bản ghi nào với khóa đó.


Xin chào @ParagFlume, tôi đã gặp lỗi tương tự "Cập nhật hàng loạt trả về số hàng không mong đợi từ bản cập nhật: 0 số hàng thực tế: 0 dự kiến: 1" khi tôi cố gắng chọn một đối tượng từ cơ sở dữ liệu của mình, vấn đề chỉ tồn tại trong bất kỳ sản phẩm nào, bạn có bất kỳ sản phẩm nào không ý tưởng về cách giải quyết vấn đề này, tôi đang ở trong một tình huống nguy cấp. Trân trọng !
James

Tôi nghĩ rằng truy vấn không tìm thấy bất kỳ bản ghi nào trong db, nó đang mong đợi tìm thấy một bản ghi trong db mà nó đang cố cập nhật. bạn có thể kiểm tra thủ công / trình duyệt truy vấn nếu bản ghi thực sự tồn tại trên db.
ParagFlume

có bản ghi tồn tại, nhưng vấn đề của tôi là tại sao ngủ đông cố gắng cập nhật, tôi chỉ đang sử dụng một dịch vụ chọn để lấy đối tượng từ cơ sở dữ liệu!
James

có thể bạn đang cố gắng thay đổi trạng thái của các đối tượng. và phiên vẫn mở.
ParagFlume

Làm thế nào tôi có thể kiểm tra hành vi này, bởi vì tôi không phải là người phát triển dịch vụ ...
James

5

Như Julius nói điều này xảy ra khi một bản cập nhật Xảy ra trên một vật thể có con của nó bị xóa. . Các trường đơn giản khác của nó) Vì vậy, ... để làm việc này, hãy xóa các con (trong Giao dịch) bằng cách gọi childrenList.clear()(Không lặp qua các con và xóa từng con với một số childDAO.delete(childrenList.get(i).delete()))và đặt @OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)Về phía đối tượng Cha. Sau đó cập nhật cha (chaDAO.update (cha)). (Lặp lại cho mọi đối tượng người cha) Kết quả là trẻ em có liên kết với cha mình bị tước bỏ và sau đó chúng bị loại bỏ như trẻ mồ côi theo khuôn khổ.


5

Tôi gặp phải vấn đề này khi chúng tôi có mối quan hệ một-nhiều.

Trong tệp ánh xạ hbm hibernate cho master, cho đối tượng có sắp xếp kiểu thiết lập, được thêm vào cascade="save-update"và nó hoạt động tốt.

Không có điều này, theo mặc định, ngủ đông cố gắng cập nhật cho một bản ghi không tồn tại và bằng cách đó, nó sẽ chèn vào.


4

Nó cũng có thể xảy ra khi bạn cố gắng vào UPDATEmột KHÓA CHÍNH .


3

tôi gặp vấn đề tương tự và tôi đã xác minh điều này có thể xảy ra do khóa chính tăng tự động. Để giải quyết vấn đề này, không chèn giá trị tăng tự động với tập dữ liệu. Chèn dữ liệu mà không cần khóa chính.


3

Một cách khác để có được lỗi này là nếu bạn có một mục null trong bộ sưu tập.


2

Nó xảy ra khi bạn cố gắng xóa cùng một đối tượng và sau đó cập nhật lại cùng một đối tượng sử dụng điều này sau khi xóa

phiên.clear ();


2

Điều này cũng xảy ra với tôi, vì tôi có id là Long và tôi đã nhận được từ chế độ xem giá trị 0 và khi tôi cố lưu trong cơ sở dữ liệu, tôi đã gặp lỗi này, sau đó tôi đã sửa nó bằng cách đặt id thành null.


1

Tôi gặp phải vấn đề này khi tôi bắt đầu thủ công và thực hiện các giao dịch bên trong phương thức được chú thích là @Transactional. Tôi đã khắc phục sự cố bằng cách phát hiện nếu một giao dịch đang hoạt động đã tồn tại.

//Detect underlying transaction
if (session.getTransaction() != null && session.getTransaction().isActive()) {
    myTransaction = session.getTransaction();
    preExistingTransaction = true;
} else {
    myTransaction = session.beginTransaction();
}

Sau đó, tôi cho phép Spring xử lý giao dịch.

private void finishTransaction() {
    if (!preExistingTransaction) {
        try {
            tx.commit();
        } catch (HibernateException he) {
            if (tx != null) {
                tx.rollback();
            }
            log.error(he);
        } finally {
            if (newSessionOpened) {
                SessionFactoryUtils.closeSession(session);
                newSessionOpened = false;
                maxResults = 0;
            }
        }
    }
}

1

Điều này xảy ra khi bạn khai báo Bean Managed của JSF là

@RequestScoped;

khi nào bạn nên khai báo là

@SessionScoped;

Trân trọng;


1

Tôi đã gặp lỗi này khi tôi cố cập nhật một đối tượng với id không tồn tại trong cơ sở dữ liệu. Lý do cho lỗi của tôi là tôi đã gán thủ công một thuộc tính có tên 'id' cho biểu diễn JSON của phía máy khách và sau đó khi khử lưu trữ đối tượng trên phía máy chủ, thuộc tính 'id' này sẽ ghi đè lên biến đối tượng ( cũng được gọi là 'id') mà Hibernate được cho là sẽ tạo ra. Vì vậy, hãy cẩn thận khi đặt tên va chạm nếu bạn đang sử dụng Hibernate để tạo định danh.


1

Tôi cũng đã gặp thử thách tương tự. Trong trường hợp của tôi, tôi đã cập nhật một đối tượng thậm chí không tồn tại, sử dụnghibernateTemplate .

Trên thực tế, trong ứng dụng của mình, tôi đã nhận được một đối tượng DB để cập nhật. Và trong khi cập nhật các giá trị của nó, tôi cũng đã cập nhật ID của nó do nhầm lẫn, và tiếp tục cập nhật nó và gặp phải lỗi đã nói.

Tôi đang sử dụng hibernateTemplatecho các hoạt động CRUD.


1

Sau khi đọc tất cả các câu trả lời, làmtntt tìm bất cứ ai để nói về tính chất nghịch đảo của hibernate.

Theo tôi, bạn cũng nên xác minh trong bản đồ mối quan hệ của mình xem từ khóa ngược có được giải quyết một cách thích hợp hay không. Nghịch đảoTừ khóa được tạo để xác định bên nào là chủ sở hữu để duy trì mối quan hệ. Quy trình cập nhật và chèn thay đổi tùy theo thuộc tính này.

Giả sử chúng ta có hai bảng:

hiệu trưởng , trung bình

với mối quan hệ từ một đến nhiều . Các lớp ánh xạ ngủ đông lần lượt là Hiệu trưởngTrung .

Vì vậy, lớp Hiệu trưởng có một bộ các đối tượng ở giữa . Tệp ánh xạ xml phải như sau:

<hibernate-mapping>
    <class name="path.to.class.Principal" table="principal_table" ...>
    ...
    <set name="middleObjects" table="middle_table" inverse="true" fetch="select">
        <key>
            <column name="PRINCIPAL_ID" not-null="true" />
        </key>
        <one-to-many class="path.to.class.Middel" />
    </set>
    ...

Vì nghịch đảo được đặt thành Trực tuyến đúng, có nghĩa là lớp Middle Middle là chủ sở hữu mối quan hệ, vì vậy lớp Hiệu trưởng sẽ KHÔNG CẬP NHẬT mối quan hệ.

Vì vậy, thủ tục cập nhật có thể được thực hiện như thế này:

session.beginTransaction();

Principal principal = new Principal();
principal.setSomething("1");
principal.setSomethingElse("2");


Middle middleObject = new Middle();
middleObject.setSomething("1");

middleObject.setPrincipal(principal);
principal.getMiddleObjects().add(middleObject);

session.saveOrUpdate(principal);
session.saveOrUpdate(middleObject); // NOTICE: you will need to save it manually

session.getTransaction().commit();

Điều này làm việc cho tôi, bạn có thể đề xuất một số phiên bản để cải thiện giải pháp. Bằng cách đó tất cả chúng ta sẽ được học.


1

Trong trường hợp của chúng tôi, cuối cùng chúng tôi đã tìm ra nguyên nhân gốc rễ của StaleStateException.

Trong thực tế, chúng tôi đã xóa hàng hai lần trong một phiên ngủ đông. Trước đó chúng tôi đã sử dụng lib ojdbc6 và điều này là ổn trong phiên bản này.

Nhưng khi chúng tôi nâng cấp lên odjc7 hoặc ojdbc8, việc xóa các bản ghi hai lần là một ngoại lệ. Có lỗi trong mã của chúng tôi, nơi chúng tôi đã xóa hai lần, nhưng điều đó không rõ ràng trong ojdbc6.

Chúng tôi đã có thể sao chép với đoạn mã này:

Detail detail = getDetail(Long.valueOf(1396451));
session.delete(detail);
session.flush();
session.delete(detail);
session.flush();

Trên đầu tiên hibernate tuôn ra và thay đổi cơ sở dữ liệu. Trong lần quét thứ hai, hibernate so sánh đối tượng của phiên với bản ghi của bảng thực tế, nhưng không thể tìm thấy, do đó, ngoại lệ.


1

Vấn đề này chủ yếu xảy ra khi chúng ta đang cố lưu hoặc cập nhật đối tượng đã được nạp vào bộ nhớ bằng một phiên chạy. Nếu bạn đã tìm nạp đối tượng từ phiên và bạn đang cố cập nhật trong cơ sở dữ liệu, thì ngoại lệ này có thể bị ném.

Tôi đã sử dụng session.evict (); để xóa bộ nhớ cache được lưu trữ trong chế độ ngủ đông trước hoặc nếu bạn không muốn có nguy cơ mất dữ liệu, tốt hơn bạn nên tạo một đối tượng khác để lưu trữ dữ liệu tạm thời.

     try
    {
        if(!session.isOpen())
        {
            session=EmployeyDao.getSessionFactory().openSession();
        }
            tx=session.beginTransaction();

        session.evict(e);
        session.saveOrUpdate(e);
        tx.commit();;
        EmployeyDao.shutDown(session);
    }
    catch(HibernateException exc)
    {
        exc.printStackTrace();
        tx.rollback();
    }

1

Vấn đề Hibernate 5.4.1 và HHH-12878

Trước Hibernate 5.4.1, các ngoại lệ lỗi khóa lạc quan (ví dụ: StaleStateExceptionhoặc OptimisticLockException) không bao gồm tuyên bố lỗi.

Vấn đề HHH-12878 được tạo ra để cải thiện Hibernate để khi ném ngoại lệ khóa lạc quan, việc PreparedStatementtriển khai JDBC cũng được ghi lại:

if ( expectedRowCount > rowCount ) {
    throw new StaleStateException(
            "Batch update returned unexpected row count from update ["
                    + batchPosition + "]; actual row count: " + rowCount
                    + "; expected: " + expectedRowCount + "; statement executed: "
                    + statement
    );
}

Thời gian thử nghiệm

Tôi đã tạo ra BatchingOptimisticLockingTestkho lưu trữ GitHub Java hiệu suất cao của tôi để chứng minh cách thức hoạt động mới.

Đầu tiên, chúng tôi sẽ xác định một Postthực thể xác định một thuộc @Versiontính, do đó cho phép cơ chế khóa tối ưu ngầm :

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Version
    private short version;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public short getVersion() {
        return version;
    }
}

Chúng tôi sẽ kích hoạt tính năng tạo khối JDBC bằng 3 thuộc tính cấu hình sau:

properties.put("hibernate.jdbc.batch_size", "5");
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");

Chúng tôi sẽ tạo ra 3 Postthực thể:

doInJPA(entityManager -> {
    for (int i = 1; i <= 3; i++) {
        entityManager.persist(
            new Post()
                .setTitle(String.format("Post no. %d", i))
        );
    }
});

Và Hibernate sẽ thực thi một đợt chèn JDBC:

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

Query: [
    INSERT INTO post (title, version, id) 
    VALUES (?, ?, ?)
], 
Params:[
    (Post no. 1, 0, 1), 
    (Post no. 2, 0, 2), 
    (Post no. 3, 0, 3)
]

Vì vậy, chúng ta biết rằng đợt JDBC hoạt động tốt.

Bây giờ, hãy nhân rộng vấn đề khóa lạc quan:

doInJPA(entityManager -> {
    List<Post> posts = entityManager.createQuery("""
        select p 
        from Post p
        """, Post.class)
    .getResultList();

    posts.forEach(
        post -> post.setTitle(
            post.getTitle() + " - 2nd edition"
        )
    );

    executeSync(
        () -> doInJPA(_entityManager -> {
            Post post = _entityManager.createQuery("""
                select p 
                from Post p
                order by p.id
                """, Post.class)
            .setMaxResults(1)
            .getSingleResult();

            post.setTitle(post.getTitle() + " - corrected");
        })
    );
});

Giao dịch đầu tiên chọn tất cả các Postthực thể và sửa đổi các titlethuộc tính.

Tuy nhiên, trước khi lần đầu tiên EntityManagerđược xóa, chúng ta sẽ thực hiện chuyển đổi thứ hai bằng executeSyncphương thức.

Giao dịch thứ hai sửa đổi giao dịch đầu tiên Post, vì vậy nó versionsẽ được tăng lên:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - corrected', 1, 1, 0)
]

Bây giờ, khi giao dịch đầu tiên cố gắng thực hiện EntityManager, chúng ta sẽ nhận được OptimisticLockException:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - 2nd edition', 1, 1, 0), 
    ('Post no. 2 - 2nd edition', 1, 2, 0), 
    ('Post no. 3 - 2nd edition', 1, 3, 0)
]

o.h.e.j.b.i.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements

o.h.e.j.b.i.BatchingBatch - HHH000315: Exception executing batch [
    org.hibernate.StaleStateException: 
    Batch update returned unexpected row count from update [0]; 
    actual row count: 0; 
    expected: 1; 
    statement executed: 
        PgPreparedStatement [
            update post set title='Post no. 3 - 2nd edition', version=1 where id=3 and version=0
        ]
], 
SQL: update post set title=?, version=? where id=? and version=?

Vì vậy, bạn cần nâng cấp lên Hibernate 5.4.1 hoặc mới hơn để hưởng lợi từ cải tiến này.


0

Điều này xảy ra nếu bạn thay đổi một cái gì đó trong tập dữ liệu bằng cách sử dụng truy vấn sql gốc nhưng đối tượng vẫn tồn tại cho cùng một tập dữ liệu có trong bộ đệm của phiên. Sử dụng session.evict (yourObject);



0

Một trong những trường hợp

SessionFactory sf=new Configuration().configure().buildSessionFactory();
Session session=sf.openSession();

UserDetails user=new UserDetails();

session.beginTransaction();
user.setUserName("update user agian");
user.setUserId(12);
session.saveOrUpdate(user);
session.getTransaction().commit();
System.out.println("user::"+user.getUserName());

sf.close();

0

Tôi đã phải đối mặt với ngoại lệ này, và ngủ đông đã hoạt động tốt. Tôi đã cố gắng chèn thủ công một bản ghi bằng pgAdmin, ở đây vấn đề đã trở nên rõ ràng. Truy vấn chèn SQL trả về 0 insert. và có một chức năng kích hoạt gây ra vấn đề này bởi vì nó trả về null. Vì vậy, tôi chỉ phải thiết lập nó để trở lại mới. và cuối cùng tôi đã giải quyết được vấn đề.

Hy vọng rằng sẽ giúp bất kỳ cơ thể.


0

Tôi đã gặp lỗi này vì tôi đã ánh xạ nhầm IDcột bằng cách sử dụngId(x => x.Id, "id").GeneratedBy.**Assigned**();

Vấn đề được giải quyết bằng cách sử dụng Id(x => x.Id, "id").GeneratedBy.**Identity**();


0

Điều này xảy ra với tôi vì tôi thiếu khai báo ID trong lớp bean.


0

Trong trường hợp của tôi, có một vấn đề với Cơ sở dữ liệu vì một trong các Procs được lưu trữ đã tiêu thụ tất cả CPU gây ra thời gian phản hồi DB cao. Một khi điều này đã bị giết vấn đề đã được giải quyết.

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.