1. Contrôle de programmation DML
Nous avons introduit les opérations liées aux requêtes, puis nous devons expliquer le contenu des trois autres, les ajouts, les suppressions et les modifications. Pour expliquer un par un, le premier est le contenu dans le nouveau (insert).
1. Contrôle de la politique de génération d'ID
Nous avons laissé un problème lorsque nous l'avons ajouté auparavant, c'est-à-dire qu'une fois l'ajout réussi, l'ID de clé primaire est une très longue chaîne de contenu.Ce que nous voulons plus, c'est s'auto-développer en fonction des champs de la table de base de données.Avant de résoudre ce problème problème, nous allons d'abord analyser comment choisir l'ID :
- Différentes tables appliquent différentes stratégies de génération d'identifiants
- journal : incrément (1,2,3,4,...)
- Commandes d'achat : règles spéciales (FQ23948AK3843)
- Commande à emporter : date zone concernée et autres informations (10 04 20200314 34 91)
- Table relationnelle : l'identifiant peut être omis
- ……
Différentes entreprises doivent utiliser différentes méthodes de génération d'ID, quelles stratégies de génération de clé primaire sont fournies dans MP et comment devons-nous choisir ?
Ici, nous devons utiliser une annotation de MP appelée@TableId
Point de connaissance 1 : @TableId
nom | @TableId |
---|---|
taper | annotation d'attribut |
Emplacement | au-dessus de la définition de la propriété utilisée pour représenter la clé primaire dans la classe modèle |
effet | Définir la stratégie de génération de l'attribut de clé primaire dans la classe actuelle |
attributs associés | valeur (par défaut) : définit le nom de la clé primaire de la table de base de données type : définit la stratégie de génération de l'attribut de clé primaire et vérifie la valeur en fonction de la valeur d'énumération de IdType |
1. Construction de l'environnement
Avant de construire des requêtes conditionnelles, préparons l'environnement
-
Créer un projet SpringBoot
-
Ajouter les dépendances correspondantes dans pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itheima</groupId> <artifactId>mybatisplus_03_dml</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
Écrire l'interface UserDao
@Mapper public interface UserDao extends BaseMapper<User> { }
-
Écrire des classes de modèles
@Data public class User { private Long id; private String name; @TableField(value="pwd",select=false) private String password; private Integer age; private String tel; @TableField(exist=false) private Integer online; }
-
Écrire la classe de démarrage
@SpringBootApplication public class Mybatisplus03DqlApplication { public static void main(String[] args) { SpringApplication.run(Mybatisplus03DqlApplication.class, args); } }
-
Écrire un fichier de configuration
# dataSource spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC username: root password: root # mp日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
écrire une classe de test
@SpringBootTest class Mybatisplus02DqlApplicationTests { @Autowired private UserDao userDao; @Test void testGetAll(){ List<User> userList = userDao.selectList(null); System.out.println(userList); } }
-
test
@SpringBootTest class Mybatisplus03DqlApplicationTests { @Autowired private UserDao userDao; }
-
La structure de projet finale créée est :
2. Démo de code
Stratégie AUTO
Étape 1 : Définissez la stratégie de compilation sur AUTO
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
Étape 2 : Supprimer les données de test et modifier l'auto-incrémentation
- Supprimez les données de test et laissez l'identifiant actuel augmenter
- Étant donné que la valeur de l'ID de clé primaire générée auparavant est relativement longue, elle augmentera la valeur de la croissance automatique de MySQL, elle doit donc être ajustée à la dernière valeur d'id.
Étape 3 : Exécuter la méthode nouvellement ajoutée
@Test
void insert() {
User user = new User();
user.setName("热爱编程的小白白");
user.setPassword("root");
user.setAge(22);
user.setTel("1768543416");
userDao.insert(user);
}
On constatera que l'ajout est réussi et que l'identifiant de la clé primaire commence également à partir de 5
Après ces trois étapes de démonstration, vous constaterez AUTO
que l'effet estUtiliser l'incrémentation automatique de l'ID de base de données, lors de l'utilisation de cette stratégie, vous devez vous assurer que la table de base de données correspondante est définie avec l'incrémentation automatique de la clé primaire ID, sinon elle ne sera pas valide.
Ensuite, nous pouvons entrer le code source pour voir quelles sont les stratégies de génération d'ID ?
Après avoir ouvert le code source, vous constaterez que vous ne voyez pas les commentaires chinois, ce qui nous oblige à cliquer sur le coin supérieur droit Download Sources
, et il téléchargera automatiquement le fichier java de cette classe pour vous, et nous pourrons voir le détail contenu des commentaires. Parce que cette technologie est faite par des Chinois, les commentaires dans son code sont relativement faciles à comprendre.
Après avoir téléchargé le code source, vous pouvez voir ce qui suit :
Comme le montre le code source, en plus de la stratégie AUTO, il existe plusieurs stratégies de génération comme suit :
- NONE : aucune stratégie de génération d'identifiant n'est définie
- ENTRÉE : l'utilisateur saisit manuellement l'identifiant
- ASSIGN_ID : l'algorithme Snowflake génère un identifiant (compatible avec les types numériques et de chaîne)
- ASSIGN_UUID : utilisez l'algorithme de génération d'UUID comme stratégie de génération d'identifiant
- Plusieurs autres stratégies sont obsolètes et seront remplacées par ASSIGN_ID et ASSIGN_UUID.
développer:
Qu'est-ce que l'ID distribué ?
- Lorsque la quantité de données est suffisamment importante, un serveur de base de données ne peut pas la stocker. À ce stade, plusieurs serveurs de base de données sont nécessaires pour le stockage.
- Par exemple, le tableau des commandes peut être stocké sur différents serveurs
- Si vous utilisez la clé primaire d'auto-incrémentation de la table de la base de données, il y aura un conflit car elle se trouve sur deux serveurs
- À ce stade, un ID unique au monde est nécessaire, qui est l'ID distribué.
Stratégie d'ENTRÉE
Étape 1 : Définissez la stratégie de génération sur INPUT
@Data
public class User {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
Remarque : Cette stratégie de génération d'ID doit supprimer la stratégie d'auto-incrémentation de la table.
Étape 2 : Ajouter des données et définir l'ID manuellement
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testSave(){
User user = new User();
//设置主键ID的值
user.setId(666L);
user.setName("加油!");
user.setPassword("冲冲冲");
user.setAge(12);
user.setTel("18754434333");
userDao.insert(user);
}
}
Étape 3 : Exécuter la méthode nouvellement ajoutée
Si la valeur de l'ID de clé primaire n'est pas définie, une erreur sera signalée et le message d'erreur est que l'ID de clé primaire n'a pas de valeur :
Si l'ID de clé primaire est défini, les données sont ajoutées avec succès, comme suit :
Stratégie ASSIGN_ID
Étape 1 : Définissez la stratégie de génération sur ASSIGN_ID
@Data
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
Étape 2 : Ajouter des données sans définir d'ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testSave(){
User user = new User();
user.setName("张三");
user.setPassword("123466");
user.setAge(32);
user.setTel("125607778989");
userDao.insert(user);
}
}
Remarque : Cette stratégie de génération n'a pas besoin de définir manuellement l'ID. Si l'ID est défini manuellement, la valeur définie par elle-même sera utilisée.
Étape 3 : Exécuter la méthode nouvellement ajoutée
L'ID généré est un type de données Long.
Stratégie ASSIGN_UUID
Étape 1 : Définissez la stratégie de génération sur ASSIGN_UUID
Il convient de noter que lors de l'utilisation de uuid, le type de la clé primaire ne peut pas être Long, mais doit être remplacé par le type String
@Data
public class User {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
}
Étape 2 : Modifier le type de clé primaire de la table
Le type de clé primaire est défini sur varchar et la longueur doit être supérieure à 32, car la clé primaire générée par UUID est de 32 bits, et si la longueur est petite, l'insertion échouera.
Étape 3 : Ajouter des données sans définir d'ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void insert() {
User user = new User();
user.setName("张三");
user.setPassword("123466");
user.setAge(32);
user.setTel("125607778989");
userDao.insert(user);
}
}
Étape 4 : Exécuter la méthode nouvellement ajoutée
Parlons ensuite de l'algorithme du flocon de neige :
L'algorithme Snowflake (SnowFlake), qui est une implémentation d'algorithme officiellement donnée par Twitter, est écrit en Scala. Le résultat généré est un entier 64 bits et sa structure est la suivante :
- 1 bit, pas besoin, car le bit le plus élevé en binaire est le bit de signe, 1 signifie nombre négatif, 0 signifie nombre positif. L'identifiant généré est généralement un entier, donc le bit le plus élevé est fixé à 0.
- Horodatage 41 bits, utilisé pour enregistrer l'horodatage, niveau milliseconde
- ID de machine de travail 10 bits, utilisé pour enregistrer l'ID de machine de travail, où le 5 bits haut est l'ID du centre de données, sa plage de valeurs est de 0 à 31 et le 5 bits bas est l'ID de nœud de travail, sa plage de valeurs est de 0 à 31, la combinaison des deux peut accueillir jusqu'à 1024 nœuds
- Le numéro de série occupe 12 bits, et chaque nœud commence à s'accumuler à partir de 0 toutes les millisecondes, et peut accumuler jusqu'à 4095, et un total de 4096 identifiants peuvent être générés
3. Comparaison des stratégies de génération d'identité
Présenté les stratégies de génération de ces ID de clé primaire, laquelle devrions-nous utiliser à l'avenir ?
- AUCUN : ne définissez pas la stratégie de génération d'identifiant, MP n'est pas généré automatiquement, approximativement égal à INPUT, donc les deux méthodes doivent être définies manuellement par l'utilisateur, mais le premier problème avec le réglage manuel est que le même ID est susceptible de provoquer conflits de clés, afin de s'assurer que les clés primaires n'entrent pas en conflit Vous devez faire beaucoup de jugements, et c'est plus compliqué à mettre en œuvre.
- AUTO : l'ID de la base de données est auto-incrémenté. Cette stratégie convient lorsqu'il n'y a qu'un seul serveur de base de données et ne peut pas être utilisée comme ID distribué.
- ASSIGN_UUID : il peut être utilisé dans un environnement distribué, et il peut être garanti qu'il est unique, mais la clé primaire générée est une chaîne de 32 bits, qui est trop longue pour occuper de l'espace et ne peut pas être triée, et les performances de la requête sont également lent
- ASSIGN_ID : il peut être utilisé dans une situation distribuée. Les nombres générés sont de type long et peuvent être triés avec de hautes performances. Cependant, la stratégie générée est liée à l'heure du serveur. Si l'heure système est modifiée, cela peut entraîner clés primaires en double.
- En résumé, chaque stratégie de clé primaire a ses propres avantages et inconvénients, et c'est le choix le plus judicieux à choisir en fonction de la situation réelle de votre propre entreprise de projet.
4. Configuration simplifiée
Nous avons déjà terminé le paramétrage du mappage des relations entre les tables et de la stratégie de clé primaire de la base de données, puis nous parlerons de leur configuration simplifiée pour l'utilisation de ces deux contenus :
Paramètres de stratégie de clé primaire de classe de modèle
C'est en effet un peu lourd, peut-on le configurer quelque part pour que toutes les classes du modèle puissent utiliser la stratégie d'ID de clé primaire ?
La réponse est oui, il suffit d'ajouter le contenu suivant au fichier de configuration :
mybatis-plus:
global-config:
db-config:
id-type: assign_id
Une fois configurée, la stratégie d'ID de clé primaire pour chaque classe de modèle sera assign_id.
Relation de mappage entre les tables de base de données et les classes de modèle
MP utilisera la première lettre minuscule du nom de classe de la classe de modèle comme nom de table par défaut. Si les noms des tables de la base de données commencent par , tbl_
nous devons ajouter toutes les classes de modèle @TableName
, telles que :
La configuration reste relativement lourde, la méthode simplifiée consiste à paramétrer le contenu suivant dans le fichier de configuration :
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
Définissez le contenu du préfixe de la table, de sorte que MP ajoutera tbl_
la première lettre minuscule de la classe de modèle, et elle sera simplement assemblée dans le nom de la table de la base de données.
2. Opération multi-enregistrement
Examinons d'abord les questions suivantes :
J'ai ajouté beaucoup de produits au panier auparavant, mais après quelques jours, j'ai constaté que ces articles ne sont plus recherchés, que dois-je faire ?
Il est très simple à supprimer, mais il est lent et fastidieux de le supprimer un par un, de sorte que l'utilisateur reçoit généralement une opération par lots, c'est-à-dire qu'il y a une case à cocher devant, l'utilisateur peut en cocher plusieurs à la fois ou sélectionner tout, puis supprimer Le panier peut être vidé une fois, et c'est 批量删除
l'opération qu'il faut utiliser.
Plus précisément comment implémenter plusieurs suppressions, trouvons la méthode API correspondante
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
La signification littérale de la méthode de traduction est : supprimer (supprimer par lots en fonction de l'ID), et le paramètre est une collection pouvant stocker plusieurs valeurs d'ID.
Exigence : Supprimez les données dans la table de base de données en fonction de l'ensemble d'ID entrant.
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testDelete(){
//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);
userDao.deleteBatchIds(list);
}
}
Une fois l'exécution réussie, les données de la table de base de données seront supprimées en fonction de l'identifiant spécifié.
En plus de la suppression en bloc en fonction de l'ensemble d'identifiants, vous pouvez également effectuer une requête par lots en fonction de l'ensemble d'identifiants. Examinons d'abord l'API.
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
Le nom de la méthode est traduit par : requête (requête par lots basée sur l'ID), et le paramètre est une collection qui peut stocker plusieurs valeurs d'ID.
Exigence : interroger les informations utilisateur en fonction de l'ensemble d'ID entrant
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetByIds(){
//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);
}
}
Le résultat de la requête sera interrogé en fonction de la valeur d'identifiant spécifiée transmise dans
3. Pierre tombale
L'étape suivante consiste à expliquer l'une des opérations les plus importantes de la suppression, la suppression logique. Analysons d'abord le problème :
-
Il s'agit d'un tableau des employés et des contrats qu'ils signent. La relation est qu'un employé peut signer plusieurs contrats. Il s'agit d'un tableau d'un (employé) à plusieurs (contrats)
-
L'ID de l'employé est 1 et trois contrats au total ont été signés. S'il part à ce moment-là, nous devons supprimer les données de la table des employés et l'opération de suppression sera exécutée.
-
Si la table a une relation clé primaire-étrangère lors de la conception, les trois premiers éléments de données de la table de contrat doivent également être supprimés en même temps
-
Si vous voulez compter le montant total du contrat signé plus tard, vous constaterez que c'est faux, car les informations du contrat signé par l'employé 1 ont été supprimées
-
Si seul l'employé est supprimé mais que les données de la table des contrats ne sont pas supprimées, les informations sur l'employé correspondant au numéro d'employé du contrat n'existent pas, il y aura alors des données inutiles et il n'y aura pas de contrat de propriétaire.
-
Par conséquent, après analyse, nous ne devons pas supprimer les données du tableau, mais devons les conserver, mais nous devons distinguer les démissionnaires des titulaires, afin de résoudre les problèmes ci-dessus, tels que :
- La façon de faire la distinction est d'ajouter une colonne de données à la table des employés
deleted
. Si c'est 0, cela signifie l'employé actuel. S'il est démissionné, il sera remplacé par 1. (Les significations de 0 et 1 peuvent être personnalisées)
Donc, pour le problème métier de l'opération de suppression, il y a :
- Suppression physique : les données commerciales sont supprimées de la base de données et l'opération de suppression est effectuée
- Tombstone : définissez le champ d'état de disponibilité pour les données, définissez le champ d'état sur l'état indisponible lors de la suppression, les données sont conservées dans la base de données et l'opération de mise à jour est effectuée
Comment implémenter la suppression logique dans MP ?
Étape 1 : Modifier la table de la base de données pour ajouter deleted
des colonnes
Le nom du champ peut être arbitraire et le contenu peut également être personnalisé. Par exemple, 0
cela signifie normal 1
et cela signifie supprimer. Vous pouvez définir sa valeur par défaut sur normal lors de l'ajout d'une colonne 0
.
Étape 2 : Ajouter des attributs à la classe d'entité
(1) Ajoutez un nom d'attribut correspondant à la colonne de la table de base de données. Le nom peut être arbitraire. S'il ne correspond pas au nom de colonne de la table de données, vous pouvez utiliser @TableField pour le mappage des relations. S'il est cohérent, il correspondra automatiquement.
(2) Identifiez le champ nouvellement ajouté en tant que champ de désactivation, utilisez@TableLogic
@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
@TableLogic(value="0",delval="1")
//value为正常数据的值,delval为删除数据的值
private Integer deleted;
}
Étape 3 : Exécutez la méthode de suppression
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testDelete(){
userDao.deleteById(1L);
}
}
À en juger par les résultats des tests, la dernière opération de suppression logique est l'opération de mise à jour, qui modifiera le champ spécifié à la valeur correspondant au statut de suppression.
penser
La suppression logique affecte-t-elle la requête ?
-
Exécuter l'opération de requête
@SpringBootTest class Mybatisplus03DqlApplicationTests { @Autowired private UserDao userDao; @Test void testFind(){ System.out.println(userDao.selectList(null)); } }
Exécutez le test, vous constaterez qu'il y aura une autre condition de requête dans l'instruction sql imprimée, telle que :
Il est concevable que la suppression logique de MP ajoute une condition non supprimée à toutes les requêtes, c'est-à-dire que les données supprimées ne doivent pas être interrogées.
-
Si vous souhaitez toujours interroger toutes les données supprimées, comment y parvenir ?
@Mapper public interface UserDao extends BaseMapper<User> { //查询所有数据包含已经被删除的数据 @Select("select * from tbl_user") public List<User> selectAll(); }
-
@TableLogic
Si chaque table a besoin d'avoir une suppression logique, alors il faut ajouter des annotations aux attributs de chaque classe de modèle , comment optimiser ?Ajoutez la configuration globale dans le fichier de configuration, comme suit :
mybatis-plus: global-config: db-config: # 逻辑删除字段名 logic-delete-field: deleted # 逻辑删除字面值:未删除为0 logic-not-delete-value: 0 # 逻辑删除字面值:删除为1 logic-delete-value: 1
Après avoir introduit la suppression logique, l'essence de la suppression logique est :
L'essence de la suppression logique est en fait une opération de modification. Si un champ de désactivation est ajouté, le champ de désactivation sera automatiquement ajouté lors de l'interrogation des données.
L'instruction SQL exécutée est :
MISE À JOUR tbl_user SETsupprimé=1 où id = ? ETsupprimé=0
Le résultat des données d'exécution est :
Point de connaissance 1 : @TableLogic
nom | @TableLogic |
---|---|
taper | annotation d'attribut |
Emplacement | au-dessus de la définition d'attribut dans la classe de modèle utilisée pour représenter le champ à supprimer |
effet | Identifier le champ comme un champ pour la suppression logique |
attributs associés | value : valeur logique non supprimée delval : valeur logique supprimée |
4. Verrouillage optimiste
1. Conception
Avant d'expliquer le verrouillage optimiste, analysons d'abord le problème :
Problèmes causés par la concurrence commerciale :pic
- S'il y a 100 produits ou billets en vente, afin de s'assurer que chaque produit ou billet ne peut être acheté que par une seule personne, comment s'assurer qu'il n'y aura pas de surachat ou de ventes répétées
- Pour ce type de problème, il existe en fait de nombreuses solutions qui peuvent être utilisées
- La première chose qui vient à l'esprit est le verrou. Le verrouillage d'un serveur peut être résolu, mais s'il est verrouillé sur plusieurs serveurs, il n'y a aucun moyen de le contrôler. Par exemple, il y a deux serveurs qui vendent des billets en 12306. Si tous des verrous sont ajoutés, cela peut également amener deux threads à vendre des tickets en même temps, ou il y aura des problèmes de concurrence
- La méthode que nous présenterons ensuite est une solution pour les petites entreprises, car les performances de la base de données elle-même sont un goulot d'étranglement, et si sa simultanéité dépasse 2000, d'autres solutions doivent être envisagées.
Pour faire simple, le principal problème résolu par le verrouillage optimiste est que lorsqu'un enregistrement doit être mis à jour, on espère que cet enregistrement n'a pas été mis à jour par d'autres.
2. Idées de mise en œuvre
La mise en œuvre du verrouillage optimiste :
- Ajoutez une colonne de version à la table de base de données, par exemple, la valeur par défaut est 1
- Avant que le premier thread ne veuille modifier les données, obtenez la version = 1 dans la base de données actuelle lors de la récupération des enregistrements
- Avant que le deuxième thread ne veuille modifier les données, obtenez la version = 1 dans la base de données actuelle lors de la récupération des enregistrements
- Lorsque le premier thread effectue une mise à jour, définissez version = newVersion où version = oldVersion
- nouvelleVersion = version+1 [2]
- ancienneVersion = version [1]
- Lorsque le deuxième thread effectue la mise à jour, définissez version = newVersion où version = oldVersion
- nouvelleVersion = version+1 [2]
- ancienneVersion = version [1]
- Si les deux threads mettent à jour les données, les premier et deuxième threads peuvent s'exécuter en premier
- Si le premier thread effectue la mise à jour en premier, il changera la version en 2,
- Lorsque le deuxième thread se met à jour à nouveau, définissez version = 2 où version = 1, à ce moment, la version des données de la table de base de données est déjà 2, donc le deuxième thread ne parviendra pas à modifier
- Si le deuxième thread effectue la mise à jour en premier, il changera la version en 2,
- Lorsque le premier thread est à nouveau mis à jour, définissez version = 2 où version = 1. À ce stade, la version des données de la table de base de données est déjà 2, de sorte que le premier thread ne parviendra pas à modifier
- Peu importe qui s'exécute en premier, cela garantira qu'un seul thread peut mettre à jour les données.C'est l'analyse du principe d'implémentation du verrou optimiste fourni par MP.
Comment mettre en œuvre les étapes mentionnées ci-dessus ?
3. Étapes de mise en œuvre
Après analyse des étapes, les étapes spécifiques de mise en œuvre sont les suivantes :
Étape 1 : Ajouter une colonne à la table de la base de données
Le nom de la colonne peut être arbitraire, par exemple en utilisant version
, définissez la valeur par défaut de la colonne1
Étape 2 : Ajoutez les attributs correspondants à la classe de modèle
Selon le nom de colonne de champ ajouté, ajoutez la valeur d'attribut correspondante dans la classe de modèle
@Data
//@TableName("tbl_user") 可以不写是因为配置了全局配置
public class User {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
@TableField(value="pwd",select=false)
private String password;
private Integer age;
private String tel;
@TableField(exist=false)
private Integer online;
private Integer deleted;
@Version
private Integer version;
}
Étape 3 : Ajouter un intercepteur pour un verrouillage optimiste
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor() {
//1.定义Mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//2.添加乐观锁拦截器
mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mpInterceptor;
}
}
Étape 4 : Effectuer l'opération de mise à jour
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testUpdate(){
User user = new User();
user.setId(3L);
user.setName("Jock666");
userDao.updateById(user);
}
}
Vous constaterez que cette modification ne met pas à jour le champ de version, car les données de version ne sont pas transportées.
Ajouter des données de version
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testUpdate(){
User user = new User();
user.setId("3");
user.setName("Jock666");
user.setVersion(1);
userDao.updateById(user);
}
}
Vous constaterez que ce que nous transmettons est 1, et MP y ajoutera 1, puis le mettra à jour dans la table de la base de données.
Donc, pour obtenir un verrouillage optimiste, la première étape doit être d'obtenir la version dans la table, puis d'utiliser la version comme condition pour ajouter 1 à la version et la mettre à jour dans la table de base de données, donc quand nous interrogeons, nous il faut l'interroger
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testUpdate(){
//1.先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L);
//2.将要修改的属性逐一设置进去
user.setName("Jock888");
userDao.updateById(user);
}
}
Après avoir analysé grossièrement les étapes de mise en œuvre du verrouillage optimiste, simulons une situation de verrouillage pour voir s'il peut être réalisé que lorsque plusieurs personnes modifient les mêmes données, une seule personne peut les modifier avec succès.
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testUpdate(){
//1.先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L); //version=3
User user2 = userDao.selectById(3L); //version=3
user2.setName("Jock aaa");
userDao.updateById(user2); //version=>4
user.setName("Jock bbb");
userDao.updateById(user); //verion=3?条件还成立吗?
}
}
Exécutez le programme et analysez les résultats :
Le verrouillage optimiste a été implémenté, que dois-je faire si je ne me souviens pas des étapes ci-dessus ?
Reportez-vous à la documentation officielle pour réaliser :
https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
2. Développement rapide
1. Analyse de principe du générateur de code
Dans une phrase:
On peut remplir le contenu vide pour faire des phrases, par exemple :
Un autre exemple:
En observant le code que nous avons écrit auparavant, nous constaterons qu'il y aura beaucoup de répétitions, telles que :
Puis on s'est dit, si je veux développer un module Livre, dois-je juste remplacer tout le contenu dans la partie rouge Book
, comme par exemple :
Nous constaterons donc que pour le développement de n'importe quel module, pour ce code, il s'agit essentiellement d'un ajustement de la partie rouge, nous appelons donc la chose qui supprime le contenu rougemodèle, la partie rouge s'appelleparamètreÀ l'avenir, il vous suffira de transmettre différents paramètres pour créer des codes dao de différents modules en fonction du modèle.
Sauf que Dao peut extraire des modules, en fait, toutes nos classes communes peuvent être extraites, tant qu'elles ont des parties communes. Regardons le template de la classe model :
- ① Il peut être rempli en fonction du nom de table de la table de base de données
- ② La stratégie de génération d'ID peut être générée en fonction de la configuration de l'utilisateur
- ③ à ⑨ peuvent être remplis en fonction du nom du champ de la table de la base de données
Donc, tant que nous savons quelle table générer par code, nous pouvons remplir ce contenu.
Après l'analyse, nous constaterons que pour terminer la génération automatique de code, nous avons besoin des éléments suivants :
- Modèle : Fourni par MyBatisPlus, vous pouvez le fournir vous-même, mais il est gênant et déconseillé
- Configuration liée à la base de données : lisez la base de données pour obtenir des informations sur les tables et les champs
- Configuration personnalisée du développeur : configuration manuelle, telle que la stratégie de génération d'ID
2. Implémentation du générateur de code
Étape 1 : Créer un projet Maven
Code 2 : Importer le package jar correspondant
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>mybatisplus_04_generator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--spring webmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Étape 3 : Écrire la classe d'amorçage
package com.itheima;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
public class CodeGenerator {
public static void main(String[] args) {
//1.获取代码生成器的对象
AutoGenerator autoGenerator = new AutoGenerator();
//设置数据库相关配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
autoGenerator.setDataSource(dataSource);
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); //设置代码生成位置
globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("热爱编程的小白白"); //设置作者
globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.AUTO); //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.aaa"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("domain"); //设置实体类包名
packageInfo.setMapper("dao"); //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("user"); //设置当前参与生成的表名,参数为可变参数
// strategyConfig.setTablePrefix("tbl_"); //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true); //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);
//2.执行生成操作
autoGenerator.execute();
}
}
Pour le contenu du code dans le générateur de code, on peut directement obtenir le code du document officiel pour modification,
https://mp.baomidou.com/guide/generator.html
Étape 5 : Exécutez le programme
Après une exécution réussie, beaucoup de code sera généré dans le projet en cours, le code contient controller
, service
etmapper
entity
À ce stade, le générateur de code a terminé son travail et nous pouvons rapidement créer des classes correspondantes basées sur les tables de la base de données, simplifiant ainsi notre développement de code.
3. CRUD de service en MP
Passez en revue notre précédente écriture de code de couche métier, écrivez des interfaces et des classes d'implémentation correspondantes :
public interface UserService{
}
@Service
public class UserServiceImpl implements UserService{
}
Une fois l'interface et la classe d'implémentation disponibles, les méthodes doivent être déclarées dans l'interface et la classe d'implémentation.
public interface UserService{
public List<User> findAll();
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
public List<User> findAll(){
return userDao.selectList(null);
}
}
Après avoir vu le code ci-dessus, MP a déclaré que ces méthodes sont relativement fixes et générales, donc je vais vous aider à les extraire, donc MP fournit une interface de service et une classe d'implémentation, qui sont : et, cette dernière est une implémentation spécifique de la IService
première ServiceImpl
accomplit .
À l'avenir, le Service que nous avons écrit nous-mêmes pourra être modifié comme suit :
public interface UserService extends IService<User>{
}
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService{
}
L'avantage après la modification est que MP nous a aidés à réaliser quelques ajouts, suppressions, modifications et requêtes de base de la couche métier, qui peuvent être utilisées directement.
Écrivez une classe de test pour tester :
@SpringBootTest
class Mybatisplus04GeneratorApplicationTests {
private IUserService userService;
@Test
void testFindAll() {
List<User> list = userService.list();
System.out.println(list);
}
}
Remarque : L'environnement MyBatis n'est pas configuré dans le projet. Si vous souhaitez l'exécuter, vous devez extraire et améliorer le contenu du fichier de configuration avant de l'exécuter.
Réflexion : Quelles méthodes peuvent être utilisées dans la couche de service encapsulée par MP ?
Voir la documentation officielle : https://mp.baomidou.com/guide/crud-interface.html
, vous pouvez vous référer à la documentation officielle pour apprendre et utiliser ces méthodes fournies. Les noms des méthodes peuvent changer un peu, mais les paramètres et les valeurs de retour correspondant aux méthodes sont fondamentalement similaires.
Notes de: Tutoriel sur le framework SSM du programmeur Dark Horse
3. Recommandation de livre
La technologie de base de la machine virtuelle Java est complète : à travers des cas de combat réels + des diagrammes d'effets d'exécution + des codes de base, analysez et explorez les principes sous-jacents du noyau JVM, renforcez et favorisez la mise en œuvre de l'optimisation JVM et vous apprenez à comprendre les profondeurs principes de la machine virtuelle Java pas à pas !
⭐Système : le contenu du livre est progressif, facile à comprendre et vous enseigne pas à pas la technologie de base de la machine virtuelle JVM. ⭐En profondeur : analysez et explorez les principes sous-jacents du cœur JVM et renforcez l'implémentation de JVM. Optimisation . , Localisez avec précision les détails techniques des cas de combat réels
Ce livre se concentre principalement sur les caractéristiques de base et les principes de fonctionnement de la machine virtuelle Java, analyse la structure de composition et l'implémentation sous-jacente de la JVM de manière simple et simple, et présente de nombreuses solutions de réglage des performances et l'utilisation d'outils. Enfin, le principe de mise en œuvre du modèle de mémoire JMM et le mécanisme d'optimisation du compilateur Java sont étendus, de sorte que les lecteurs peuvent non seulement apprendre les connaissances techniques de base de la JVM, mais également consolider les bases techniques du réglage JVM et de l'optimisation du code.
Ce livre est adapté à la lecture de référence par les développeurs, les chefs de projet, les architectes et les ingénieurs de réglage des performances qui ont déjà une certaine base de programmation Java. En même temps, ce livre peut également être utilisé comme ouvrage de référence pédagogique pour les écoles professionnelles et les cours de formation en informatique. .
⭐ "Une introduction simple à la machine virtuelle Java : principes et pratiques JVM" Livraison gratuite pour envoyer 4 exemplaires !
⭐Durée de l'activité : jusqu'au 14/07/2023 20:00:00
⭐Méthode du tirage au sort : utilisez le programme pour tirer une loterie.
⭐Méthode de participation : suivez le blogueur, aimez, marquez et commentez "La vie est courte, j'aime java" dans la zone de commentaire ⭐Un
total de 4 livres seront offerts lors de cet événement, et 4 amis seront sélectionnés dans le commentaire espace à offrir gratuitement ! !
⭐Date limite de l'événement : 2023-07-14 20:00:00
⭐Liste des gagnants⭐
vⅤ_Leon
rentrera chez lui dans un moment℡Nanyang
Xiaoliu
est à la station C