[JAVA] Jusqu'à INSERT et SELECT sur Postgres avec botte de printemps et feuille de thym

Ce que j'ai fait

spring boot Résumé des progrès réalisés pendant les études. Dans Article que j'ai écrit la dernière fois, j'ai fait Hello World avec Spring Boot. Après tout, je travaille localement. Voici ce que j'ai fait cette fois.

--J'ai fait un formulaire de saisie avec thymeleaf --Emission INSERT et SELECT SQL pour postgres DB en utilisant les données d'entrée à l'écran --Afficher le résultat

J'en ai fait un format pour écrire des commentaires pour chaque code et fichier. Ce n'est pas très organisé, donc il semble difficile d'y revenir plus tard ...

Ce qui a été fait

code

https://github.com/PG-practice/lyricWall/tree/simple_ui_connect_db

Diagramme de transition d'écran

spring画面遷移.png

Diagramme de pseudo collaboration

Spring20200503構成.png

Ce que j'ai appris

--Comment faire DI (injection de dépendance) de la botte à ressort. Une tonne. --Flow jusqu'à la connexion DB avec démarrage à ressort et affichage du résultat

Préparation autre que le code

Créer une table

La table a été générée manuellement avec le SQL suivant.

CREATE TABLE IF NOT EXISTS lyricTable (
    artist VARCHAR(32),
    title VARCHAR(32),
    words_writer VARCHAR(16),
    music_composer VARCHAR(16),
    lyric VARCHAR(1024),
    url VARCHAR(256),
    PRIMARY KEY (artist,title)
);

Paramètres de connexion postgres

Ajouter une dépendance

Article que j'ai écrit la dernière fois À l'époque, j'ai pensé que ce serait bien d'avoir postgresql, mais il semble que jdbc soit nécessaire, j'ai donc ajouté ce qui suit.

build.gradle


dependencies {
         //réduction
	compile('org.springframework.boot:spring-boot-starter-jdbc')
}

Description des informations de connexion

Il semble que application.propaties puisse être yaml, alors je l'ai fait yaml. Le dernier nom de base de données dans l'URL est obligatoire.

application.yaml


spring:
 datasource:
    driver-class-name: "org.postgresql.Driver"
    #Le dernier nom de base de données dans l'URL est requis
    url: "jdbc:postgresql://ubuntu:5432/postgres"
    username: "postgres"
    password: "" 

Remarque pour chaque code

Commentaires détaillés pour que vous puissiez y revenir plus tard.

Lyric.java

Lyric.java


public class Lyric {
    private String artist;
    private String title;
    private String words_writer;
    private String music_composer;
    private String lyric;
    private String url;
    
    //Setter, getter, constructeur sans argument
    //(réduction)
}

LyricDaoImpl.java Le code d'interface est omis. Mise en œuvre uniquement.

LyricDaoImpl.java


//De nombreuses instructions d'importation

@Repository
public class LyricDaoImpl implements LyricDao {
    //Modèle pour DI. Tout ce que vous avez à faire est de l'appeler avec AutoWired sans nouveau.
    private final JdbcTemplate jdbcTemplate;
    //DI référence les instances lorsque cela est nécessaire en combinaison avec ci-dessus
    @Autowired
    public LyricDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    //Obtenez des informations sur la chanson du chanteur désigné
    @Override
    public List<Lyric> getSongs(String artistName) {
        //Je veux réécrire le prepareStatement car ce sera une contre-mesure contre l'injection SQL, mais cette fois c'est comme ça.
        String sql = "SELECT * FROM lyricTable WHERE artist='" + artistName + "';";
        List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);

