[JAVA] Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA

Aperçu

environnement

Exemple de code

Liste des fichiers

├── build.gradle
├── settings.gradle
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           ├── Counter.java
        │           ├── CounterController.java
        │           ├── CounterRepository.java
        │           └── CounterService.java
        └── resources
            ├── application.properties
            └── data.sql

build.gradle

plugins {
  id 'org.springframework.boot' version '2.2.6.RELEASE'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  // Spring
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  // Lombok
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  // H2 Database
  runtimeOnly 'com.h2database:h2'
}

settings.gradle

rootProject.name = 'my-app'

src/main/resources/application.properties

application.properties


#Paramètres de la base de données H2
#AUTOCOMMIT avec autocommit désactivé=OFF
#TRACE pour sortir les journaux_LEVEL_SYSTEM_OUT=Précisez 2
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;AUTOCOMMIT=OFF;TRACE_LEVEL_SYSTEM_OUT=2
#Spécifiez pour générer des journaux tels que Spring Framework et Hibernate ORM
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.com.example=DEBUG
#N'utilisez pas le modèle Ouvrir EntityManager dans la vue
spring.jpa.open-in-view=false

src/main/resources/data.sql

--Ajouter les données initiales à la base de données
INSERT INTO counter (name, count, updated_at) VALUES ('mycounter', 0, LOCALTIMESTAMP);

src/main/java/com/example/Counter.java

Classe d'entité.

package com.example;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;
import java.time.LocalDateTime;

/**
 *Équivaut à un enregistrement dans la table DB.
 */
@Data //Générez automatiquement des méthodes utiles telles que getter setter avec Lombok
@Entity //Traiter comme une entité JPA
public class Counter {

  //Nom du compteur
  @Id //Faire en sorte que JPA reconnaisse cette variable comme l'ID de l'objet
  private String name;

  //Numéro de compte
  private int count;

  //Mettre à jour la date et l'heure
  private LocalDateTime updatedAt;
}

src/main/java/com/example/CounterController.java

package com.example;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@SpringBootApplication
@RestController
@Slf4j // org.slf4j.Le journal des variables finales statiques de type enregistreur est généré automatiquement
public class CounterController {

  public static void main(String[] args) {
    SpringApplication.run(CounterController.class, args);
  }

  @Autowired
  private CounterService service;

  @GetMapping("/counter/{name}")
  public Map counter(@PathVariable("name") String name) {

    //Compter jusqu'à
    log.debug("Before: countup");
    Counter counter = service.countup(name);
    log.debug("After: countup");

    //Générer une réponse JSON
    return Map.of(counter.getName(), counter.getCount());
  }
}

src/main/java/com/example/CounterService.java

package com.example;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.LocalDateTime;

@Service
@Slf4j // org.slf4j.Le journal des variables finales statiques de type enregistreur est généré automatiquement
public class CounterService {

  @Autowired
  private CounterRepository repository;

  /**
   *Comptez le compteur.
   *
   * @nom du paramètre Nom du compteur
   * @liste des compteurs de retour
   */
  @Transactional //Démarrer la transaction au début de la méthode, valider à la fin
  public Counter countup(String name) {

    //Obtenez 1 enregistrement de DB
    log.debug("Before: getOne");
    Counter counter = repository.getOne(name);
    log.debug("After: getOne");

    //Nombre de mises à jour
    log.debug("Before: setCount");
    counter.setCount(counter.getCount() + 1);
    log.debug("After: setCount");

    //Mettre à jour la date et l'heure
    log.debug("Before: setUpdatedAt");
    counter.setUpdatedAt(LocalDateTime.now());
    log.debug("After: setUpdatedAt");

    //Obtenez 1 enregistrement de DB
    log.debug("Before: getOne");
    Counter result = repository.getOne(name);
    log.debug("After: getOne");

    return result;
  }
}

src/main/java/com/example/CounterRepository.java

package com.example;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 *Référentiel pour l'accès à la base de données.
 *La méthode fournie par Spring Data JPA en standard est générée automatiquement.
 */
@Repository
public interface CounterRepository extends JpaRepository<Counter, String> { //Spécifiez l'entité et le type de clé primaire
}

