# Kundennummer als Primary Key?



## miketech (18. Sep 2007)

Hi zusammen,

ich möchte Kunden in einer Tabelle in einer MySQL Datenbank speichern. Ich bin mir noch nicht ganz sicher, ob ich den Primary Key als Kundennummer nehmen soll oder ob die Kundennummer ein extra Feld sein soll. Auf der einen Seite genieße ich hierdurch den Vorteil des autoincrement und es ist eben schon unique. Auf der anderen Seite vermische ich hier vielleicht Logik mit Datenbankinterner Repräsentation. Was meint Ihr dazu? 

(ja es hat nicht mit JDBC direkt zu tun  )

Gruß

Mike


----------



## HoaX (18. Sep 2007)

ich würde die kundennummer nicht als pk nehmen. aber es spricht nichts dagegen z.B. mit einem trigger den wert des pk als kundennummer zu übernehmen beim anlegen.


----------



## miketech (19. Sep 2007)

Und wieso nicht? 

Gruß

Mike


----------



## The_S (19. Sep 2007)

Also da Kundennummern eindeutig sind, kannste die Kundennummer auch genauso gut als PK nehmen. Ma braucht ja net überall ne zusätzliche ID, was auch (jenachdem wie viele Kunden da drin stehen) Speicherplatz sparen kann.


----------



## tfa (19. Sep 2007)

Alles, was irgendwie eine "Business"-Bedeutung hat und sich ändern könnte, darf nicht als Primärschlüssel verwendet werden. Stell Dir vor, zwei Firmen schließen sich zusammen. Die Kundendatenbanken müssen auch verschmolzen werden. Es könnte zu Kollisionen kommen, wenn es zwei gleiche Kundennummern in den alten DBs gibt.
Das Management könnte auch auf die Idee kommen, von numerischen auf alphanumerische Nummern umzusteigen.
Deine Befürchtung, Businesslogik mit DB-Interna zu vermischen, ist berechtigt.

tfa


----------



## *Hendrik (19. Sep 2007)

tfa hat gesagt.:
			
		

> Alles, was irgendwie eine "Business"-Bedeutung hat und sich ändern könnte, darf nicht als Primärschlüssel verwendet werden. Stell Dir vor, zwei Firmen schließen sich zusammen. Die Kundendatenbanken müssen auch verschmolzen werden. Es könnte zu Kollisionen kommen, wenn es zwei gleiche Kundennummern in den alten DBs gibt.
> tfa



Das mag vielleicht ein Grund sein, um sich spätere (Mehr)Arbeit zu ersparen, jedoch kein Grund um die Kundennummer nicht als PK zu nehmen. Schließlich kann ich beim Fall einer Verschmelzung die Kunden in einer neuen Tabelle zusammenführen, was meistens sowieso der Fall ist, da sich die einzelnen Felder der Tabellen nicht gleichen. Dann bekommt die Kundennummer z.B. eine 1 oder eine 2 vorgestellt, je nachdem von welcher alten Firma sie kam.

Generell bin ich auch brennend an dem Thema PK interessiert, da ich momentan vor der Frage stehe, was ich für einen PK nehme. Bei mir ist es - wahrscheinlich ähnlich wie bei Miketech (ich habe Deine Threads hier etwas verfolgt) - eine Swing-Anwendung, mehrere User, Hibernate und MySQL. Bisher war ich es immer gewöhnt z.B. Kundennummer, Artikelnummer, Lagernummer usw. als PK zu nehmen. Nun wird ja auch von Hibernate oder anderen empfohlen technische Schlüssel zu verwenden, wobei es hier dann die unterschiedlichen Stratigien gibt, wie der Schlüssel erzeugt werden soll. Und an diesen Strategien hänge ich derzeit. Naja wie auch immer, unabhängig davon, wie ich den PK schließlich erzeuge oder erzeugen lasse, bin ich der Meinung, dass ich kein separates ID-Feld für z.B. Kundennummer o.ä. benötige.


----------



## maki (19. Sep 2007)

> Naja wie auch immer, unabhängig davon, wie ich den PK schließlich erzeuge oder erzeugen lasse, bin ich der Meinung, dass ich kein separates ID-Feld für z.B. Kundennummer o.ä. benötige.


Du willst eine ID für alle entities, oder möchtest du jede Klasse anders implementieren was equals, hascode und objekt ID betrifft?

Eine Basisklasse (zB Entity) welche die ID enthält und equals und hashcode überschreibt ist viel einfacher zu warten und imho notwendig.


----------



## miketech (19. Sep 2007)

Hi,

also ich denke, dass man es nicht zwingend benötigt. Aber die Vermischung von Anwendungs- und DB-Logik ist durchaus berechtigt. Annahme: Man möchte eine Kundennummer ändern. Kommt vielleicht eher selten vor, aber ist ja möglich. Kann man ja auch auf andere Szenarien übertragen. 

Da über den PK der Tabelle häufig Fremdschlüsselbeziehungen erstellt werden, wäre es sehr schwer die KNr zu ändern, wenn sie der PK der Tabelle ist.

Zu Hibernate: Ich glaube hier wird auch ausdrücklich empfohlen nicht mit dem PK zu arbeiten oder? Der PK dient hier viel mehr zu Identifikation des Datensatzes. 

