Datenbank mit REST API

matze86

Bekanntes Mitglied
Hallo, wie mir freundlicherweise schon im anderen Thread der Hinweis gegeben wurde, das "vor" einer Datenbank eine REST-API muss.

Ich habe mich schon ein bisschen schlau gemacht (die REST-API greift auf die Datenbank zu und holt sich so die Daten), aber bevor ich in falsche Richtungen mich orientiere, habe ich mal 2 Fragen.

Kann man eine REST-API selbst bauen (Java, Python)? Und wenn ja gibt es (vielleicht deutsche) Anleitungen dazu?

Ziel ist so etwas auf einen eigenen Linux-Server bzw einen Linux-Server VPS-Server zu installieren.
 

KonradN

Super-Moderator
Mitarbeiter
Kann man eine REST-API selbst bauen (Java, Python)?
Ja, das ist kein Thema und sogar relativ einfach möglich.

Und wenn ja gibt es (vielleicht deutsche) Anleitungen dazu?
Ich kenne vor allem Englische Anleitungen. Man kann sich da z.B. Spring Boot oder Quarkus ansehen. (Und da auch mal nach Deutschen Anleitungen suchen. Sollten aber nicht zu alt sein, da sich beide Frameworks zügig weiter entwickeln.)
Ziel ist so etwas auf einen eigenen Linux-Server bzw einen Linux-Server VPS-Server zu installieren.
Das geht dann ganz ohne Probleme.
 

matze86

Bekanntes Mitglied
OK, das hört siech für mich schon mal vielversprechend an, nur das ich Spring Boot und Quarkus noch nie gehört habe.
Braucht man das unbedingt?
 

KonradN

Super-Moderator
Mitarbeiter

matze86

Bekanntes Mitglied
Bisher bin ich der Anleitung gefolgt
und habe darüber hinaus noch weitere Video-Tutorial angeschaut.

Jetzt bin ich soweit, das ich zu erste Test's komme, doch leider gibt er ein Fehler aus.
Code:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class
Das heißt ich muss eine Datenbanktabelle anlegen.

Aber zum generellen testen möchte ich es ohne Datenbank machen. Geht das überhaupt?
 

Jw456

Top Contributor
Aber zum generellen testen möchte ich es ohne Datenbank machen. Geht das überhaupt?
Du könntest die AutoConfiguration von Spring erstmals deaktivieren zum testen.
Dazu in den application.properties


Java:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

setzen.
 

matze86

Bekanntes Mitglied
Selbst mit einer existierenden Datenbank mit den Zeilen
Java:
spring.datasource.url=jdbc:mysql://ipzudatenbank:3306/testdatenbank
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.jpa.hibernate.ddl-auto=update

meldet der Compiler:
Code:
Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
    If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
 

matze86

Bekanntes Mitglied
Ich glaube zu wissen diesen bei spring boot initializr mit angegeben zu haben.

Wie kann man so etwas prüfen?
Edit: so sieht meine pom.xml aus:

XML:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>de.resttest</groupId>
    <artifactId>matze</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>matze</name>
    <description>das ist ein test fuer rest</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Nach dem Ausführen wird in der Zeile 5 (<parent>) eine Fehlermeldung angezeigt mit unzähligen Einträgen.
 
Zuletzt bearbeitet:

matze86

Bekanntes Mitglied
Jetzt habe ich viele Anleitungen sorgfältig durchgearbeitet,z.B. auch diese, Das mit den Ausgaben von allen Daten und eins hinzufügen klappt schon mal ohne Probleme.

Jetzt wollte ich in der Klasse Controller eine Methode hinzufügen, welche ein bestimmter Name oder nur eine bestimmte id ausgibt.
Leider klappt das noch nicht so wie ich mir das vorstelle.
Ich habe eine Klasse "User" erstellt, was name und ort beinhaltet, inklusive den Methoden set- und getter.
Dann eine Klasse UserRebo, die als interface von CrudRepository dient (was kann man noch hier eintragen?).
Und eine Klasse ControllerUser, welche beim Aufruf über eine url angesteuert wird und die "GetMapping" - Methoden ausgewertet werden.

Nun möchte ich alles erweitern, so das man entweder nach einer id oder einen Namen sucht und ausgeben lässt.

Aufrufen möchte ich die letzte Methode im controller "getID"
Postman gibt mir einen Fehler wenn ich "localhost:8082/api/user/2" eingebe.

Die id 2 gibt es wenn ich "all" ausgebe sehe ich es.
Hier mal die Klassen damit ihr wisst, was ich bis jetzt gemacht habe.

Java:
@Entity
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    private String name;
    private String ort;

    public void setID(Long id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
  public void setOrt(String ort) {
      this.ort = ort;
  }
 
  public Long getID() {
      return this.id;
  }
 
  public String getName() {
      return this.name;
  }
 
  public String getOrt() {
      return this.ort;
  } 

}

Java:
public interface UserRepo extends CrudRepository<User, Long>{

    List<User> findByName(String lastName);
    User findById(long id);
    
}
Java:
@Controller
@RequestMapping("api") //der Pfad nach der url:port also url:port/api    -- name kann beliebeig sein
public class ControllerUser{
    // Dies bedeutet, die Bean namens userRepository zu erhalten
    // Wird von Spring automatisch generiert, wir werden es verwenden, um die Daten zu verarbeiten   
    @Autowired
    private UserRepo userRepository;
    
