2016-06-15 4 views
1

Мы используем спящий режим 4.3 Multitenancy, поставщик связи hikaricp:Multitenancy, Hibernate 4.3, Wildfly 9, режим автокоммит Hikaricp ложное ejb3 JTA контейнера управляемых транзакций и дао

<property name="hibernate.multiTenancy" value="DATABASE"/> 
    <property name="hibernate.multi_tenant_connection_provider" value="multitenancy.HikariTenantConnectionProvider"/> 
    <property name="hibernate.tenant_identifier_resolver" value="multitenancy.ThreadLocalIdentifierResolver"/> 

Во всех случаях мы хотим, чтобы контейнер управление наши транзакции, используя стандарт java ee ejb.

ПРОБЛЕМА:

Autocommit

В автоматической фиксации режим следующий процесс произойдет позволяет сказать, что мы следующий код в дао шаблоне voucherService:

@TransactionAttribute(TransactionAttributeType.MANDATORY) 
private Voucher save(Voucher entity, Boolean isCreate) throws Exception { 

     voucherPoolService.save(entity); 

     List<VoucherTag> tags = _detachTags(entity); 
     List<VoucherCaption> captions = _detachCaptions(entity); 

     if (isCreate) { 
      voucherDAO.save(entity); 
     } else { 
      voucherDAO.merge(entity); 
     } 

     retailerService.updateTime(entity); 

     _clearCollections(entity); 
     _attachTags(entity, tags); 
     _attachCaptions(entity, captions); 

     voucherDAO.merge(entity); 

     return entity; 
} 

Rest вызов:

@POST 
    @Transactional(value = Transactional.TxType.REQUIRES_NEW, rollbackOn = {Exception.class}) 
    public Voucher create(Voucher entity) throws Exception { 
    if (getValidator() != null) { 
     getValidator().validateCreate(entity); 
    } 
    return getService().save(entity, true); // create 
    } 

Как это будет обрабатываться в режиме автообновления:

КОНТЕЙНЕР -> JTA транзакций -> JDBC

START TRANSACTION; 
INSERT INTO voucher_pool ... 
COMMIT; 

START TRANSACTION; 
INSERT INTO voucher ... 
COMMIT; 

START TRANSACTION; 
INSERT INTO retailer ... 
COMMIT; 

START TRANSACTION; 
INSERT INTO voucher_tags ... 
COMMIT; 


START TRANSACTION; 
INSERT INTO voucher_tags ... 
COMMIT; 

Проблемы:

Если один из службы объекта бросками исключение SQL, например, на линии:

retailerService.updateTime(entity); 

JTA транзакции АНП? отката, в этот момент нет возможности отката:

START TRANSACTION; 
INSERT INTO voucher_pool ... 
COMMIT; 

START TRANSACTION; 
INSERT INTO voucher ... 
COMMIT; 

Потому что ваши уже переданы в базе данных как отдельные трансакции взаимодействия jdbc. Как с этим справиться?

ответ

1

Решения? Autocomit ложные

<property name="hibernate.connection.autocommit" value="false"/> 

Существует одна большая проблема с этим вы должны явно сказать о каждой реализации, когда совершить пример дао ручной фиксации реализации:

public void commit() throws Exception { 
    Session session = getEntityManager().unwrap(Session.class); 
    Transaction tx = session.getTransaction(); 
    if (tx.isActive()) { 
     session.doWork(new Work() { 
      @Override 
      public void execute(Connection connection) throws SQLException { 
       connection.commit(); 
      } 
     }); 
    } 
} 

Использование этого:

@TransactionAttribute(TransactionAttributeType.MANDATORY) 
private Voucher save(Voucher entity, Boolean isCreate) throws Exception { 

     voucherPoolService.save(entity); 

     List<VoucherTag> tags = _detachTags(entity); 
     List<VoucherCaption> captions = _detachCaptions(entity); 

     if (isCreate) { 
      voucherDAO.save(entity); 
     } else { 
      voucherDAO.merge(entity); 
     } 

     retailerService.updateTime(entity); 

     _clearCollections(entity); 
     _attachTags(entity, tags); 
     _attachCaptions(entity, captions); 

     voucherDAO.merge(entity); 
     // you have to explicitly say when to commit 
     getDao().commit(); 

     return entity; 
} 

Вы бы спросили себя, почему я не ставил его в общую реализацию dao? Но вы попадете в ту же ситуацию, что и режим автокомитов, и вы этого не сделаете.

К счастью, у меня есть красивое решение с использованием Hibernate перехватчики, который будет обрабатывать, что без явного определения A:

getDao().commit(); 

Вот единица настойчивости пример вашего упорства.XML:

