[JAVA] Ce que j'ai fait lors de la migration de la série Spring Boot 1.4 vers la série 2.0

introduction

Dernière fois est passé de Spring Boot 1.5 à Spring Boot 2.0. Cette fois, dans un autre projet, j'écrirai sur ce à quoi j'étais accro quand je l'ai élevé de 1.4 à 2.0.

La principale difficulté cette fois était

--Il a fallu beaucoup de temps pour déterminer si la cause du problème était la partie 1.4-> 1.5 ou la partie 1.5-> 2.0. --Thymeleaf a une grande influence, donc les tests sont difficiles --Sérialisation / désérialisation autour de Spring Session --Contre-mesures pour le déploiement en production

C'est autour.

Augmenter la version de Spring Boot

Mise à jour de la version spring-boot-starter-parent

Spécifiez la version de Spring Boot dans pom.xml.

pom.xml


  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath />
  </parent>

Dépendances Hikari PC supprimées

J'utilisais à l'origine HikariPC, je vais donc le supprimer des dépendances.

pom.xml


    <dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
    </dependency>

Compiler et écraser les erreurs

SpringBootServletInitializer introuvable

1.png

Le package de SpringBootServletInitializer a changé, alors réimportez-le.

Org.springframework.boot.context.embedded manquant.

2.png

Le paquet a changé en ʻorg.springframework.boot.web.servlet.`, alors réimportez-le

DataSourceBuilder introuvable

3.png

Le paquet a changé, alors réimportez-le

WebMvcConfigurerAdapter obsolète

Remplacez ʻextends WebMvcConfigurerAdapter par ʻimplements WebMvcConfigurer

@EnableWebMvcSecurity obsolète

Changez pour @ EnableWebSecurity.

https://docs.spring.io/spring-security/site/docs/current/reference/html/mvc.html

org.apache.velocity.app introuvable

4.png

Migrez de «vitesse» à «moustache».

pom.xml


-    <dependency>
-	<groupId>org.apache.velocity</groupId>
-	<artifactId>velocity</artifactId>
-    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-mustache</artifactId>
+    </dependency>

Remplacez le fichier de modèle de vitesse * .vm par le fichier de modèle de moustache * .mustache.

Selon le format du modèle de moustache

${hoge}
↓
{{hoge}}

Remplacez tout comme ça

Vous pouvez tous les remplacer à la fois par shell, mais si vous le faites avec IntelliJ refactor, le code de l'appelant sera également trouvé et indiqué, donc si le nombre est petit, IntelliJ refactor peut être bon.

org.json introuvable

5.png

Ajoutez la dépendance suivante

pom.xml


	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-json</artifactId>
	</dependency>

Réimporter avec le package suivant org.springframework.boot.configurationprocessor.json.JSONObject

Erreur dans SpringApplication.run

6.png

L'argument de SpringApplication.run a changé

Modifié comme suit

        public static void main(String[] args) {
-               Object[] objects = { HogeApplication.class, FugaService.class };
-               SpringApplication.run(objects, args);
+               final SpringApplication application = new SpringApplication(HogeApplication.class, FugaService.class);
+               application.run(args);
        }

De plus, héritez de la classe suivante

SpringBootServletInitializer

Correspondance autour du changement de méthode JPA

7.png

Je vais le réparer en silence

https://spring.io/blog/2017/06/20/a-preview-on-spring-data-kay#improved-naming-for-crud-repository-methods

AutoConfigureTestDatabase introuvable

8.png

import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase; De import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; Changer le package en

Migration de Thymeleaf

https://www.thymeleaf.org/doc/articles/thymeleaf3migration.html

Remplacer th: remplacer par par th: remplacer

Mécaniquement comme ça

find src/main/resources -type f -name "*.html" -print | xargs sed -i  -e 's/th:substituteby/th:replace/g'

Suppression de type = "text / css" pour lire le CSS dans la balise de lien

Mécaniquement comme ça

find src/main/resources -type f -name "*.html" -print | xargs sed -i  -e 's@type=\"text/css\"@@g'

Supprimer inline = "text" / inline = "inline"

Tout en regardant le contenu, supprimez-le.

Thymeleaf dans la balise Script n'est pas développée

Un tel mec

<script>(window.dataLayer || (window.dataLayer = [])).push(<span th:remove="tag" th:utext="${hoge}"/>)</script>

Réparez-le comme ça

<script type="text/javascript" th:inline="javascript">/*<![CDATA[*/
     (window.dataLayer || (window.dataLayer = [])).push(/*[(${hoge})]*/)
 /*]]>*/</script>

Autre

Dans Spring Boot, si @EnableWebMvc est attaché, supprimez-le

SessionScope

Erreur lorsque @ SessionScope ne peut pas être référencé lors de l'exécution de SpringSecurity ʻonAuthenticationSuccess Error creating bean with name 'user': Scope 'session' is not active for the current thread;`