    @PostMapping("/add") //nur an post -Anfragen  also url:port/api/add
    // @ResponseBody bedeutet, dass der zurückgegebene String die Antwort ist, kein Ansichtsname
    // @RequestParam bedeutet, dass es sich um einen Parameter aus der GET- oder POST-Anfrage handelt
    public @ResponseBody String addNewUser (@RequestParam String name, @RequestParam String ort) {
        User n = new User();
        n.setName(name);
        n.setOrt(ort);
        userRepository.save(n);
        return "Saved";
    }
    
    @GetMapping("/all") //nur anzeigen -Anfragen  also url:port/api/all
    public @ResponseBody Iterable<User> getAllUsers() {
    // Dies gibt ein JSON oder XML mit den Benutzern zurück
    return userRepository.findAll();
    }
    
    //die besagt Methode funktionier so leider nicht,
    @GetMapping("user/{id}")
    public Optional<User> getID(@PathVariable Long id) {
        return userRepository.findById(id);
    }
        
        
        
}
Ich wäre für paar Tipps sehr dankbar.
 

KonradN

Super-Moderator
Mitarbeiter
Postman gibt mir einen Fehler wenn ich "localhost:8082/api/user/2" eingebe.
Wie war das mit meinem Auto? Mein Auto gibt mir einen Fehler. Kannst Du mir sagen, was kaputt ist?

Was ist denn so schwer daran, einfach einmal genau zu sagen, was Du zurück bekommst? Die Response Codes haben alle eine Bedeutung und die kann man durchaus mit angeben.

Ansonsten fällt mir auf, dass Du in den Mappings nicht einheitlich mit den / arbeitest. Ich habe eigentlich immer, dass ich mit / starte. Sprich:
@GetMapping("/user/{id}")

Ob das weggelassen werden kann, kann ich im Augenblick nicht sagen, da ich mir das im Detail so nicht angesehen habe. (Für mich startet man mit einem / und gut ist es - damit bin ich bisher sehr gut gefahren. Und ich werde es jetzt nicht ausprobieren, ob es auch anders geht.)

Und bitte: Fang nicht mit Abkürzungen an! Das ist ein UserRepository und nicht UserRepo oder so! Und gibt Variablen vernünftige Bezeichner! n ist kein vernünftiger Bezeichner. Zumal n für einen User? Das kann der createdUser sein - dann ist klar: In der Variable ist der User drin, der erzeugt wurde!
 

matze86

Bekanntes Mitglied
Also mit dem "/" ist es egal, habe es gerade getestet, aber werde mir angewöhnen es immer mit "/" zu machen.
Postman gibt mir aus:
Java:
{
    "timestamp": "2022-12-07T12:17:30.612+00:00",
    "status": 404,
    "error": "Not Found",
    "path": "/api/user/2"
}
 

matze86

Bekanntes Mitglied
So ich habe jetzt nochmal alles "durchstudiert", jetzt habe ich die Methoden "put", "delete" und "get" erstellt bzw. überarbeitet.
Jetzt funktioniert es wie es soll.
Alle Methoden funktionieren. Die Frage ist ob es vom Code sauber ist.
Hier erstmal der Controller:
Java:
@Controller
@RequestMapping("/api") //der Pfad nach der url:port also url:port/api    -- name kann beliebeig sein
public class ControllerUser{
    @Autowired
    private UserRepo userRepository;
    
    @PostMapping("/add")
    public @ResponseBody String addNewUser (@RequestParam String name, @RequestParam String ort) {
        User user = new User();
        user.setName(name);
        user.setOrt(ort);
        userRepository.save(user);
        return "Saved";
    }
    
    @GetMapping("/all")
    public @ResponseBody Iterable<User> getAllUsers () {
        return userRepository.findAll();
    }
    

    //hier wird explizit die ID als Parameter angegeben
    @GetMapping("/user")
     public @ResponseBody Optional<User> nameSearch(@RequestParam Long id) {
            return userRepository.findById(id);
      }
    
    //die ID wird an die URL gehängt
    @GetMapping("/user/{id}")
     public @ResponseBody Optional<User> idSearch(@PathVariable Long id) {
            return userRepository.findById(id);
    }
    
    //ein Eintrag löschen mit dem Parameter ID
    @DeleteMapping("/del")
     public @ResponseBody String loeschen(@RequestParam Long id) {
        userRepository.deleteById(id);
        return "gelöscht";     
    }
    
    //einen Eintrag verändern
    @PutMapping("/put")
     public @ResponseBody String put(@RequestParam Long id, @RequestParam String ort) {
        User user = new User();
        //gesetzt wird die übergebene ID und den Ort
        user.setID(id);
        user.setOrt(ort);
        //hier wird der Name zurückgegeben
        String name = userRepository.findById(id).get().getName();
        //den Namen setzen
        user.setName(name);
        //und speichern
        userRepository.save(user);
        return "erfolgreich";
    }
    
    //hier wird explizit nach dem Namen gesuch und wiedergegeben
    @GetMapping("/einzelausgabe")
     public @ResponseBody List<User> einzel(@RequestParam String name) {
            return userRepository.findByName(name);
      }     
}

Was mir noch für Fragen aufwerfen ist, das es viele Tutorial's gibt und alle unterschiedliche Controller mit verschiedenen Methoden haben wie z.B. ResponseEntity.

Mir stellt sich die Frage was braucht man wann und was wirklich?
Oder kann man mit meinen Controller "leben" der alles beinhaltet?
 

Oneixee5