Vorteil, wenn KNr = PK: Wenn man Fremdschlüsselbeziehungen hat und andere Tabellen auf einen Kunden verweisen hat man hier automatisch die KNr und muss diese nicht erst durch Joins herausfinden. Ist zwar nicht so das K.O Kriterium, aber spart in manchen Fällen, wenn man vielleicht nur die KNr benötigt zu einem bestimmten Datensatz einen Join 

Andere Sache: Ist eine KNr eigentlich ein numerischer Wert? Oder ist das ein String? Also jetzt unter der Annahme, dass eine KNr die Form 23498235 hat. Kann bei einem numerischen Wert die Gefahr bestehen, dass man den Wertebereich verlässt? Natürlich kann man kalkulieren, dass man nicht 2^16 Kunden haben wird oder so  Aber man fängt ja auch nicht bei 1 an zu zählen. Was macht hier mehr Sinn?

Gruß

Mike


----------



## maki (19. Sep 2007)

Wenn du keine fachlichen Schlüssel verwendest, bist du flexibler.

Ich hatte schon den Fall, dass mir der Kunde plötzlich erklärt hat (o-ton) "das zwei datensätze diesselbe eindeutige bezeichnung" haben können... ich hab ihm daraufhin erklärt was "eindeutig" (unique) bedeutet... naja, hatte damals fachliche schlüssel und ziemlich viel Ärger deswegen.

Ob es sich lohnt wenn es um die SQL statements (bez. der Vermeidung von Joins) geht: Wie oft musst du die denn schreiben?


----------



## ms (19. Sep 2007)

In den Projekten, in denen ich mitgearbeitet habe und Hibernate verwendet wurde, wurde ausschließlich mit künstlichen Schlüsseln gearbeitet. So wie Maki es schon erwähnt hat haben wir auch eine Basisklasse mit der id und der version verwendet.

Das joinen ist vielleicht etwas mehr Schreibaufwand aber kein schlagendes Argument.
Eine fachliche Einschränkung auf ein oder mehrere Felder kann man ja trotzdem machen zb. durch unique oder not-null.

ms


----------



## *Hendrik (19. Sep 2007)

maki hat gesagt.:
			
		

> Du willst eine ID für alle entities, oder möchtest du jede Klasse anders implementieren was equals, hascode und objekt ID betrifft?
> 
> Eine Basisklasse (zB Entity) welche die ID enthält und equals und hashcode überschreibt ist viel einfacher zu warten und imho notwendig.



Bisher ist es meine Absicht jede Klasse anders zu implemetieren, d.h. jede Klasse bekommt eine andere ID, was meinem eigentlichen Ansatz Kundennummer, Artikelnummer usw. als PK zu verwenden ja entspricht. Sollte ich davon abkommen, ist der Einsatz einer Basisklasse sicherlich sinnvoll - muss ich nochmal drüber nachdenken.



			
				miketech hat gesagt.:
			
		

> Da über den PK der Tabelle häufig Fremdschlüsselbeziehungen erstellt werden, wäre es sehr schwer die KNr zu ändern, wenn sie der PK der Tabelle ist.



Sollte nicht die DB dafür Sorge tragen, dass der Fremdschlüssel ebenfalls geändert wird, wenn der PK geändert wird? EDIT: Stichwort Referenzielle Integrität



			
				miketech hat gesagt.:
			
		

> Zu Hibernate: Ich glaube hier wird auch ausdrücklich empfohlen nicht mit dem PK zu arbeiten oder? Der PK dient hier viel mehr zu Identifikation des Datensatzes.



Zumindest in dem Buch Spring & Hibernate ist davon die Rede keine natürlichen Schlüssel zu verwenden. Allerdings ist das ja in den meisten Fällen nicht der Fall. Die Adressnummer ist ja auch ein technischer Schlüssel und hat mit dem Kunden nur indirekt etwas zu tun.



			
				maki hat gesagt.:
			
		

> Ich hatte schon den Fall, dass mir der Kunde plötzlich erklärt hat (o-ton) "das zwei datensätze diesselbe eindeutige bezeichnung" haben können... ich hab ihm daraufhin erklärt was "eindeutig" (unique) bedeutet... naja, hatte damals fachliche schlüssel und ziemlich viel Ärger deswegen.



Naja, man sollte seine Schlüssel schon entsprechend wählen. Was verstehst Du den unter einem fachlichen Schlüssel? Kundennummer, Artikelnummer, Lagernummer usw. sind ja keine fachlichen sondern technische Schlüssel - und eine Bezeichnung würde man normalerweise auch nicht als PK verwenden.


----------



## ms (19. Sep 2007)

*Hendrik hat gesagt.:
			
		

> Naja, man sollte seine Schlüssel schon entsprechend wählen. Was verstehst Du den unter einem fachlichen Schlüssel? Kundennummer, Artikelnummer, Lagernummer usw. sind ja keine fachlichen sondern technische Schlüssel - und eine Bezeichnung würde man normalerweise auch nicht als PK verwenden.



Sehe ich nicht so, Kundennummer und Artikelnummer sind in meinen Augen sehrwohl fachliche Schlüssel, sie definieren einen Kunden bzw. einen Artikel eindeutig in z.B. einem Unternehmen. Da muss kein EDV-System dahinter stecken.
Oder nehmen wir eine Rechnungsnummer. Rechnungsnummern hat es schon gegeben als es noch keine Computerunterstützung gegeben hat. Wir erinnern uns an die guten alten Rechnungsblöcke, die waren (bzw. sind) auch durchnummeriert.

