This is almost a continuation of
http://forum.hibernate.org/viewtopic.php?t=109
I have a Stateless Session bean which containts my hibernate code.
My current problem is that DB changes are not getting rolledback when the Session bean function throws an exception.
Also if I remove session.flush() calls from within the bean methods nothing is written to the DB (making me think, has the container actually commited a transaction)
My understanding, from reading Ed Romans book, is that for a CMT Bean the container will automagically rollback the transaction if the method throws. Perhaps this assumption is incorrect.
I have a gut feeling that my hibernate session is not aware of the container transaction.
Is this caused beacuase Im using PostgreSQL which is not an XA datasource?
Or
Am I mission something with the way im using Hibernate/JBoss?
I feel really dumb for asking all these newbie questions, if I get this working, I'll write a tutorial to redeem myself.
Thanks in advance
Peter Henderson.
Anyway onto the code.
The Stateless Session bean
Code:
/**
* @ejb.bean name="PurchaseOrder"
* description="PurchaseOrder test bean"
* jndi-name="ejb/starjarcrm/PurchaseOrder"
* type="Stateless"
* transaction-type="Container"
*
* @ejb.security-role-ref role-link="Administrator"
* role-name="admin"
*
* @ejb.permission role-name="Administrator"
*
* @ejb.transaction type="Required"
*
*/
public class PurchaseOrderBean implements SessionBean {
org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger( this.getClass() );
private SessionContext ctx;
/** Retrieves the HibernateSession factory from JNDI, then creates a new Session.
* @throws NamingException If the HibernateFactory was not found.
* @throws HibernateException If hibernate could not create a session.
* @return A hibernate session.
*/
private Session getSession() throws NamingException, HibernateException {
Object objref = new InitialContext().lookup("java:/hibernate/HibernateFactory");
SessionFactory sf = (SessionFactory)PortableRemoteObject.narrow(objref, SessionFactory.class);
return sf.openSession();
}
/** Adds a line item to a Purchase Order, updating StockItem totals as needed.
* If the Purchase order has already been sent, update the stock item to reflect the
* new qty on order.
*
* @ejb.interface-method view-type="remote"
* @ejb.transaction type="Required"
* ---ejb.transaction type="RequiresNew"---
*
* @param poid The ID of the Purchase Order.
* @param supplierPartID
* @param qty in supplier units
* @throws PurchaseOrderDBException
* @throws NamingException
*/
public void addPOLineItem( Long poid, Long supplierPartID, double qty ) throws PurchaseOrderDBException, NamingException {
System.out.println("ENTERED addPOLineItem");
long startTime = System.currentTimeMillis();
Session s = null;
Transaction tx = null;
try {
s = getSession();
//tx = s.beginTransaction(); // now done by the container.
SupplierPartDTO sup = (SupplierPartDTO)s.load(SupplierPartDTO.class, supplierPartID, LockMode.UPGRADE );
PurchaseOrderDTO po = (PurchaseOrderDTO)s.load(PurchaseOrderDTO.class, poid, LockMode.UPGRADE );
StockItemDTO si = (StockItemDTO)s.load(StockItemDTO.class, sup.getStock().getID(), LockMode.UPGRADE );
if( po.getCompany()!=sup.getCompany() ) {
// cant add a part from a different supplier.
throw new PurchaseOrderDBException("Cannot add a part from a different supplier. PurchaseOrder is for " + po.getCompany().getName() + " part if supplier by " + sup.getCompany().getName() );
}
PurchaseOrderLineItemDTO poli = new PurchaseOrderLineItemDTO();
poli.setConversion( sup.getConversionRate() );
poli.setDescription( sup.getDescription() );
//poli.setID( // Assigned by DB
poli.setLineNo( po.getLineItems() .size() + 1 );
//poli.setPurchaseOrder( // Assigned when we add to the PO
poli.setStockItem( si );
poli.setStockQuantity( qty * sup.getConversionRate() );
poli.setSupplierPart( sup );
poli.setSupplierQty( qty );
poli.setSupplierQtyReceived( 0 );
poli.setSupplierStockCode( sup.getSupplierPartNumber() );
poli.setTotal( qty * sup.getPrice() );
poli.setUnitPrice( sup.getPrice() );
po.addLineItem( poli );
po.updateTotals();
if( po.getSentDate()!=null ) {
// PO has been sent, so adjust qty on order.
si.setQuantityOnOrderPO( si.getQuantityOnOrderPO()+poli.getStockQuantity() );
}
s.update(si);
s.save( poli );
// make sure the status is correct.
updatePOStatus(s, po);
s.update(po);
s.flush();
//tx.commit(); // now done by the container.
log.debug("Success addPOLineItem");
if( (0+1)==1 ) { // need this otherwise we get compiler erros about code not being reachable.
// Throw an exception.
// If everything is working, the add line item should be rolledback
throw new PurchaseOrderDBException("TESTING FAILURE NOTHING SHOULD HAPPEN TO THE DB");
}
} catch(HibernateException he ) {
log.error("Failure addPOLineItem", he );
try {
if(tx!=null) tx.rollback();
} catch( HibernateException rbe ) {
log.error( "Failed to rollback transaction", rbe ) ;
}
throw new PurchaseOrderDBException("Hibernate problem");
} finally {
if( s!=null ) try{ s.close(); }catch(Exception e) {}
}
if( log.isDebugEnabled() ) {
long endTime = System.currentTimeMillis();
long dur = endTime - startTime;
log.debug( "PERFORMANCE: addPOLineItem " + poid + " took " + dur + "ms" );
}
System.out.println("EXIT addPOLineItem");
}
Notice after all the DB manipulation I have inserted a throw("TESTING..") exception.
Hibernate JMX config.
Code:
<server>
<mbean code="net.sf.hibernate.jmx.HibernateService" name="jboss.jca:service=HibernateFactory,
name=HibernateFactory">
<depends>jboss.jca:service=RARDeployer</depends>
<depends>jboss.jca:service=LocalTxCM,name=StarjarCRMDS</depends>
<classpath codebase='.' archives='starjarcrmdb.jar' />
<!-- Make it deploy ONLY after DataSource had been started -->
<attribute name="MapResources">
mappings/AddressDTO.hbm.xml, mappings/PurchaseOrderDTO.hbm.xml,
mappings/BOMComponentDTO.hbm.xml, mappings/PurchaseOrderLineItemDTO.hbm.xml,
mappings/BOMDTO.hbm.xml, mappings/QuoteDTO.hbm.xml,
mappings/CategoryDTO.hbm.xml, mappings/QuoteLineItemDTO.hbm.xml,
mappings/CompanyDTO.hbm.xml, mappings/QuoteSectionDTO.hbm.xml,
mappings/ConfigDTO.hbm.xml, mappings/QuoteValidityDTO.hbm.xml,
mappings/ContactCategoryDTO.hbm.xml, mappings/SalesAreaDTO.hbm.xml,
mappings/ContactDTO.hbm.xml, mappings/SalesChannelDTO.hbm.xml,
mappings/ContactStatusDTO.hbm.xml, mappings/SalesOrderDTO.hbm.xml,
mappings/ContactTitlesDTO.hbm.xml, mappings/SalesOrderLineItemDTO.hbm.xml,
mappings/ContactTypeDTO.hbm.xml, mappings/SpecialProductsDTO.hbm.xml,
mappings/CorrespondenceDTO.hbm.xml, mappings/StockItemDTO.hbm.xml,
mappings/CountriesDTO.hbm.xml, mappings/StockTransactionDTO.hbm.xml,
mappings/CountyDTO.hbm.xml, mappings/SupplierPartDTO.hbm.xml,
mappings/DeliveryOptionsDTO.hbm.xml, mappings/TaskActivitiesDTO.hbm.xml,
mappings/HistoryActionsDTO.hbm.xml, mappings/TaskDTO.hbm.xml,
mappings/HistoryDTO.hbm.xml, mappings/UserDTO.hbm.xml,
mappings/IndustrySectorDTO.hbm.xml, mappings/WorkOrderDTO.hbm.xml,
mappings/PaymentTermsDTO.hbm.xml, mappings/WorkOrderLineItemDTO.hbm.xml,
mappings/PhoneNumberDTO.hbm.xml
</attribute>
<attribute name="JndiName">java:/hibernate/HibernateFactory</attribute>
<attribute name="Datasource">java:/StarjarCRMDS</attribute>
<attribute name="Dialect">net.sf.hibernate.dialect.PostgreSQLDialect</attribute>
<attribute name="TransactionStrategy">net.sf.hibernate.transaction.JTATransactionFactory</attribute>
<attribute name="TransactionManagerLookupStrategy">net.sf.hibernate.transaction.JBossTransactionManagerLookup</attribute>
<attribute name="UseOuterJoin">false</attribute>
<attribute name="ShowSql">false</attribute>
<attribute name="UserTransactionName">java:/UserTransaction</attribute>
<!--attribute name="UserTransactionName">java:comp/UserTransaction</attribute-->
<!--attribute name="UserTransactionName">java:comp/UserTransaction/</attribute-->
</mbean>
</server>
Datasouce config
Code:
<datasources>
<local-tx-datasource>
<jndi-name>StarjarCRMDS</jndi-name>
<connection-url>jdbc:postgresql://192.168.200.50:5432/starjarcrm</connection-url>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>postgres</user-name>
<password></password>
<transaction-isolation>TRANSACTION_SERIALIZABLE</transaction-isolation>
</local-tx-datasource>
</datasources>