Top Contributor
Sauber ist es eigentlich nicht. User ist eine Entity, es ist ein Antipattern eine Entity aus einem Rest-Endpunkt zurückzugeben. Man würde ein UserDTO erstellen und dieses zurückgeben.
Der Hintergrund ist: Stell dir vor jemand erweitert User und schreibt da ein Feld rein, welches schutzwürdig ist, z.B.: sexuelle Orientierung. Jetzt gibt dein Rest-Endpunkt automatische diese Daten weiter. Das sollte nicht passieren, auch wenn die Daten nicht direkt in deiner Programmoberfläche oder Webseite sichtbar sind, können sie trotzdem ausgelesen werden. Daher erstellt man ein DTO-Objekt, welches nur die Daten enthält, welche der Endpunkt zurückgeben soll. Auch wenn die Daten im Moment identisch sind. Programme werden sich aber mit der Zeit verändern.
Eine vernünftige IDE(-Einstellung) würde dich auch automatisch auf das Problem hinweisen. Es empfiehlt sich immer die statische Codeanalyse einzuschalten/installieren, z.B.: Sonarlint finde ich ganz gut für Einsteiger.
 
Zuletzt bearbeitet:

Oneixee5

Top Contributor
Put macht mir auch Schwierigkeiten:
Java:
    //einen Eintrag verändern
    @PutMapping("/put")
     public @ResponseBody String put(@RequestParam Long id, @RequestParam String ort) {
        User user = new User();
        //gesetzt wird die übergebene ID und den Ort
        user.setID(id);
        user.setOrt(ort);
        //hier wird der Name zurückgegeben
        String name = userRepository.findById(id).get().getName();
        //den Namen setzen
        user.setName(name);
        //und speichern
        userRepository.save(user);
        return "erfolgreich";
    }
Ich würde es eher so schreiben:
Java:
    //einen Eintrag verändern
    @PutMapping("/put")
    @Transactional
     public @ResponseBody String put(@RequestParam Long id, @RequestParam String ort) {
        User user = userRepository.findById(id).orElseThrow(...);
        user.setOrt(ort);
        //und speichern
        userRepository.save(user); // das sollte gar nicht nötig sein, weglassen
        return "erfolgreich";
    }
Statt "erfolgreich" geben wir eigentlich immer das vollständige Object zurück. Es können sich zwischenzeitlich noch weiter Felder geändert haben. So kann man sein Programm aktualisieren.
 
Zuletzt bearbeitet:

matze86

Bekanntes Mitglied
Sauber ist es eigentlich nicht. User ist eine Entity, es ist ein Antipattern eine Entity aus einem Rest-Endpunkt zurückzugeben. Man würde ein UserDTO erstellen und dieses zurückgeben.
Der Hintergrund ist: Stell dir vor jemand erweitert User und schreibt da ein Feld rein, welches schutzwürdig ist, z.B.: sexuelle Orientierung. Jetzt gibt dein Rest-Endpunkt automatische diese Daten weiter. Das sollte nicht passieren, auch wenn die Daten nicht direkt in deiner Programmoberfläche oder Webseite sichtbar sind, können sie trotzdem ausgelesen werden. Daher erstellt man ein DTO-Objekt, welches nur die Daten enthält, welche der Endpunkt zurückgeben soll. Auch wenn die Daten im Moment identisch sind. Programme werden sich aber mit der Zeit verändern.
Eine vernünftige IDE(-Einstellung) würde dich auch automatisch auf das Problem hinweisen. Es empfiehlt sich immer die statische Codeanalyse einzuschalten/installieren, z.B.: Sonarlint finde ich ganz gut für Einsteiger.

Was heißt das jetzt genau? Wie sollte ich vorgehen? Was ist ein DTO-Objekt? Vielleicht kannst du dazu ein Schema verlinken was aufzeigt wie man es richtig macht.
 

matze86

Bekanntes Mitglied
Ich habe mir mal die Seite angeschaut, versucht es zu verstehen. Aber leider kommen mehr Fragen auf als Antworten.
Ich weiß nicht mal wie ich den Code verstehen soll, wo soll er hin, wie binde ich mein vorhandenes Rest-Projekt ein?
Was macht die Klasse
Java:
PostDto
u.s.w. Leider habe ich im Netz auch nicht so viel gesehen, was mich weiter bringt.
 

Oneixee5

Top Contributor
Ich habe mir mal die Seite angeschaut, versucht es zu verstehen. Aber leider kommen mehr Fragen auf als Antworten.
Ich weiß nicht mal wie ich den Code verstehen soll, wo soll er hin, wie binde ich mein vorhandenes Rest-Projekt ein?
Was macht die Klasse
Java:
PostDto
u.s.w. Leider habe ich im Netz auch nicht so viel gesehen, was mich weiter bringt.
In dem Artikel gibt es einen Link zu Github, da kann man sich den fertigen Code anschauen.
 

KonradN

Super-Moderator
Mitarbeiter
Also das Beispiel hat eine Entity Post und man will nun nicht dieses Entity raus geben sondern nur ein modifiziertes PostDTO.
Daher braucht man nun eunen Mapper, der aus einer Post Instanz eine PostDTO Instanz macht.

Das ist alles. Und das kannst Du 1:1 umwandeln. Dazu "Post" durch den Namen deiner Entity ersetzen.
 

matze86

Bekanntes Mitglied
Alles gut und schön, aber ich würde gerne wissen wie das ganze im Prinzip arbeitet, also auch die einzelnen Methoden etc.
Ich wünschte mir in den Anleitungen steht über jede Method und Klasse eine kleine Beschreibung. Es wird leider immer alles im Groben beschrieben.
 

mihe7

Top Contributor
aber ich würde gerne wissen wie das ganze im Prinzip arbeitet, also auch die einzelnen Methoden etc.
Ich wünschte mir in den Anleitungen steht über jede Method und Klasse eine kleine Beschreibung.
Im Artikel findest Du einen Link auf http://modelmapper.org/getting-started/ - dort steht beschrieben, wie der ModelMapper funktioniert inkl. Benutzerhandbuch.