        List<Lyric> list = new ArrayList<Lyric>();
        for(Map<String, Object> result:resultList){
            Lyric lyric = new Lyric();
            lyric.setArtist((String)result.get("artist"));
            lyric.setTitle((String)result.get("title"));
            lyric.setWords_writer((String)result.get("words_writer"));
            lyric.setMusic_composer((String)result.get("music_composer"));
            lyric.setLyric((String)result.get("lyric"));
            lyric.setUrl((String)result.get("url"));
            list.add(lyric);
        }
        return list;
    }

    //INSÉRER plusieurs informations de chanson dans la liste.
    @Override
    public void insertSongs(List<Lyric> list) {
        String sql = "INSERT INTO lyricTable VALUES (?,?,?,?,?,?)";
        //Preparedstatement Plusieurs INSERTs. Je l'ai utilisé tel quel comme modèle. Maintenant ça marche.
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter(){
            
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                Lyric lyric = list.get(i);
                ps.setString(1, lyric.getArtist());
                ps.setString(2, lyric.getTitle());
                ps.setString(3, lyric.getWords_writer());
                ps.setString(4, lyric.getMusic_composer());
                ps.setString(5, lyric.getLyric());
                ps.setString(6, lyric.getUrl());
            }
        
            @Override
            public int getBatchSize() {
                return list.size();
            }
        });

    }

    
}

--ʻInsertSongs () autorise plusieurs INSERT, mais une seule chanson sort de l'écran. ――Il semble que batchUpdate` envoie des requêtes pour chaque taille de lot dans un lot, mais il est lent car il n'INSERT qu'une ligne à la fois simplement en regroupant les connexions. Il semble que le lot INSERT peut être effectué s'il est défini.

LyricServiceImpl.java Le code d'interface est omis. Mise en œuvre uniquement.

LyricServiceImple.java


//De nombreuses instructions d'importation
@Service
public class LyricServiceImpl implements LyricService {
    //Modèle pour DI.
    private final LyricDao dao;

    @Autowired
    public LyricServiceImpl(LyricDao dao) {
        //L'instance de classe d'implémentation est DI
        this.dao = dao;
    }

    @Override
    public List<Lyric> getSongs(String artistName) {
        // TODO Auto-generated method stub
        return dao.getSongs(artistName);
    }

    @Override
    public void insertSongs(List<Lyric> list) {
        // TODO Auto-generated method stub
        dao.insertSongs(list);
    }

}

WebMvcControllerAdvice.java Définir une méthode couramment traitée avant Controller

WebMvcControllerAdvice.java


@ControllerAdvice
public class WebMvcControllerAdvice {

    //Il semble que les caractères vides envoyés depuis html soient traités comme null
    @InitBinder
    public void initBinder(WebDataBinder dataBinder) {
        dataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

    //Si une erreur de base de données se produit, placez un message d'erreur et un objet Form vide dans le modèle et renvoyez le html
    @ExceptionHandler(PSQLException.class)
    public String sqlException(PSQLException e, Model model){
        model.addAttribute("errorMessage",e.getMessage());
        model.addAttribute("searchForm", new SearchForm());
        model.addAttribute("insertForm", new InsertForm());
        return "form.html";
    }
   
}

SearchForm.java Le type qui sera lié à th: object =" $ {searchForm} " dans la balise de formulaire qui contient le bouton de sélection. Le nom de la variable doit correspondre à l'argument de la méthode en html et en contrôleur.

SearchForm.java


//Un peu d'importation etc.

public class SearchForm {
    @NotNull(message="Veuillez entrer le nom du chanteur")
    private String artistName;

   //Setter, getter, constructeur
}

InsertForm.java Le type qui sera lié à th: object =" $ {searchForm} " dans la balise de formulaire qui contient le bouton d'insertion. Le nom de la variable doit correspondre à l'argument de la méthode en html et en contrôleur.

InsertForm.java


public class InsertForm {
//Une petite déclaration d'importation, etc.
    @NotNull(message="La saisie du nom du chanteur est obligatoire pour l'inscription")
    private String artistName;

    @NotNull(message = "La saisie du titre de la chanson est requise pour l'enregistrement")
    private String title;

    private String wordsWriter;
    private String musicComposer;
    private String lyric;
    private String url;

   //Setter, getter, constructeur
}

LyricController.java

LyricController.java


//déclaration d'importation, etc.

//Local"http://localhost:8080/lyric"La demande qui est arrivée est mappée dans cette classe
@Controller
@RequestMapping("/lyric")
public class LyricController {
    //DI sans nouveau
    private final LyricService lyricService;