<persistence-unit name="webservices" transaction-type="JTA"> 
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> 
<class>Your entitys here</class> 
<class>Your entitys here</class> 
<properties> 
    <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> 
    <property name="hibernate.use_outer_join" value="true"/> 
    <property name="hibernate.connection.provider_class" 
       value="com.zaxxer.hikari.hibernate.HikariConnectionProvider"/> 
    <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> 
    <property name="hibernate.connection.autoReconnect" value="true"/> 
    <property name="hibernate.connection.autocommit" value="false"/> 
    <property name="hibernate.connection.release_mode" value="after_transaction"/> 

    <property name="hibernate.ejb.use_class_enhancer" value="true"/> 
    <property name="hibernate.ejb.interceptor" 
       value="webservices.dao.transaction.TransactionInterceptor"/> 

    <property name="hibernate.multiTenancy" value="DATABASE"/> 
    <property name="hibernate.multi_tenant_connection_provider" 
       value="webservices.multitenancy.HikariTenantConnectionProvider"/> 
    <property name="hibernate.tenant_identifier_resolver" 
       value="webservices.multitenancy.ThreadLocalIdentifierResolver"/> 

    <property name="hibernate.current_session_context_class" value="thread"/> 

    <property name="hibernate.transaction.jta.platform" 
       value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/> 
    <property name="hibernate.transaction.auto_close_session" value="true"/> 

    <property name="hibernate.hikari.maximumPoolSize" value="30"/> 
    <property name="hibernate.hikari.connectionTestQuery" value="SELECT 1"/> 
    <property name="hibernate.hikari.leakDetectionThreshold" value="10000"/> 
    <property name="jboss.entity.manager.factory.jndi.name" value="webservicesEMF"/> 
    <property name="jboss.entity.manager.jndi.name" value="webservicesEM"/> 
    <property name="hibernate.hikari.idleTimeout" value="300000"/> 
    <property name="hibernate.hikari.transactionIsolation" value="TRANSACTION_SERIALIZABLE"/> 

</properties> 

Generic даосские части:

@Stateless 
public class GenericDAO<E, ID extends Serializable> extends GenericDAOImpl<E, ID> { 

    private Class<E> entityClass; 
    private Class<ID> idClass; 

    protected static Logger log = LogManager.getLogger(GenericDAO.class); 

    @PersistenceContext(name = "webservices") 
    protected EntityManager em; 

    private static final JPASearchProcessor searchProcessor = new JPASearchProcessor(new JPAAnnotationMetadataUtil()); 

    public GenericDAO() { 
    super(); 
    entityClass = (Class<E>) DAOUtil.getTypeArguments(GenericDAOImpl.class, this.getClass()).get(0); 
    init(); 
    } 

    @PostConstruct 
    protected void initialize() { 
    setEntityManager(em); 
    setSearchProcessor(searchProcessor); 
    }  

DAORegistry части:

@Stateless 
public class DAORegistry { 

    public static EntityManager getEntityManager() { 
    EntityManager em = null; 
    try { 
     InitialContext ic = new InitialContext(); 
     em = (EntityManager) ic.lookup("webservicesEM"); 
    } catch (NamingException e) { 
     e.printStackTrace(); 
    } 
    return em; 
    } 

И МАГИЯ Уловка:

Нанести перехватчик:

<property name="hibernate.ejb.interceptor" value="webservices.dao.transaction.TransactionInterceptor"/> 

И ваша реализация перехватчик:

public class TransactionInterceptor extends EmptyInterceptor { 

@Override 
public void beforeTransactionCompletion(Transaction noTx) { 
    DAORegistry.getEntityManager().unwrap(Session.class); 
    Transaction tx = session.getTransaction(); 
    if (tx.isActive() && !tx.wasRolledBack() && !tx.wasCommitted()){ 
      session.doWork(Connection::commit); 
    } 

} 

Теперь Вам не нужно больше ручной фиксации реализации:

getDao().commit(); 

Ваш код дао будет работать в любом месте, как ожидается, с хорошей реализации JTA CMT , Код ниже будет работать как шарм:

@TransactionAttribute(TransactionAttributeType.MANDATORY) 
private Voucher save(Voucher entity, Boolean isCreate) throws Exception { 

    voucherPoolService.save(entity); 

    List<VoucherTag> tags = _detachTags(entity); 
    List<VoucherCaption> captions = _detachCaptions(entity); 

    if (isCreate) { 
     voucherDAO.save(entity); 
    } else { 
     voucherDAO.merge(entity); 
    } 

    retailerService.updateTime(entity); 

    _clearCollections(entity); 
    _attachTags(entity, tags); 
    _attachCaptions(entity, captions); 

    voucherDAO.merge(entity); 

    return entity; 
} 

Для более недель я искал какой-то пример, который предоставит мне такую ​​хорошую реализацию и я не мог найти его, поэтому я хочу поделиться своим Усвоением.

Теперь вы можете использовать свой стандарт EJB3, как вы ожидали, он должен работать,

Оставьте мне свои комментарии и Усвоение ниже. :)