J'utilise MyBatis3 jusqu'à présent, mais j'avais du mal à gérer la version du code source généré automatiquement, donc je cherchais un autre mappeur O / R autour de Spring. J'ai donc découvert Spring Data JDBC dans ce document et noté la partie d'extension que j'utilise souvent.
Java 11 Gradle 5.4.1 Spring Boot 2.3.1.RELEASE Spring Data JDBC 2.0.1.RELEASE
Jusqu'à présent, Spring Data a publié des modules prenant en charge JPA, qui est l'API de persistance RDB la plus couramment utilisée pour les applications Java. Le nouveau Spring Data JDBC est publié comme un module plus simple et plus facile à comprendre que JPA. Le document officiel mentionne spécifiquement les points suivants.
Contrairement à Spring Data JPA, il n'y a pas autant de fonctions, donc la conception selon la méthode fournie par Spring Data JDBC semble être la clé pour utiliser Spring Data JDBC.
Sélectionnez ce qui suit dans Spring Initializr et téléchargez le projet.
article | Les choix |
---|---|
Project | Gradle Project |
Language | Java |
Spring Boot | 2.2.1 |
Dependencies | Spring Data JDBC、Lombok |
Il est possible de sélectionner un projet et une langue différents, mais certaines des sources présentées ici seront remplacées. La version de Spring Boot qui peut être sélectionnée varie en fonction de l'heure, mais ce n'est pas grave si vous sélectionnez la version par défaut.
build.gradle devrait ressembler à ceci:
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
Créez ʻapplication.yml sous
src / main / resources` et définissez la source de données comme suit. Cette fois, j'ai configuré H2Database pour qu'il démarre en mode PostgreSQL.
application.yml
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:;DB_CLOSE_ON_EXIT=TRUE;MODE=PostgreSQL
username: sa
password:
Créez un fichier SQL schema.sql
qui décrit le test DDL sous src / main / resources
.
schema.sql
create table member
(
id varchar not null
constraint member_pk
primary key auto_increment,
name varchar not null
);
N'oubliez pas d'annoter la propriété qui correspond à la clé primaire de la table avec @ Id
. Comme l'ID de la classe Member lorsqu'elle n'est pas persistante est Null, ajoutez également l'annotation @ Wither
.
Member.java
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode(of = {"id"})
@ToString
public class Member {
@Id
@Wither
private final String id;
private final String name;
}
Créez un référentiel qui hérite de CrudRepository
.
Spécifiez l'argument de type dans l'ordre du type d'entité et du type d'ID.
MemberRepository.java
public interface MemberRepository extends CrudRepository<Member, String> {
}
Avec cela seul, la méthode suivante est définie dans MemberCredentialRepository.
C'est similaire à JPA. La méthode save exécutera l'instruction INSERT et l'instruction UPDATE, mais la logique qui détermine laquelle exécuter est la suivante.
Cette fois, j'ai adopté le modèle 1.
Préparez une classe de test pour vérifier l'opération. Je me demande si je peux enregistrer les données pour le moment et vérifier si les données sont incluses.
MemberRepositoryTest.java
@DataJdbcTest
class MemberRepositoryTest {
@Autowired
private MemberRepository memberRepository;
@Test
void test() {
String name = "Kuchita";
Member save = memberRepository.save(new Member(null, name));
assertNotNull(save.getId());
Optional<Member> maybeMember = memberRepository.findById(save.getId());
assertTrue(maybeMember.isPresent());
maybeMember
.ifPresent(member -> assertEquals(save.getName(), member.getName()));
}
}
Accorder @ DataJdbcTest
démarrera la base de données intégrée par défaut.
Cette fois, j'ai utilisé h2, donc ça va, mais si vous voulez vous connecter à un serveur de base de données externe, ajoutez ce qui suit à ʻapplication.properties`.
application.properties
spring.test.database.replace=none
En ajoutant comme ça, vous pouvez utiliser votre serveur de base de données préféré au moment du test.
Spring Data JDBC réalise l'accès à la base de données en créant une interface qui hérite de l'interface Repository préparée à l'avance. Une interface pratique est fournie en standard qui fournit des méthodes de base en fonction de l'ID spécifié dans l'argument type et du type de l'entité.
Les cours préparés à l'avance sont les suivants.
Nom de l'interface | spécification |
---|---|
Repository<Entity, Id> | Fournit une interface de référentiel vide la plus élémentaire. |
CrudRepository<Entity, Id> | Outre CRUDcount OuexistsById Fournissez des méthodes telles que. |
PagingAndSortingRepository<Entity, Id> | En plus de ce qui précède, il fournit une méthode pour renvoyer le résultat de la pagination et du tri. |
Je pense que ce n'est pas grave si vous l'utilisez correctement comme suit.
** Spring Data JDBC 1.1.1.RELEASE actuellement ** PagingAndSortRepository ne fonctionnait pas correctement. stack overflow -PagingAndSortingRepository methods throw error when used with spring data jdbc-
Vous souhaiterez peut-être définir une interface commune à tous les projets, en plus de l'interface standard. Dans ce cas, étendons l'interface. À ce stade, ajoutez l'annotation @ NoRepositoryBean
à la classe.
Vous trouverez ci-dessous l'interface de base du référentiel Crud, qui définit uniquement les méthodes qui chargent l'entité.
ReadOnlyRepository.java
@NoRepositoryBean
public interface ReadOnlyRepository extends Repository<Member, String> {
Iterable<Member> findAll();
Optional<Member> findById(String id);
}
Pour les méthodes qui ne sont pas fournies en standard, ajoutez l'annotation @ Query
à la méthode et décrivez et définissez SQL dans l'argument de l'annotation. Les paramètres que vous voulez passer à SQL peuvent être spécifiés avec : nom de l'argument
.
MemberRepository.java
public interface MemberRepository extends CrudRepository<Member, String> {
@Query("SELECT * FROM member WHERE name = :name")
List<Member> getMembersByNameEquals(String name);
}
Le modèle d'implémentation de base consiste à définir des objets immuables ou des JavaBeans. Je vais le décrire en supposant que l'identifiant est généré automatiquement.
Une entité qui définit un champ immuable et un constructeur qui prend tous les champs comme arguments.
Pour définir plusieurs constructeurs avec des arguments, vous devez ajouter l'annotation @ PersistenceConstructor
au constructeur utilisé pour l'instanciation dans Spring Data JDBC.
Si vous ne souhaitez pas annoter Spring Data JDBC, vous pouvez définir une méthode de fabrique séparément.
Ajoutez l'annotation «@ Id» à la colonne qui devient l'identifiant. Étant donné que l'identifiant se verra attribuer l'identifiant émis dans la base de données après l'enregistrement de l'entité, définissez le flétrissement pour que l'identifiant puisse être mis à jour.
** * La référence a montré deux méthodes, soit pour utiliser le constructeur d'argument complet, soit pour utiliser Wither, mais comme l'ancienne méthode ne fonctionnait pas dans l'environnement actuel, je vais introduire la méthode utilisant Wither. Je suis. ** **
Member.java
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode(of = {"id"})
@ToString
public class Member {
@Id
@With
private final String id;
private final String name;
public static Member createInstance(String name) {
return new Member(null, name);
}
}
Une entité qui définit les accesseurs pour les constructeurs et les champs par défaut.
Member.java
@Getter
@Setter
@EqualsAndHashCode(of = {"id"})
@ToString
public class Member {
@Id
private String id;
private String name;
public static Member createInstance(String name) {
return new Member(null, name);
}
}
@ PersistenceConstructor
.Dans Spring Data JDBC, la méthode save est utilisée pour enregistrer et mettre à jour l'entité. Le fait que vous souhaitiez émettre une instruction INSERT ou une instruction UPDATE dépend du fait que l'entité est déjà persistante ou pas encore persistante. Il existe deux logiques de jugement principales dans Spring Data JDBC.
Si l'ID n'est pas généré automatiquement côté base de données, il est préférable de définir l'entité selon la deuxième méthode.
Spring Data JDBC a un support limité pour les relations hasOne et hasMany. Conservez une entité ou son ensemble dans un champ uniquement si la relation entre l'entité racine et l'entité au sein de son agrégat dans la conception pilotée par domaine est maintenue.
Si vous définissez aléatoirement la relation entre hasOne et hasMany, NULL et Empty ne seront pas définis par "si les données existent réellement ou non" mais par "si les données sont jointes ou non par SQL". Même si vous n'utilisez pas Spring Data JDBC, cela peut entraîner de graves bogues et réduire la productivité.
La conversion de type peut être personnalisée à l'aide de Converter ou Converter Factory.
Appliquez le Converter et le ConverterFactory créés avec une classe de configuration qui hérite de AbstractJdbcConfiguration.
Vous pouvez appliquer votre propre classe de conversion en remplaçant la méthode jdbcCustomConversions
.
JdbcConfiguration.java
@Configuration
public class JdbcConfiguration extends AbstractJdbcConfiguration {
@Override
public JdbcCustomConversions jdbcCustomConversions() {
return new JdbcCustomConversions(List.of(
// Converter/Enregistrer le bean de ConverterFactory
));
}
}
Implémentez Conveter si la destination est une classe unique spécifique, telle que la conversion Enum en String.
Affectez @ ReadingConverter
ou @ WritingConveter
au Conveter défini. S'il s'agit d'un convertisseur utilisé lors de la lecture de la base de données, ajoutez @ ReadingConverter
, et s'il s'agit d'un convertisseur utilisé lors de l'écriture dans la base de données, ajoutez @ WritingConverter
.
EnumToStringConverter.java
@WritingConverter
public enum EnumToStringConverter implements Converter<Enum, String> {
INSTANCE;
@Override
public String convert(Enum e) {
return e.name();
}
}
Si la destination de conversion est une implémentation d'interface ou une sous-classe d'une classe spécifique, telle que la conversion de String en Enum, implémentez ConverterFactory.
Puisque l'argument formel de ConverterFactory # getConverter
est les informations de la classe de destination de conversion, il est avantageux que les informations de la classe de destination de conversion puissent être gérées lors du processus de création d'une instance de Converter.
Les annotations sont les mêmes que pour Converter.
StringToEnumFactory.java
@ReadingConverter
public enum StringToEnumFactory implements ConverterFactory<String, Enum> {
INSTANCE;
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> aClass) {
return new StringToEnum<T>(aClass);
}
@RequiredArgsConstructor
private static class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
@Override
public T convert(String s) {
return s == null ? null : Enum.valueOf(enumType, s);
}
}
}
Vous pouvez utiliser l'annotation «@ Embedded» pour mapper des objets de valeur et des colonnes définis par l'utilisateur.
L'entité Member
avec l'objet de valeur ʻAddress` dans le champ peut être mappée à une colonne de table en la définissant comme suit:
Address.java
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode
public class Address {
private final String postcode;
private final String prefecture;
private final String addressLine;
}
Member.java
@Getter
@EqualsAndHashCode(of = {"id"})
@ToString
public class Member {
@Id
@With
private final String id;
private final String name;
@Embedded(prefix = "address_", onEmpty = Embedded.OnEmpty.USE_NULL)
private final Address address;
public static Member createInstance(String name, Address address) {
return new Member(null, name, address);
}
}
Comme dans l'exemple, l'annotation «@ Embedded» doit avoir deux arguments, «prefix» et «onEmpty».
prefix
Chaque champ de l'objet de valeur est mappé à une colonne en utilisant "préfixe" et "nom de champ dans l'objet de valeur". Ici, le mappage champ-colonne de ʻAddress` est résolu comme suit.
Nom de domaine | Nom de colonne |
---|---|
postcode | address_postcode |
prefecture | address_prefecture |
addressLine | address_address_line |
onEmpty
Spécifie la valeur à définir dans le champ de l'entité si le champ correspondant à l'objet de valeur est NULL.
Définir la valeur | Contenu |
---|---|
USE_NULL | Définir NULL |
USE_EMPTY | Définir un objet de valeur vide |
Fondamentalement, je pense qu'il est sûr d'utiliser USE_NULL.
C'est la fin de la première édition, et j'aimerais organiser les connaissances tout en les exploitant dans le futur.
Référence officielle JDBC Spring Data stack overflow -PagingAndSortingRepository methods throw error when used with spring data jdbc- Convertir Enum en numéro de non-commande avec Spring Data JDBC
Recommended Posts