Environnement de développement de cet article: Spring-Boot: 2.0.3.Release + Java1.8
Pourquoi faire
Suppression douce: c'est-à-dire qu'aucune opération de suppression réelle n'est effectuée. En raison de l'existence d'une liaison (clés étrangères) entre nos entités, la suppression de certaines données conduira à d'autres données incomplètes. Par exemple, l'enseignant de la classe d'ordinateur 1801 est Zhang San. À l'heure actuelle, si nous supprimons Zhang SAN, alors lorsque nous interrogeons la classe d'ordinateur 1801, puisque Zhang San n'existe plus, nous signalerons l'erreur EntityNotFound. Bien sûr, dans une base de données avec des contraintes de clés étrangères, si Zhang San est enseignant de la classe 1801, alors nous supprimerons directement Zhang SAN et rapporterons une exception contraignante. En d'autres termes, le comportement de la suppression directe de Zhang SAN ne peut pas être exécuté.
Mais parfois, nous avons besoin de le supprimer. Par exemple, un employé part, puis nous voulons supprimer l'employé dans la gestion des employés. Cependant: l'employé a des antécédents dans le tableau de données. Par exemple, nous avons enregistré que la structure de données du deuxième semestre de 2017 était Zhang Sanjiao. Ensuite, en raison de l'existence de contraintes, une erreur de liaison sera signalée lors de la suppression de Zhang SAN. En d'autres termes: l'embarras qu'il soit supprimé mais ne peut pas être supprimé.
Cela utilise la suppression douce mentionnée dans cet article. La soi-disant délétion douce signifie que je ne supprime pas vraiment les données dans le tableau de données, mais ajoute plutôt une marque à l'enregistrement de le supprimer.
Spring JPA prend en charge la suppression douce, nous pouvons trouver plus d'articles de bonne qualité pour résoudre ce problème. Les étapes générales sont: 1. Ajouter @SQLDEDELETE ("Mise à jour xxxx set deleted = 1 where id = ?")。2.加入@Where(clause = "deleted = false") . Mais cette solution n'est pas parfaite. Spécifiquement manifesté dans:
Prenons l'exemple de Zhang San en tant que professeur en classe 1801.
Après avoir ajouté l'annotation, nous pouvons en effet supprimer avec succès Zhang San. Après l'opération de suppression, nous avons vérifié le tableau de données et le dossier de Zhang San est en effet toujours là. Mais pour le moment, si nous faisons all ou une requête page , nous obtiendrons une erreur 500 EntiyNotFound . En effet, pendant toutes les requêtes, JPA ajoute automatiquement les paramètres de requête dans @Where . En raison de deleted = true pour les données associées, une exception que l'entité associée n'est pas trouvée.
Mais le fait est: bien que l'entité soit supprimée, elle est toujours là et nous voulons l'appliquer à la requête de l'association. Il n'est pas prévu qu'il aura une exception 500 EntiyNotFound .
La solution de cet article met en œuvre:
Solution
Mise en œuvre
initialisation
Créez trois nouvelles entités: ClazzTest, Clazz, Teacher et créez une nouvelle entité de classe abstraite BaseEntity . Où ClazzTest est utilisé pour démontrer l'exception qui se produit lors de l'utilisation @Where(clause = "deleted = false") .
package com.mengyunzhi.springbootsamplecode.softdelete.entity; import javax.persistence.maptSuperclass; @MappySuperClassPublic Résumé Classe BaseEntity {private boolean Deleted = false; // setter et getter} package com.mengyunzhi.springbootsamplecode.softdelete.entity; import org.hibernate.annotations.sqldelete; import javax.persistence.entity; import javax.persistence.genereatedvalue; import javax.persistence.id; / ** * catégorie * / @ @ entité @ sqldeletete (sql = "uping `klass` set supprimé = 1 où id =?") Classe publique Klass étend la base de base {@id @GeneratedValue Private Long ID; nom de chaîne privé; // setter et getter} @ Entité @ SQLDEDELETE (SQL = "Update` KLASS_TEST` SET DELELED = 1 WHERE ID =? ") @ Où (clause =" Deleted = false ") Classe publique KLASSTEST étend la base de base {@id @generatedvalue privé Long Id; Nom de la chaîne privée;} Réécrire la crudéposition
package com.mengyunzhi.springbootsamplecode.softdelete.core; import org.springframework.data.jpa.repository.query; import org.springframework.data.repository.crudepository; import org.springframework.data.repository.noreOpositororyBan; javax.transaction.transactional; import java.util.optional; / ** * appliquer de la suppression soft * default @where (clause = "supprimé = 0") provoquera une exception de l'objetNotFound lorsqu'une requête associée est effectuée à l'intérieur de HiberNate * Redéfinir l'interface ici * Référence: référence: https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa/22202469 * @Author Mengyunzhi Développement de logiciels de Hebei University of Technology * / @ NorepositoryBeanPublic Interface SoftdeletsOrtory <T, ID> Extend CRUDREPOSITORy <T, Id> @Override @Transactional @Query ("SELECT E FROM # {# EntityName} E où e.id =? 1 et e.deleted = false") facultatif <T> findById (id id); @Override @Transactional Boolean existyid (id id) {return findbyid (id) .ispresent (); } @Override @Transactional @Query ("SELECT E FROM # {# EntityName} E où e.deleted = false") iTable <T> findall (); @Override @Transactional @Query ("SELECT E FROM # {# EntityName} e Where E.Id in? 1 et E.Deleted = false") iTable <T> FindallById (iTable <id> ids); @Override @Transactional @Query ("SELECT COUNT (E) FROM # {# EntityName} E où e.deleted = false") Long Count ();} Créer une nouvelle classe d'entrepôt
Héritage de CruDrepository de Spring.
/ ** * Classe * @author Panjie * / Interface publique KlassRepository étend SoftdeleteCrudepository <Klass, Long> {} L'interface publique KlassteStRepository étend SoftdeleteCrudepository <Klasstest, long> {} Interface publique TeacherRepository étend CruDrepository <Teacher, Long> {} test
package com.mengyunzhi.springbootsamplecode.softdelete.repository; import com.mengyunzhi.springbootsamplecode.softdelete.entity.klass; import com.mengyunzhi.springbootsamplecode.softdelete.entity.klasstest; com.Mengyunzhi.springbootsampleCode.softdelete.entity.teacher; import org.assertj.core.api.assertions; import org.junit.test; import org.junit.runner.runwith; import org.slf4j.logger; import org.slf4j.loggerfactory; org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.test.context.springboottest; import org.springframework.orm.jpa.jpaobjectrevalFailureException; import org.springframework.test.Context.junit4.springrunner; java.util.list; Importer java.util.optional; / ** * @author Panjie * / @ springboottest @ runwith (springrunner.class) public class enseigningrepositorytest {private final static logger = loggerfactory.getLogger (enseignRepositoryTest.class); @Autowired klassRepository klassRepository; @Autowired KlassteStRepository KlassteStRepository; @Autowired TeacherRepository TeacherRepository; @Test public void findbyid () {logger.info ("Créer un nouveau professeur avec Klass et Klasstest"); Klass klass = new klass (); klassRepository.save (klass); Klasstest klasstest = new klasstest (); klassteSTrepository.save (klasstest); Enseignant enseignant = nouveau professeur (); enseign.setklass (klass); Teacher.Setklasstest (Klasstest); enseigningrepository.save (professeur); Logger.info ("Recherchez l'enseignant, affirmez que l'entité a été trouvée et aucune exception ne s'est produite"); Facultatif <Seacher> TeacherOptional = TeacherRepository.FindById (Teacher.getId ()); Assertions.essertThat (enseignational.get ()). Isnotnull (); Logger.info ("Supprimer le Klass associé, puis rechercher l'entité enseignant, affirmer que l'entité a été trouvée, aucune exception ne s'est produite. Affirmez qu'il existe toujours une entité Klass supprimée dans l'entité enseignant"); klassRepository.deleteById (klass.getId ()); TeacherOptional = TeacherRepository.FindById (Teacher.getId ()); Assertions.essertThat (enseignational.get ()). Isnotnull (); Assertions.essertThat (enseignational.get (). Getklass (). GetID ()). IsEqualto (klass.getId ()); Logger.info ("Recherchez la liste des enseignants, aucune exception ne s'est produite. Affirmez qu'il existe un dossier d'entité KLASS supprimé dans l'entité enseignant"); List <Seacher> TeacherList = (List <Seacher>) TeacherRepository.Findall (); pour (Teacher Teacher1: TeacherList) {assertions.essertThat (Teacher1.getKlass (). GetId ()). IsEqualto (klass.getId ()); } logger.info ("Supprimer le Klasstest associé, puis rechercher l'entité enseignant, affirmer que le klasstest supprimé a été trouvé"); klassteStRepository.deleteById (klasstest.getId ()); TeacherOptional = TeacherRepository.FindById (Teacher.getId ()); Assertions.essertThat (enseignational.get ()). Isnotnull (); Assertions.essertThat (enseignational.get (). Getklasstest (). GetID ()). IsEqualto (klasstest.getId ()); Logger.info ("Recherchez la liste des enseignants à nouveau, affirmez qu'une exception de JPAOBjectTrevalalFailureException se produira (une exception en entitéNotfound est capturée, encapsulée et lancée) Exception"); Boolean CatchException = false; essayez {enseignRepository.findall (); } catch (jpaObjectRetEVAlFailureException e) {CatchException = true; } Assertions.AssertThat (CatchException) .istrue (); }} Résumer
Lorsque vous utilisez l'annotation par défaut @sqldedelete et @where, les données JPA peuvent bien gérer la méthode findById (), mais elle ne parvient pas bien à gérer la méthode findall (). Ici, nous avons implémenté la méthode de réécriture de la méthode CrunRepository. Lorsque nous effectuerons des requêtes de base, nous utilisons notre méthode personnalisée pour ajouter supprimé = true. Lorsque JPA effectue une requête d'association, puisque nous ne définissons pas l'annotation @Where, toutes les données seront interrogées, évitant ainsi l'exception qui se produit parfois lorsque la requête Foundall () est effectuée.
Dans cet article, nous ne donnons qu'un exemple de code.
Si vous avez besoin du code complet, veuillez cliquer: https://github.com/mengyunzhi/springbootsamplecode/tree/master/softdelete.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.