Beispiel einer einfachen CRUD Anwendung mit Springframework .Net und NHibernate

Thomas Darimont

Erfahrenes Mitglied
Hallo,

//Einleitung...

...hier mal ein kleines Beispiel zur Kombination von Spring.NET und NHibernate am Beispiel
eines einfachen Dienstes der Personen speichern und wieder auslesen kann. Der (Dumme)Service delegiert in unserem
Beispiel einfach die entsprechenden Aufrufe an eine DAO (Data Access Objekt) welches den Hibernate Support vom
SpringFramework verwendet. Die gesamte Anwendung konfigurieren wir über Spring.

Unser IPersonService Interface:
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Person = De.Tutorials.Training.Spring.Domain.Person;

namespace De.Tutorials.Training.Spring.Services
{
    public interface IPersonService
    {
        void Save(Person person);
        void Delete(Person person);
        IList FindAll();
    }
}

Unsere PersonService Implementierung:
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using IPersonDAO = De.Tutorials.Training.Spring.DAO.IPersonDAO;
using Person = De.Tutorials.Training.Spring.Domain.Person;

namespace De.Tutorials.Training.Spring.Services.Internal
{
    public class PersonService : IPersonService
    {
        private IPersonDAO personDAO;

        public IPersonDAO PersonDAO
        {
            get { return personDAO; }
            set { personDAO = value; }
        }
    
        #region IPersonService Members

        public void Save(Person person)
        {
            PersonDAO.MakePersistent(person);
        }

        public void Delete(Person person)
        {
            PersonDAO.MakeTransient(person);
        }

        public IList FindAll()
        {
            return PersonDAO.FindAll();
        }

        #endregion
    }
}

Unser IPersonDAO Interface:
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Person = De.Tutorials.Training.Spring.Domain.Person;

namespace De.Tutorials.Training.Spring.DAO
{
    public interface IPersonDAO
    {
        void MakePersistent(Person person);
        void MakeTransient(Person person);
        IList FindAll();
    }
}

Unsere PersonDAO Implementierung (Wir verwenden Hibernate als Persistenz Mechanismus)
C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Person = De.Tutorials.Training.Spring.Domain.Person;
using AdoDaoSupport = Spring.Data.Support.AdoDaoSupport;
using HibernateDaoSupport = Spring.Data.NHibernate.Support.HibernateDaoSupport;

namespace De.Tutorials.Training.Spring.DAO.Internal
{
    public class PersonDAO : HibernateDaoSupport, IPersonDAO
    {
        #region IPersonDAO Members

        public void MakePersistent(Person person)
        {
            this.HibernateTemplate.SaveOrUpdate(person);
        }

        public void MakeTransient(Person person)
        {
            this.HibernateTemplate.Delete(person);
        }

        public IList FindAll()
        {
            return this.HibernateTemplate.Find("FROM Person");
        }
        #endregion
    }
}

Unsere Person Domain Object das wir Persistieren wollen:
C#:
using System;
using System.Collections.Generic;
using System.Text;

namespace De.Tutorials.Training.Spring.Domain
{
    public class Person
    {

        public Person()
        {
        }

        public Person(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        private int id;

        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        private string firstName;

        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        private string lastName;

        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }

        public override string ToString()
        {
            return this.id + ": " + this.firstName + " " + this.lastName;
        }
    }
}

Das entsprechende Hibernate Mapping File:
XML:
<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <class name="De.Tutorials.Training.Spring.Domain.Person, SpringTraining" table="person" lazy="false">

        <id name="Id" column="Id" type="Int32">
            <generator class="native"/>
        </id>

        <property name="FirstName" column="firstname" type="String"/>
        <property name="LastName"    column="lastname"    type="String"/>

    </class>
</hibernate-mapping>
Wichtig! In diesem Beispiel muss dieses Mapping als Eingebette Ressource (Embedded Resource) deklariert sein.

Hier unsere Springframework.net Konfiguration in einem Standard App.config File:
XML:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<sectionGroup name="spring">
			<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
			<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
		</sectionGroup>
	</configSections>
	<spring>
		<context name="spring.root">
			<resource uri="config://spring/objects"/>
		</context>
		<objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
			<object name="personService" 
					type="De.Tutorials.Training.Spring.Services.Internal.PersonService,SpringTraining">
				<property name="personDAO" ref="personDAO"/>
			</object>

			<object name="personDAO" 
					type="De.Tutorials.Training.Spring.DAO.Internal.PersonDAO,SpringTraining">
				<property name="SessionFactory" ref="sessionFactory"/>
			</object>

			<object id="dbProvider" type="Spring.Data.Support.SqlProvider, Spring.Data">
				<property name="ConnectionString" value="Data Source=localhost;Initial Catalog=test;User Id=root;Password=tutorials"/>
			</object>
			
			<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
				<property name="dbProvider" ref="dbProvider"/>
				<property name="HibernateProperties">
					<dictionary>
						<entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
						<entry key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
						<entry key="hibernate.connection.driver_class" value="NHibernate.Driver.MySqlDataDriver"/>
					</dictionary>
				</property>
				<property name="MappingAssemblies">
					<list>
						<value>SpringTraining</value>
					</list>
				</property>
			</object>

			<object id="hibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
				<property name="dbProvider" ref="dbProvider"/>
				<property name="sessionFactory" ref="sessionFactory"/>
			</object>
		</objects>
	</spring>
