我们正在使用hibernate 4.3 multiTenancy,hikaricp连接提供程序:
<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标准来管理我们的事务。
问题:
自动提交
在自动提交模式下,发生以下过程可以说我们在dao模式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;
}
休息电话:
@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
}
如何在自动提交模式下处理:
CONTAINER - &gt; JTA交易 - &gt; 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事务api将回滚,此时无法回滚:
START TRANSACTION;
INSERT INTO voucher_pool ...
COMMIT;
START TRANSACTION;
INSERT INTO voucher ...
COMMIT;
因为你已经在数据库中作为单独的jdbc tranasactions提交了。 如何以一种很好的方式处理这个问题?
答案 0 :(得分:1)
解决方案? Autocomit false
<property name="hibernate.connection.autocommit" value="false"/>
在提交示例dao手动提交实现时,必须明确说明每个实现有一个大问题:
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拦截器来处理它而不明确定义:
getDao().commit();
以下是persistance.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 dao部分:
@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();
您的dao代码可以在预期的任何地方使用,具有良好的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标准,
请在下面留下您的意见和建议。 :)