ms


----------



## *Hendrik (19. Sep 2007)

ms hat gesagt.:
			
		

> Sehe ich nicht so, Kundennummer und Artikelnummer sind in meinen Augen sehrwohl fachliche Schlüssel, sie definieren einen Kunden bzw. einen Artikel eindeutig in z.B. einem Unternehmen. Da muss kein EDV-System dahinter stecken.
> Oder nehmen wir eine Rechnungsnummer. Rechnungsnummern hat es schon gegeben als es noch keine Computerunterstützung gegeben hat. Wir erinnern uns an die guten alten Rechnungsblöcke, die waren (bzw. sind) auch durchnummeriert.
> 
> ms



Wie gesagt, es kommt auf die Definition an, dementsprechend also fachliche == natürliche Schlüssel in Deiner Definition? Siehe Künstliche Schlüssel - so sind z.B. Rechnungsnummer künstliche (technische) Schlüssel, die eingeführt wurden, um die einzelnen Rechnungen zu unterscheiden, ob mit Computer oder auf einem Rechnungsblock ist dabei egal.


----------



## ms (19. Sep 2007)

*Hendrik hat gesagt.:
			
		

> Wie gesagt, es kommt auf die Definition an, dementsprechend also fachliche == natürliche Schlüssel in Deiner Definition? Siehe Künstliche Schlüssel - so sind z.B. Rechnungsnummer künstliche (technische) Schlüssel, die eingeführt wurden, um die einzelnen Rechnungen zu unterscheiden, ob mit Computer oder auf einem Rechnungsblock ist dabei egal.


Wenn man es so betrachtet hast du natürlich recht. Aber es geht doch letztendlich darum wie man in einem RDBMS die Relationen abbildet. Da hat 'künstlicher (technischer) Schlüssel' schon eine ganz andere Bedeutung wie deine Definition. Zumindest sehe ich das so. 

Eine interessante Diskussion.

ms


----------



## maki (19. Sep 2007)

Für mich:

fachlicher Schlüssel = KundenNr, ArtikelNr, etc.)
künstlicher Schlüssel = id, definiert die Objekt Identität

Mit Hibernate kümmern wir uns nicht mehr um RI und andere RDBMS Details, wir interessieren uns für Kardinalitäten und andere OO Aspekte


----------



## tfa (19. Sep 2007)

*Hendrik hat gesagt.:
			
		

> Naja, man sollte seine Schlüssel schon entsprechend wählen. Was verstehst Du den unter einem fachlichen Schlüssel? Kundennummer, Artikelnummer, Lagernummer usw. sind ja keine fachlichen sondern technische Schlüssel - und eine Bezeichnung würde man normalerweise auch nicht als PK verwenden.



Meiner Meinung nach ist alles, was irgendwie eingetippt, auf dem Bildschirm angezeigt oder ausgedruckt wird, Businesslogik, also fachlich. Dies als PK einzusetzen, kann klappen. Es könnte aber auch gründlich schief gehen. Die Erfahrung kann jeder selber machen. Ich sehe jedenfalls kein gutes Argument, dass gegen Kunstschlüssel spricht.
Mehr zu dem Thema bei Ambler.



			
				maki hat gesagt.:
			
		

> Mit Hibernate kümmern wir uns nicht mehr um RI und andere RDBMS Details, wir interessieren uns für Kardinalitäten und andere OO Aspekte


Das kann ich nur unterstützen :toll:  :toll:
Schließlich wollen wir doch objektorientierte Software entwickeln und uns nicht unnötig mit dem O/R-Impedance-Mismatch herumschlagen.


----------



## *Hendrik (25. Sep 2007)

Nachdem ich jetzt einiges über die Verwendung von surrogate keys/künstlichen Schlüsseln und natürlichen Schlüsseln gelesen habe, werde ich wohl für meine Tabellen künstliche Schlüssel verwenden. Da allerdings ja weiterhin Kundennummer, Personalnummer, Belegnummern usw. benötigt werden, bleibt die Frage, wie diese generiert, behandelt, überwacht werden. Welche Strategien verwendet ihr, gerade im Multi-User-Betrieb, damit diese Werte unique bleiben und auch damit keine Wertebereiche ausgelassen werden? Oder gibt es da bereits Möglichkeiten dies durch Hibernate machen zu lassen (es sollen vom User eingestellte Nummernkreise beachtet werden)?


----------



## Nova (12. Mrz 2008)

Ich schließe mich der Frage von *Hendrik an.
Hatte bisher auch fachliche Schlüssel und überlege nun doch besser technische zu nehmen, verwende dann auch Hibernate (bisher reines JDBC)

Wie stellt man am besten sicher das die fachlichen Schlüssel dann noch Unique sind und ordentlich durchnummeriert werden. z.B. bei Rechnungsnummern findet das Finanzamt es bestimmt nicht lustig wenn mehrere Rechnungen dieselbe Nummer haben oder zwischendrin Nummern fehlen...
Gibt es da was in Hibernate?


----------



## maki (12. Mrz 2008)

Die DB ID sollte immer ein rein technischer Schlüssel sein.