Ansonsten besteht ja kein Zwang, diese Bibliothek - oder überhaupt eine - für das Mapping zu verwenden. Wir erzeugen z. T. direkt JsonObject-Instanzen, die wir zurückgeben oder haben DTO-Klassen, die wir manuell füllen bzw. als Adapter fungieren.
 

matze86

Bekanntes Mitglied
Ansonsten besteht ja kein Zwang, diese Bibliothek - oder überhaupt eine - für das Mapping zu verwenden. Wir erzeugen z. T. direkt JsonObject-Instanzen, die wir zurückgeben oder haben DTO-Klassen, die wir manuell füllen bzw. als Adapter fungieren.
Genau, das meine ich ja, ich habe aktuell kein grundsätzlichen Überblick wie so etwas generell funktionieren muss.
Aber ich werde mich nach und nach mal durcharbeiten, inkl. mit den hilfreichen Tipps hier im Forum.
Wenn ich mal noch eine Frage melde ich mich zu gegebener Zeit nochmal.
 

mihe7

Top Contributor
Genau, das meine ich ja, ich habe aktuell kein grundsätzlichen Überblick wie so etwas generell funktionieren muss.
Ganz grundsätzlich ist es einfach so, dass über die Leitung keine Objekte sondern eine Serialisierung der Objekte bzw. der Objektdaten gehen können. Damit Sender und Empfänger Daten austauschen können, müssen sie die "gleiche Sprache" sprechen. In Webservices wird oft JSON verwendet, weil es a) weit weniger geschwätzig als XML ist und b) von JavaScript im Browser sowieso verstanden wird.

Jetzt hast Du also ein Person-Objekt und das muss irgendwie, sagen wir mal in JSON, serialisiert werden. SpringBoot/Jakarta EE können das automatisch: man gibt als Rückgabetyp z. B. Person an, liefert ein Person-Objekt zurück und das Framework erzeugt daraus einen JSON-Text.

Statt nun direkt ein Person-Objekt zurückzuliefern, kann man auch eine "Sicht" auf eine Person (oder auf Person und Objekte weiterer Klassen) zurückliefern, dazu kann man ein DTO verwenden (s. dazu Kommentar #21 von @Oneixee5).

Ein DTO ist einfach ein Objekt, das eben gerade die Daten enthält, die über die Leitung gehen sollen. Erstellt werden kann dieses wie jedes andere Objekt auch: man schreibt eine Klasse, erzeugt eine Instanz, setzt die Properties.

Nehmen wir mal an, wir hätten eine Person-Klasse, die Name, Vorname und Geburtsdatum darstellt. Aus Gründen des Datenschutzes sollen aber nur Name und Vorname über den REST-Endpoint geliefert werden.
Java:
class PersonDTO {
    public String name;
    public String vorname;

    public PersonDTO(Perosn perosn) {
        name = person.getName();
        vorname = person.getVorname();
    }
}
Gibst Du jetzt in Deiner REST-Methode statt Person PersonDTO zurück, wird niemals das Geburtsdatum über die Leitung gehen. Die Person-Klasse kann später auch weitere Attribute erhalten, die die Außenwelt nichts angehen: das DTO stellt sicher, dass diese nicht übermittelt wreden.
 

matze86

Bekanntes Mitglied
Ich habe mal das ganze im Ruhigen durchgearbeitet und versuch die Zusammenhänge zu verstehen.
Als erste nehme ich mal das Beispiel von https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application.

Das Prinzip ist, soweit ich es verstanden habe, ich habe wie in mein Post 17 und 20 eine normale User-Klasse, eine User-Repository-Klasse, eine Controller-Klasse.
Hinzu füge ich in folgendes: in der UserdtoApplication Klasse
Java:
@Bean
public ModelMapper modelMapper() {
    return new ModelMapper();
}
Warum auch immer das braucht.

Dann lege ich eine Klasse UserDTO , die z.b. nur die ID und den Namen frei gibt. Der Ort bleibt "geheim".

In Der Controller-Klasse lege ich diese Methode an
Java:
  private userDTO convertToDto(User user) {
        userDTO userdto = modelMapper.map(user,UserDto.class);
        userDTO.setSubmissionDate(post.getSubmissionDate(),
            userService.getCurrentUser().getPreference().getTimezone());
        return userDTO;
Die "wandelt" den die User Klasse in UserDTO um.

Java:
@GetMapping(value = "/{id}")
    @ResponseBody
    public userDTO getPost(@PathVariable("id") Long id) {
        return convertToDto(userRepository.findById(id));
    }
In der Methode wird wird dann nur der Namen von User angezeigt, nicht mehr der Ort.
Ist das so in etwa richtig?

Was ich "noch" nicht verstanden habe ist, was hat es mit den ganzen "Service" unter https://github.com/eugenp/tutorials...in/java/com/baeldung/springpagination/service auf sich?
 

mihe7

Top Contributor
Warum auch immer das braucht.
Das Spring-Framework muss für die Injection einer ModelMapper-Instanz via @Autowired wissen, wie es eine Instanz von ModelMapper erzeugen kann. Das wird hier mit einer Factory-Method erledigt, die mit @Bean annotiert werden muss.

Ist das so in etwa richtig?
Ja, wobei ich das nur im Grundsatz sagen kann, weil ich den ModelMapper nicht kenne.

Was ich "noch" nicht verstanden habe ist, was hat es mit den ganzen "Service" unter https://github.com/eugenp/tutorials...in/java/com/baeldung/springpagination/service auf sich?
Die Annotation dient m. W. schlicht dazu, eine Spring-Komponente im Code explizit als Service (i. S. des Domain-Driven-Design) und nicht einfach als gewöhnliche Komponente darzustellen.
 
Zuletzt bearbeitet von einem Moderator:

matze86

Bekanntes Mitglied
OK. Also soweit habe ich es mal an meinen Code angepasst.
Also könnte ich die Services weg lassen und das in UserDTO einpflegen?

So sieht mein aktueller angepasster Code aus:

Der Controller
Java:
@Controller
@RequestMapping("/api")
public class ControllerUser{

    @Autowired
    private UserRepo userRepository;
    
    @Autowired
    private ModelMapper modelMapper;

    @GetMapping("/user/{id}")
    @ResponseBody
    public UserDTO getPost(@PathVariable Long id) {
        return convertToDto(userRepository.findById(id));
    }
   
     private UserDTO convertToDto(User user) {
         UserDTO userdto = modelMapper.map(user,UserDTO.class);
         return userdto;
     }
    
    
     private UserDTO convertToEntity(UserDTO userDTO) throws ParseException {
         UserDTO user = modelMapper.map(userDTO,UserDTO.class);
         return user;
     }
    
}
Da meckert Eclipse (Zeile 14), Die Methode convertToDto(User) im Typ ControllerUser ist für die Argumente nicht anwendbar (Optional<User>).

Die Methode convertToEntity muss ich auch noch vervollständigen, aber erst einmal muss es mit convertToDto funktionieren.

Der normale User:
Java:
@Entity
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    private String name;
    private String ort;

    public void setID(Long id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
  public void setOrt(String ort) {
      this.ort = ort;
  }
 
  public Long getID() {
      return this.id;
  }
 
  public String getName() {
      return this.name;
  }
 
  public String getOrt() {
      return this.ort;
  } 

}
Und der UserDTO
Java:
public class UserDTO {
    
    private String name;
    private Long id;
    
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setID(Long id) {
        this.id = id;
    }
    
     public Long getID() {
          return this.id;
      }
      
      public String getName() {
          return this.name;
      }
}

Irgend etwas fehlt in meinen Code noch, denn es gibt mir noch alles inkl. den Ort aus.
 

Oneixee5

Top Contributor
Dien Repository gibt offenbar ein Optional statt eines Entity zurück.
Java:
public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {
        super(message);
    }

}

...
   
    @GetMapping("/user/{id}")
    @ResponseBody
    public UserDTO getPost(@PathVariable Long id) {
        return convertToDto(userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("Unavailable")));
    }
 

