Spring 1.2.7
Hibernate version: Hibernate 3.2.0.cr2
Hibernate Annotations 3.2.0.cr1
This is an unusual problem we are having, and I would very much appreciate some help :-
1) We have a situation where we seem to need to use hibernatetemplate merge() rather than saveOrUpdate() to persist a graph of of entities (Parent Entity with oneToMany assocation of child entities)
- We send transfer objects back to our client tier.
- When the modified TOs come back to the server tier they get translated as is into Hibernate Entity classes - effectively detached as this point (we can't change this architectural decision).
- We use Spring hibernateDAOSupport to call merge() on the parent entity. The parent entity has a oneToMany association to a child entity e.g.
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY )
@JoinColumn(name = "circumstancesFk")
@Cascade(org.hibernate.annotations.CascadeType.DEL ETE_ORPHAN)
public List<ApplicantDO> getApplicants() {
return applicants;
}
Previously our problem was that DELETE_ORPHAN that wasn't working until we used merge(). If we used a saveOrUpdate() hibernate did not detect that some orphans that had been removed from the child collection - updates and inserts all worked but NOT child deletes.
Using merge() meant we could see that Hibernate would re-select the entity and then detect the deleted child orphans :-)
(reading the hibernate docs I see the quote re: merge operation:- "if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance" )
Hibernate now issues the delete (for the object that was deleted for the TO collection and therefore missing from the Entity Collection).
2) Using merge() is now causing us a problem in that the operation that uses merge() must also provide functionality to INSERT the entity graph as well. If the TOs and Entities have a primary key of null the INSERT is correctly performed, however we have found that the merged Entity is not updated with its new primary key values or version.
We have tried catching the returned object e.g.
circumstances = (CircumstancesDO) getHibernateTemplate().merge(circumstances);
however this doesn't seem to update the Id in the returned object.
I notice that the docs for a merge() operation state
"Note that merge will not update the identifiers in the passed-in object graph (in contrast to TopLink)! Consider registering Spring's IdTransferringMergeEventListener if you'd like to have newly assigned ids transferred to the original object graph too."
So I tried using the IdTransferringMergeEventListener from latest Spring RC release (that is compatible with Hibernate 3.1), it gets invoke during the merge but get :-
14:42:36,949 DEBUG [AbstractSaveEventListener] delaying identity-insert due to no transaction in progress
in the IdTransferringMergeEventListener, then it finds that the id is null i.e.
Serializable id = persister.getIdentifier(event.getResult(), session.getEntityMode());
gets null id.
We are using EJB3 Session Beans with Hibernate 3 Entities (via Hibernate Session not EJB3 entity manager) - so Transactions are demarcated in EJB3 SSB using annotation, I believe they are working ok, because the datasource is TX aware.
So how is IdTransferringMergeEventListener supposed to work ? i.e. in situations where there is a transaction vs. no transaction ?
Also do I need to do anything else to make Hibernate Session aware of the EJB3 container transaction? or is using TX aware datasource enough?
I also tried tried configuring JtaTransactionManager (we're using JBoss), but I get the same result - in that the IdTransferringMergeEventListener still gets id of null and the message
14:42:36,949 DEBUG [AbstractSaveEventListener] delaying identity-insert due to no transaction in progress
still appears.
Questions :-
- Is there a better solution to the delete orphan issue with detached objects? I like having a single operation that copes with both inserts/updates/deletes but this seems difficult when using detached objects ?
- How is IdTransferringMergeEventListener supposed to work ?
Any help would be most appreciated.
application context snipped :-
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTran sactionManager">
<property name="userTransactionName"><null/></property>
<property name="transactionManagerName">
<value>java:/TransactionManager</value>
</property>
</bean>
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.annotati on.AnnotationSessionFactoryBean">
<property name="dataSource" ref="mformDS" />
<property name="annotatedClasses" ref="annotatedClassesList">
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.query.factory_class">
org.hibernate.hql.classic.ClassicQueryTranslatorFa ctory
</prop>
<prop key="hibernate.show_sql">£{hibernate.show_sql}</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry>
<key>
<value>pre-update</value>
</key>
<ref bean="validatePreUpdateEventListener"/>
</entry>
<entry>
<key>
<value>pre-insert</value>
</key>
<ref bean="validatePreInsertEventListener"/>
</entry>
<entry key="merge">
<bean class="com.rdf.infra.spring.hibernate3.support.IdT ransferringMergeEventListener"/>
</entry>
</map>
</property>
</bean>
EJB3 transaction annotations :-
import javax.ejb.TransactionAttribute;
@TransactionAttribute(TransactionAttributeType.REQ UIRED)
public SearchCriteriaDTO saveSearchCriteria(String userId, SearchCriteriaDTO searchCriteria) {
return getDelegateBean().saveSearchCriteria(userId, searchCriteria);
}
thanks for your time.
In the log I can see the JTA mgr :-
16:47:54,060 INFO [JtaTransactionManager] Using JTA UserTransaction: org.springframework.transaction.jta.UserTransactio nAdapter@735a55
16:47:54,060 INFO [JtaTransactionManager] Using JTA TransactionManager: org.jboss.tm.TxManager@12faeca
16:47:54,278 INFO [Version] Hibernate Annotations 3.2.0.CR1
16:47:54,310 INFO [Environment] Hibernate 3.2 cr2
16:47:54,310 INFO [Environment] loaded properties from resource hibernate.properties: {hibernate.bytecode.use_reflection_optimizer=false }
but then get :-
16:48:14,763 DEBUG [AbstractSaveEventListener] delaying identity-insert due to no transaction in progress
16:48:24,716 DEBUG [AbstractFlushingEventListener] processing flush-time cascades
16:48:24,716 DEBUG [AbstractFlushingEventListener] dirty checking collections
16:48:24,716 DEBUG [AbstractFlushingEventListener] Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
16:48:24,716 DEBUG [AbstractFlushingEventListener] Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
16:48:24,716 DEBUG [Printer] listing entities:
16:48:24,731 DEBUG [Printer] com.rdf.mform.domain.user.ExistingMortgageDetailsD O{monthlyPayment=component[currency,amount]{amount=49900, currency=GBP}, propertyLocation=England, loanAmount=component[currency,amount]{amount=50000000, currency=GBP}, loanAmountInterestPart=component[currency,amount]{amount=0, currency=GBP}, repaymentType=Repayment, dealType=DiscountedVariable, latestVersion=0, id=null, loanAmountRepaymentPart=null, loanTermYears=10}
16:48:24,731 DEBUG [AbstractBatcher] about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
16:48:24,731 DEBUG [ConnectionManager] opening JDBC connection
16:48:24,731 DEBUG [SQL] insert into existing_mortgage_details (latestVersion, repaymentType, loanAmountCur, loanAmount, loanTermYears, propertyLocation, loanAmountRepaymentPartCur, loanAmountRepaymentPart, loanAmountInterestPartCur, loanAmountInterestPart, dealType, monthlyPaymentCur, monthlyPayment) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
16:48:24,731 INFO [STDOUT] Hibernate: insert into existing_mortgage_details (latestVersion, repaymentType, loanAmountCur, loanAmount, loanTermYears, propertyLocation, loanAmountRepaymentPartCur, loanAmountRepaymentPart, loanAmountInterestPartCur, loanAmountInterestPart, dealType, monthlyPaymentCur, monthlyPayment) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
16:48:24,747 DEBUG [IdentifierGeneratorFactory] Natively generated identity: 1002
16:48:24,747 DEBUG [AbstractBatcher] about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
16:48:24,747 DEBUG [ConnectionManager] releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
16:48:24,762 DEBUG [ConnectionManager] transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!


Reply With Quote