Um fachliche gleicheit zu definieren überschreibt (oder lässt überschreiben ) man die equals und hashcode Methoden.


----------



## semi (12. Mrz 2008)

Nova hat gesagt.:
			
		

> Wie stellt man am besten sicher das die fachlichen Schlüssel dann noch Unique sind und ordentlich durchnummeriert werden. z.B. bei Rechnungsnummern findet das Finanzamt es bestimmt nicht lustig wenn mehrere Rechnungen dieselbe Nummer haben oder zwischendrin Nummern fehlen...
> Gibt es da was in Hibernate?


Yepp, siehe Table-Annotation.
	
	
	
	





```
@Table( ..., uniqueConstraints = @UniqueConstraint(columnNames = {"ACCOUNTKEY"}))
public class Account {

   @Id
   ... was auch immer sonst noch
   private Long id;

   @Column(name = "ACCOUNTKEY")
   private String key;

   ...
}
```

Edit: Ich glaube, ich spinne.  Das ist nur bei zusammengesetzten Keys in dieser Form nötig, sonst reicht ein
unique in der Column-Annotation

```
...
public class Account {

   @Id
   ... was auch immer sonst noch
   private Long id;

   @Column(name = "ACCOUNTKEY", unique = true)
   private String key;

   ...
}
```


----------



## ARadauer (14. Mrz 2008)

wo wäre eigentlich der nachteil von künstlichen schlüsseln? ausser der speicherplatz?


----------



## Nova (14. Mrz 2008)

Man braucht bei bestimmten Abfragen einen Join. z.B. jede Rechnung gehört zu einem Auftrag. Wenn man nun eine Rechnung hat könnte man bei fachlichen Schlüsseln die Auftragsnummer sofort auslesen. Bei künstlichen Schlüsseln bräuchte man einen Join zwischen der Rechnung und dem Auftrag um an die Auftragsnummer zu kommen.
Ansonsten halt noch wenn man was von Hand in der Datenbank sucht oder ändern will (phpmyadmin), sollte aber eigentlich nicht vorkommen und ist von daher nicht wirklich ein Argument.

Ansonsten kenn ich mich mit dem Bereich auch nicht so aus :? Bin auch erst durch diesen und ähnliche Threads auf die Idee gekommen das man überhaupt sowas wie künstliche Schlüsseln nehmen könnte wenn auch ein passender fachlicher existiert, an der Uni wurde sowas gar nicht behandelt... (genauso wie Indizes, foreign-Keys, Unique,...)


@semi:
Eigenltich ganz einfach, aber wie bekomme ich Hibernate dazu die Variable automatisch hochzuzählen? Vor allem wenn es ein Alphanummerischer Wert ist? Also z.B. Rechnungsnummer "R13456"?
Hatte bisher direkt mit JDBC gearbeitet und nur "13456" gespeichert und das "R" jeweils vor dem speichern entfernt und nach dem lesen hinzufügt, aber ich denke das ist nicht so das wahr, oder wie würdet ihr das machen?

Die Buchstaben davor dienen dazu zu erkennen um welche Nummer es sich handelt, die Kunden geben bei Überweisungen gerne die verrücktesten Nummern an als Verwendungszweck...


----------



## maki (14. Mrz 2008)

Ich finde semis Ansatz ist immer noch zusehr auf das Db Design ausgerichtet, ich kann zwar das Java Objekt mit dem Wert befüllen (Doppelter Wert der eigentlich unique sein sollte), aber beim Speichern kracht es dann.
Mit der equals & hascode Methode brauche ich das gar nicht mehr, denn Hibernate sorgt selbst dafür das es nur ein Objekt gibt welches "gleich" ist.


----------



## Nova (14. Mrz 2008)

Verstehe nicht ganz was du meinst.

Klar kracht es beim speichern wenn man den Wert von hand auf einen bereits existierenden setzt. Aber wie soll man das verhindern?
equals und hashCode helfen da aber auch nicht wenn ich das richtig verstanden habe, weil wenn ein Objekt mit 10 Attributen von denen nur eines (z.B. Rechnungsnummer) gleich ist wie beim zweiten dann sind diese doch bezüglich equals und hashCode nicht gleich, oder steh ich gerade auf dem Schlauch?

Aber ich will die Rechnungsnummer auch gar nicht selbst vergeben, das soll allein Aufgabe von Hibernate sein. Denn die Rechnungsnummer musst fortlaufend nummeriert sein udn wenn ich sie selbst setzen würde könnte ich nicht garantieren das diese gleichzeitig fortlaufend nummeriert und unique sind. (z.B. wenn 2 Threads gleichzeitig eine Rechnung bearbeiten)
Ich will Hibernate nur sagen können das es bei R12345 anfangen soll zu nummerieren und dann aufsteigend, also R12346, R12347 usw. nummerieren soll. Auf keinen Fall darf eine Nummer doppelt sein (aber das verhindert ja schon die Datenbank) aber es darf auch auf keinen Fall eine Nummer fehlen.


----------



## maki (14. Mrz 2008)

Objektidentität und gleichheit sind zwei verschiedene paar Stiefel.