Résultat d'exécution

Lancer l'application Spring Boot

Lancez l'application Spring Boot avec la tâche gradle bootRun.

$ gradle bootRun

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

Si vous regardez le journal Spring Boot, vous pouvez voir que Hibernate ORM 5.4.12.Final est utilisé.

org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect

Une table DB est automatiquement générée à partir de la définition de la classe d'entité.

org.hibernate.SQL                        : drop table counter if exists
org.hibernate.SQL                        : create table counter (name varchar(255) not null, count integer not null, updated_at timestamp, primary key (name))
o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'

Journal de la base de données H2. Comme décrit dans data.sql, l'instruction insert a été émise.

2020-05-09 23:21:10 jdbc[3]: 
/*SQL */drop table counter if exists;
2020-05-09 23:21:10 jdbc[3]: 
/*SQL t:6*/create table counter (name varchar(255) not null, count integer not null, updated_at timestamp, primary key (name));
2020-05-09 23:21:10 jdbc[3]: 
/*SQL #:1 t:16*/INSERT INTO counter (name, count, updated_at) VALUES ('mycounter', 0, LOCALTIMESTAMP);

Vérifiez le comportement à CounterService # countup

Accès avec la commande curl depuis un autre terminal.

$ curl http://localhost:8080/counter/mycounter
{"mycounter":1}

Consultez le journal Spring Boot. Lors de l'appel de CounterService # countup.

com.example.CounterController            : Before: countup
o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [com.example.CounterService.countup]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(1254645459<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4ef360f9]

Lors de l'appel de CounterRepository # getOne.

com.example.CounterService               : Before: getOne
o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1254645459<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
com.example.CounterService               : After: getOne

Lors de l'appel de Counter # setCount. Il semble que nous publions enfin une instruction select ici.

com.example.CounterService               : Before: setCount
org.hibernate.SQL                        : select counter0_.name as name1_0_0_, counter0_.count as count2_0_0_, counter0_.updated_at as updated_3_0_0_ from counter counter0_ where counter0_.name=?
o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [mycounter]
o.h.type.descriptor.sql.BasicExtractor   : extracted value ([count2_0_0_] : [INTEGER]) - [0]
o.h.type.descriptor.sql.BasicExtractor   : extracted value ([updated_3_0_0_] : [TIMESTAMP]) - [2020-05-09T23:21:10.472636]
com.example.CounterService               : After: setCount

Journal de la base de données H2.

2020-05-09 23:21:19 jdbc[3]: 
/*SQL l:153 #:1*/select counter0_.name as name1_0_0_, counter0_.count as count2_0_0_, counter0_.updated_at as updated_3_0_0_ from counter counter0_ where counter0_.name=? {1: 'mycounter'};
2020-05-09 23:21:19 jdbc[3]: 
/*SQL l:58 #:1*/SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=? {1: 'QUERY_TIMEOUT'};

Lors de l'appel de Counter # setUpdatedAt. Il semble qu'il n'a pas été traité.

com.example.CounterService               : Before: setUpdatedAt
com.example.CounterService               : After: setUpdatedAt

Même si vous appelez la méthode setter de l'entité plusieurs fois (setCount et setUpdatedAt), le processus de mise à jour n'est pas exécuté à ce stade.

Lors de l'appel de CounterRepository # getOne.

com.example.CounterService               : Before: getOne
o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1254645459<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
com.example.CounterService               : After: getOne

Une fois que tout le code de CounterService # countup est terminé, une instruction de mise à jour est émise et validée.

o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(1254645459<open>)]
org.hibernate.SQL                        : update counter set count=?, updated_at=? where name=?
o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [INTEGER] - [1]
o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [TIMESTAMP] - [2020-05-09T23:21:19.247019]
o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [mycounter]

Journal de la base de données H2.

2020-05-09 23:21:19 jdbc[3]: 
/*SQL l:53 #:1 t:3*/update counter set count=?, updated_at=? where name=? {1: 1, 2: TIMESTAMP '2020-05-09 23:21:19.247019', 3: 'mycounter'};
2020-05-09 23:21:19 jdbc[3]: 
/*SQL */COMMIT;
2020-05-09 23:21:19 jdbc[3]: 
/*SQL */COMMIT;