</configuration>

Die Tabelle in der MySQL Datenbank:
Code:
mysql> desc person;
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstname | varchar(255) | YES  |     | NULL    |                |
| lastname  | varchar(255) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> select * from person;
Empty set (0.00 sec)

Ausführen des Beispiels:
Beispiel Ausgabe:
Code:
32: Thomas633015665742656250 Darimont
33: Bart633015665742656250 Simpson
Drücken Sie eine beliebige Taste . . .

Code:
mysql> select * from person;
+----+--------------------------+----------+
| id | firstname                | lastname |
+----+--------------------------+----------+
| 32 | Thomas633015665742656250 | Darimont |
| 33 | Bart633015665742656250   | Simpson  |
+----+--------------------------+----------+
2 rows in set (0.00 sec)

Das Projekt (ohne) Bin (Release oder Debug) findet man im Anhang.
Um das ganze zum Laufen zu bekommen muss man die Source Versionen von:
Spring.NET 1.1.0 Preview 3
http://www.springframework.net/download.html

Spring.NET NHibernate Integration
http://www.springframework.net/downloads/Spring.Data.NHibernate/

nhibernate 1.2.0.Beta2
http://sourceforge.net/project/showfiles.php?group_id=73818

in die Solution mit aufnehmen... und dann alles zusammen neu Kompilieren. Es kann auch sein, dass die Proejkte unterschiedliche Versionen von log4net verwenden... bei mir hats funktioniert, wenn ich die Version aus dem Spring.Net bin-Distributionsverzeichnis genommen habe.

Ach ja für das Beispiel braucht man noch den MySql.NET DataProvider:
http://mysql.org/downloads/connector/net/1.0.html

In diesem Beispiel hab iche der einfachheit halber auf die Verwendung eines Spring.Transaction.Interceptor.TransactionProxyFactoryObject's verzichtet. Damit ist es möglich in der Konfiguration (deklarativ) Methoden-Patterns anzugeben die unter bestimmten Transaction-Propagation Konfigurationen (Propagation Required, Requires New, Supported, Not Support, Never, Mandatory, Nested) ausgeführt werden sollen.

Gruß Tom
 

Anhänge

  • 27350attachment.zip
    81,7 KB · Aufrufe: 228
Zuletzt bearbeitet von einem Moderator:
Hallo Tom!

Ich habe mir das Beispiel angeschaut und verstehe nicht ganz wie du die Springkonfigurationsdatei lädst.

so sieht ja dein Code in der Programklasse aus:
C#:
using System;
using System.Collections.Generic;
using System.Text;
using IApplicationContext = Spring.Context.IApplicationContext;
using ContextRegistry = Spring.Context.Support.ContextRegistry;
using IPersonService = De.Tutorials.Training.Spring.Services.IPersonService;
using Person = De.Tutorials.Training.Spring.Domain.Person;

namespace De.Tutorials.Training.Spring
{
    public class Program
    {
        static void Main(string[] args)
        {
            IApplicationContext applicationContext = ContextRegistry.GetContext();
            IPersonService personService = (IPersonService)applicationContext.GetObject("personService");
            Person tom = new Person("Thomas"+ DateTime.Now.Ticks, "Darimont");
            Person bart = new Person("Bart" + DateTime.Now.Ticks, "Simpson");

            personService.Save(tom);
            personService.Save(bart);

            foreach (Person person in personService.FindAll())
            {
                Console.WriteLine(person);
            }
        }
    }
}

mit ContextRegistry.GetContext() lädst du deinen Kontext ohne irgendwo einen Pfad zu übergeben. Wie geht das? oder Was passiert da?


Gruß Konstantin
 
Hallo!

Mit ContextRegistry.GetContext() schaut Spring automatisch in der entsprechenden App.config Datei nach ob irgendwo ein Spring-Kontext Element mit dem Namen spring.root definiert ist.

Gruß Tom
 
Hallo!