Wenn die zB. Dokumentnummer ein Objekt eindeutig macht, dann überschreibe ich die equals und hascode Methode dahingehend.
In die euals & hashcode Methode gehört nur das, nicht alle attribute.
Hibernate wird dafür sorgen, dass es nur ein Objekt gibt, das gleich ist.
Das brauche ich gar nicht  wie früher auf DB ebene zu gehen 

Falls der link hier noch nicht gepostet wurde: http://www.hibernate.org/109.html


----------



## Nova (14. Mrz 2008)

In dem von dir geposteten Links wird aber doch gerade empfohlen eben NICHT den automatisch generierten Key zu nehmen. Daher ist ein künstlicher und ein Business-Key besser da man dann den Business-Key holen kann, jedoch nur falls dieser nicht auch automatisch generiert wird was bei mir aber derfall ist.
Sprich wenn ich 2 Rechnungs-Objekte anlege haben diese vor dem ersten save() sowohl den technischen Schlüssel "null" als auch den Business-Key "null", wären also bezüglich equals() und hashCode() gleich, sprich lege ich beide in ein Set wird nur eines der Objekte gespeichert und das andere geht verloren. Verwende ich alle Attribute passiert das nur wenn ich die Objekte noch überhaupt nicht befüllt habe, ob das jetzt soviel bringt müsste man sich überlegen, immerhin wird der equals() Vergleich ja wesentlich teurer wenn man mehr Attribute vergleichen muss.

So hab ich es jedenfalls verstanden  ???:L


----------



## byte (14. Mrz 2008)

maki hat gesagt.:
			
		

> Ich finde semis Ansatz ist immer noch zusehr auf das Db Design ausgerichtet, ich kann zwar das Java Objekt mit dem Wert befüllen (Doppelter Wert der eigentlich unique sein sollte), aber beim Speichern kracht es dann.
> Mit der equals & hascode Methode brauche ich das gar nicht mehr, denn Hibernate sorgt selbst dafür das es nur ein Objekt gibt welches "gleich" ist.


Aber das DB-Schema sollte schon konsistent mit den Objekten sein. Klar arbeitet man über die Java-Objekte und implementiert dort equals und hashcode, aber die Unique Constraints sollten trotzdem nicht fehlen. Spätestens, wenn mal eine zweite Anwendung auf die DB zugreift, muss sichergestellt werden, dass auch nur korrekte Datensätze gespeichert werden.


----------



## maki (14. Mrz 2008)

> Aber das DB-Schema sollte schon konsistent mit den Objekten sein. Klar arbeitet man über die Java-Objekte und implementiert dort equals und hashcode, aber die Unique Constraints sollten trotzdem nicht fehlen. Spätestens, wenn mal eine zweite Anwendung auf die DB zugreift, muss sichergestellt werden, dass auch nur korrekte Datensätze gespeichert werden.


Wenn wirklich eine 2. Anwendung  auf die DB schreibt, ist die Kacke am dampfen sobald Hibernate im Spiel ist.

Denn dann ist die DB nicht unbedingt immer "aktuell", sondern eher als Verlängerung von Hibernate zu sehen, und Hibernate kümmert sich um die Objekte und deren Persitierung, vor allem aber Caching(!), etc. pp.

Um dann alle(!) Daten in der DB zu haben und sicherzustellen dass diese auch die aktuellen Objekte wiederspiegeln, muss die Anwendung bzw. Hibernate runtergefahren sein, denn sonst ist Hibernate und nur Hibernate der Master über die Daten und Objekte


----------



## byte (14. Mrz 2008)

Verstehe ich nicht, wie Du das jetzt meinst. Es schreiben immer *viele* Anwendungen auf eine DB. Wo soll da ein Problem mit Hibernate sein?
Natürlich muss man sich überlegen, wie man mit der Nebenläufigkeit umgeht, aber das ist kein Hibernate-spezifisches Problem sondern so alt wie Datenbanken selbst (Stichwort: Propagation Level, Optimistic Locking). Aber deswegen ist es halt wichtig, dass das DB-Schema auch konsistent ist. Hibernate befreit einen nicht davon, ein sinnvolles DB-Schema zu erzeugen.


----------



## Nova (14. Mrz 2008)

Das es Probleme bei mehreren Anwendungen gibt hab ich mir auch schon gedacht, werde dieses "Problem" auch später haben und mich daher auch noch damit beschäftigen müssen...

Aber die eigentliche Frage war ja wie ich Hibernate beibringen kann wie es einen alphanumerischen Schlüssel zu generieren hat.
Dieser (alphanumerische) Schlüssel muss eindeutig UND aufsteigend+ohne Lücken sein
Dieses Problem habe ich bei vielen meiner persistenten Objekten: Kunde, Auftrag, Rechnung, Paketnummer
Im Buch Spring & Hibernate wird leider auf einen solchen Fall nicht eingegangen, nichtmal auf den Fall das es überhaupt einen Schlüssel gibt des nicht die ID ist  


Das muss doch irgendwie mit hibernate gehen, also sowas wie:
-suche aus Tabelle "keys" nach dem Schlüssel "next_invoice_key" und speichere in in der Variablen key
-inkrementiere den Wert in der Datenbank um 1
-baue einen String myString mit "R" + key
-Speichere das invoice-Objekt und verwende myString als Rechnungsnummer