matze86

Bekanntes Mitglied
Ich bin gerade dabei vom UserDTO zum User einen Eintrag zu verändern.

Dabei legte ich die Methode "convertToEntity" an und die Methode die ein Name verändert.
Im Beispiel wurde gezeigt, dass man auch "UserDTO" mit als Parameter übergeben soll.
Jetzt stellt sich mir die Frage wie ich das zum testen in Postman eingeben soll und ob das so überhaupt funktioniert, denn ich möchte ja den Namen von UserDTO (der nur von außerhalb sichtbar ist) ändern, das User den Namen ändert.

Vielleicht kann mir jemand einen kleinen Tipp geben, ich habe vom vielen lesen leider den durchblick verloren.

Java:
//einen Eintrag verändern
@PutMapping("/put")
@ResponseBody
public UserDTO put(@RequestParam Long id, @RequestBody UserDTO userDto, @RequestParam String name) throws ParseException {
    User user = convertToEntity(userDto);
    user.setName(name);
    userRepository.save(user);
    return convertToDto(userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("Unavailable")));
}

//von UserTDO zum User, hier uebergebe ich den modelMapper den UserDTO und soll ihn in User "umwandeln"
private User convertToEntity(UserDTO userDTO) throws ParseException {
    User user = modelMapper.map(userDTO,User.class);     
    return user;
}
 

matze86

Bekanntes Mitglied
Das ganze habe ich jetzt nochmal auf komplett anderen Wege gemacht.
Es funktioniert auch und zeige es euch mal:
UserRepository
Java:
public interface UserRepo extends JpaRepository<User, Long>{
    List<User> findByName(String lastName);
    User findById(long id);
}
User
Java:
@Entity
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    private String name;
    private String ort;

    public void setID(Long id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
  public void setOrt(String ort) {
      this.ort = ort;
  }
 
  public Long getID() {
      return this.id;
  }
 
  public String getName() {
      return this.name;
  }
 
  public String getOrt() {
      return this.ort;
  } 
}
UserDTO
Java:
public class UserDTO {
    
    private String name;
    private Long id;
    
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setID(Long id) {
        this.id = id;
    }
    
    public Long getID() {
          return this.id;
    }
      
    public String getName() {
          return this.name;
    }
}
UserConverter
Java:
@Component
public class UserConverter {

    
    public UserDTO entityToDTOo(User user) {
        ModelMapper mapper =new ModelMapper();
        UserDTO map = mapper.map(user, UserDTO.class);
        return map;
    }
    
    public List<UserDTO> entityToDTO(List<User> user) {
        return user.stream().map(x -> entityToDTOo(x)).collect(Collectors.toList());
    }
    
    public User dtoToEntity(UserDTO userdto) {
        ModelMapper mapper = new ModelMapper();
        User map = mapper.map(userdto, User.class);
        return map;
    }
    
    public List<User> dtoToEntity(List<UserDTO> userdto){
        return userdto.stream().map(x -> dtoToEntity(x)).collect(Collectors.toList());
    }   
}
und der Controller
Java:
@RestController
@RequestMapping("/api")
public class ControllerUser{

