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 ...
https://github.com/PG-practice/lyricWall/tree/simple_ui_connect_db
--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
model
et retourner html.
--PRG (POST-REDIRECT-GET), une des mesures pour éviter la double transmission
--Multiple INSERT par spring jdbc + instruction préparée semble être lent?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)
);
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')
}
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: ""
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)
}
@ Entity
(peut-être parce que je n'ai pas de DI?)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";
}
}
model
. J'essaie d'accéder à une propriété en html, donc j'obtiens une erreur s'il n'y a pas d'objet (voir ci-dessous). Quelle est la meilleure pratique?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.
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,
model
→ L'objet reste lié dans le navigateurmodèle
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`.
SearchForm
dans l'argument, le nom de propriété de ʻartistNameest le même, donc il a également été lié en même temps.
@ ModelAttribute` est correct car la valeur n'était pas automatiquement associée au formulaire non lié.model
et que vous le renvoyez, vous obtiendrez une erreur de marque blanche due à quelque chose comme th: value =" $ * {artistName} "
. C'est pourquoi le gestionnaire d'erreurs de WebMvcControllerAdvice.java
crée volontairement une instance et la place dans model
.――Avantages et comportement du DI
<div th:if="${#fields.hasErrors('title')}" th:errors="*{title}"></div>
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