Wobei dann noch sichergestellt sein muss das nicht 2 Threads gleichzeitig den Key lesen und damit denselben key verwenden. Außerdem muss das ganze in eine transaktion damit ein Rollback gemacht werden kann (es dürfen ja keine Lücken in der numerierung entstehen)
Edit: sprich: wenn das speichern des Objektes schiefgeht muss man auch den key wieder auf den alten Wert zurücksetzen damit er später wieder verwendet werden kann


----------



## maki (14. Mrz 2008)

> Es schreiben immer viele Anwendungen auf eine DB.


Bei uns nicht 



> Wo soll da ein Problem mit Hibernate sein?


Ganz einfach: Wann werden die Daten in die DB geschrieben? Wenn Hibernate sagt: "Jetzt"
Das ist der Fall wenn die Session endet. Davor sind die Daten zwar in der Anwendung geändert, denn Hibernate weiss ja bescheid, aber in der DB können ohne weiteres die alten Werte stehen 

Die DB ist ja nur aussatz auf das Persistenzframework, aktuelle Daten bekommt man im zweifelsfall nur von der Persistenzschicht, nicht aus der DB über eine andere Connection.

Da Hibernate stark vom Caching gebrauch macht, gibt es ja keinen Grund zweimal dieselben Daten in der Session aus der DB zu lesen... wieso sollte in der DB auch etwas anderes stehen als das was Hibernate gerade selber reingeschrieben hat?

Problematisch wird das, wenn mehrere Anwendungen, d.h. mehrere Hibernate Instanzen offen sind die nix voneinander wissen und versuchen, dieselbe DB als Speicher zu nutzen.

Kurz gesagt: Wenn Hibernate im Spiel ist, sind die Daten in der DB nicht notwendigerweise diesselben wie in der Session.
Auch werden Änderungen in der Db nicht notwendigerweise von Hibernate bemerkt.


----------



## tfa (14. Mrz 2008)

Mit der Generierung solcher Sachen wie Rechnungsnummer, Kundennummer usw. sollte Hibernate meiner Meinung nach nichts zu tun haben. Das ist Sache der Businesslogik und nicht der Persistenzschicht.


----------



## Nova (14. Mrz 2008)

Aber wie soll ich das trennen? Die Rechnungsnummer soll ja wirklich erst beim speichern des Objektes generiert werden, sprich dann wenn auch der technische Schlüssel gesetzt wird.
Wenn ich mir den Schlüssel per Businesslogik erstellen und dann das Objekt gar nicht gespeichert wird habe ich ein Problem (Lücke in der Numerierung).

Vielleicht stehe ich ja auch einfach nur auf dem Schlauch und es ist ganz einfach  ???:L


----------



## byte (14. Mrz 2008)

maki hat gesagt.:
			
		

> > Es schreiben immer viele Anwendungen auf eine DB.
> 
> 
> Bei uns nicht


Willst Du damit sagen, dass bei Euch maximal 1 Client gleichzeitig mit der DB verbunden ist? ???:L Man setzt doch grade eine DB ein, damit viele Clients eine Datenquelle benutzen können. 



> Kurz gesagt: Wenn Hibernate im Spiel ist, sind die Daten in der DB nicht notwendigerweise diesselben wie in der Session.
> Auch werden Änderungen in der Db nicht notwendigerweise von Hibernate bemerkt.


Ja schon klar. Das ist aber ein generelles Problem der Nebenläufigkeit und hat nicht speziell was mit Hibernate zu tun. Wenn Du per JDBC Daten aus der DB liest, diese dann veränderst und dann zurückschreibst, können sich die Daten auch schon in der DB verändert haben. Genauso ist es halt bei Hibernate mit den Sessions. Datenbanken erlauben es leider nicht, dass man z.B. Listener anmelden kann um mitzukriegen, wenn sich Datensätze ändern.  Man muss sich eine geeignete Strategie überlegen, wie man damit umgehen möchte.

Siehe z.B. Optimistic Concurrency Control: http://www.hibernate.org/hib_docs/reference/en/html/transactions.html


Ursprünglich gings mir aber einfach darum, dass man beim Modellentwurf mit Hibernate nicht auf die DB-Constraints verzichten sollte, weil man nicht davon ausgehen kann, dass auf immer und ewig ausschließlich mit diesem Hibernate-Mapping auf die DB zugegriffen wird.


----------



## maki (14. Mrz 2008)

> Willst Du damit sagen, dass bei Euch maximal 1 Client gleichzeitig mit der DB verbunden ist? icon_scratch.gif Man setzt doch grade eine DB ein, damit viele Clients eine Datenquelle benutzen können. icon_wink.gif


Connection Pooling wird schon verwqendet, aber alle Connection weren von einem "Client" benutzt, einer Hibernate Instanz die im JBoss läuft.



> Ja schon klar. Das ist aber ein generelles Problem der Nebenläufigkeit und hat nicht speziell was mit Hibernate zu tun. Wenn Du per JDBC Daten aus der DB liest, diese dann veränderst und dann zurückschreibst, können sich die Daten auch schon in der DB verändert haben. Genauso ist es halt bei Hibernate mit den Sessions. Datenbanken erlauben es leider nicht, dass man z.B. Listener anmelden kann um mitzukriegen, wenn sich Datensätze ändern. icon_wink.gif Man muss sich eine geeignete Strategie überlegen, wie man damit umgehen möchte.


