nicht - Galcho

Werbung
Object Relational Mapping
Stefan Lieser
Email: [email protected]
Web: www.lieser-online.de
Agenda
Domain Driven Design
Begriffsklärung und Konzepte



Mapping Metadaten
Klassen Hierarchie auf Tabellen abbilden
Locking, Transaktionen
Beispiele



„Zu Fuß“ mit ADO.NET
NHibernate
LINQ
Domain Driven Design
Relevanter Ausschnitt der Welt wird in
Form von business objects modelliert.
Geschäftslogik und Regeln werden
innerhalb der business objects als
Methoden implementiert.
Datenbankzugriffe werden NICHT in den
business objects selbst implementiert
sondern in einem Repository.
Impedance mismatch
Relationale Datenbanken



Mengen und Relationen als mathematische Grundlage
Hoher Verbreitungsgrad (z.B. in „Legacy
Anwendungen“)
Reine Datenhaltung
Objektorientierte Programmierung



Sehr ausdrucksstark
Daten und Operationen werden zusammengefasst
Keine Persistenz
Problembereiche


Unterschiedliche Datentypen
Klassenhierarchie auf Tabellen abbilden
Object Relational Mapper
Object Relational Mapper ermöglichen
einen automatisierten Übergang zwischen
Objekten und relationalen Datenbanken.


Mapping in Form von Metadaten
Generieren von SQL Statements für CRUD
(Create, Retrieve, Update, Delete)
Kenntnisse in relationaler
Datenbanktechnologie sind nach wie vor
erforderlich.
Object Relational Mapper
Code für Persistenz und Business Logik
bleibt getrennt
Datenbank Schema wird durch Mapping
auf die Klassenstruktur abgebildet


Bei Änderungen am Schema oder der
Klassenstruktur muss lediglich das Mapping
angepasst werden.
Wechsel der Datenbankengine einfach
möglich
Mapping
Wohin mit den Metadaten?

Attribute-based
 Z.B. Java XDoclet, .NET Attribute
 Vorteil: direkt im Code integriert
 Nachteil: nicht sehr flexibel

Mapping file
 XML
 Vorteil: sehr flexibel
 Nachteil: syntaktisch anspruchsvoll
Attribute-based Mapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Class(Table = "Addresses")]
public class Address2 {
private int m_Id;
private string m_Name;
private string m_Street;
[Id(0, TypeType = typeof(int), Column = "RecId")]
[Generator(1, Class = "native")]
public int Id {
get { return m_Id; }
set { m_Id = value; }
}
[Property()]
public string Name {
get { return m_Name; }
set { m_Name = value; }
}
}
Datei basiertes Mapping
1 <?xml version="1.0" encoding="utf-8"?>
2 <hibernate-mapping xmlns:xsd="http://www.w3.org/2001/XMLSchema"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xmlns="urn:nhibernate-mapping-2.0">
5
<class name="ORM.Address, ORM" table="Addresses" dynamic-update="true" >
6
<id name="Id" column="RecID" type="System.Int32" access="field">
7
<generator class="native" />
8
</id>
9
<property name="Name" column="Name" type="System.String" access="field" />
10
<property name="Street" column="Street" type="System.String" access="field" />
11
<property name="Town" column="Town" type="System.String" access="field" />
12
</class>
13 </hibernate-mapping>
Klassen Hierarchie Mapping
Table per concrete class

Jede konkrete Klasse in einer eigenen Tabelle
Table per class hierarchy

Eine Tabelle für eine Klassenhierarchie,
Unterscheidung des konkreten Typs über eine
Discriminator Spalte
Table per subclass

Normalisierte Tabellenstruktur
Table per concrete class
Zahlung
+Kunde
+Betrag
Kreditkarte
+Nummer
+Gültigkeit
<<table>>
Kreditkarte
Abbuchung
+Kontonummer
+BLZ
+Inhaber
RecId
Kunde
Betrag
Nummer
Gültigkeit
<<table>>
Abbuchung
RecId
Kunde
Betrag
Kontonummer
BLZ
Inhaber
„Alle Zahlungen eines Kunden“ benötigt zwei Select‘s:
SELECT * FROM Kreditkarte WHERE Kunde = 'Lieser'
SELECT * FROM Abbuchung WHERE Kunde = 'Lieser'
Table per class hierarchy
Zahlung
+Kunde
+Betrag
Kreditkarte
+Nummer
+Gültigkeit
<<table>>
Zahlung
Abbuchung
+Kontonummer
+BLZ
+Inhaber
RecId
Kunde
Betrag
Zahlart
Nummer
Gültigkeit
BLZ
Inhaber
„Alle Zahlungen eines Kunden“ benötigt nur ein Select:
SELECT * FROM Zahlung WHERE Kunde = 'Lieser'
Einschränkung auf eine Zahlart über den Discriminator:
SELECT * FROM Zahlung WHERE Zahlart = 'K'
Table per subclass
<<table>>
ZahlungDetail
Zahlung
RecId
Kunde
Betrag
+Kunde
+Betrag
Kreditkarte
+Nummer
+Gültigkeit
Abbuchung
+Kontonummer
+BLZ
+Inhaber
<<table>>
KreditkarteDetail
<<table>>
AbbuchungDetail
RecId
ZahlungId
Nummer
Gültigkeit
RecId
ZahlungId
Kontonummer
BLZ
Inhaber
„Alle Zahlungen eines Kunden“ mittels outer join:
SELECT * FROM ZahlungDetail ZD
LEFT JOIN KreditkarteDetail KD ON KD.ZahlungId = ZD.RecId
LEFT JOIN AbbuchungDetail AD ON AD.ZahlungId = ZD.RecId
WHERE ZD.Kunde = 'Lieser‚
Einschränkung auf eine Zahlart über inner join
Polymorphie
Polymorphe Abfragen