    @Autowired
    private UserRepo userRepository;
    
    @Autowired
    private UserConverter converter;

    //Alles ausgeben
    @GetMapping("/all")
    public List<UserDTO> findAll(){
        List<User> findAll = userRepository.findAll();
        return converter.entityToDTO(findAll);
    }
    
    //Ausgabe Objekt mit bestimmter ID
    @GetMapping("/user/{id}")
    public UserDTO findById(@PathVariable Long id) {
        User orElse = userRepository.findById(id).orElse(null);
        return converter.entityToDTOo(orElse);
    }
    
    //ein kompletes neues Objekt hinzufuegen
    @PostMapping("/neu")
    public UserDTO save(@RequestParam String name) {
        UserDTO dto = new UserDTO();
        dto.setName(name);
        User user = converter.dtoToEntity(dto);
        user =  userRepository.save(user);
        return converter.entityToDTOo(user);
    }
    
    //einen Eintrag verändern
    //und dien gesamten eintrag zurueck geben
    @PutMapping("/put")
    public UserDTO put(@RequestParam Long id, @RequestParam String name) {
        UserDTO dto = new UserDTO();
        dto.setName(name);
        dto.setID(id);
        User user = converter.dtoToEntity(dto);
        user =  userRepository.save(user);
        
        return converter.entityToDTOo(user);
    }
    
    //ein Eintrag löschen mit dem Parameter ID
    @DeleteMapping("/del")
    public List<UserDTO> loeschen(@RequestParam Long id) {
        userRepository.deleteById(id);
            
        return converter.entityToDTO(userRepository.findAll());
    }
    
    //hier wird explizit nach dem Namen gesuch und wiedergegeben
    @GetMapping("/einzelausgabe")
    public List<UserDTO> einzel(@RequestParam String name) {
        List<User> user = userRepository.findByName(name);
        return converter.entityToDTO(user);
    }             
}

Ist der Code so vertretbar?
 

matze86

Bekanntes Mitglied
Wir würden den User aus dem Repo holen und die Properties des DTO auf den persistenten User mappen.
Also im Prinzip umgekehrt?
Das die es nicht den User sonder den UserDTO in der Repo vertreten ist?


Zum Thema Sicherheit, Jetzt muss ich noch ein Username und auch Passwort vergeben. Welche Methode ist hier so Zielführend und sicher?
 

mihe7

Top Contributor
Also im Prinzip umgekehrt?
Was heißt umgekehrt?

Das die es nicht den User sonder den UserDTO in der Repo vertreten ist?
Nein.

Es geht einfach darum, dass Du aus dem DTO direkt einen User erstellst. Diese Entity kann aber nur über die Informationen verfügen, die im DTO existieren, der Rest wäre "leer". Würdest Du nun das so erzeugte User-Objekt im Repo speichern, wären die ggf. im existierenden User bereits vorhandenen Informationen verloren.

Spendier der User-Entity einfach mal ein Geburtsdatum. Jetzt bekommst Du von außen eine Änderung via UserDTO, das neben der ID nur den Namen enthält. Du erzeugst nun ein neues User-Objekt mit ID und Namen und speicherst den Spaß im Repository ab. Damit überschreibst Du Dir das Geburtsdatum des bereits vorhandenen Users.

Deswegen machen wir etwas wie
Java:
User user = userRepository.findById(dto.getId());
user.setName(dto.getName()); // mappen der DTO-Properties auf den User
userRepository.save(user);
 

KonradN

Super-Moderator
Mitarbeiter
OK.
Und wie ist das mit Authorization? Kann man da OAuth2.0 nehmen?
Das ist ja jetzt vom bisherigen Thema losgelöst. Generell ist das der Weg, den ich empfehlen würde. Also ein separates Identity Management (z.B. Keycloak - das ist einfach aufzusetzen auf Entwickler Systemen) und dann wird das lediglich genutzt.

Damit umgehst Du viele kritische Dinge wie z.B, die sicherer Verwahrung von Daten. (Du hattest das speichern von Passwörtern erwähnt. Wenn, dann wird nur ein Hash - oder besser: "seeded hash" - gespeichert, aber niemals die Passwörter.) Etwas, das Du nicht speicherst, das kann nicht böswillig gelesen werden.

Und es bietet Firmen die Möglichkeit, da eigene identity Management Lösungen zu nutzen. (Kennt evtl. der eine oder andere: man will sich irgendwo anmelden wie z.B. Microsoft und nach Eingabe der Email Adresse erscheint dann ein Login des Firmen Systems)
 

matze86

Bekanntes Mitglied
Spendier der User-Entity einfach mal ein Geburtsdatum. Jetzt bekommst Du von außen eine Änderung via UserDTO, das neben der ID nur den Namen enthält. Du erzeugst nun ein neues User-Objekt mit ID und Namen und speicherst den Spaß im Repository ab. Damit überschreibst Du Dir das Geburtsdatum des bereits vorhandenen Users.

Aber was ich mich gerade frage, das geht nur bei Objekten die schon existieren.

Das geht aber nicht wenn man ein neues Objekt erstellt, denn der DTO User hat ja nur "Rechte" auf name und id.
Da macht ja das erstellen keinen Sinn über DTO User. Oder wie ist das in der Prxis?
 

KonradN

Super-Moderator
Mitarbeiter
Aber was ich mich gerade frage, das geht nur bei Objekten die schon existieren.
Das Aktualisieren eines vorhandenen Users macht nur Sinn, wenn der User existiert. Ja :)