Also in meinem Beispiel lag sie im Root-Verzeichnis meines Visual Studio Projektes. Afaik kann man aber auch die App.config Location angeben.

Gruß Tom
 
Hallo Tom,

wollte gerade mir irgendein spring-beispiel anschauen.
Deinen code fand ich interessant, bloß kriege ich beim versuch, das sample zu starten eine exception:
"Cannot instantiate Type [Spring.Context.Support.XmlApplicationContext] using ctor [Void .ctor(System.String, Boolean, System.String[])] : 'Ein Aufrufziel hat einen Ausnahmefehler verursacht.'"}
Mit message :
"Error instantiating context 'spring.root'."
Was könnte das sein?

Source: "System.Configuration""System.Configuration"

Stack trace :
" bei System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)\r\n bei System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)\r\n bei System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)\r\n bei System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)\r\n bei System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)\r\n bei System.Configuration.BaseConfigurationRecord.GetSection(String configKey, Boolean getLkg, Boolean checkPermission)\r\n bei System.Configuration.BaseConfigurationRecord.GetSection(String configKey)\r\n bei System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)\r\n bei System.Configuration.ConfigurationManager.GetSection(String sectionName)\r\n bei Spring.Util.ConfigurationUtils.GetSection(String sectionName)\r\n bei Spring.Context.Support.ContextRegistry.InitializeContextIfNeeded()\r\n bei Spring.Context.Support.ContextRegistry.GetContext()\r\n bei De.Tutorials.Training.Spring.Program.Main(String[] args) in D:\\Development\\OrderSys\\sample\\De.Tutorials.Training.Spring\\De.Tutorials.Training.Spring\\De\\Tutorials\\Training\\Spring\\Program.cs:Zeile 15.\r\n bei System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)\r\n bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)\r\n bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()\r\n bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)\r\n bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)\r\n bei System.Threading.ThreadHelper.ThreadStart()"

Danke
Gruß
Boris

PS. hab' mir exceptions näher angeschaut: alles hängt mit dll's zusammen, die nicht im Einklang sind.
2.0 -lösung verlangt, dass nhibernate 1.0.1.0 einhgebunden ist, 1.1 Net solution kommt eigentlich nur mit spring.data.hnibernate aus...
wäre nicht schlecht, wenn auch die passenden dll's im zip-archive wären; wie kann man sonst das beispiel nachvollziehen?
 
Zuletzt bearbeitet:
Hallo!

Schau mal in deine App.config.
Dort musst du einen Spring Context mit dem namen spring.root definiert haben.
(Siehe meine App.config) als Beispiel. Wenn du keine spring.root Kontext definierst bekommst du per default diese von dir genannte Fehlermeldung.
XML:
...
<spring>
        <context name="spring.root">
...

Gruß Tom
 
Zuletzt bearbeitet von einem Moderator:
Danke Tom für den Hinweis,

context-eintrag war schon drin, ich habe deine config-datei übernommen; das problem war, wie gesagt, inkonsistenz zwischen verschiedenen dll's. Es hat bei mir etwa 1 tag gedauert, bis ich alles hatte, aber dann ging's.
noch ein ppar bemerkungen:
1)using AdoDaoSupport = Spring.Data.Support.AdoDaoSupport; funktionierte bei mir( Spring für .Net 1.1, distribution von etwa mitte november 2006) nicht mehr- jetzt heisst es
using AdoDaoSupport = Spring.Data.AdoDaoSupport;(AdoDaoSupport ist eine Ebene höher untergebracht)
2)Falls hibernate beta eingebunden werden sollte, sollte der config eintrag
<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"> in NHibernate12 umgenannt werden.

3)Noch ein tip für "blutige spring/hibernate Anfänger" wie mich: damit key generation mit
<id name="Id" column="Id" type="Int32"> <generator class="native"/> </id>
funktioniert, sollte bei der db-spalte bei z.B. SQL Server die entsprechende property
identitätsspecification(Ist Identity) auf "ja"gesetzt werden

Ähnliches beispiel gibt es auch unter http://www.codeproject.com/cs/design/SpringPlusHibernate.asp
Da wird allerdings eine custom dll für die nhibernate-einbindung benutzt.

Auf alle Fälle, Danke für das Beispiel.
Hier wird Data Transfer Objects -Ansatz mit DAO gezeigt;
Interessant wäre , wie man "detached" objects einsetzt.

gruß
boris