class A { ... }
class X : A { ... }
class Y : A { ... }
select * from A
 Mapping für X und Y muss berücksichtigt werden
 Wenn X/Y jeweils in eigener Tabelle gespeichert
sind, werden mehrere Tabellen abgefragt
Lazy vs. Eager Loading
Lazy Load


Eigenschaft eines Objektes wird erst aus der
Datenbank geladen, wenn darauf zugegriffen wird.
Das Nachladen muss innerhalb der gleichen Session
geschehen in der das Objekt geladen wurde.
Eager Loading


Daten werden mittels JOIN sofort geladen
Nur eine Collection kann per eager loading geladen
werden, sonst würden (durch Kreuzproduktbildung)
ggf. sehr große Datenmengen geliefert.
Traversing the object graph
class A {
public B b;
public IList C;
}
A a;
Save(a);
b und C werden automatisch in die Datenbank
übertragen sofern sie geändert wurden.
Kann bei Bedarf abgeschaltet werden über
ISession.FlushMode = FlushMode.Never;
Locking
Pessimistic


Datensätze werden während ihrer
Bearbeitung in der Datenbank gesperrt
Nachteil: streng serieller Zugriff, dadurch evtl.
schlechtes Antwortverhalten
Optimistic


Erst beim Aktualisieren der Datenbank wird
geprüft ob die Daten von einem anderen
Nutzer geändert wurden.
Sehr gute Skalierbarkeit
Optimistic Locking
Alle Spalten mit ihren vorherigen Werten
vergleichen

UPDATE Adressen SET Strasse='My way'
WHERE RecId=5 AND Name='Lieser' AND Strasse='Way'
Timestamp

UPDATE Adressen SET Strasse='My way',
Timestamp='22.08.2006 14:06:05:87'
WHERE RecId=5 AND Timestamp='20.08.2006 11:34:53:96'
Version

UPDATE Adressen SET Strasse='My way',
Version=Version + 1
WHERE RecId=5 AND Version=5
Unit of Work
Logisch zusammenhängende Änderungen
müssen als Transaktion ausgeführt werden
Unit of Work als Pattern für Transaktionen




Constructor der UnitOfWork startet die
Transaktion
Insert/Update/Delete Methoden ergänzen die
Transaktion
Commit beendet die Transaktion
Im Fehlerfall wird ein Rollback ausgeführt
Unit Of Work
client
anzeige
1 : new()
erscheinung
unit of work
database
2 : BeginTransaction()
3 : new()
4 : AddNew(anzeige)
5 : new()
6 : AddNew(erscheinung)
7 : Execute()
8 : Insert(erscheinung)
9 : Insert(anzeige)
10 : CommitTransaction()
ORM „zu Fuß“ mit ADO.NET
Für jede Klasse werden CRUD Operationen
geschrieben.
Vorgehensweise lässt sich mit Code
Generatoren automatisieren.
NHibernate (LGPL)
Basiert auf Hibernate 2.1 (Java)
Mapping wahlweise über Attribute oder
Datei
Abfrage



Criteria
Hybernate Query Language (HQL)
Query by Example
LINQ und ADO.NET
Language Integrated Query




Objektorientierte Abfragesprache
In die Sprache (C# und VB) integriert
Compile-time Prüfung (!)
Nicht auf Datenbanken beschränkt
ADO.NET Entity Framework


Client Views als Indirektionsebene
Migrationsweg für „alte“ ADO.NET
Anwendungen
LINQ Beispiel
var custs = from c in db.Customers
where c.City == "London"
select c;
var custs = (from c in db.Customers
where c.City == "London"
select c)
.Including(r => r.Orders);
Und Tschüss...
Die PowerPoint Datei sowie die
Beispiele finden Sie unter
http://www.lieser-online.de
Herunterladen