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 getObjectById
phươ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ó SessionFactory
là 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 SessionFactory
trên mọi getObjectById
phươ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 Session
nên luôn luôn được đóng lại
Bạn đã không đóng Session
trong một finally
khố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.load
phương thức JavaDoc có thể đưa ra một HibernateException
thự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 finally
khố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 synchronized
từ 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 synchronized
từ 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_READ
hoặ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
Exception
bị 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 .