hier der auszug aus meiner .config
XML:
<configuration>
	<configSections>
		<sectionGroup name="spring">
			
			<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
			<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
			<section name="parsers" type="Spring.Context.Support.ConfigParsersSectionHandler, Spring.Core" />
		</sectionGroup>
		<section name="log4net" 
		  type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />

	</configSections>
	<spring>
	


		<context name="spring.root">
			<resource uri="config://spring/objects"/>
		</context>


		<parsers>
			<parser namespace="http://www.springframework.net/database" 
					type="Spring.Data.DatabaseConfigParser, Spring.Data"
					schemaLocation="assembly://Spring.Data/Spring.Data/spring-database.xsd" />
		</parsers>

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


			<db:dbProvider id="DbProvider" 
                provider="System.Data.SqlClient" 
                connectionString="Data Source=blabla;Database=blabla;User ID=blabla;Password=blabla;Trusted_Connection=False"/>
			<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
				<!-- TODO Provide dedicated NHibernate Schema -->
				<property name="DbProvider" ref="DbProvider"/>
				
				<property name="MappingAssemblies">
				<list>
				<!--List the assemblies where the hbm.XML files exists 
				The hbm.XML files must be marked in VS.Net as Embedded Resources-->
				<value>Serad.OrderTool.Data</value>
				</list>
				</property>
				<!--
				<property name="MappingResources">
					<list>
						<value>assembly://Serad.OrderTool.Data//AppraisalOrder.hbm.xml</value>
						<value>assembly://Serad.OrderTool.Data//Test.hbm.xml</value> 
					</list>
				</property>
				-->

				<property name="HibernateProperties">
					<dictionary>
						<entry key="hibernate.connection.provider"
								value="NHibernate.Connection.DriverConnectionProvider"/>
						<entry key="hibernate.dialect"
								value="NHibernate.Dialect.MsSql2000Dialect"/>
					</dictionary>
				</property>
			</object>

			<object id="hibernateTransactionManager" 
            type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">

				<property name="DbProvider" ref="DbProvider"/>
				<property name="sessionFactory" ref="sessionFactory"/>
			</object>

			<!-- Transaction Interceptor based on attribute [Transaction()] -->
			<object id="transactionInterceptor" 
					type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">
				<property name="TransactionManager" ref="hibernateTransactionManager"/>
				<!-- note do not have converter from string to this property type registered -->
				<property name="TransactionAttributeSource">
					<object type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"/>
				</property>
			</object>


			<object name="appraisalOrderService" type="Serad.OrderTool.Data.Spring.Services.Internal.AppraisalOrderService,Serad.OrderTool.Data">
				<property name="appraisalOrderDAO" ref="appraisalOrderDAO" />
			</object>
			<object name="appraisalOrderDAO" type="Serad.OrderTool.Data.Spring.DAO.Internal.AppraisalOrderDAO,Serad.OrderTool.Data">
				<property name="SessionFactory" ref="sessionFactory" />
			</object>

			<object name="testService" type="Serad.OrderTool.Data.Spring.Services.Internal.TestService,Serad.OrderTool.Data">
				<property name="testDAO" ref="testDAO" />
			</object>
			<object name="testDAO" type="Serad.OrderTool.Data.Spring.DAO.Internal.TestDAO,Serad.OrderTool.Data">
				<property name="SessionFactory2" ref="sessionFactory" />
			</object>

			<object name="testService" type="Serad.OrderTool.Data.Spring.Services.Internal.TestService,Serad.OrderTool.Data">
				<property name="testDAO" ref="testDAO" />
			</object>
			<object name="testDAO" type="Serad.OrderTool.Data.Spring.DAO.Internal.TestDAO,Serad.OrderTool.Data">
				<property name="SessionFactory" ref="sessionFactory" />
			</object>
			
			<object id="AdoExceptionTranslator" 
				type="Spring.Data.Support.ErrorCodeExceptionTranslator, Spring.Data" >
				<constructor-arg name="provider" ref="DbProvider"/>
			</object>
			
		</objects>



	</spring>
 
Zuletzt bearbeitet von einem Moderator:
ich habe das mal ausprobiert jedoch bekomme ich bei dieser zeile eine fehlermeldung:

Code:
IApplicationContext context = new XmlApplicationContext("file://config.xml");
IInterface interface= (IInterface)context.GetObject("foo");

und zwar :
Initialization of object failed: Cannot instantiate an interface DependencyInjection.IInterface.

klassen kann er jedoch initialisieren, also wenn ich z.B.:

Code:
ClassFactory factory= (ClassFactory)context.GetObject("foo");

schreibe dann macht er das auch ohne probleme, aber ich würde doch gerne das interface initialisieren können.
 
"Initialization of object failed: Cannot instantiate an interface DependencyInjection.IInterface."
sagt mir nicht viel

in vs.net kann man sich exceptions ausführlicher anschauen.
mir hat geholfen, die "inner exception" runterzubrechen, bis ich am ende des exception-baums war...
 

Neue Beiträge

Zurück