Das geht aber nicht wenn man ein neues Objekt erstellt, denn der DTO User hat ja nur "Rechte" auf name und id.
Da macht ja das erstellen keinen Sinn über DTO User. Oder wie ist das in der Prxis?
Prinzipiell geht es genau so beim Erstellem von neuen Usern.

Aber die fachlichen Fragen kannst nur Du klären. Generell kann man mehrere DTO Klassen haben. Dann ahst Du halt noch eine UserCreateDTO Klasse, die nur Felder hat, die beim Erzeugen Sinn machen.
 

matze86

Bekanntes Mitglied
Genau, die habe ich jetzt erstellt und funktioniert wie es funktionieren soll.

Bei der Put-Methode hatte ich einen Denkfehler, da wurde der ort z.b. immer gelöscht.
Jetzt sieht es so aus, und es funktioniert auch.
Vielen dank euch allen.

Java:
@PutMapping("/put")
    public UserDTO put(@RequestParam Long id, @RequestParam String name) {
        UserDTO dto = new UserDTO();
        dto.setID(id);
        dto.setName(name);
        User user = userRepository.findById(dto.getID()).orElse(null);
        user.setName(dto.getName());
        userRepository.save(user);
        return converter.entityToDTOo(user);
    }
 

KonradN

Super-Moderator
Mitarbeiter
Also Dein Code ist so wenig Sinnvoll:

a) Warum erzeugst Du erst ein DTO, wenn Du es eh nicht brauchst? DTO sind Data Transfer Objects - also zur Weitergabe von Daten. Aber in Zeile 3 des Codes erzeugst Du eine Instanz, die Du nicht weiter gibst. Das ist so also schlicht sinnlos. In Zeile 6 kannst Du direkt die id verwenden und in Zeile 7 den Namen. Die DTO Klasse kommt also wirklich erst am Ende ins Spiel.

