Guten Tag ,
Was macht man in Spring um NullPointer zu vermeiden , falls Repository null liefert?
Ich habe folgendes Repository:
Java:
@RepositorypublicinterfaceProgrammRepositoryextendsJpaRepository<Programm,ProgrammPK>{@Query(value="select max(programm_id) from sco.programm",nativeQuery=true)intgetMaxId();}
Aber wenn ich noch keine Daten in der DB habe ,liefert es natürlich null.
Folgende code liefert mir deswegen Nullpointerexception:
Java:
int programm_id_int = progRepository.getMaxId()+1;
[/code]
There was an unexpected error (type=Internal Server Error, status=500).
Null return value from advice does not match primitive return type for: public abstract int scoSpring.repo.ProgrammRepository.getMaxId()
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract int scoSpring.repo.ProgrammRepository.getMaxId()
Evtl. noch als Hinweis:
ANSI SQL wäre die coalesce Methode wohl eine Möglichkeit. Dann hätte man etwas, das Datenbankübergreifend hoffentlich funktionieren wird.
Evtl. noch als Hinweis:
ANSI SQL wäre die coalesce Methode wohl eine Möglichkeit. Dann hätte man etwas, das Datenbankübergreifend hoffentlich funktionieren wird.
Ich habe native benutzt, weil ich habe es nicht geschafft vom Model die max id zu kriegen weil es bei mir multiple Primary key ist und in einer getrennte Klasse liegt.->
Code:
@Entity
@NamedQuery(name="Programm.findAll", query="SELECT p FROM Programm p")
public class Programm implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private ProgrammPK id;
public Programm(ProgrammPK id, Timestamp hinzugefuegt, String programm) {
super();
this.id = id;
this.hinzugefuegt = hinzugefuegt;
this.programm = programm;
}
und das ist die PrimaryKey classe
Java:
@EmbeddablepublicclassProgrammPKimplementsSerializable{//default serial version id, required for serializable classes.privatestaticfinallong serialVersionUID =1L;@Column(name="projekt_id", insertable=false, updatable=false)privateint projektId;@Column(name="programm_id")privateint programmId;publicProgrammPK(){}publicProgrammPK(int projektId,int programmId){this.projektId=projektId;this.programmId=programmId;}
Natürlich wäre besser vom PK classe max programm_id zu bekommen ,aber ich weis nicht wie.
OptionalInt könnte auch klappen, dass wäre die bessere Wahl als Integer mit null.
Mit COALESCE musst du aufpassen und sicherstellen, dass 0 kein valider Wert ist, negative Zahlen solltest du dann wahrscheinlich ebenso ausschließen.
"die größte Id" ist in den meisten Fällen seehr merkwürdig, was willst du damit denn machen? Vielleicht gibt es einen deutlich sinnvolleren Weg. Eine ID sollte ja üblicherweise semantisch keine Zahl sein, sondern eben nur ein Identifier.
An der Stelle finde ich die Konstruktion mit einer eigenen ID Klasse, aber Max-Werten aus der DB fragwürdig.
Als PK sollte man entweder verwenden:
* Eine technische ID - am besten aus der DB per Sequence oder ähnlichem erzeugt
* Eine fachliche ID, die aus der Anwendung kommt
Das hier sieht etwas nach einem Mix aus beiden aus, weswegen man da eine maximale ID aus der DB braucht. Bei so Eigenbaulösungen "Lese maximale ID aus der DB, erhöhe um 1" läuft man extrem schnell in Probleme rein, die gemeinerweise im Test kaum auftreten. Probleme, die da schnell entstehen:
* Was ist wenn es zwei Instanzen der Anwendung (per Load-Balancer) gibt, die auf die gleiche DB zu greifen? Da wird das lustig mit den Caches von Hibernate und Co.
* was ist, wenn es mehrere Threads gibt, die gleichzeitig ein neues Objekt anlegen wollen? Da können schnell Race Conditions entsehen.
Daher würde ich tatsächlich mal hinterfragen: Wofür braucht man die maximale ID? Entweder sollte das ein fachlicher Schlüssel sein - dann brauche ich keine maximale ID. Oder es ist ein technischer - dann sollte es Aufgabe der DB sein mir den nächsten verfügbaren zu geben.
Wenn man die Id selbst ermitteln will und dazu eine gewisse Logik aufbaut, die außerhalb der DB liegt und lediglich den Max-Wert abfragt, dann bekomme ich da immer Bauschschmerzen.
Was ist, wenn da mehrere Threads sowas versuchen? Dann sehen beide, dass Max von mir aus 5 ist um dann die 6 zu nutzen?
Aber die genaue Logik, die verwendet wurde, ist ja nicht bekannt. Daher kann man da natürlich nichts sagen. Und evtl. findet ja auch alles in einer Transaktion statt, die gewisse Teile der Datenbank dann auch sperrt um sowas zu vermeiden?
Aber in der Beziehung hätte ich hier eher meine Sorgen muss ich gestehen.
Die Id würde ich der Datenbank überlassen. Dann würdest Du lediglich ein Programm haben, bei dem die programm_id null / 0 ist (Halt den Wert, der signalisiert, dass die id noch nicht vergeben wurde). Beim Speichern wird dann die Datenbank die id vergeben und die id lässt man sich dann beim Speichern auch zurück geben und trägt dies dann in seiner Entity ein. Das findet dann aber innerhalb des Repositories statt.
Das wäre so ein Vorgehen, das man verwenden könnte. Wenn das ProgramPK eine entsprechende Entity ist, dann kann das schon entsprechend sein - das finde ich gerade etwas verwirrend - für einen zusammengesetzten PK habe ich eigentlich keine eigene Entity - ist das auf Datenbankseite auch so abgebildet, dass das eine eigene Klasse ist?
Die Modelle wurden automatisch so generiert. Ich habe die nur erweitert. Autoinkrement auf programm_id zu setzen kann ich auch nicht. Die Programme gehören in der DB immer zu einem Projekt und beim löschen von Projekten, werden auch die dazugehörige Programme mit gelöscht.
Also wie ist es am besten zu lösen wenn es kein autoinkrement für programm_id möglich ist und wie bekommt man in dem Fall die max id vom Modell und nicht von der DB mit nativeQuerry?
Also erst einmal ist der Weg technisch so möglich (Egal wie unschön das so ist).
So ein unique index das Insert vorhandener Programm_id Werte verhindert, kann man das so bauen.
Dann willst Du aber den Fall abfangen, dass die programm_id schon vorhanden ist -> um dann ein Insert ggf. erneut zu probieren.
Also wie ist es am besten zu lösen wenn es kein autoinkrement für programm_id möglich ist und wie bekommt man in dem Fall die max id vom Modell und nicht von der DB mit nativeQuerry?
Wenn ich dich richtig verstanden habe du meinst autoincrement? Wenn ja ,dann geht nicht nativ(mehrere andere Tabelen wo diese id als Teil des PK ist..zmb(
Java:
@EmbeddablepublicclassQuelltextPKimplementsSerializable{//default serial version id, required for serializable classes.privatestaticfinallong serialVersionUID =1L;@Column(name="projekt_id", insertable=false, updatable=false)privateint projektId;@Column(name="programm_id", insertable=false, updatable=false)privateint programmId;@Column(name="zeilen_nr")privateint zeilenNr;
)
wie es emuliert geht kenne ich nicht und deswegen nicht ausprobiert.
Wenn ich dich richtig verstanden habe du meinst autoincrement? Wenn ja ,dann geht nicht nativ(mehrere andere Tabelen wo diese id als Teil des PK ist..zmb(
Ob die ID irgendwo als Fremdschlüssel benutzt wird, ist dafür aber völlig egal. Wenn es eine Programm-Tabelle gibt, kann die programmId trotzdem "AUTO_INCREMENT" sein.
ERROR 1833: Cannot change column 'programm_id': used in a foreign key constraint 'kommentar_programm' of table 'sco.kommentar'
Leider ist nicht egal..also direkt in der DB geht nicht.
Hab es auf AI gestzt mit SET FOREIGN_KEY_CHECKS = 0; und after update wieder auf SET FOREIGN_KEY_CHECKS = 1;
So Jetzt habe programm_id autoincrement..dan muss ich doch Alle Modelle ändern?
Ich habe 11 Modelle ,wo es wirklich ein Teil der Id ist)) Und nachdem ich das Program gespeichert habe muss ich die programm_id kennen für die restlichen Tabellen.Das ging mit einfachen Java db drivers mit Statement.getGeneratedKeys() wie es in Spring geht muss ich nachschauen.
Ich habe 11 Modelle ,wo es wirklich ein Teil der Id ist)) Und nachdem ich das Program gespeichert habe muss ich die programm_id kennen für die restlichen Tabellen.Das ging mit einfachen Java db drivers mit Statement.getGeneratedKeys() wie es in Spring geht muss ich nachschauen.
@RepositorypublicinterfaceProgrammRepositoryextendsJpaRepository<Programm,ProgrammPK>{@Query(value="select last_insert_id() from sco.programm",nativeQuery=true)intgetLastId();}
und im Controller nach dem insert für andere Tabelen :
Java:
progRepository.save(programm);
programm_id_int = progRepository.getLastId();for(int i =0; i <Werte.length -1; i++){QuelltextPK quellPK =newQuelltextPK(projekt_Id,programm_id_int,i +1);Quelltext quel=newQuelltext(quellPK,Werte[i][1]);
quellRepository.save(quel);}
EmployeeGenerator übernimmt dabei die Abfrage und Generierung der ID. Grundsätzlich kann man das auch so aufbauen, dass es generisch für verschiedene Typen verwendbar ist. Es wird dann nur sehr unübersichtlich.
Ich danke dir für die Beispiele .Ich weiß, dass du mir etwas zeigen willst, was sehr nützlich sein soll ,aber ich verstehe nicht was. Es ist genau so wie die Aussage von mrBrown ,wegen Tutorial gucken. Ich weiß ,dass ich sehr viele Fehler mache , aber weswegen ich Tutorials angucken soll und welche verstehe ich nicht. Ich habe sehr viele geguckt und sogar von baeldung.com Spring Data JPA: The Certification Class bezahlt und zu 60% durchgemacht. Leute ich bin kein Profi in Spring, ich lerne erst gerade. Jetziger Stand ist dass ich die programmId autoinkrement in der DB gemacht habe ,und direkt nach dem Inserten generierte id mit der Querry @Query(value="select last_insert_id() from sco.programm" ,nativeQuery=true) für die nachfolgende Tabellen ermittle und speichere.
Es ist genau so wie die Aussage von @mrBrown ,wegen Tutorial gucken. Ich weiß ,dass ich sehr viele Fehler mache , aber weswegen ich Tutorials angucken soll und welche verstehe ich nicht. Ich habe sehr viele geguckt und sogar von baeldung.com Spring Data JPA: The Certification Class bezahlt und zu 60% durchgemacht. Leute ich bin kein Profi in Spring, ich lerne erst gerade.
Das "Warum" ist einfach: du hast, plump ausgedrückt, keine Ahnung, was du dort machst.
Ein spezifisches Tutorial kann ich dir nicht empfehlen, "Spring Data JPA: The Certification Class" klingt aber zumindest dem Titel nach nicht völlig falsch. Wenn du aber wirklich 60% gemacht hast und trotzdem dieser Code bei raus kommt ist der Kurs sein Geld nicht wert...
Für den "richtigen" Weg solltest du dir mal angucken, was die save-Methode so zurückgibt, und warum sie das macht. Ein "last_insert_id" ist da keinesfalls nötig, das macht JPA quasi alles automatisch.
Also normalerweise hast du ja Entities. Beim Speichern Word die id in der gespeicherten Instanz gesetzt. Wenn du nun also die Id der gespeicherten Instanz irgendwo brauchst, dann kannst du diese da bekommen. (Siehe Code in #30)
Nach dem Speichern noch die Datenbank befragen kann böse enden. Wenn zwei Elemente parallel gespeichert werden, dann bekommen ggf. Beide Threads die gleich last inserted id.
Aber du hast in dem Datenmodel oft auch nicht fremde Ids sondern Referenzen. Daher brauchst Du auch die Id in der Regel nicht sondern du arbeitest mit der Instanz, die du dann zuweist.
"solche Programme" heißt in diesem Fall etwas nicht funktionierendes, sonst gäb's hier ja keine Fragen
Das "keine Ahnung" war auch nicht böse gemeint, dass sollte einfach nur eine stumpfe Feststellung sein, da du eben nicht genau weißt, wie man mit JPA arbeitet – ein gutes Tutorial würde da helfen, oder alternativ jemand, der es dir erklärt (falls du grad eine Ausbildung oder Studium machst)
Hier wird das Model angesprochen, das letztendlich auch die max id von der DB hollen soll.Die Frage:lohnt es sich soviel Aufwand wegen dem was auch das hier macht?:->
Java:
@Query(value="select COALESCE(MAX(programm_id),0) from sco.programm",nativeQuery=true)intgetMaxId();
Danke ,das wusste ich nicht. Aber vor dem ersten Speichern brauche ich doch die Id,die ich mit der DB klären soll. Auch wie Oneixee5 zeigt das Objekt könnte man generieren ,nur vor dem allerersten speichern muss ich doch die Id von der DB holen?
Danke ,das wusste ich nicht. Aber vor dem ersten Speichern brauche ich doch die Id,die ich mit der DB klären soll. Auch wie Oneixee5 zeigt das Objekt könnte man generieren ,nur vor dem allerersten speichern muss ich doch die Id von der DB holen?
Die ID dient in der Regel nur dazu, die Beziehungen in der DB zu speichern (das übernimmt aber alles JPA) und Objekte technisch zu identifizieren, wenn sie einmal gespeichert sind. Dementsprechend braucht mit die ID - wenn überhaupt - erst nach dem speichern.
Danke ,das wusste ich nicht. Aber vor dem ersten Speichern brauche ich doch die Id,die ich mit der DB klären soll. Auch wie @Oneixee5 zeigt das Objekt könnte man generieren ,nur vor dem allerersten speichern muss ich doch die Id von der DB holen?
ProgrammPK programmPK=new ProgrammPK(projekt_Id);
Programm programm=new Programm(programmPK,new Timestamp(System.currentTimeMillis()),progra_name);
int programm_id_int=progRepository.save(programm).getId().getProgrammId();
System.out.println("programm_id_int="+programm_id_int);
programm_id_int=0
2021-08-27 12:20:59.808 WARN 11400 --- [nio-8080-exec-9] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1452, SQLState: 23000
Programm id ist in der DB autoincrement..warum liefert save 0 obwohl in der DB wir es richtig gespeichert?
The GeneratedValue annotation may be applied to a primary key property or field of an entity or mapped superclass in conjunction with the Id annotation. The use of the GeneratedValue annotation is only required to be supported for simple primary keys. Use of the GeneratedValue annotation is not supported for derived primary keys.
Sofern das kein echtes Praxis Projekt ist - verzichte auf den Composite Primary Key und nimmt einfache, technische Ids vom Typ Long. Das was du da versuchst zu bauen ist architektonisch gelinde gesagt Unfug.
Entweder es gibt eine Primary Key Spalte, die aus der Datenbank generiert wird (ID+GeneratedValue) dran
ODER
es gibt einen Primary Key, der komplett fachlich ist und nicht auf irgendwelche Datenbank-IDs beruht.
Du vermischst das gerade und hast so das schlechteste aus beiden Welten.
Leides ist es und es geht nicht ohne Composite Primary Key weil jedes Programm gehört zu bestimmten Projekt und alle von restlichen Tabellen gehören zu irgendeinem Programm von irgendeinem Projekt um das Löschen zu kaskadieren.
Leides ist es und es geht nicht ohne Composite Primary Key weil jedes Programm gehört zu bestimmten Projekt und alle von restlichen Tabellen gehören zu irgendeinem Programm von irgendeinem Projekt um das Löschen zu kaskadieren.
Ich sehe da den Grund nicht, es sei denn jemand hat ein komplett falsches Datenmodell gebaut.
Jedes Objekt hat einen Primary Key - ID vom Typ Long.
Wenn ein Programm zu einem Projekt gehört, dann gibt es einen Foreign Key von Programm => Projekt (der diese technischen IDs) nutzt.
Ein Primary Key dient dazu ein Objekt zu identifizieren - und nicht zu sagen "Das gehört aber zu X". Das sind Foreign Keys. Wenn möglich, zeig mal dein Datenbank Schema mit Beziehungen zwischen den Tabellen - bzw. dein fachliches Datenmodell.
Ja, ich habe das gemacht wo es noch nicht autoincrement war und dem Projekt 1 Programm1 gewesen war, und im Projekt 2 auch das Programm 1 möglich war .Jetzt habe ich das autoincrement gemacht und man kann Primary Key ändern.Ich teste es.