Như thế nào synchronized hoạt động của từ khóa Java
Khi bạn thêm synchronized từ khóa vào một phương thức tĩnh, phương thức chỉ có thể được gọi bởi một luồng duy nhất tại một thời điểm.
Trong trường hợp của bạn, mọi cuộc gọi phương thức sẽ:
- tạo một cái mới
SessionFactory
- tạo một cái mới
Session
- lấy thực thể
- trả lại thực thể cho người gọi
Tuy nhiên, đây là những yêu cầu của bạn:
- Tôi muốn điều này để ngăn truy cập thông tin vào cùng một thể hiện DB.
- ngăn chặn
getObjectByIdđược gọi cho tất cả các lớp khi nó được gọi bởi một lớp cụ thể
Vì vậy, ngay cả khi getObjectByIdphương thức này là an toàn luồng, việc thực hiện là sai.
SessionFactory thực hành tốt nhất
Nó SessionFactorylà an toàn cho luồng và nó là một đối tượng rất tốn kém để tạo vì nó cần phân tích các lớp thực thể và xây dựng biểu diễn siêu mô hình thực thể bên trong.
Vì vậy, bạn không nên tạo cuộc gọi SessionFactorytrên mọi getObjectByIdphương thức.
Thay vào đó, bạn nên tạo một ví dụ singleton cho nó.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Các Sessionnên luôn luôn được đóng lại
Bạn đã không đóng Sessiontrong một finallykhối và điều này có thể rò rỉ tài nguyên cơ sở dữ liệu nếu một ngoại lệ được ném khi tải thực thể.
Theo Session.loadphương thức JavaDoc có thể đưa ra một HibernateExceptionthực thể nếu không thể tìm thấy thực thể trong cơ sở dữ liệu.
Bạn không nên sử dụng phương pháp này để xác định xem có tồn tại một thể hiện hay không (sử dụng get()thay thế). Chỉ sử dụng điều này để truy xuất một thể hiện mà bạn cho là tồn tại, trong đó sự không tồn tại sẽ là một lỗi thực tế.
Đó là lý do tại sao bạn cần sử dụng một finallykhối để đóng Session, như thế này:
public static synchronized Object getObjectById (Class objclass, Long id) {
Session session = null;
try {
session = sessionFactory.openSession();
return session.load(objclass, id);
} finally {
if(session != null) {
session.close();
}
}
}
Ngăn chặn truy cập đa luồng
Trong trường hợp của bạn, bạn muốn đảm bảo chỉ có một luồng được truy cập vào thực thể cụ thể đó.
Nhưng synchronizedtừ khóa chỉ ngăn hai chủ đề gọigetObjectById đồng thời. Nếu hai luồng gọi phương thức này lần lượt theo cách khác, bạn vẫn sẽ có hai luồng sử dụng thực thể này.
Vì vậy, nếu bạn muốn khóa một đối tượng cơ sở dữ liệu nhất định để không có luồng nào khác có thể sửa đổi nó, thì bạn cần sử dụng khóa cơ sở dữ liệu.
Các synchronizedtừ khóa chỉ hoạt động trong một JVM duy nhất. Nếu bạn có nhiều nút web, điều này sẽ không ngăn truy cập đa luồng trên nhiều JVM.
Những gì bạn cần làm là sử dụng LockModeType.PESSIMISTIC_READhoặcLockModeType.PESSIMISTIC_WRITE trong khi áp dụng các thay đổi cho DB, như thế này:
Session session = null;
EntityTransaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.getTransaction();
tx.begin();
Post post = session.find(
Post.class,
id,
LockModeType.LockModeType.PESSIMISTIC_READ
);
post.setTitle("High-Performance Java Perisstence");
tx.commit();
} catch(Exception e) {
LOGGER.error("Post entity could not be changed", e);
if(tx != null) {
tx.rollback();
}
} finally {
if(session != null) {
session.close();
}
}
Vì vậy, đây là những gì tôi đã làm:
- Tôi đã tạo một cái mới
EntityTransaction giao dịch và bắt đầu một giao dịch cơ sở dữ liệu mới
- Tôi đã tải
Post thực thể trong khi giữ một khóa trên bản ghi cơ sở dữ liệu liên quan
- Tôi đã thay đổi
Post thực thể và cam kết giao dịch
- Trong trường hợp
Exceptionbị ném, tôi quay lại giao dịch
Để biết thêm chi tiết về ACID và giao dịch cơ sở dữ liệu, hãy xem bài viết này .