    @Autowired
    public LyricController(LyricService lyricService) {
        this.lyricService = lyricService;
    }
    //Pour la clé de modèle"insertCompleteMessage"S'il y en a, utilisez-le tel quel, sinon ajoutez
    @GetMapping("/form")
    public String form(Model model, @ModelAttribute("insertCompleteMessage") String message){
        model.addAttribute("title", "artist name");
        //Renvoie le fichier correspondant dans le dossier du modèle
        return "form.html";
    }
    //J'obtiens une erreur de résultat lorsque je suis pris dans l'anotation de validation de la classe SearchForm
    @GetMapping("/select")
    public String select(@Validated SearchForm searchForm, BindingResult result, Model model){
        if(result.hasErrors()){
            model.addAttribute("title", "Error Page");
            return "form.html";
        }
        List<Lyric> list = lyricService.getSongs(searchForm.getArtistName());
        if(list.size()!=0){
            model.addAttribute("lyricList", list);
        }else{
            model.addAttribute("noResultMessage", "La chanson de l'artiste correspondant n'est pas enregistrée");
        }
        return "list.html";
    }
    //Redirection et portée flash pour PRG(Attributs qui disparaissent après une demande)utilisation
    @PostMapping("/insert")
    public String insert(@Validated InsertForm insertForm, BindingResult result, Model model, RedirectAttributes redirectAttributes){
        if(result.hasErrors()){
            model.addAttribute("title", "Error Page");
            return "form.html";
        }
        //Tout d'abord, il n'y a qu'un seul formulaire de saisie. DAO est un format de liste, alors faites une liste avec un élément
        List<Lyric> list = convertInsertForm(insertForm);
        //Ecrire le traitement en cas d'erreur TDDO
        lyricService.insertSongs(list);
        //@GetMapping("/form")Rediriger vers. Paramètres de portée Flash.
        redirectAttributes.addFlashAttribute("insertCompleteMessage", "insert complete");
        return "redirect:form";
    }

    //Même si SearchForm n'est pas inclus dans l'argument ci-dessus, il sera inclus dans le modèle sans autorisation. Le nom de la méthode peut être n'importe quoi.
    @ModelAttribute
    SearchForm setSearchForm(){
        return new SearchForm();
    }    

    @ModelAttribute
    InsertForm setInsertForm(){
        return new InsertForm();
    }

    //Convertir de Inseart Form en Lyric
    public static List<Lyric> convertInsertForm(InsertForm insertForm){
        List<Lyric> list = new ArrayList<>();
        Lyric lyric = new Lyric();
        lyric.setArtist(insertForm.getArtistName());
        lyric.setTitle(insertForm.getTitle());
        lyric.setWords_writer(insertForm.getWordsWriter());
        lyric.setMusic_composer(insertForm.getMusicComposer());
        lyric.setLyric(insertForm.getLyric());
        lyric.setUrl(insertForm.getUrl());
        list.add(lyric);
        return list;
    }

}

--Si l'argument BindingResult result n'est pas placé immédiatement après l'objet Form, une erreur de marque blanche se produira lors de l'accès. L'ordre des arguments est pertinent.

Qu'est-ce que PRG

La dernière redirection de @ PostMapping s'appelle PRG pour éviter la double transmission.

--Double transmission qui peut être empêchée --Après INSERT, renvoyez en mettant à jour le navigateur --Sur la page affichée après INSERT, renvoyez en appuyant à nouveau sur le bouton INSERT. --Double transmission qui semble impossible à empêcher

Si vous définissez la fin sur return form.html et renvoyez du html sans redirection,