Ganz genau, auch interessant in diesem Zusammenhang:
http://www.hibernate.org/333.html



> Ursprünglich gings mir aber einfach darum, dass man beim Modellentwurf mit Hibernate nicht auf die DB-Constraints verzichten sollte, weil man nicht davon ausgehen kann, dass auf immer und ewig ausschließlich mit diesem Hibernate-Mapping auf die DB zugegriffen wird.


Prinzipiell auch nicht verkehrt, jedoch hatte ich schon den Fall, dass ich in Zweifelhaften Fällen die Relationale Konsitenz zugunsten Hibernates unter den Tisch fallen lies.

zB wenn eine Referenz zwar fachlich nicht null sein darf, im Mapping not-null=false gesetzt war.
Das kommt vor, wenn eine Bidirektionale Beziehung ins Spiel kommt, in der sich beide Objekte kennen, keine referenz null sein darf (in Java), beide Objekte aber noch transient sind, d.h. eines zuerst gespeichert werden muss.


----------



## maki (14. Mrz 2008)

Nova hat gesagt.:
			
		

> Aber wie soll ich das trennen? Die Rechnungsnummer soll ja wirklich erst beim speichern des Objektes generiert werden, sprich dann wenn auch der technische Schlüssel gesetzt wird.
> Wenn ich mir den Schlüssel per Businesslogik erstellen und dann das Objekt gar nicht gespeichert wird habe ich ein Problem (Lücke in der Numerierung).
> 
> Vielleicht stehe ich ja auch einfach nur auf dem Schlauch und es ist ganz einfach  ???:L


Nova, wie tfa bereits gesagt hatte: Einen fachlichen wert zu setzen ist aufgabe der Business Schicht, nicht der Persistenz Schicht oder gar DB.

"Mut zur Lücke" würde dein Problem recht einfach lösen 
Ansonsten wirds' etwas komplizierter...


----------



## Nova (14. Mrz 2008)

Lücken in der Numerieung kämen beim Finanzamt sicher ganz schlecht an :wink: 

Ob es ein fachlicher Schlüssel ist kommt ja auch irgendwie auf die Definition an, denn ich kann mir den Wert ja eben nicht selbst aussuchen und um einen zu "generieren" muss ich die Datenbank nach der derzeit höchsten ID bzw. nächsten ID befragen können. Und an Hibernate vorbei auf der Datenbank zu arbeiten ist wie ja schon erörtert wurde nicht unproblematisch.
Wenn es ein numerischer Wert wäre würde ich die Datenbank auf "auto-increment" stellen und fertig (bzw. in Hibernate einen IdGenerator dafür "misbrauchen"), oder nicht?

Bin für alle  Lösungsvorschläge offen, solange die 3 Bedingungen erfüllt sind
-Rechnungsnummer aufsteigend
-keine lücken
-keine Duplikate


----------



## tfa (14. Mrz 2008)

maki hat gesagt.:
			
		

> "Mut zur Lücke" würde dein Problem recht einfach lösen
> Ansonsten wirds' etwas komplizierter...


Würd ich auch sagen, oder will der Kunde lückenlose Nummern?
Du könntest dann die nächste Nummer in einer DB-Tabelle halten. Das Holen der
Nummer, Abspeichern des neuen Datensatzes und Erhöhen der Nummer geschieht 
in einer Transaktion.


----------



## byte (14. Mrz 2008)

Nova hat gesagt.:
			
		

> Lücken in der Numerieung kämen beim Finanzamt sicher ganz schlecht an :wink:


Dann implementierst Du es so, dass keine Lücken entstehen. Wo ist das Problem?



> Ob es ein fachlicher Schlüssel ist kommt ja auch irgendwie auf die Definition an, denn ich kann mir den Wert ja eben nicht selbst aussuchen und um einen zu "generieren" muss ich die Datenbank nach der derzeit höchsten ID bzw. nächsten ID befragen können.


Alles ausser der Kunstschlüssel sind fachliche Daten. Wenn Du den "nächsten" Datensatz erzeugen willst, guckst Du halt, welches der letzte war und zählst ihn hoch, oder Du hälst den nächsten Wert vor. Aufpassen musst Du nur beim nebenläufigen Schreiben, aber dafür gibts Transaktionen.



> Und an Hibernate vorbei auf der Datenbank zu arbeiten ist wie ja schon erörtert wurde nicht unproblematisch.
> Wenn es ein numerischer Wert wäre würde ich die Datenbank auf "auto-increment" stellen und fertig (bzw. in Hibernate einen IdGenerator dafür "misbrauchen"), oder nicht?


Wieso an Hibernate vorbei? Du holst Dir die letzte Nummer (per HQL, SQL oder was auch immer) und erzeugst dann die nächste Nummer für den neuen Datensatz. Du musst halt nur aufpassen, dass durch Nebenläufigkeit nicht zweimal die gleiche Nummer erzeugt wird. Du musst also in der Transaktion beim commit() prüfen, ob die Nummer tatsächlich noch nicht vergeben ist und im Fehlerfall die Nummer neu berechnen mit den aktualisierten Daten.


----------



## Nova (14. Mrz 2008)

Hm, ok.
Würde also heißen das ich jeweils vor jedem session.save(invoice) mit setInvoiceId die Rechnungsnummer von Hand setzen muss? (und da ich diesen Wert noch nicht habe zuerst aus der Datenbank holen muss)

