3
votes

Erreur SQL: 23503, SQLState: 23503 dans l'application Spring Boot 2.1.4.RELEASE

J'ai une application SpringBoot 2.1.4.RELEASE RESTful Web Service., utilisant Spring Initializer, Tomcat intégré, moteur de modèle Thymeleaf et package sous forme de fichier JAR exécutable.

J'ai cette classe:

XXX

et cette méthode dans le référentiel:

019-04-16 11:21  [main] WARN  o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions(137) - SQL Error: 23503, SQLState: 23503
2019-04-16 11:21  [main] ERROR o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions(142) - Referential integrity constraint violation: "FK56KKTN0YV9SJJIOJJVJBPAGNW: PUBLIC.T_MENU_ALERT_NOTIFICATION FOREIGN KEY(MENU_ALERT_ID) REFERENCES PUBLIC.T_MENU_ALERT(ID) (1)"; SQL statement:
delete from t_menu_alert where id=? [23503-197]

J'ai créé un test Junit:

MenuAlertNotification menuAlertNotification = new MenuAlertNotification (menuAlert);        
        menuAlertNotificationService.save(menuAlertNotification);               
        assertFalse (menuAlertNotification.getRead());      
        Long menuAlertNotificationId = menuAlertNotification.getId();       
        List<Long> notificationIdList = new ArrayList <Long>();     
        notificationIdList.add  (menuAlertNotificationId);

        menuAlertNotificationService
                .changeMenuNotificationReadStatus (notificationIdList, user.getId(), Boolean.TRUE);


4 commentaires

Je pense que le problème réside dans "n.menuAlert.menu.user.id =: userId" parce que vous êtes la propriété d'accès de l'enfant d'un enfant. et en le comparant avec id, c'est pourquoi ce problème se produit. Comme MenuAlert est impatient, il est récupéré mais son enfant ne peut pas être récupéré comme le menu et l'utilisateur enfant du menu, leur FetchType sera Lazy. Comme ils ne sont pas récupérés lors de l'exécution, ce problème peut se produire.


Je suppose que vous créez un nouveau menuAlert dans votre test unitaire et que le framework de test essaie de le supprimer à la fin de votre test. Pouvez-vous inclure le test unitaire entier avec des annotations sur le test.


Oui, veuillez ajouter le test complet si nos réponses n'ont pas résolu le problème.


Veuillez regarder ma réponse et la marquer en conséquence si elle est utile


4 Réponses :


0
votes

L'erreur est causée par le fait que la base de données va supprimer un enregistrement est référencée par d'autres enregistrements avec une clé étrangère. (Contrainte de clé étrangère)

Je suppose que dans votre cas de test, avant l'extrait de code que vous avez analysé ici, il y a un code d'opération de suppression, quelque chose comme

MenuAlertRepository.deleteAll()

Spring JPA n'exécutera pas cette instruction immédiatement en cas de transaction. Après cela, Spring JPA a rencontré la requête JPQL (changeMenuNotificationReadStatus), puis il a vidé toutes les instructions mises en cache dans db. À ce moment, l'instruction de suppression était exécutée exactement. Ensuite, l'exception de contrainte de clé étrangère a été levée.


0 commentaires

3
votes

L'ID n'est pas généré lorsque vous faites MenuAlertNotification menuAlertNotification = new MenuAlertNotification (menuAlert); ainsi que Long menuAlertNotificationId = menuAlertNotification.getId (); cela retournera toujours null .

Vous devriez changer la deuxième ligne de votre test junit en

menuAlertNotification = menuAlertNotificationService.save(menuAlertNotification);

Je suppose que votre service menuAlertNotificationService.save (menuAlertNotification ); renvoie quelque chose comme return notificationRepo.save (entity) .

De cette façon, vous avez le nouvel objet rempli avec l'ID après que la ligne est insérée et ne sera donc pas obtenir l'exception indiquée.


0 commentaires

0
votes

Comme Jian l'a dit, ce n'est pas l'appel à changeMenuNotificationReadStatus qui provoque l'erreur, mais probablement une instruction précédente qui n'a pas été supprimée.

Quelque chose génère cette instruction de suppression, qui est responsable de la violation de la contrainte FK:

@Service
public class MenuAlertNotificationService {
    private MenuAlertNotificationRepository repository;

    public MenuAlertNotification save(MenuAlertNotification entity) {
        return repository.save(entity);
    } 
}

Dans vos tests, vous pouvez utiliser les méthodes saveAndFlush au lieu de save , et flush régulièrement pour émettre Requêtes de modification SQL et voyez où se trouve le problème.

Ici, JPA tente de supprimer MenuAlert avant de supprimer le MenuAlertNotification de référence, de sorte que la contrainte FK se bloque comme vous le savez probablement.

Comme une notification n'a aucun sens pour une alerte supprimée, vous pouvez également mettre en cascade la suppression:

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)

ou modifier la contrainte FK Action ON DELETE au niveau de la base de données: CASCADE supprimera les MenuAlertNotification de référence et SET NULL effacera l'association en mettant null sur le référencement MenuAlertNotificati on.menuAlert .

Vous pouvez également écrire du code java pour supprimer les MenuAlertNotification avant le MenuAlert , en fonction des conditions. p>

Je serais curieux de lire tout votre code de test.

Adil Khalil, vous avez raison de dire que si la méthode Spring Data JPA Repository.save est utilisée directement, nous devons obtenir le retour value pour avoir l'ID mis à jour. Mais ici, je pense qu'il appelle une couche de service qui doit retourner la valeur mise à jour:

delete from t_menu_alert where id=?


0 commentaires

2
votes

Le problème est que vous utilisez une base de données en mémoire et que lorsque la méthode de test est terminée, Junit supprime toutes les tables mais pas dans le bon ordre.

Le code de la couche Service doit dépendre du référentiel. Cependant, pour tester la couche de service, vous n'avez pas besoin de savoir ou de vous soucier de la façon dont la couche de persistance est implémentée.

Idéalement, vous devriez être en mesure d'écrire et de tester notre code de couche Service sans câblage dans notre couche de persistance complète.

Pour y parvenir, vous pouvez utiliser le support de simulation fourni par Spring Boot Test.


0 commentaires