- vừa được xem lúc

Auto-Generated Id với JPA

0 0 12

Người đăng: Zero

Theo Viblo Asia

Intro

hướng dẫn này nói về cách sử lý auto-generated id với JPA. có 2 khái niệm cần hiểu trước khi xem ví dụ ở dưới gồm: vòng đời và chiến lượt sinh id (id generation strategy).

Vòng đời và chiến lược sinh id

Mỗi entity có các trạng thái sau trong vòng đời của nó gồm: new, managed, detachedremoved. Chúng ta chỉ quan tâm đến newmanaged. Trong suốt quá trình tạo đối tượng, thực thể ở trạng thái new..

Do đó, EntityManager không biết về đối tượng này. Gọi phương thức persist trên EntityManager, đối tượng sẽ chuyển từ trạng thái new sang trạng thái managed. Phương pháp này yêu cầu một transaction hoạt động.

JPA định nghĩa bốn chiến lược để tạo id. Chúng ta có thể nhóm bốn chiến lược này thành hai loại:

  • Id được phân bổ trước và có sẵn cho EntityManager trước khi xác nhận

  • Id được phân bổ sau khi cam kết giao dịch Để biết thêm chi tiết về từng chiến lược tạo id, hãy tham khảo bài viết When Does JPA Set the Primary Key.

Vấn đề

Trả về id của một đối tượng có thể trở thành một nhiệm vụ nặng nề. Chúng ta cần hiểu các nguyên tắc được đề cập ở phần trước để tránh các vấn đề. Tùy thuộc vào cấu hình JPA, các dịch vụ có thể trả về các đối tượng có id bằng 0 (hoặc null). Trọng tâm sẽ là việc triển khai lớp Service và cách các sửa đổi khác nhau có thể cung cấp cho chúng ta giải pháp.

Tạo một mô-đun Maven với đặc tả JPA và Hibernate. Để đơn giản, tôi sẽ sử dụng H2 in-memory database.

Hãy bắt đầu bằng cách tạo một domain entity và ánh xạ nó vào bảng cơ sở dữ liệu. Trong ví dụ này, tôi sẽ tạo một thực thể User với một số thuộc tính cơ bản:

@Entity
public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String username; private String password; //...
}

Sau domain class, chúng ta sẽ tạo lớp UserService. Lớp này sẽ có một tham chiếu đến EntityManager và một phương thức để lưu các đối tượng User vào cơ sở dữ liệu:

public class UserService { EntityManager entityManager; public UserService(EntityManager entityManager) { this.entityManager = entityManager; } @Transactional public long saveUser(User user){ entityManager.persist(user); return user.getId(); }
}

Thiết lập này là một lỗi phổ biến đã đề cập trước đây. Chúng ta có thể chứng minh rằng giá trị trả về của phương thức saveUser bằng 0 bằng một bài test.

@Test
public void whenNewUserIsPersisted_thenEntityHasNoId() { User user = new User(); user.setUsername("test"); user.setPassword(UUID.randomUUID().toString()); long index = service.saveUser(user); Assert.assertEquals(0L, index);
}

Tiếp theo sẽ tiều hiểu tại sao nó lại xảy ra và cách xử lý nó.

Điều khiển Transaction thủ công

Sau khi tạo đối tượng, thực thể User của chúng ta ở trạng thái mới. Trạng thái thực thể thay đổi thành được quản lý sau lệnh gọi phương thức persist (là phương thức lưu entity vào) trong phương thức saveUser. Chúng tôi nhớ từ phần tóm tắt rằng đối tượng được quản lý sẽ nhận được id sau khi thực hiện transaction. Vì phương thức saveUser vẫn đang chạy nên giao dịch được tạo bởi chú thích @Transactional vẫn chưa được commit. Thực thể được quản lý của chúng tôi nhận được id khi saveUser kết thúc quá trình thực thi.

Một giải pháp khả thi là gọi phương thức flush trên EntityManager theo cách thủ công. Mặt khác, chúng tôi có thể kiểm soát các giao dịch theo cách thủ công và đảm bảo rằng phương thức của chúng tôi trả về id chính xác. Chúng ta có thể làm điều này với EntityManager:

@Test
public void whenTransactionIsControlled_thenEntityHasId() { User user = new User(); user.setUsername("test"); user.setPassword(UUID.randomUUID().toString()); entityManager.getTransaction().begin(); long index = service.saveUser(user); entityManager.getTransaction().commit(); Assert.assertEquals(2L, index);
}

Sử dụng chiến lược sinh id

Cho đến thời điểm hiện tại, tôi đã sử dụng loại thứ hai, trong đó việc phân bổ id xảy ra sau khi commit transaction. Các chiến lược phân bổ trước có thể cung cấp cho chúng ta id trước khi thực hiện giao dịch vì chúng giữ một số id trong bộ nhớ. Tùy chọn này không phải lúc nào cũng có thể triển khai được vì không phải tất cả các công cụ cơ sở dữ liệu đều hỗ trợ tất cả các chiến lược tạo. Việc thay đổi chiến lược thành GenerationType.SEQUENCE có thể giải quyết vấn đề của chúng tôi. Chiến lược này sử dụng chuỗi cơ sở dữ liệu thay vì cột tăng tự động như trong GenerationType.IDENTITY.

Để thay đổi chiến lược, chúng tôi chỉnh sửa lớp domain entity của mình:

@Entity
public class User { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private long id; //...
}

Conclusion

Trong bài viết này đã đề cập đến các kỹ thuật tạo id trong JPA. source ở Github

Nguồn tham khảo

Bài viết này được dịch và viết lại dựa trên bài na Returning an Auto-Generated Id with JPA

Bình luận

Bài viết tương tự

- vừa được xem lúc

Cách tốt nhất để ánh xa mối quan hệ @OneToMany với JPA và Hibernate

1. Giới thiệu.

0 0 66

- vừa được xem lúc

Lỗi Multi query trong JPA Và Cách Xử Nó

1. Giới Thiệu Vấn Đề.

0 0 60

- vừa được xem lúc

Dùng hibernate đã lâu? Thế bạn có biết JPA là gì?

JPA là gì. JPA có thể xem như cầu nối giữa Java object và cơ sở dữ liệu quan hệ.

0 0 30

- vừa được xem lúc

Tránh select khi insert entity trong JPA với Proxy entity

Hello, mình đã comeback đây. Trước giờ mình hay viết bài về Java và Spring Boot, nay có chủ đề này hay quá nên đá qua JPA một tí.

0 0 34

- vừa được xem lúc

Quản lý Transaction với Spring và JPA

Spring và Spring Data hỗ trợ việc quản lý transaction khiến cho việc này cực kỳ đơn giản, tất cả những gì chúng ta cần làm là chú thích một class hay một method với @Transactional annotation. Nhưng th

0 0 28

- vừa được xem lúc

Vấn đề N+1 queries

1. Vấn đề N+1 queries là gì.

0 0 11