Créez le fichier de configuration suivant

WebRequestContextListener.java


package jp.hoge;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextListener;

import javax.servlet.annotation.WebListener;

@Configuration
@WebListener
public class WebRequestContextListener extends RequestContextListener {
}

Erreur dans la méthode Hibernate SaveAndFlush

java.sql.SQLSyntaxErrorException: Table 'hoge.hibernate_sequence' doesn't exist
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:536)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115)
	at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983)
	at com.mysql.cj.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1826)
	at com.mysql.cj.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1923)
	at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)

Changer GenerationType

-    @GeneratedValue(strategy=GenerationType.AUTO)
+    @GeneratedValue(strategy=GenerationType.IDENTITY)

Erreur de mise en veille prolongée pendant l'exécution

org.springframework.dao.InvalidDataAccessResourceUsageException: error performing isolated work; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: error performing
 isolated work
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242)
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
        at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
        at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
        at com.sun.proxy.$Proxy127.save(Unknown Source)
        at jp.hoge.service.FugaService.execute(FugaService.java:218)
        at jp.hoge.controller.FugaController.execute(FugaController.java:101)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)

Les mappages des générateurs d'Hibernate ont changé.

Corrigé en ajoutant les paramètres suivants à application.properties

application.properties


spring.jpa.hibernate.use-new-id-generator-mappings=false

Après le déploiement sur STG, etc.

Erreur de chargement du profil

<SpringProfile> dans logback-spring.xml ne parvient pas à charger correctement le profil Pour executableJar, définissez-le comme -Dspring-boot.run.profiles = nom de l'environnement.

Dans ce projet, nous déploierons la guerre sur Tomcat, donc Définissez-le dans ʻapplication.properties comme spring-boot.run.profiles = nom de l'environnement`.

En fait, le profil est divisé lorsque le fichier war est généré dans pom.xml, il est donc défini comme suit.

application.properties


spring-boot.run.profiles=${spring.profiles.active}

pom.xml


<profiles>
		<profile>
			<id>local</id>
			<properties>
				<spring.profiles.active>local</spring.profiles.active>
			</properties>
		</profile>
		<profile>
			<id>stg</id>
			<properties>
				<spring.profiles.active>stg</spring.profiles.active>
			</properties>
		</profile>
</profiles>

Profil au moment de la construction

mvn package -Pstg

