Điều này đã được chính tôi trả lời nhanh chóng ở đây trong bài đăng này, nhưng ẩn thực tế là chúng tôi đã dành hơn hai tuần để thử các chiến lược khác nhau để khắc phục điều này. Vì vậy, đây là cách triển khai cuối cùng mà chúng tôi quyết định sử dụng.
Ý tưởng cơ bản: Tạo triển khai javax.persistence.spi.PersistenceProvider của riêng bạn bằng cách mở rộng cái được cung cấp bởi Hibernate. Đối với tất cả các hiệu ứng, đây là điểm duy nhất mà mã của bạn sẽ được gắn với Hibernate hoặc bất kỳ triển khai cụ thể của nhà cung cấp nào khác.
public class MyHibernatePersistenceProvider extends org.hibernate.jpa.HibernatePersistenceProvider {
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
return new EntityManagerFactoryWrapper(super.createContainerEntityManagerFactory(info, properties));
}
}
Ý tưởng là gói các phiên bản ngủ đông của EntityManagerFactory và EntityManager với sự triển khai của riêng bạn. Vì vậy, bạn cần tạo các lớp triển khai các giao diện này và giữ bên trong việc triển khai cụ thể của nhà cung cấp.
Đây là EntityManagerFactoryWrapper
public class EntityManagerFactoryWrapper implements EntityManagerFactory {
private EntityManagerFactory emf;
public EntityManagerFactoryWrapper(EntityManagerFactory originalEMF) {
emf = originalEMF;
}
public EntityManager createEntityManager() {
return new EntityManagerWrapper(emf.createEntityManager());
}
// Implement all other methods for the interface
// providing a callback to the original emf.
EntityManagerWrapper là điểm đánh chặn của chúng tôi. Bạn sẽ cần phải thực hiện tất cả các phương pháp từ giao diện. Tại mọi phương thức mà một thực thể có thể được sửa đổi, chúng tôi bao gồm một lệnh gọi đến một truy vấn tùy chỉnh để đặt các biến cục bộ tại cơ sở dữ liệu.
public class EntityManagerWrapper implements EntityManager {
private EntityManager em;
private Principal principal;
public EntityManagerWrapper(EntityManager originalEM) {
em = originalEM;
}
public void setAuditVariables() {
String userid = getUserId();
String ipaddr = getUserAddr();
String sql = "SET LOCAL application.userid='"+userid+"'; SET LOCAL application.ipaddr='"+ipaddr+"'";
em.createNativeQuery(sql).executeUpdate();
}
protected String getUserAddr() {
HttpServletRequest httprequest = CDIBeanUtils.getBean(HttpServletRequest.class);
String ipaddr = "";
if ( httprequest != null ) {
ipaddr = httprequest.getRemoteAddr();
}
return ipaddr;
}
protected String getUserId() {
String userid = "";
// Try to look up a contextual reference
if ( principal == null ) {
principal = CDIBeanUtils.getBean(Principal.class);
}
// Try to assert it from CAS authentication
if (principal == null || "anonymous".equalsIgnoreCase(principal.getName())) {
if (AssertionHolder.getAssertion() != null) {
principal = AssertionHolder.getAssertion().getPrincipal();
}
}
if ( principal != null ) {
userid = principal.getName();
}
return userid;
}
@Override
public void persist(Object entity) {
if ( em.isJoinedToTransaction() ) {
setAuditVariables();
}
em.persist(entity);
}
@Override
public <T> T merge(T entity) {
if ( em.isJoinedToTransaction() ) {
setAuditVariables();
}
return em.merge(entity);
}
@Override
public void remove(Object entity) {
if ( em.isJoinedToTransaction() ) {
setAuditVariables();
}
em.remove(entity);
}
// Keep implementing all methods that can change
// entities so you can setAuditVariables() before
// the changes are applied.
@Override
public void createNamedQuery(.....
Giảm: Các truy vấn chặn (SET LOCAL) có thể sẽ chạy nhiều lần bên trong một giao dịch đơn lẻ, đặc biệt nếu có một số câu lệnh được thực hiện trên một lệnh gọi dịch vụ. Với tình hình hoàn cảnh, chúng tôi quyết định giữ nó theo cách này do thực tế là nó là một SET LOCAL đơn giản trong lệnh gọi bộ nhớ tới PostgreSQL. Vì không có bàn nào tham gia, chúng tôi có thể sống với thành tích hiệu suất.
Bây giờ chỉ cần thay thế nhà cung cấp tính bền vững của Hibernate bên trong Persence.xml :
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="petstore" transaction-type="JTA">
<provider>my.package.HibernatePersistenceProvider</provider>
<jta-data-source>java:app/jdbc/exemplo</jta-data-source>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" />
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
</properties>
</persistence-unit>
Lưu ý thêm, đây là CDIBeanUtils mà chúng tôi phải trợ giúp với người quản lý bean trong một số dịp đặc biệt. Trong trường hợp này, chúng tôi đang sử dụng nó để tra cứu tham chiếu đến HttpServletRequest và Hiệu trưởng.
public class CDIBeanUtils {
public static <T> T getBean(Class<T> beanClass) {
BeanManager bm = CDI.current().getBeanManager();
Iterator<Bean<?>> ite = bm.getBeans(beanClass).iterator();
if (!ite.hasNext()) {
return null;
}
final Bean<T> bean = (Bean<T>) ite.next();
final CreationalContext<T> ctx = bm.createCreationalContext(bean);
final T t = (T) bm.getReference(bean, beanClass, ctx);
return t;
}
}
Công bằng mà nói, điều này không chính xác chặn các sự kiện Giao dịch. Nhưng chúng tôi có thể bao gồm các truy vấn tùy chỉnh mà chúng tôi cần bên trong giao dịch.
Hy vọng rằng điều này có thể giúp những người khác tránh được nỗi đau mà chúng tôi đã trải qua.