  1. L'attribut reste dans model → L'objet reste lié dans le navigateur
  2. La dernière requête devient POST → POSTed lorsque le navigateur est mis à jour Il se trouve que. Pour éviter cela
  3. Dissocier le modèle
  4. Effectuer une requête GET de force et renvoyer le code HTML C'est PRG (POST-REDIRECT-GET) à faire.

La propriété de model n'est pas héritée au moment de la redirection (il a été confirmé par le débogueur que l'objet Form dans le modèle est initialisé au moment de la redirection). Par conséquent, à l'exception des caractères «insertion terminée» dans «redirectAttributes», la réponse est à l'état initialisé. De plus, le message «insertion terminée» est dans la portée flash (qui disparaît avec une seule requête), donc il disparaît lorsque vous actualisez votre navigateur.

list.html L'objet correspondant à la clé donnée à model peut être reçu côté serveur avec $ {nom de propriété}.

list.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeLeaf.org">
<head>
    <meta charset="UTF-8">
    <title th:text="${title}">Insert</title>
</head>
<body>
    <h1 th:text="${title}">title</h1>
    <p th:text="${noResultMessage}"></p>
    <table th:if="${lyricList}">
        <tr>
            <th>Artist Name</th><th>song title</th><th>lyric</th>
        </tr>
        <tr th:each="lyric:${lyricList}">
            <td th:text="${lyric.artist}"></td>
            <td th:text="${lyric.title}"></td>
            <td th:text="${lyric.lyric}"></td>
        </tr>
    </table>
</body>

form.html Écran principal. Accès à http: // localhost: 8080 / lyric / form.

form.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeLeaf.org">
<head>
    <meta charset="UTF-8">
    <title th:text="${title}">Insert</title>
</head>
<body>
    <h1 th:text="${title}">title</h1>
    <h1 th:text="${redirectTitle}">title</h1>
    <form method="GET" action="#" th:action="@{/lyric/select}" th:object="${searchForm}">
        <label for="selArtistNameId">Artist Name:</label>
        <input id="selArtistNameId" name="artistName" type="text" th:value="*{artistName}">
        <div th:if="${#fields.hasErrors('artistName')}" th:errors="*{artistName}"></div>
        <input type="submit" value="search">
    </form>
    <h1>Enregistrement de morceau</h1>
    <form method="POST" action="#" th:action="@{/lyric/insert}" th:object="${insertForm}">
        <label for="insArtistNameId">Artist Name:</label>
        <input id="insArtistNameId" name="artistName" type="text" th:value="*{artistName}">
        <div th:if="${#fields.hasErrors('artistName')}" th:errors="*{artistName}"></div>
        <label for="title">Song Title:</label>
        <input id="titleId" name="title" type="text" th:value="*{title}">
        <div th:if="${#fields.hasErrors('title')}" th:errors="*{title}"></div>
        <label for="wordsWriter">Paroles:</label>
        <input id="wordsWriterId" name="wordsWriter" type="text" th:value="*{wordsWriter}">
        <label for="musicComposer">Composition:</label>
        <input id="musicComposerId" name="musicComposer" type="text" th:value="*{musicComposer}">
        <label for="lyric">Paroles:</label>
        <input id="lyricId" name="lyric" type="text" th:value="*{lyric}">
        <label for="url">URL des paroles:</label>
        <input id="urlId" name="url" type="text" th:value="*{url}">
        <input type="submit" value="insert">
    </form>
    <p th:text="${insertCompleteMessage}"></p>
    <p th:if="${errorMessage}" th:text="'Message d'erreur:'+${errorMessage}"></p>
</body>
</html>