Avec ce sentiment, il est intégré dans ʻapplication.properties`.

Après vous être connecté via Spring Security, toutes les variables membres de l'objet User après avoir obtenu la session sont nulles.

NullPointerExepotion se produit lors de la tentative d'accès à une variable membre de User Cependant, même si vous regardez le contenu de Redis, vous ne pouvez pas le lire car il est sérialisé ...

Une fois, créez un sérialiseur qui se convertit en JSON et l'enregistre dans Redis avec le sentiment suivant

HttpSessionConfig.java


@ConditionalOnProperty(name = "spring.session.store-type", havingValue = "redis", matchIfMissing = false)
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 20 * 24 * 60 * 60) //default=1800 -> 20days
@Configuration
public class HttpSessionConfig implements BeanClassLoaderAware {

   @Autowired
   private LettuceConnectionFactory lettuceConnectionFactory;

   private ClassLoader classLoader;

   @Bean
   public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
	final ObjectMapper mapper = new ObjectMapper()
			.registerModules(SecurityJackson2Modules.getModules(classLoader))
			.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
	return new GenericJackson2JsonRedisSerializer(mapper);
  }

      //Pour Elasticache
      @Bean
      public static ConfigureRedisAction configureRedisAction() {
          return ConfigureRedisAction.NO_OP;
      }

	@Override
	public void setBeanClassLoader(final ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
}

Ensuite, lors de la transition vers Controller après la connexion, il était défini sur Redis avec tous les champs nuls.

Le contrôleur après la connexion détermine si ʻid dans l'objet ʻUser est nul, et s'il est nul, il est reconditionné. Une fois connecté, je peux gérer la session sans aucun problème.

En fait, je voulais pouvoir avoir des informations de session au format JSON, mais comme la structure de la classe User était une structure imbriquée compliquée, j'ai abandonné en raison de contraintes de temps ... Trop d'informations dans la classe User ...

Correspondance au moment du déploiement de la production

Il semble que le SerialVersionUID géré en interne pour la sérialisation et la désérialisation a changé avec la mise à niveau de la version de SpringSession, donc si vous déployez le code mis à jour, les utilisateurs déjà connectés ne pourront pas désérialiser les informations de session.

Donc, je mettrai dans la correspondance lors de la désérialisation.

https://sdqali.in/blog/2016/11/02/handling-deserialization-errors-in-spring-redis-sessions/

L'article est un peu vieux et les bibliothèques dépendantes ont changé, je l'ai donc changé comme suit.

HttpSessionConfig.java


- public class HttpSessionConfig {
+ public class HttpSessionConfig extends RedisHttpSessionConfiguration {

+	@Autowired
+	RedisTemplate<Object, Object> redisTemplate;
+ 	@Bean
+	@Override
+	public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
+		return super.springSessionRepositoryFilter(new SafeDeserializationRepository<>(sessionRepository, redisTemplate));
+	}

SafeDeserializationRepository.java


public class SafeDeserializationRepository<S extends Session> implements SessionRepository<S> {
    private final SessionRepository<S> delegate;
    private final RedisTemplate<Object, Object> redisTemplate;
     private static final String BOUNDED_HASH_KEY_PREFIX = "spring:session:sessions:";
     public SafeDeserializationRepository(SessionRepository<S> delegate,
            RedisTemplate<Object, Object> redisTemplate) {
        this.delegate = delegate;
        this.redisTemplate = redisTemplate;
    }
     @Override
    public S createSession() {
        return delegate.createSession();
    }
     @Override
    public void save(S session) {
        delegate.save(session);
    }
     @Override
    public S findById(String id) {
        try {
            return delegate.findById(id);
        } catch(SerializationException e) {
            log.info("Deleting non-deserializable session with key {}", id);
            redisTemplate.delete(BOUNDED_HASH_KEY_PREFIX + id);
            return null;
        }
    }
     @Override
    public void deleteById(String id) {
        delegate.deleteById(id);
    }
}

Cela permettra aux utilisateurs existants de se déconnecter une fois, de se reconnecter pour enregistrer les nouvelles données de session dans Redis, puis de s'y référer.

Résumé

En général, le support ci-dessus l'a fait fonctionner dans un environnement de production. (Je pense qu'il y en a d'autres)

De plus, à l'origine, Tomcat devrait être de la série 8.5 ou supérieure, mais la version 8.0 ne semble pas poser de problème, j'ai donc oublié de mettre à niveau Tomcat.

Si vous sautez certaines versions majeures de Spring Boot, ce sera très difficile, alors mettons à jour fréquemment ...! !!

Page de référence

Recommended Posts

Ce que j'ai fait lors de la migration de la série Spring Boot 1.4 vers la série 2.0
Ce que j'ai fait lors de la migration de la série Spring Boot 1.5 vers la série 2.0
Tokoro j'ai réécrit dans la migration de Wicket 7 à 8
Mise à niveau de la botte à ressort de la série 1.5 à la série 2.0
L'histoire de la montée de Spring Boot de la série 1.5 à la série 2.1 part2
L'histoire de la montée de la série Spring Boot 1.5 à la série 2.1
Essayez Spring Boot de 0 à 100.
03. J'ai envoyé une demande de Spring Boot à l'API de recherche de code postal
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
Qu'est-ce que @Autowired dans Spring Boot?
Je souhaite contrôler la taille de fichier maximale pour le téléchargement de fichiers pour chaque URL dans Spring Boot
02. J'ai créé une API pour me connecter de Spring Boot à MySQL (My Batis)
Je veux contrôler le message d'erreur par défaut de Spring Boot
[Android] Je souhaite obtenir l'auditeur à partir du bouton de ListView
L'histoire de la transition de Spring Boot 1.5 à 2.1
Modifications lors de la migration de Spring Boot 1.5 vers Spring Boot 2.0
Modifications lors de la migration de Spring Boot 2.0 vers Spring Boot 2.2
J'étais accro au @Transactional de Spring
Je veux connaître la méthode du contrôleur où l'exception a été levée dans le ExceptionHandler de Spring Boot
[Spring Boot] J'ai étudié comment implémenter le post-traitement de la demande reçue.
Étapes pour rendre Spring Boot capable de faire référence à la valeur dans le fichier de propriétés
Ce que j'ai fait lorsque JSF ne pouvait pas afficher les informations de la base de données dans la vue
Mon mémorandum que je veux faire ValidationMessages.properties UTF8 dans Spring Boot
Comment obtenir la valeur du paramètre (valeur de la propriété) à partir de la base de données dans Spring Framework
Qu'est-ce que CHECKSTYLE: OFF trouvé dans la source Java? Checkstyle à savoir de
Résumé de ce que j'ai appris sur Spring Boot
[Rails] J'ai essayé de faire passer la version de Rails de 5.0 à 5.2
Migration d'Eclipse vers IntelliJ (en cours)
J'ai essayé d'organiser la session en Rails
Comment ajouter un chemin de classe dans Spring Boot
Ce que j'ai fait lorsque j'ai converti Java en Kotlin
Comment se lier avec un fichier de propriétés dans Spring Boot
Essayez d'automatiser la migration avec Spring Boot Flyway
Je voulais classer la botte à ressort dans un multi-projet
[Spring Boot] Comment se référer au fichier de propriétés
Résumé de ce que j'ai appris dans Spring Batch
Afficher la tâche Gradle dans le projet Spring Boot
Je veux obtenir la valeur en Ruby
[Spring Boot] Comment obtenir des propriétés dynamiquement à partir d'une chaîne contenue dans une URL
Comment définir des variables d'environnement dans le fichier de propriétés de l'application Spring Boot
Que trouve-t-on ... (3 points) dans la source Java? Argument de longueur variable à connaître
J'étais accro à NoSuchMethodError dans Cloud Endpoints
Spécifiez le codage des ressources statiques dans Spring Boot
Ce à quoi j'étais accro lors du développement d'une application Spring Boot avec VS Code
[Java] Je souhaite calculer la différence par rapport à la date
Je veux intégrer n'importe quel TraceId dans le journal
Exécution asynchrone des requêtes examinée dans Spring Boot 1.5.9
paiza Ce que j'ai fait pour devenir Ruby B rang
Comment créer un projet Spring Boot dans IntelliJ
Comment utiliser CommandLineRunner dans Spring Batch of Spring Boot
Présentez swagger-ui à l'API REST implémentée dans Spring Boot
Déployer le projet Spring Boot sur Tomcat dans XAMPP
J'ai essayé d'implémenter la méthode de division mutuelle d'Eugrid en Java