PDA

View Full Version : Transaction attributes and NHibernate



cheachwood
08-30-2007, 02:29 PM
Hi,

I am trying to use the Transaction Attribute with NHibernate and Generics, here are part of my code :
for a DAO.xml file :

<!-- Récupère les propriétés de la BD définis dans le fichier de configuration principal -->
<object type="Spring.Objects.Factory.Config.PropertyPlaceholderC onfigurer, Spring.Core">
<property name="ConfigSections" value="databaseSettings"/>
</object>

<!-- Configuration de la BD et de NHibernate -->
<db:provider id="DbProvider"
provider="SqlServer-2.0"
connectionString="Integrated Security=false; Data Source=${db.datasource};Database=${db.database};Us er ID=${db.user};Password=${db.password};"/>

<object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref ="SessionFactory"/>
</object>

<import resource ="~/Config/DeclarativeServicesAttributeDriven.xml"/>

<!-- Configuration d'une session NHibernate -->
<object id="SessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>Cleos2008.DAL</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/>

<entry key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2005Dialect"/>

<entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"/>
</dictionary>
</property>
</object>

<!-- Définition du template Hibernate qui sera utilisé par les objets DAO-->
<object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate, Spring.Data.NHibernate12">
<property name="SessionFactory" ref="SessionFactory" />
<property name="TemplateFlushMode" value="Auto" />
<property name="CacheQueries" value="true" />
</object>

<!-- Data Access Objects -->
<object id="referentielDAO" type="Cleos2008.DAL.NHibernate.Aide.NHibernateReferentie lDAO, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

from a App.xml file :

<!-- Récupère les propriétés de la BD définis dans le fichier de configuration principal -->
<object type="Spring.Objects.Factory.Config.PropertyPlaceholderC onfigurer, Spring.Core">
<property name="ConfigSections" value="databaseSettings"/>
</object>

<!-- Configuration de la BD et de NHibernate -->
<db:provider id="DbProvider"
provider="SqlServer-2.0"
connectionString="Integrated Security=false; Data Source=${db.datasource};Database=${db.database};Us er ID=${db.user};Password=${db.password};"/>

<object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref ="SessionFactory"/>
</object>

<import resource ="~/Config/DeclarativeServicesAttributeDriven.xml"/>

<!-- Configuration d'une session NHibernate -->
<object id="SessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>Cleos2008.DAL</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/>

<entry key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2005Dialect"/>

<entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"/>
</dictionary>
</property>
</object>

<!-- Définition du template Hibernate qui sera utilisé par les objets DAO-->
<object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate, Spring.Data.NHibernate12">
<property name="SessionFactory" ref="SessionFactory" />
<property name="TemplateFlushMode" value="Auto" />
<property name="CacheQueries" value="true" />
</object>

<!-- Data Access Objects -->
<object id="referentielDAO" type="Cleos2008.DAL.NHibernate.Aide.NHibernateReferentie lDAO, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

from a NHibernateObjectDomaineDAO.cs file