--Par exemple, l'objet $ {insertForm} est associé à la valeur de name = au moment de la transmission et au nom de propriété de l'argument de la méthode de mappage ʻInsertForm dans Controller`.

Découvrir

――Avantages et comportement du DI

référence:

Grâce à l'inclusion de la société dans udemy for Business, GW a été occupé, mais cet article est probablement basé sur les cours suivants. Le contenu est complètement différent, mais certaines méthodes sont empruntées telles quelles. https://www.udemy.com/course/java_spring_beginner/

Recommended Posts

Jusqu'à INSERT et SELECT sur Postgres avec botte de printemps et feuille de thym
Tentative de SSR Vue.js avec Spring Boot et GraalJS
Gérez l'API de date et d'heure Java 8 avec Thymeleaf avec Spring Boot
Jusqu'à "Hello World" avec Spring Boot
Connectez-vous à la base de données avec spring boot + spring jpa et effectuez l'opération CRUD
Flux jusqu'à la sortie des données de la table à afficher avec Spring Boot
8 choses à insérer dans DB en utilisant Spring Boot et JPA
Implémenter CRUD avec Spring Boot + Thymeleaf + MySQL
Implémenter la fonction de pagination avec Spring Boot + Thymeleaf
Ajoutez une botte de printemps et un dégradé à éclipse
Exécutez l'application WEB avec Spring Boot + Thymeleaf
Comment appliquer immédiatement les modifications de Thymeleaf au navigateur avec #Spring Boot + maven
Jusqu'à l'acquisition de données avec Spring Boot + MyBatis + PostgreSQL
Comment utiliser MyBatis2 (iBatis) avec Spring Boot 1.4 (Spring 4)
Comment utiliser h2db intégré avec Spring Boot
Essayez d'implémenter la fonction de connexion avec Spring Boot
Créez une application CRUD avec Spring Boot 2 + Thymeleaf + MyBatis
Créez votre propre utilitaire avec Thymeleaf avec Spring Boot
Essayez d'automatiser la migration avec Spring Boot Flyway
[Java] Article pour ajouter une validation avec Spring Boot 2.3.1.
Je voulais classer la botte à ressort dans un multi-projet
[Introduction à Spring Boot] Fonction d'authentification avec Spring Security
Botte de printemps + Heroku Postgres
Télécharger avec Spring Boot
Plans pour prendre en charge JDK 11 pour Eclipse et Spring Boot
Paramètres de connexion à MySQL avec Spring Boot + Spring JDBC
Essayez d'utiliser un conteneur DI avec Laravel et Spring Boot
Changer d'environnement avec Spring Boot application.properties et l'annotation @Profile
Mappez automatiquement DTO aux entités avec l'API Spring Boot
Si vous souhaitez séparer le traitement Spring Boot + Thymeleaf
Spring Boot avec les paramètres du filtre de sécurité Spring et les points addictifs
[Introduction à Spring Boot] Soumettez un formulaire à l'aide de thymeleaf
Créer Restapi avec Spring Boot (jusqu'à l'exécution de l'application)
Jusqu'à ce que vous commenciez à développer avec Spring Boot dans eclipse 1
Comment démarrer par environnement avec Spring Boot de Maven
Jusqu'à ce que vous commenciez à développer avec Spring Boot dans eclipse 2
Connectez Spring Boot et Angular en toute sécurité avec OpenAPI Generator
Jusqu'à ce que vous créiez un projet Spring Boot dans Intellij et que vous le transmettiez à Github
Essayez Spring Boot de 0 à 100.
Générer un code à barres avec Spring Boot
Hello World avec Spring Boot
Implémenter GraphQL avec Spring Boot
Démarrez avec Spring Boot
Implémenter l'API REST avec Spring Boot et JPA (Application Layer)
Implémenter l'API REST avec Spring Boot et JPA (couche d'infrastructure)
Découpez SQL en fichier de propriété avec jdbcTemplate of spring boot
Exécutez LIFF avec Spring Boot
Connexion SNS avec Spring Boot
Introduction à Spring Boot ① ~ DI ~
Téléchargement de fichiers avec Spring Boot
Spring Boot commençant par copie
Introduction à Spring Boot ② ~ AOP ~
Comment appeler et utiliser l'API en Java (Spring Boot)
Spring Boot à partir de Docker
Hello World avec Spring Boot
Définir des cookies avec Spring Boot
Utilisez thymeleaf3 avec le parent sans spécifier spring-boot-starter-parent dans Spring Boot
Utiliser Spring JDBC avec Spring Boot
Avec Spring Boot, hachez le mot de passe et utilisez l'enregistrement des membres et la sécurité Spring pour implémenter la fonction de connexion.
Ajouter un module avec Spring Boot