La première entrée en 2018 est ... J'ai vu @ sndr "Spring Data JDBC Preview" et j'ai pensé "Hey" Spring Data JDBC C'est un mémo quand je l'ai essayé. Il semble qu'il ne dispose toujours que d'un support de niveau CRUD simple, mais Spring Data JDBC a été officiellement publié et Spring Data REST Nous sommes (très) impatients d'être soutenus sur: //projects.spring.io/spring-data-rest/)! ↓ ** Depuis sa sortie officielle le 21/09/2018, le contenu a été modifié en fonction de la version 1.0.0.RELEASE! ** **
REMARQUE: Historique des mises à jour
2018-02-06 :
- DATAJDBC-161 Prend en charge les modifications d'interface en raison de la prise en charge
- Revalidé avec Spring Boot 2.0.0.RC1
2018-02-08 :
- Ajout de l'utilisation de la méthode
@ Query
(correspond à DATAJDBC-172)2018-03-08 :
- Ajouté que le type simple est pris en charge comme valeur de retour de la méthode
@ Query
en raison du support de DATAJDBC-175.- Revalidé avec Spring Boot 2.0.0.RELEASE
2018-03-09 :
- Ajout d'une description sur la gestion des objets liés
- En ce qui concerne ce qui précède, a ajouté qu'il est nécessaire d'importer Lovelace-BUILD-SNAPSHOT du train spring-data-release lors de l'utilisation de Spring Data JDBC sur Spring Boot 2.0.0.RELEASE.
2018-03-10 :
- Ajout de l'utilisation de la requête de mise à jour (
@ Modifying
) (correspondant à DATAJDBC-182)2018-03-23 :
- Correction de ne pas utiliser
DefaultNamingStrategy
en raison de la prise en charge de DATAJDBC-189 (l'implémentation par défaut est définie commeNamingStrategy.INSTANCE
)- Mise à jour vers MyBatis Spring Boot Starter 1.3.2
2018-03-31 :
- Correction de ne pas spécifier
NamedParameterJdbcOperations
lors de la génération deDefaultDataAccessStrategy
en raison de la prise en charge de DATAJDBC-155- Ajout de
NamingStrategy
pour prendre en charge les cas de serpent (délimiteurs de soulignement) avec le support de DATAJDBC-184.2018-04-03 :
- Avec le support de DATAJDBC-178, en définissant une instance arbitraire
NamespaceStrategy
dansMyBatisDataAccessStrategy
, la convention de dénomination de l'espace de noms peut être modifiée. Ajouté qu'il peut être changé2018-05-18 :
- Ajout de la description de la prise en charge de la fonction d'audit basée sur les annotations (correspondant à DATAJDBC-204)
- Mettre à jour vers Spring Boot 2.0.2.RELEASE et revalider
2018-05-19 :
- Modifications de la configuration du package reflétées (correspondant à DATAJDBC-138)
2018-06-28 :
- Reflète la configuration du package et les changements de nom de classe (correspond à DATAJDBC-226)
- Ajouté que l'implémentation par défaut de
NamingStrategy
a été changée en Snake Case en réponse à DATAJDBC-207.- Mis à jour vers Spring Boot 2.0.3.RELEASE et revalidé
2018-07-03 :
- Suppression de l'importation explicite de spring-data-releasetrain Lovelace-BUILD-SNAPSHOT et ajout de
<spring-data-releasetrain.version> Lovelace-BUILD-SNAPSHOT </ spring-data-releasetrain.version>
aux propriétés. Fixé à ajouter2018-07-20 :
- Reflète le changement de méthode d'application du convertisseur personnalisé (correspondant à DATAJDBC-235)
- Correction d'un problème où MyBatis donne une erreur lors de deleteAll. Il semble que l'ID SQL qui supprime l'entité a changé! ??
2018-07-28 :
- Reflète le changement dans la méthode d'application de
JdbcConfiguration
(correspondant à DATAJDBC-243)2018-09-22 :
- Correction de la version de vérification JDBC Spring Data à 1.0.0.RELEASE
- Correction de la version de vérification de Spring Boot à 2.0.5.
- Décrit spring-boot-starter-data-jdbc
2018-09-23 :
- Correction de l'explication sur la façon de spécifier / étendre JdbcConfiguration (correction due à l'influence de DATA JDBC-267)
Les sources décrites dans cette entrée sont publiées dans les référentiels suivants. (Puisque Spring JDBC et MyBatis sont mélangés et vérifiés, il existe quelques différences par rapport à la description de l'entrée.)
Tout d'abord, dans SPRING INITIALIZR, sélectionnez «H2», «JDBC» et «MyBatis (uniquement lors de l'utilisation de MyBatis)» comme dépendances pour créer un projet. (Cette entrée est basée sur le projet Maven) Ensuite, ajoutez "spring-data-jdbc" au projet créé par SPRING INITIALIZR. Lors de l'utilisation de Spring Data JDBC dans la série Spring Boot 2.0, il est nécessaire de spécifier la version Lovelace-RELEASE de spring-data-releasetrain dans la propriété fournie par Spring Boot comme suit.
pom.xml
<properties>
<spring-data-releasetrain.version>Lovelace-RELEASE</spring-data-releasetrain.version>
</properties>
pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc</artifactId>
</dependency>
Préparez un DDL pour créer une table.
src/main/resources/schema.sql
CREATE TABLE IF NOT EXISTS todo (
id IDENTITY
,title TEXT NOT NULL
,details TEXT
,finished BOOLEAN NOT NULL
);
Créez un objet Todo qui représente l'enregistrement dans la table TODO. Ajoutez «@ Id» à la propriété qui contient la valeur de clé.
src/main/java/com/example/demo/domain/Todo.java
package com.example.demo.domain;
import org.springframework.data.annotation.Id;
public class Todo {
@Id
private int id;
private String title;
private String details;
private boolean finished;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public boolean isFinished() {
return finished;
}
public void setFinished(boolean finished) {
this.finished = finished;
}
}
Créez une interface de référentiel pour manipuler les objets du domaine. Le but est d'hériter du Croud Repository
fourni par Spring Data.
src/main/java/com/example/demo/repository/TodoRepository.java
package com.example.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.example.demo.domain.Todo;
public interface TodoRepository extends CrudRepository<Todo, Integer> {
}
En faisant cela ... Vous pouvez utiliser l'objet Todo en utilisant les méthodes suivantes définies dans CrudRepository
.
Référence: Extrait du référentiel Crud
package org.springframework.data.repository;
import java.util.Optional;
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
Spring Data JDBC fournit DataAccessStrategy
comme interface pour résumer la méthode d'exécution de SQL, et pour le moment," Spring JDBC (NamedParameterJdbcOperations
) implémentation" et "MyBatis implementation" sont intégrés.
Si vous utilisez l'implémentation Spring JDBC, le SQL à exécuter lorsque vous appelez la méthode définie dans CrudRepository
est généré automatiquement (= vous n'avez pas besoin d'écrire du SQL pour les opérations CRUD).
Créez une classe de configuration avec @ EnableJdbcRepositories
et @Import (JdbcConfiguration.class)
. Cependant ... Si vous voulez changer le bean défini dans JdbcConfiguration
, vous devez créer une classe de configuration qui hérite de JdbcConfiguration
et l'enregistrer comme conteneur DI. Par exemple, si vous avez besoin de conversions de type qui ne sont pas prises en charge par défaut, vous pouvez remplacer la méthode jdbcCustomConversions
et renvoyer JdbcCustomConversions
avec n'importe quel Converter
spécifié. Dans cette entrée, Converter
est ajouté pour convertir le type TEXT ( Clob
) de la base de données H2 en String
. Au fait ... Si vous utilisez VARCHAR au lieu de TEXT, vous n'avez pas besoin de remplacer la méthode jdbcCustomConversions
.
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig extends JdbcConfiguration {
@Override
protected JdbcCustomConversions jdbcCustomConversions() {
return new JdbcCustomConversions(Collections.singletonList(new Converter<Clob, String>() {
@Override
public String convert(Clob clob) {
try {
return clob == null ? null : clob.getSubString(1L, (int) clob.length());
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}));
}
}
Note:
NamingStrategy
est fourni en tant qu'interface pour déterminer la stratégie de dénomination des noms de colonne et des noms de propriété. Par défaut,NamingStrategy.INSTANCE
est utilisé, mais vous pouvez changer le comportement par défaut en définissant un bean pourNamingStrategy
. ~~ De plus,NamingStrategy
qui prend en charge les cas de serpent (délimiteurs de soulignement) a été ajouté en prenant en charge DATAJDBC-184 (lors de son utilisation) Définition du haricot requise). ~~ Le comportement par défaut est traité comme un cas de serpent (délimiteur de soulignement) (le comportement par défaut a été modifié pour prendre en charge DATAJDBC-206).
Lors de l'utilisation de l'implémentation MyBatis, il est nécessaire de définir le SQL à exécuter lorsque la méthode définie dans CrudRepository
est appelée côté MyBatis. (= Il est également nécessaire d'écrire du SQL pour les opérations CRUD).
Créez une classe de configuration avec @ EnableJdbcRepositories
et @Import (JdbcConfiguration.class)
, et définissez un Bean de MyBatisDataAccessStrategy
(implémentation MyBatis) comme DataAccessStrategy
.
@EnableJdbcRepositories
@Import(JdbcConfiguration.class)
@Configuration
public class SpringDataJdbcConfig {
@Bean
DataAccessStrategy dataAccessStrategy(SqlSession sqlSession) {
return new MyBatisDataAccessStrategy(sqlSession);
}
}
NOTE:
06/02/2018: Avec le support de DATAJDBC-161, l'objet à passer à l'argument constructeur de
MyBatisDataAccessStrategy
est passé deSqlSessionFactory
àSqlSession
. (En fait changé enSqlSessionTemplate
).
Définissez l'emplacement et l'alias de type du fichier XML Mapper.
src/main/resources/application.properties
mybatis.mapper-locations=classpath:/com/example/demo/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.domain
Définissez le SQL correspondant à la méthode de CrudRepository
. Lors de l'utilisation de MyBatis via Spring Data JDBC, il est nécessaire de connaître certaines règles spéciales lors de la définition de SQL.
MyBatisContext
(un extrait de MyBatisContext
est publié séparément)CrudRepository
et la définition SQL n'est pas biunivoque (contrairement à la règle de correspondance de l'interface Mapper de MyBatis). Pour une assistance spécifique, reportez-vous au [README] de Spring Data JDBC (https://github.com/spring-projects/spring-data-jdbc#mybatis).NOTE:
2018-04-03: Avec le support de DATAJDBC-178, en définissant une instance arbitraire
NamespaceStrategy
dansMyBatisDataAccessStrategy
, le nom Vous pouvez modifier la convention de dénomination des espaces.
src/main/resources/com/example/demo/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">
<!-- statements for CrudRepository method -->
<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
INSERT INTO todo
(title, details, finished)
VALUES
(#{instance.title}, #{instance.details}, #{instance.finished})
</insert>
<update id="update">
UPDATE todo SET
title = #{instance.title}, details = #{instance.details}, finished = #{instance.finished}
WHERE
id = #{instance.id}
</update>
<delete id="delete">
DELETE FROM todo WHERE id = #{id}
</delete>
<delete id="deleteAll">
DELETE FROM todo
</delete>
<select id="existsById" resultType="_boolean">
SELECT count(*) FROM todo WHERE id = #{id}
</select>
<select id="findById" resultType="Todo">
SELECT
id, title, details, finished
FROM
todo
WHERE
id = #{id}
</select>
<select id="findAll" resultType="Todo">
SELECT
id, title, details, finished
FROM
todo
ORDER BY
id
</select>
<select id="findAllById" resultType="Todo">
SELECT
id, title, details, finished
FROM
todo
<where>
<foreach collection="id" item="idValue" open="id in("
separator="," close=")">
#{idValue}
</foreach>
</where>
ORDER BY
id
</select>
<select id="count" resultType="_long">
SELECT count(*) FROM todo
</select>
</mapper>
Référence: Extrait de MyBatisContext
package org.springframework.data.jdbc.mybatis;
import java.util.Map;
public class MyBatisContext {
private final Object id;
private final Object instance;
private final Class domainType;
private final Map<String, Object> additonalValues;
public MyBatisContext(Object id, Object instance, Class domainType, Map<String, Object> additonalValues) {
this.id = id;
this.instance = instance;
this.domainType = domainType;
this.additonalValues = additonalValues;
}
public Object getId() {
return id;
}
public Object getInstance() {
return instance;
}
public Class getDomainType() {
return domainType;
}
public Object get(String key) {
return additonalValues.get(key);
}
}
Je ne vais pas l'expliquer dans cette entrée + Je ne l'ai pas vérifié, mais il semble qu'il soit possible d'utiliser plusieurs implémentations (par exemple Spring JDBC et MyBatis) ensemble en utilisant CascadingDataAccessStrategy
.
Le référentiel Spring Data JDBC est injecté et utilisé comme tout autre projet Spring Data.
@Autowired
private TodoRepository todoRepository;
@Test
public void insertAndFineById() {
Todo newTodo = new Todo();
newTodo.setTitle("soirée à boire");
newTodo.setDetails("Ginza 19:00");
todoRepository.save(newTodo);
Optional<Todo> todo = todoRepository.findById(newTodo.getId());
Assertions.assertThat(todo.isPresent()).isTrue();
Assertions.assertThat(todo.get().getId()).isEqualTo(newTodo.getId());
Assertions.assertThat(todo.get().getTitle()).isEqualTo(newTodo.getTitle());
Assertions.assertThat(todo.get().getDetails()).isEqualTo(newTodo.getDetails());
Assertions.assertThat(todo.get().isFinished()).isFalse();
}
@ Query
Toute requête peut être exécutée (en utilisant la fonction Spring JDBC) en ajoutant une méthode avec @ Query
au référentiel.
~~WARNING:~~ ~~ Avec l'implémentation actuelle, il n'est pas possible d'exécuter la mise à jour SQL (il semble qu'il y ait des plans pour la supporter). ~~
NOTE: 10/03/2018: DATAJDBC-182 prend également en charge l'exécution de la mise à jour SQL.
src/main/java/com/example/demo/repository/TodoRepository.java
package com.example.demo.repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.example.demo.domain.Todo;
public interface TodoRepository extends CrudRepository<Todo, Integer> {
@Query("SELECT * FROM todo WHERE id = :id")
Optional<Todo> findOptionalById(@Param("id") Integer id);
@Query("SELECT * FROM todo WHERE id = :id")
Todo findEntityById(@Param("id") Integer id);
@Query("SELECT * FROM todo ORDER BY id")
Stream<Todo> findAllStream();
@Query("SELECT * FROM todo ORDER BY id")
List<Todo> findAllList();
@Query("SELECT count(*) FROM todo WHERE finished = :finished")
long countByFinished(@Param("finished") Boolean finished);
@Query("SELECT count(*) FROM todo WHERE finished = :finished")
boolean existsByFinished(@Param("finished") Boolean finished);
@Query("SELECT current_timestamp()")
LocalDateTime currentDateTime();
@Modifying
@Query("UPDATE todo SET finished = :finished WHERE id = :id")
boolean updateFinishedById(@Param("id") Integer id, @Param("finished") boolean finished);
}
NOTE:
@ Param
peut être omis en spécifiant l'option-parameters
du compilateur Java.
Actuellement, les types de retour pris en charge sont
T
(classe de domaine)java.util.Optional<T>
java.lang.Iterable <T>
(comme java.util.List <T>
)java.util.stream.Stream<T>
Et ʻorg.springframework.data.domain.Page
Les valeurs de retour prises en charge pour les méthodes de mise à jour sont
int
(Integer
)boolean
(Boolean
)void
Est.
~~WARNING:~~ ~~ Dans l'implémentation actuelle, il n'est pas possible de spécifier un type autre que la classe de domaine comme une valeur numérique (ʻint
,
long, etc.) ou une valeur booléenne comme valeur de retour (c'est-à-dire, la méthode ...
@ Queryderecording. Vous ne pouvez pas spécifier un SQL pour obtenir le nombre ou un SQL pour vérifier l'existence d'enregistrements). Il peut être géré par la méthode introduite dans "Ajout d'opérations personnalisées" ci-dessous, mais ... J'ai l'impression que je manque de considération, alors je vais lui donner un problème. ⇒ [DATAJDBC-175](https://jira.spring.io/browse/DATAJDBC-175) ~~ ↓ 2018-03-08: Il est désormais possible de renvoyer des types autres que des classes de domaine (dits types simples) tels que des nombres (ʻint
,long
, etc.) et des valeurs booléennes comme valeurs de retour! !! En interne ...SingleColumnRowMapper
+ La résolution du type de données Spring est effectuée en coopération avecConversionService
appliqué à JDBC. À propos, Spring Framework 5.0.4.RELEASE ou supérieur est requis pour cette prise en charge.
"Mécanisme d'ajout d'opérations personnalisées (méthodes personnalisées)" dans Spring Data Ce mécanisme peut également être utilisé dans Spring Data JDBC.
Définissez une interface pour définir des opérations personnalisées (méthodes personnalisées).
src/main/java/com/example/demo/repository/CustomizedTodoRepository.java
package com.example.demo.repository;
import com.example.demo.domain.Todo;
public interface CustomizedTodoRepository {
Iterable<Todo> findAllByFinished(boolean finished);
}
Héritez de l'interface créée avec Todo Repository
.
src/main/java/com/example/demo/repository/TodoRepository.java
package com.example.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.example.demo.domain.Todo;
public interface TodoRepository extends CrudRepository<Todo, Integer>, CustomizedTodoRepository {
}
Lorsque vous utilisez Spring JDBC, créez la classe d'implémentation suivante.
src/main/java/com/example/demo/repository/CustomizedTodoRepositoryImpl.java
package com.example.demo.repository;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import com.example.demo.domain.Todo;
public class CustomizedTodoRepositoryImpl implements CustomizedTodoRepository {
private static final RowMapper<Todo> ROW_MAPPER = new BeanPropertyRowMapper<>(Todo.class);
private final NamedParameterJdbcOperations namedParameterJdbcOperations;
public CustomizedTodoRepositorySpringJdbcImpl(NamedParameterJdbcOperations namedParameterJdbcOperations) {
this.namedParameterJdbcOperations = namedParameterJdbcOperations;
}
public Iterable<Todo> findAllByFinished(boolean finished) {
return this.namedParameterJdbcOperations.query(
"SELECT id, title, details, finished FROM todo WHERE finished = :finished ORDER BY id",
new MapSqlParameterSource("finished", finished), ROW_MAPPER);
}
}
Lorsque vous utilisez MyBatis, créez la classe d'implémentation suivante.
src/main/java/com/example/demo/repository/CustomizedTodoRepositoryImpl.java
package com.example.demo.repository;
import org.apache.ibatis.session.SqlSession;
import com.example.demo.domain.Todo;
public class CustomizedTodoRepositoryImpl implements CustomizedTodoRepository {
private final String NAMESPACE = Todo.class.getName() + "Mapper";
private final SqlSession sqlSession;
public CustomizedTodoRepositoryMyBatisImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public Iterable<Todo> findAllByFinished(boolean finished) {
return this.sqlSession.selectList(NAMESPACE + ".findAllByFinished", finished);
}
}
Ajoutez également la définition SQL.
src/main/resources/com/example/demo/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">
<!-- ... -->
<!-- statements for custom repository method -->
<select id="findAllByFinished" resultType="Todo">
SELECT
id, title, details, finished
FROM
todo
WHERE
finished = #{finished}
ORDER BY
id
</select>
</mapper>
Spring Data JDBC prend en charge les opérations de persistance sur les objets associés qui ont une relation 1: 1 ou 1: N. Cependant, l'état du support diffère entre l'implémentation Spring JDBC et l'implémentation MyBatis. Si vous jetez un coup d'œil rapide ... Le statut de prise en charge des opérations de mise à jour est le même. Cependant, pour le fonctionnement du système de référence lors de l'utilisation de MyBatis, l'implémentation côté MyBatis (jointure de table + 1: 1/1: N mapping utilisant l'association et la collection) est requise.
Créez une table pour conserver les objets associés.
src/main/resources/schema.sql
CREATE TABLE IF NOT EXISTS activity (
id IDENTITY
,todo INTEGER NOT NULL --Colonne qui stocke l'ID de l'objet de domaine
,todo_key INTEGER NOT NULL --Colonne qui stocke la clé d'identification (et la clé de tri) des objets associés dans l'objet de domaine
,content TEXT NOT NULL
,at TIMESTAMP NOT NULL
);
Dans l'implémentation par défaut de Spring Data JDBC, le nom de colonne de "colonne qui stocke l'ID de l'objet de domaine" est "nom de classe de l'objet de domaine", et le nom de colonne de "colonne qui stocke la clé d'identification de l'objet associé dans l'objet de domaine" est Il devient "colonne qui stocke l'ID de l'objet domaine +" _key "".
Créez un objet de domaine qui représente l'activité de TODO et associez-le à l'objet Todo.
src/main/java/com/example/demo/domain/Activity.java
package com.example.demo.domain;
import org.springframework.data.annotation.Id;
import java.time.LocalDateTime;
public class Activity {
@Id
private int id;
private String content;
private LocalDateTime at;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public LocalDateTime getAt() {
return at;
}
public void setAt(LocalDateTime at) {
this.at = at;
}
}
src/main/java/com/example/demo/domain/Todo.java
public class Todo {
// ...
private List<Activity> activities;
// ...
public List<Activity> getActivities() {
return activities;
}
public void setActivities(List<Activity> activities) {
this.activities = activities;
}
}
Ici, l'objet de domaine qui contient l'objet associé avec une relation 1: N est exploité en utilisant la méthode définie dans CrudRepository
.
Exemple d'exécution d'opération CRUD
@Test
public void oneToMany() {
// Insert
Todo newTodo = new Todo();
newTodo.setTitle("soirée à boire");
newTodo.setDetails("Ginza 19:00");
Activity activity1 = new Activity();
activity1.setContent("Created");
activity1.setAt(LocalDateTime.now());
Activity activity2 = new Activity();
activity2.setContent("Started");
activity2.setAt(LocalDateTime.now());
newTodo.setActivities(Arrays.asList(activity1, activity2));
todoRepository.save(newTodo);
// Assert for inserting
Optional<Todo> loadedTodo = todoRepository.findById(newTodo.getId());
Assertions.assertThat(loadedTodo.isPresent()).isTrue();
loadedTodo.ifPresent(todo -> {
Assertions.assertThat(todo.getId()).isEqualTo(newTodo.getId());
Assertions.assertThat(todo.getTitle()).isEqualTo(newTodo.getTitle());
Assertions.assertThat(todo.getDetails()).isEqualTo(newTodo.getDetails());
Assertions.assertThat(todo.isFinished()).isFalse();
Assertions.assertThat(todo.getActivities()).hasSize(2);
Assertions.assertThat(todo.getActivities().get(0).getContent()).isEqualTo(activity1.getContent());
Assertions.assertThat(todo.getActivities().get(1).getContent()).isEqualTo(activity2.getContent());
});
// Update
Activity activity3 = new Activity();
activity3.setContent("Changed Title");
activity3.setAt(LocalDateTime.now());
loadedTodo.ifPresent(todo -> {
todo.setTitle("[Change] " + todo.getTitle());
todo.getActivities().add(activity3);
});
todoRepository.save(loadedTodo.get());
// Assert for updating
loadedTodo = todoRepository.findById(newTodo.getId());
Assertions.assertThat(loadedTodo.isPresent()).isTrue();
loadedTodo.ifPresent(todo -> {
Assertions.assertThat(todo.getTitle()).isEqualTo("[Change] " + newTodo.getTitle());
Assertions.assertThat(todo.getActivities()).hasSize(3);
Assertions.assertThat(todo.getActivities().get(0).getContent()).isEqualTo(activity1.getContent());
Assertions.assertThat(todo.getActivities().get(1).getContent()).isEqualTo(activity2.getContent());
Assertions.assertThat(todo.getActivities().get(2).getContent()).isEqualTo(activity3.getContent());
});
// Delete
todoRepository.deleteById(newTodo.getId());
// Assert for deleting
Assertions.assertThat(todoRepository.findById(newTodo.getId())).isNotPresent();
}
Si vous utilisez l'implémentation Spring JDBC, vous n'avez rien à faire. Si vous appelez simplement la méthode CrudRepository, Spring Data JDBC générera et exécutera le SQL.
Si vous utilisez l'implémentation MyBatis, vous devez définir SQL dans le fichier XML Mapper.
Premièrement ... Définissez le SQL pour l'insertion d'objets associés
src/main/resources/com/example/demo/mapper/ActivityMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
INSERT INTO activity
(todo, todo_key, content, at)
VALUES
(#{additonalValues.Todo}, #{additonalValues.Todo_key}, #{instance.content}, #{instance.at})
</insert>
</mapper>
Le point ici est que "l'ID de l'objet de domaine" et la "clé d'identification (et clé de tri) de l'objet associé dans l'objet de domaine" sont stockés dans la propriété ʻadditonalValuesde type
Map`. , Le nom de clé où la valeur est stockée est la même règle que le nom de colonne.
Suivant ... Définissez SQL pour les objets liés à DELETE. Ce SQL est également appelé lors de la mise à jour d'un objet de domaine. En d'autres termes ... Tous les objets associés avec une relation 1: N sont SUPPRIMÉS puis à nouveau INSÉRÉS.
src/main/resources/com/example/demo/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.TodoMapper">
<!-- ... -->
<delete id="delete-activities">
DELETE FROM activity WHERE todo = #{id}
</delete>
<delete id="deleteAll-activities">
DELETE FROM activity WHERE todo = #{id}
</delete>
<!-- ... -->
</mapper>
Enfin ... Modifiez pour obtenir des objets liés dans le SQL qui font référence à l'objet de domaine. Plus précisément ... Rejoignez les tables contenant les informations des objets associés et mappez les objets associés aux objets du domaine à l'aide de la fonction ResultMap de MyBatis.
src/main/resources/com/example/demo/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
<!-- ... -->
<select id="findById" resultMap="todoMap">
SELECT
t.id, t.title, t.details, t.finished
, a.id as activity_id, a.content as activity_content, a.at as activity_at
FROM
todo t
LEFT OUTER JOIN activity a ON a.todo = t.id
WHERE
t.id = #{id}
ORDER BY
a.todo_key
</select>
<resultMap id="todoMap" type="Todo">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="details" property="details"/>
<result column="finished" property="finished"/>
<collection property="activities" columnPrefix="activity_" ofType="Activity">
<id column="id" property="id"/>
<result column="content" property="content"/>
<result column="at" property="at"/>
</collection>
</resultMap>
<!-- ... -->
</mapper>
Si vous n'aimez pas les noms de colonnes générés par l'implémentation par défaut de Spring Data JDBC ... Vous pouvez le changer en définissant la classe d'implémentation de NamingStrategy
comme un bean. Ici, nous allons présenter comment changer Todo
en todo_id
et Todo_key
en le nom de la colonne sort_order
.
Premièrement ... Modifiez le nom de la colonne de la table.
src/main/resources/schema.sql
CREATE TABLE IF NOT EXISTS activity (
id IDENTITY
,todo_id INTEGER NOT NULL --Changer le nom de la colonne
,sort_order INTEGER NOT NULL --Changer le nom de la colonne
,content TEXT NOT NULL
,at TIMESTAMP NOT NULL
);
Next ... Bean définition de la classe d'implémentation de NamingStrategy
.
@Bean
NamingStrategy namingStrategy() {
return new NamingStrategy(){
@Override
public String getReverseColumnName(RelationalPersistentProperty property) {
return NamingStrategy.super.getReverseColumnName(property).toLowerCase() + "_id";
}
@Override
public String getKeyColumn(RelationalPersistentProperty property) {
return "sort_order";
}
};
}
Si vous utilisez l'implémentation MyBatis, vous devrez également modifier le SQL.
src/main/resources/com/example/demo/mapper/ActivityMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="instance.id">
INSERT INTO activity
(todo_id, sort_order, content, at)
VALUES
(#{additonalValues.todo_id}, #{additonalValues.sort_order}, #{instance.content}, #{instance.at})
</insert>
</mapper>
src/main/resources/com/example/demo/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.domain.ActivityMapper">
<!-- ... -->
<delete id="delete-activities">
DELETE FROM activity WHERE todo_id = #{id}
</delete>
<delete id="deleteAll-activities">
DELETE FROM activity WHERE todo_id = #{id}
</delete>
<!-- ... -->
<select id="findById" resultMap="todoMap">
SELECT
t.id, t.title, t.details, t.finished
, a.id as activity_id, a.content as activity_content, a.at as activity_at
FROM
todo t
LEFT OUTER JOIN activity a ON a.todo_id = t.id
WHERE
t.id = #{id}
ORDER BY
a.sort_order
</select>
<!-- ... -->
</mapper>
Dans Spring Data, "Mécanisme de définition des valeurs dans la colonne (colonne d'audit) qui contient quand, qui et quand et les données mises à jour (dernière mise à jour) -data / commons / docs / 2.1.0.RELEASE / reference / html / # audit), et cette fonction peut également être utilisée avec Spring Data JDBC.
IMPORTANT:
Au moment de Spring Data JDBC 1.0.0.RELEASE (Lovelace), il y a une partie que la fonction d'audit pour les objets imbriqués (1: 1, 1: N) ne peut pas être prise en charge. Cela semble être un problème du côté de Spring Data Commons, pas du côté de Spring Data JDBC.
- [DATACMNS-1297] (https://jira.spring.io/projects/DATACMNS/issues/DATACMNS-1297): Une erreur se produit toujours lorsque la propriété d'audit existe dans la classe du côté N qui a une relation 1: N (données) Entraînera une erreur même s'il y a 0 cas)
- [DATACMNS-1296] (https://jira.spring.io/projects/DATACMNS/issues/DATACMNS-1296) : Une erreur se produit si l'objet lui-même est «nul» lorsque la propriété d'audit existe dans la classe imbriquée.
Pour utiliser la fonction d'audit, ajoutez @ org.springframework.data.jdbc.repository.config.EnableJdbcAuditing
à la classe de configuration.
@EnableJdbcAuditing //ajouter à
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
// ...
}
Une classe qui implémente l'interface ʻorg.springframework.data.domain.AuditorAware` pour enregistrer qui a créé et mis à jour
package com.example.demo;
import org.springframework.data.domain.AuditorAware;
import java.util.Optional;
public class MyAuditorAware implements AuditorAware<String> {
static ThreadLocal<String> currentUser = ThreadLocal.withInitial(() -> "default");
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(currentUser.get());
}
}
NOTE:
Ici, l'implémentation est telle que le nom d'utilisateur défini dans la variable thread-local est simplement renvoyé en tant que créateur / dernier programme de mise à jour des données, mais lors du développement d'une application réelle, Spring Security etc. Il est courant de renvoyer le nom d'utilisateur de connexion géré par la fonction d'authentification de.
Et enregistrez-le dans le contexte de l'application.
@EnableJdbcAuditing
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
// ...
@Bean
AuditorAware<String> auditorAware() {
return new MyAuditorAware();
}
// ...
}
De plus, si vous voulez changer la méthode d'acquisition du temps "quand" de l'implémentation par défaut (CurrentDateTimeProvider
=LocalDateTime.now ()
), appliquez un objet qui implémente l'interface ʻorg.springframework.data.auditing.DateTimeProvider. Spécifiez le BeanID de l'objet enregistré dans le contexte et enregistré dans l'attribut
dateTimeProviderRef de
@ EnableJdbcAuditing`.
@EnableJdbcAuditing(dateTimeProviderRef = "dateTimeProvider")
@Import(JdbcConfiguration.class)
@EnableJdbcRepositories
@Configuration
public class SpringDataJdbcConfig {
// ...
@Bean
DateTimeProvider dateTimeProvider(ObjectProvider<Clock> clockObjectProvider) {
return () -> Optional.of(LocalDateTime.now(clockObjectProvider.getIfAvailable(Clock::systemDefaultZone)));
}
// ...
}
Après avoir activé la fonction d'audit, ajoutez d'abord une colonne d'audit au tableau.
CREATE TABLE IF NOT EXISTS todo (
id IDENTITY
,title TEXT NOT NULL
,details TEXT
,finished BOOLEAN NOT NULL
,created_at TIMESTAMP
,created_by VARCHAR(64)
,last_updated_at TIMESTAMP
,last_updated_by VARCHAR(64)
);
À partir de Spring Data JDBC 1.0.0.RELEASE (Lovelace), Fonction d'audit basée sur les annotations est prise en charge. .RELEASE / reference / html / # auditing.annotations )seulement.
Lorsque vous utilisez la fonction d'audit basée sur les annotations, ajoutez une propriété à la valeur de la colonne d'audit et ajoutez l'annotation suivante à la colonne cible.
@CreatedDate
@CreatedBy
@LastModifiedDate
@LastModifiedBy
~~WARNING:~~
~~ Depuis Spring Data JDBC 1.0 M3 (Lovelace), cela ne fonctionne pas correctement si le type de propriété avec
@ Id
est une primitive. (DATAJDBC-216) ~~ -> 2018-05-18 Pris en charge: grin:
package com.example.demo.domain;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.relational.core.mapping.Column;
import java.time.LocalDateTime;
import java.util.List;
public class Todo {
@Id
private int id;
private String title;
private String details;
private boolean finished;
//Date et heure de création
@CreatedDate
@Column("created_at")
private LocalDateTime createdAt;
//Auteur
@CreatedBy
@Column("created_by")
private String createdBy;
//Dernière modification
@LastModifiedDate
@Column("last_updated_at")
private LocalDateTime lastUpdatedAt;
//Dernière mise à jour
@LastModifiedBy
@Column("last_updated_by")
private String lastUpdatedBy;
private List<Activity> activities;
// setters/getters
}
NOTE:
Bien que non lié à la fonction d'audit,
@ Column
est une annotation prise en charge après la sortie de Spring Data JDBC 1.0 M3 (Lovelace) (DATAJDBC-106 DONNÉES JDBC-106)). ~~ Au fait, s'il y a certaines règles telles que le mappage entre les colonnes de cas de serpent et les noms de propriété de cas de chameau, vous pouvez utiliserNamingStrategy
pour absorber la différence de noms. ~~ (→ DATAJDBC-206 Avec le support, le mappage entre le nom de la colonne de cas de serpent et le nom de propriété de cas de chameau devient l'opération par défaut. devenu)
La fonctionnalité d'audit basée sur l'interface n'est pas prise en charge à partir de Spring Data JDBC 1.0.0.RELEASE (Lovelace). En effet, Spring Data JDBC ne prend pas encore en charge la propriété ʻOptional` (DATAJDBC-205).
Pour le moment ... Il semble y avoir spring-data-jdbc-boot-starter dans le référentiel personnel du développeur. Il n'est déployé nulle part pour le moment, vous devez donc l'installer et l'utiliser dans votre référentiel local. (Je ne l'ai pas utilisé cette fois pour le moment) ↓ Spring Boot 2.1 (2.1.0.M4) fournira AutoConfigure et Starter!
NOTE:
J'ai écrit "Essayez spring-boot-starter-data-jdbc (2.1.0.BUILD-SNAPSHOT)".
Je pense que c'est une bibliothèque qui continue de se développer et de grandir, alors j'aimerais garder un œil sur les tendances. Je pense que ce sera assez pratique si vous pouvez définir des méthodes personnalisées dans l'interface Repository et spécifier SQL avec des annotations (en ce qui concerne README, il semble y avoir un plan pour le supporter ⇒ pris en charge !!). Et ce serait formidable si la coopération avec Spring Data REST est prise en charge. ↓ Enfin la version 1.0.0 est officiellement sortie! !! C'est toujours une bibliothèque en développement, mais je pense qu'elle a beaucoup grandi par rapport à quand j'ai écrit cette entrée (2018/01/08). [Référence Spring Data REST (Spring Data REST 3.1.0.RELEASE)](https://docs.spring.io/spring-data/rest/docs/3.1.0.RELEASE/reference/html/#getting-started En ce qui concerne .bootstrap), il semble que le support officiel ne soit pas encore disponible (je vais vous donner un problème ...).
Recommended Posts