Wenn ich diese Operationen in eine Methode "generateNextInvocieId" auslagere, d.h. laden der aktuell höchsten Id aus der Datenbank, erhöhen um 1, speichern in datenbank, zurückliefern des Wertes) würde das also ungefähr so aussehen:

```
// Transaktion starten
Invoice invoice = new Invoice();
// hier invoice befüllen
String invoiceId = generateNextInvoiceId();
invoice.setInvoiceId(invoiceId);
session.save(invoice);
// Transaktion beenden
```

Wobei die statische Methode "generateNextInvoiceId" synchronized ist und zur generierung der Rechnungsnummer ausschließlich diese Methode genutzt wird. Diese Methode sollte falls vorhanden wird die bestehende transaktion weiter genutzt und ansonsten einen eigene erstellt.
Nur was passiert bei einem Rollback nach ausführung der Methode? Da könnten dann ja Lücken enstehen, müsste ich also den ganzen Teil:

```
String invoiceId = generateNextInvoiceId();
invoice.setInvoiceId(invoiceId);
session.save(invoice);
```
auch in eine statische synchronized Methode packen? Oder kann ich da mit dem Isolationslevel der Transaktion arbeiten?


----------



## Guest (14. Mrz 2008)

Nova hat gesagt.:
			
		

> @semi:
> Eigenltich ganz einfach, aber wie bekomme ich Hibernate dazu die Variable automatisch hochzuzählen? Vor allem wenn es ein Alphanummerischer Wert ist? Also z.B. Rechnungsnummer "R13456"?
> Hatte bisher direkt mit JDBC gearbeitet und nur "13456" gespeichert und das "R" jeweils vor dem speichern entfernt und nach dem lesen hinzufügt, aber ich denke das ist nicht so das wahr, oder wie würdet ihr das machen?


Ich würde mir dazu eine extra Komponente schreiben, eine Art Sequenzgenerator. Das könnte dir immer 
eine eindeutige Rechnungsnummer generieren. Wenn der Buchstabe vor der eigentlichen Rechnungsnummer
nur Lametta ist, würde ich es nicht speichern.

Gruß,
semi auf der Arbeit ohne Keks im Browser


----------



## Nova (15. Mrz 2008)

Ne reines Lametter ist das nicht, könnte sich ja mal ändern.
Gut bei der Rechnungsnummer nicht unbedingt, aber z.B. die Paketnummer fängt seit kurzem mit einer "0" an, wenn ich das als Zahl speichere kann ich später nicht mehr erkennen ob da eine "0" davor gestanden hat oder nicht. Es ist auch nicht garantiert das die Paketnummer weiterhin rein numerisch bleibt, es könnten irgendwann auch Buchstaben auftauchen (sprich aplhanumerisch).

Hab mir auch schon überlegt einen Prefix in die Datenbank zu speichern, also anstatt R12345 in eine Spalte kommt "R" in eine Spalte und "12345" in eine andere. Die DAO-Schicht könnte das dann wieder zu einem ganzen verbinden. Funktioniert natürlich nur wenn die Buchstaben am Anfang stehen, von daher nicht so das wahre.
Man könnte auch auf die Idee kommen das die Auftragsnummer so aussieht: 303-12345-56789, darin könnte man dann z.B. die Bestellart kodieren (online-Shop, Ladenlokal, Telefon,Fax,...) etc.

Hab auch schon mehrmals gelesen das man seinen eigenen Sequenzgenerator für Hibernate schreiben kann, aber noch nirgends ein HowTo oder Beispiel gefunden  :bahnhof: 

Werde es dann wohl wirklich als Businesslogik auslagern müssen, ist halt recht fehleranfällig, lässt man sich eine Id generieren und nutzt diese nicht entsteht eine Lücke, lassen sich mehrere gleichzeitig eine Id generieren muss sichergestellt sein das jeder seine eigene bekommt usw.


----------



## maki (15. Mrz 2008)

Mache Rechnungsnummer zu einem eigenen Datentyp.
zB:


```
public class InvoiceNumber {

    private String Number;

    // getter & setter
}
```
Natürlich sollte das eine Entity sein.
So kannst du sie nachher anpassen, Validierung und Einschränkungen hinzufügen, etc. pp.

Wenn du diese immer erst kurz vor dem Speichern setzt (aus einem Sequenzgenerator in der Businesslogik), sollten keine Lücken auftreten, solange alles richtig synchronized ist.

Die Id (technischer Schlüssel) kommt von Hibernate/DB, der fachliche aus der Businesslogik.


----------



## byte (15. Mrz 2008)

Eigentlich sollte es reichen, wenn Du kurz vorm Speichern die nächste freie Nummer bestimmst und dann per Transaktion speicherst. Mit einem Unique Constraint auf der Spalte umgeht man das Problem der Nebenläufigkeit, dass ein zweiter Client zuvor kommt. Dann schlägt die Transaktion fehl, wenn die Nummer schon existiert und man bestimmt die nächste. Ansonsten wie Maki schon sagte, auf Java-Seite ein eigener Datentyp im Domänenmodell...

Andere Möglichkeit: Jeweils nächste freie Nummer in einer Tabelle vorhalten und dann mit Locks arbeiten.


----------