b) Was soll dieses orElse(null)? Damit kann user null sein und Du rennst in der folgenden Zeile in eine NPE. Und die NPE ist eine typische "EntwicklerIstZuBlödException" - das sollte dir bewusst sein!
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
torresbig MySQL hibernate - could not resolve entity class ... (Datenbank Anfänger) Datenbankprogrammierung 19
M Meine Datenbank lässt sich mit meiner Methode nicht ändern Datenbankprogrammierung 1
I SaaS Applikation: pro Kunde eine Datenbank / Schema oder eine DB für alle Kunden? Datenbankprogrammierung 76
T SQL-Statement Datenbank nach SQL Statement schließen? Datenbankprogrammierung 7
M Datenbank Zugraff nach Umwandlung in .jar-Datei nicht mehr möglich Datenbankprogrammierung 4
Auf MySql Datenbank zugreifen funktioniert nicht Datenbankprogrammierung 8
MongoDB-Datenbank in Androidstudio einbinden Datenbankprogrammierung 1
thor_norsk Datenbank: Apache Derby Datenbankprogrammierung 6
B SQlite Datenbank, trotz Statements wurden nicht alle Zeilen erzeugt? Datenbankprogrammierung 35
M Entfernte Datenbank Datenbankprogrammierung 11
T Datenbank: Tabelle erstellen bei Web-Applikation Datenbankprogrammierung 4
D SQLite Datenbank in Android Studio (Java) durchsuchen Datenbankprogrammierung 3
M MySQL Datenbank in Array Datenbankprogrammierung 2
S Den letzten Eintrag aus Datenbank ziehen (Oracle SQL Dev.) Datenbankprogrammierung 14
N Datenbank abfragen nach bestimmten Wort Datenbankprogrammierung 7
Beginner22 Zugriff auf Datenbank aus Java über mariadb Datenbankprogrammierung 3
ma095 value NULL- Datenbank Postgresql Spring - intellij community Datenbankprogrammierung 0
raptorrs Plötzlich keine Verbindung zur Datenbank mehr möglich Datenbankprogrammierung 14
Davee SQLite SQLite Datenbank lässt sich nicht auf anderen PCs öffnen Datenbankprogrammierung 8
P Datenbank Tool - besser als oracle SQL Developer gesucht mit effizinte Verbindungsverwaltung Datenbankprogrammierung 2
X JPA (EclipseLink) und Oracle Datenbank Datenbankprogrammierung 2
T Datenbank auf einer Webseite aus einer Exceltabelle erstellen Datenbankprogrammierung 5
L SQL Datenbank Datenbankprogrammierung 7
L SQL Datenbank INSERT INTO Datenbankprogrammierung 6
L SQL Datenbank Tabelle insert Datenbankprogrammierung 7
L SQL Datenbank Tabelle erstellen Datenbankprogrammierung 6
nonickatall MySQL Auf neue Datensätze in einer Datenbank reagieren Datenbankprogrammierung 5
W MYSQL Datenbank Login Android Datenbankprogrammierung 3
anton1 Online Datenbank Datenbankprogrammierung 7
krgewb H2-Datenbank öffnen Datenbankprogrammierung 8
J Datenbank abfragen Datenbankprogrammierung 6
P Verbindung zu einer Access Datenbank per Eclipse oder Intellij Datenbankprogrammierung 0
O SQL Abfragen mit Mini Datenbank Datenbankprogrammierung 12
Z Datenbank Choicebox wird nicht befüllt Datenbankprogrammierung 15
S Suche In SQL Datenbank mit array Datenbankprogrammierung 6
P Enum in der Datenbank Datenbankprogrammierung 1
Z SQL- Datenbank 1.PK zu 2.FK Datenbankprogrammierung 3
E netbeans - jsp Daten in Datenbank hinzufügen Datenbankprogrammierung 2
bueseb84 Spring Boot : Update Mysql Datenbank Datenbankprogrammierung 1
Avalon Attribute werden mit Unterstrich in eine MySQL Datenbank eingetragen Datenbankprogrammierung 10
MiMa wo Datenbank verbinden/trennen? Datenbankprogrammierung 1
H MySQL Verbindung Datenbank und Eclipse (Java) Datenbankprogrammierung 5
DeltaPilot12 Datenbank connect Funktion Datenbankprogrammierung 7
J Oracle Datenbank-Tabelle per Combobox (oder ähnliches) auswählen Datenbankprogrammierung 3
N SQL Datenbank Spalte in Java Array Datenbankprogrammierung 2
J JAR-Datei und Datenbank Datenbankprogrammierung 8
Bluedaishi Datenbank Abfrage Datenbankprogrammierung 36
Kirby.exe Zeile aus der Datenbank holen wenn ein match besteht Datenbankprogrammierung 7
Kirby.exe Es werden keine Einträge in der Datenbank gemacht Datenbankprogrammierung 23
C Datenbank anlegen und über eine Website mit Daten füllen? Datenbankprogrammierung 25
J Welche Kriterien haben Einfluss auf die Geschwindigkeit einer Datenbank ? Datenbankprogrammierung 4
B Frage zu Datenbank Design bei Events (ZenDesk) Datenbankprogrammierung 1
M SQLite Datenbank mit SQLite Datenbankprogrammierung 7
C String in Datenbank einfügen Datenbankprogrammierung 11
C Keinen Zugrift auf Datenbank Datenbankprogrammierung 2
C Datenbank zugreifen Datenbankprogrammierung 10
L Auf Strato Datenbank zugreifen Datenbankprogrammierung 7
H Fehler bei getConnection zu MySQL Datenbank Datenbankprogrammierung 18
G Datenbank Statement Datenbankprogrammierung 22
M Datenbank editierbach machen in JTable Datenbankprogrammierung 13
S Datenbank MySQL und Java Datenbankprogrammierung 8
M H2 Verbindung zur Datenbank komplett schließen Datenbankprogrammierung 11
J Aufbau meiner Datenbank/Tabelle - Verbessern? So lassen? Datenbankprogrammierung 39
J Nur CRUD über Datenbank Klasse, oder auch mehr ? Datenbankprogrammierung 2
Dimax MySQL Maximale Datenlänge für eine Spalte einer Datenbank in java ermitteln Datenbankprogrammierung 15
L Appabsturz mit Datenbank Datenbankprogrammierung 4
J Zahlungseingänge von mehreren Kunden wie am besten abbilden in der Datenbank ? Datenbankprogrammierung 8
L Datenbank sichern Datenbankprogrammierung 8
S Daten von SQLite Datenbank nutzen Datenbankprogrammierung 5
J Datenbank VPS Server Datenbankprogrammierung 3
H MySQL MySQL - Keine Verbindung zur Datenbank?! Datenbankprogrammierung 4
NIckbrick MySQL Befehle aus Datenbank auslesen Datenbankprogrammierung 21
S Datenbank/Java true/false Datenbankprogrammierung 8
J JUNIT und CRUD-Datenbank Datenbankprogrammierung 4
F Datenbank/Datenabgleich/Wiederholungsabfrage Datenbankprogrammierung 12
@SupressWarnings() Eure bevorzugte Datenbank-Library Datenbankprogrammierung 9
C datenbank verbindung config Datenbankprogrammierung 23
L MySQL Android zu externer MySQL Datenbank verbinden Datenbankprogrammierung 5
M Datenbank vor unerlaubtem Zugriff durch Benutzer schützen Datenbankprogrammierung 3
U MySQL Aus Servlet in Datenbank schreiben Datenbankprogrammierung 4
H Datenbank Export mit Java Datenbankprogrammierung 3
P Daten in eine mySQL Datenbank einfügen Datenbankprogrammierung 4
S SQL-Statement Datenbank Zeitbereich durchsuchen Datenbankprogrammierung 2
L Messenger App - Wie am besten auf Datenbank zugreifen? Datenbankprogrammierung 4
H MySQL Datenbank auf Xampp nimmt keine Mediumblob an. Datenbankprogrammierung 0
D Datenbank Abfrage Datenbankprogrammierung 7
H MySQL Benutzer Login System mit Datenbank Informationen (Abfrage zu User ist auf DB gesichert) Datenbankprogrammierung 42
L Oracle Datenbank über Java aufrufen Datenbankprogrammierung 29
DaCrazyJavaExpert Derby/JavaDB Unfindbarer Fehler im Datenbank-Code Datenbankprogrammierung 87
DaCrazyJavaExpert Derby/JavaDB Wert einer Variable in der Datenbank direkt auf 1 setzten. Datenbankprogrammierung 71
W MySQL Ausgabe von Datenbank in Java-App Datenbankprogrammierung 6
M portable Datenbank Datenbankprogrammierung 2
S Name aus der Datenbank bekommen Datenbankprogrammierung 2
K Eclipse: JBoss Hibernate Tool: Kein Zugriff auf Datenbank Datenbankprogrammierung 5
MaxG. Datenbank werte vergleichen Datenbankprogrammierung 5
MaxG. Access Datenbank Datenbankprogrammierung 48
@SupressWarnings() HSQLDB Datenbank für mein Dorfbauspiel "Time of Kings" Datenbankprogrammierung 6
K SQLite Datenbank in App integrieren: Vor Auslesen schützbar? Datenbankprogrammierung 6
K Java Datenbank auslesen Datenbankprogrammierung 8
M MySQL Anbindung und Abfrage an die Datenbank Datenbankprogrammierung 2

Ähnliche Java Themen

Neue Themen


Oben