public abstract class NHibernateObjectDomaineDAO<T,idT>:HibernateDaoSupport, IObjectDomaineDAO<T,idT>
{
#region IObjectDomaineDAO<T> Membres

/// <summary>
/// Permet de faire persister un objet
/// </summary>
/// <param name="instance">Une instance du type �* persister</param>
/// <returns>Un objet du type générique</returns>
public T Save(T instance)
{
HibernateTemplate.Save(instance);
return instance;
}


from a NHibernateReferentielDAO.cs file :

public class NHibernateReferentielDAO : NHibernateObjectDomaineDAO<Referentiel, string>, IReferentielDAO<Referentiel, string>
{
}

and from a RulesReferentiel.cs file

public class RulesReferentiel : IReferentielServices<Referentiel, string>
{

private IReferentielDAO<Referentiel, string> referentielDAO;

public IReferentielDAO<Referentiel, string> ReferentielDAO
{
get { return referentielDAO; }
set { referentielDAO = value; }
}



#region IObjectServices<Referentiel,string> Membres

[LoggingAttribute("cheachwood")]
[Transaction(ReadOnly = false)]
public Referentiel Save(Referentiel instance)
{
return ReferentielDAO.Save(instance);
}

Well when I launch the application I have the exception :
Error creating context 'spring.root': Error creating object with name 'referentielDAO' defined in 'file [C:\Documents and Settings\jmartin\Mes documents\Visual Studio 2005\Projects\Cleos2008\Cleos2008.UI\bin\Debug\Con fig\DAO.xml]' : Error setting property values: PropertyAccessExceptionsException (1 errors); nested PropertyAccessExceptions are:
[Spring.Core.TypeMismatchException: Cannot convert property value of type [CompositionAopProxy_fb0bb1772abc4da4ae59c7a6ac4925 bf] to required type [Spring.Data.NHibernate.Generic.HibernateTemplate] for property 'HibernateTemplate'., Inner Exception: Spring.Core.TypeMismatchException: Cannot convert property value of type [CompositionAopProxy_fb0bb1772abc4da4ae59c7a6ac4925 bf] to required type [Spring.Data.NHibernate.Generic.HibernateTemplate] for property 'HibernateTemplate'.
Spring.Core.TypeConversion.TypeConversionUtils.Con vertValueIfNecessary(Type requiredType, Object newValue, String propertyName)]


I don't understand why, I verified all the assembly dependencies all seems to be good. Someone have an idea?

Cheachwwood

Mark Pollack
08-30-2007, 03:08 PM
Hi Cheachwwood,

It looks like the autoproxy mechanism has made a composition AOP proxy for HibernateTemplate, which shouldn't be considered to be proxied. The composition proxy implements only the interfaces of HibernateTemplate, the base class is the type name you see 'CompositionProxy_###' which can't be cast to HibernateTemplate. While changing the property to IHiberateOperations would work, we should figure out why the HibernateTemplate is being considered as a candidate for AOP proxying.

Can you show me what is inside



<import resource ="~/Config/DeclarativeServicesAttributeDriven.xml"/>


Also, if you turn on debug logging you will see a listing of which objects were proxied.

BTW, what version of Spring are you using? There were some issues relating to 'overeager' proxying in the past - mainly due to the combination of autoproxy and autowire. I suspect you are using M1 or M2 and DeclarativeServicesAttributeDriven.xml is using autowiring. If so, you can work around it by either updating to RC1, changing to explicit 'wiring' in DeclarativeServicesAttributeDriven.xml, or use <tx:attribute-driven/> (which uses explicit wiring under the covers).

Cheers,
Mark

cheachwood
08-30-2007, 03:22 PM
Hi Mark,

I am using the RC1 version of Spring.NET and here is the DeclarativeServicesAttributeDriven.xml file


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<!-- The rest of the config file is common no matter how many objects you add -->
<!-- that you would like to have declarative tx management applied to -->
<object id="autoProxyCreator"
type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoP roxyCreator, Spring.Aop">
</object>

<object id="transactionAdvisor"
type="Spring.Transaction.Interceptor.TransactionAttribut eSourceAdvisor, Spring.Data">
<property name="TransactionInterceptor" ref="transactionInterceptor"/>
</object>

<!-- Transaction Interceptor -->
<object id="transactionInterceptor"
type="Spring.Transaction.Interceptor.TransactionIntercep tor, Spring.Data">
<property name="TransactionManager" ref="HibernateTransactionManager"/>
<!-- note do not have converter from string to this property type registered -->
<property name="TransactionAttributeSource" ref="attributeTransactionAttributeSource"/>
</object>

<object id="attributeTransactionAttributeSource"
type="Spring.Transaction.Interceptor.AttributesTransacti onAttributeSource, Spring.Data">
</object>
</objects>

I didn't modify this file and I take it form the Norhtwin NHibernate sample of the Sring.Net RC1 contribution.

Thx

Mark Pollack
08-30-2007, 08:19 PM
Hi,
Thanks for the info... I don't have a quick answer, I'll investigate.
Cheers,
Mark

Mark Pollack
08-31-2007, 01:35 AM
Hi,

Can you show me the other parts of your configuraiton? The config for wiring up the service layer and if you are doing any additional AOP configuration, say for the logging attribute? Failing something obvious in the configuration we can set the logging level to debug to get a log file. (See here (http://netcommon.sourceforge.net/doc-latest/reference/html/logging.html#logging-declarative-config)for info on setting that up..)

I've made a solution that copies what you are doing and did not get that exception. I've attached it here, maybe it can be useful to you.

Cheers,
Mark

cheachwood
08-31-2007, 07:04 AM
Hi,

Yes you're right, I have another AOP configuration for the logging attribute I desactivated it and I didn't have the error again (I have another one but I will search later). I have already activate the DEBUG mode and I saw the error in my logs.

So here is my logger.xml file which declared an AOP logging configuration :

<objects xmlns="http://www.springframework.net">

<!-- Définition de la classe qui doit être appelée au déclenchement d'un méthode-->
<object id="loggingAdvice" type="Cleos2008.Logger.Service.AOP.LoggingAdvice, Cleos2008.Logger"/>

<!-- Définition de toutes les méthodes qui doivent faire l'objet d'un suivi d'action utilisateur-->
<object id="rulesAroundAdvisor" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
<property name="Advice" >
<ref local="loggingAdvice"/>
</property>
<property name="MappedNames">
<list>
<value>Save*</value>
<value>Delete</value>
<value>Find*</value>
</list>
</property>
</object>

<!-- Définition des classes qui doivent faire l'objet d'un suivi d'action utilisateur -->
<!-- Cette définition est en relation étroite avec les classes de service définies en début de fichier -->
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxy Creator, Spring.Aop">
<property name="ObjectNames">
<list>
<value>*Rules</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<value>rulesAroundAdvisor</value>
</list>
</property>
</object>
</objects>

I understand it like this (tell me if i a wrong):
1. I declared an advice that should be run : loggingAdvice
2. I declared advisors rulesAroundAdvisor that should run the advice and declaring the pointcuts wich are methods that ends with the terms Rules or Delete or Save
3. I declared and autoproxy which must filtering on the name of the services with that ends with Rules for the proxying onlu the advisor rulesAroundAdvisor

Here is the config file for the service layer ServicesLogging-AOP.xml :


<objects xmlns="http://www.springframework.net">
<!-- Définition de toutes les classes de services qui doivent faire l'objet d'un suivi d'action utilisateur -->
<object id="typeReferentielRules" type="Cleos2008.BLL.BusinessRules.Aide.RulesTypeReferent iel, Cleos2008.BLL">
<property name="typeReferentielDAO" ref="typeReferentielDAO"/>
</object>
<object id="referentielRules" type="Cleos2008.BLL.BusinessRules.Aide.RulesReferentiel, Cleos2008.BLL">
<property name="referentielDAO" ref="referentielDAO"/>
</object>
<object id="clientRules" type="Cleos2008.BLL.BusinessRules.Metier.RulesClient, Cleos2008.BLL">
<property name="clientDAO" ref="clientDAO"/>
</object>
<object id="utilisateurRules" type="Cleos2008.BLL.BusinessRules.Securite.RulesUtilisat eur, Cleos2008.BLL">
<property name="utilisateurDAO" ref="utilisateurDAO"/>
</object>
<object id="roleRules" type="Cleos2008.BLL.BusinessRules.Securite.RulesRole, Cleos2008.BLL">
<property name="roleDAO" ref="roleDAO"/>
</object>

</objects>

And here is the complete DAO.xml file:

<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.net/tx"
xmlns:db="http://www.springframework.net/database">

<!-- Ce fichier est référencé par le fichier de configuration de l'application principale -->
<description>
Définition de la configuration de la couche DAL
</description>

<!-- Récupère les propriétés de la BD définis dans le fichier de configuration principal -->
<object type="Spring.Objects.Factory.Config.PropertyPlaceholderC onfigurer, Spring.Core">
<property name="ConfigSections" value="databaseSettings"/>
</object>

<!-- Configuration de la BD et de NHibernate -->
<db:provider id="DbProvider"
provider="SqlServer-2.0"
connectionString="Integrated Security=false; Data Source=${db.datasource};Database=${db.database};Us er ID=${db.user};Password=${db.password};"/>

<object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref ="SessionFactory"/>
</object>

<import resource ="~/Config/DeclarativeServicesAttributeDriven.xml"/>

<!-- Configuration d'une session NHibernate -->
<object id="SessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>Cleos2008.DAL</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/>

<entry key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2005Dialect"/>

<entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"/>
</dictionary>
</property>
</object>

<!-- Définition du template Hibernate qui sera utilisé par les objets DAO-->
<object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
<property name="SessionFactory" ref="SessionFactory" />
<property name="TemplateFlushMode" value="Auto" />
<property name="CacheQueries" value="true" />
</object>

<!-- Data Access Objects -->
<object id="referentielDAO" type="Cleos2008.DAL.NHibernate.Aide.NHibernateReferentie lDAO, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

<object id="typeReferentielDAO" type="Cleos2008.DAL.NHibernate.Aide.NHibernateTypeRefere ntielDAO, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

<object id="clientDAO" type="Cleos2008.DAL.NHibernate.Metier.NHibernateClientDA O, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

<object id="utilisateurDAO" type="Cleos2008.DAL.NHibernate.Securite.NHibernateUtilis ateurDAO, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>

<object id="roleDAO" type="Cleos2008.DAL.NHibernate.Securite.NHibernateRoleDA O, Cleos2008.DAL">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>


</objects>

So I don't understand why the HibernateTemplate is candidate to this Advisor.

You can look at the file to see my DEBUG console.


Regards,

Cheachwood.

cheachwood
08-31-2007, 12:31 PM
OK I resolved my issue.
Finally I put all my LoggingAttribute on my Rules classes and all the Transaction Attributes on my DAO classes.
Then I modified the logger.xml like this so it could be used for all methods on the DAO objects :


<objects xmlns="http://www.springframework.net">

<!-- Définition de la classe qui doit être appelée au déclenchement d'un méthode-->
<object id="loggingAdvice" type="Cleos2008.Logger.Service.AOP.LoggingAdvice, Cleos2008.Logger"/>

<!-- Définition de toutes les méthodes qui doivent faire l'objet d'un suivi d'action utilisateur-->
<object id="rulesAroundAdvisor" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop" >
<property name="Advice" >
<ref local="loggingAdvice"/>
</property>
<property name="MappedNames">
<list>
<value>*</value>
</list>
</property>
</object>

<!-- Définition des classes qui doivent faire l'objet d'un suivi d'action utilisateur -->
<!-- Cette définition est en relation étroite avec les classes de service définies en début de fichier -->
<object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxy Creator, Spring.Aop">
<property name="ObjectNames">
<list>
<value>*DAO</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<value>rulesAroundAdvisor</value>
</list>
</property>
</object>
</objects>

And I modified the DeclarativeServicesAttributeDriven.xml like this so it could be used for all methods of the Rules objects :


<objects>
<object name="autoProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxy Creator, Spring.Aop">
<property name="InterceptorNames" value="transactionInterceptor"/>
<property name="ObjectNames">
<list>
<value>*Rules</value>
</list>
</property>
</object>

<object id="transactionAdvisor"
type="Spring.Transaction.Interceptor.TransactionAttribut eSourceAdvisor, Spring.Data">
<property name="TransactionInterceptor" ref="transactionInterceptor"/>
</object>

<!-- Transaction Interceptor -->
<object id="transactionInterceptor"
type="Spring.Transaction.Interceptor.TransactionIntercep tor, Spring.Data">
<property name="TransactionManager" ref="HibernateTransactionManager"/>
<!-- note do not have converter from string to this property type registered -->
<property name="TransactionAttributeSource" ref="attributeTransactionAttributeSource"/>
</object>

<object id="attributeTransactionAttributeSource"
type="Spring.Transaction.Interceptor.AttributesTransacti onAttributeSource, Spring.Data">
</object>
</objects>

It works, I can see the logging in the console for the Logging Advice and I can see the Transaction for Hibernate.

Tell me if this is a good practice.

Thx a lot