Après la transaction.

o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(1254645459<open>)] after transaction

Appel de comptage CounterService # terminé.

com.example.CounterController            : After: countup

Matériel de référence

Recommended Posts

Voir le comportement des mises à jour d'entités avec Spring Boot + Spring Data JPA
Vérifiez le comportement de getOne, findById et des méthodes de requête avec Spring Boot + Spring Data JPA
Voir le comportement de redirection relative avec le paramètre server.tomcat.use-relative-redirects dans Spring Boot
Accédez au h2db intégré de Spring Boot avec jdbcTemplate
Organisez les différences de comportement de @NotBlank, @NotEmpty et @NotNull avec Spring Boot + Thymeleaf
Jusqu'à l'utilisation de Spring Data et JPA Part 2
Jusqu'à l'utilisation de Spring Data et JPA Part 1
J'ai essayé le guide d'introduction de Spring Boot [Accès aux données avec JPA]
OU rechercher avec la spécification Spring Data Jpa
Examiner le comportement du délai d'expiration des transactions JPA
[Java] [Spring] Tester le comportement de l'enregistreur
Méthode d'implémentation pour source multi-données avec Spring boot (Mybatis et Spring Data JPA)
Jusqu'à l'acquisition de données avec Spring Boot + MyBatis + PostgreSQL
Vérifions la sensation de Spring Boot + Swagger 2.0
Référence mutuelle de l'entité de Spring Data JPA et ses notes
Trier par Spring Data JPA (avec tri par clé composée)
Création d'un référentiel commun avec Spring Data JPA
Spring Boot + Spring Data JPA À propos des jointures de table multiples
Paramètres du gestionnaire de ressources lors de la livraison du SPA avec la fonction de ressource statique de Spring Boot
Spécifiez le codage des ressources statiques dans Spring Boot
Un mémorandum lors de l'essai de Spring Data JPA avec STS
Compatibilité de Spring JDBC et My Batis avec Spring Data JDBC (provisoire)
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
J'ai essayé de démarrer avec Spring Data JPA
Créer Restapi avec Spring Boot (jusqu'à l'exécution de l'application)
Créer la variable de clause where dans Spring Data JPA
[Spring Boot] L'histoire selon laquelle le bean de la classe avec l'annotation ConfigurationProperties n'a pas été trouvé
Comment démarrer par environnement avec Spring Boot de Maven
Vérifiez le comportement de include, exclude et ExhaustedRetryException de Spring Retry
Contrôlez le flux de traitement Spring Batch avec JavaConfig.
Télécharger avec Spring Boot
[Spring Data JPA] La condition And peut-elle être utilisée dans la méthode de suppression implémentée automatiquement?
Comment utiliser la même classe Mapper dans plusieurs sources de données avec Spring Boot + MyBatis
L'histoire de la montée de Spring Boot de la série 1.5 à la série 2.1 part2
À propos de la fonction de Spring Boot en raison de différentes versions
Changez la cible d'injection pour chaque environnement avec Spring Boot 2
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
Site où vous pouvez voir la relation de version de spring (?)
[Java] Simplifiez la mise en œuvre de la gestion de l'historique des données avec Reladomo
Essayez d'utiliser l'API de recherche de code postal 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
Implémenter l'API REST avec Spring Boot et JPA (Domain Layer Edition)
Introduction de la bibliothèque ff4j qui réalise le basculement de fonctionnalités avec Spring Boot
Générer un code à barres avec Spring Boot
Hello World avec Spring Boot
Implémenter GraphQL avec Spring Boot
[spring] Utilisons Spring Data JPA
Bonjour tout le monde avec Spring Boot!
Exécutez LIFF avec Spring Boot
Connexion SNS avec Spring Boot
Téléchargement de fichiers avec Spring Boot
Spring Boot commençant par copie
Spring Boot à partir de Docker
Hello World avec Spring Boot
Définir des cookies avec Spring Boot
Utiliser Spring JDBC avec Spring Boot