PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana saya bisa mencegat peristiwa transaksi JTA dan mendapatkan referensi ke EntityManager saat ini yang terkait dengan transaksi?

Ini dengan cepat dijawab di sini di posting ini sendiri, tetapi menyembunyikan fakta bahwa kami menghabiskan lebih dari dua minggu mencoba berbagai strategi untuk mengatasi ini. Jadi, inilah implementasi akhir yang kami putuskan untuk digunakan.

Ide dasar: Buat implementasi Anda sendiri dari javax.persistence.spi.PersistenceProvider dengan memperluas yang diberikan oleh Hibernate. Untuk semua efek, ini adalah satu-satunya titik di mana kode Anda akan diikat ke Hibernate atau implementasi khusus vendor lainnya.

public class MyHibernatePersistenceProvider extends org.hibernate.jpa.HibernatePersistenceProvider {

    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) {
        return new EntityManagerFactoryWrapper(super.createContainerEntityManagerFactory(info, properties));
    }

}

Idenya adalah untuk membungkus versi hibernate dari EntityManagerFactory dan EntityManager dengan implementasi Anda sendiri. Jadi, Anda perlu membuat kelas yang mengimplementasikan antarmuka ini dan menyimpan implementasi khusus vendor di dalamnya.

Ini adalah 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 adalah titik intersepsi kami. Anda perlu mengimplementasikan semua metode dari antarmuka. Pada setiap metode di mana entitas dapat dimodifikasi, kami menyertakan panggilan ke kueri khusus untuk menyetel variabel lokal di database.

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(.....

Kelemahan: Kueri intersepsi (SET LOCAL) kemungkinan akan berjalan beberapa kali dalam satu transaksi, khususnya jika ada beberapa pernyataan yang dibuat pada satu panggilan layanan. Mengingat keadaannya, kami memutuskan untuk tetap seperti ini karena fakta bahwa ini adalah SET LOCAL sederhana dalam panggilan memori ke PostgreSQL. Karena tidak ada tabel yang terlibat, kita dapat hidup dengan performa yang memukau.

Sekarang ganti saja penyedia persistensi Hibernate di dalam persistence.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>

Sebagai catatan tambahan, ini adalah CDIBeanUtils yang harus kami bantu dengan manajer kacang pada beberapa acara khusus. Dalam hal ini, kami menggunakannya untuk mencari referensi ke HttpServletRequest dan Principal.

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;
    }

}

Agar adil, ini tidak benar-benar mencegat peristiwa Transaksi. Namun kami dapat menyertakan kueri khusus yang kami perlukan di dalam transaksi.

Semoga ini dapat membantu orang lain menghindari rasa sakit yang kita alami.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. konversi tipe data MySQL SET ke Postgres

  2. Bagaimana meningkatkan jumlah kueri teks untuk Django dengan Postgres

  3. Sistem file Linux dan tolok ukur pos pemeriksaan PostgreSQL

  4. masalah alias kolom postgres

  5. Apa yang dimaksud dengan pecahan TX dan XID di alat pgadmin postgres