web-dev-qa-db-fra.com

Comment vider les données dans la base de données dans une transaction active de printemps?

Je souhaite tester la méthode save () de la session d'hibernation à l'aide de la structure de test Spring. La méthode @Test est:

@Test
@Transactional
public void testSave() {
    User expected = createUser();
    getGenericDao().currentSession().save(expected);
    User actual = getUser(generatedId);
    assertUsersEqual(expected,actual);
}

Je veux jeter l'utilisateur dans la base de données. Je veux que mon utilisateur soit dans la base de données après cette méthode

getGenericDao().currentSession().save(expected);

Ensuite, je souhaite accéder à la base de données à l'aide du framework de données Spring et récupérer cet utilisateur enregistré à la ligne suivante:

User actual = getUser(generatedId);

J'ai essayé d'utiliser la méthode de vidage en veille prolongée comme:

currentSession().setFlushMode(MANUAL);
//do saving here
currentSession().flush();

Il ne jette pas mon utilisateur dans la base de données! Cependant, si je n'utilise pas l'annotation de ressort @Transactional et ne sauvegarde pas mon utilisateur dans une transaction de ressort programmatique, j'obtiens ce que je veux. Malheureusement, l’utilisateur enregistré dans la base de données n’est pas restauré car il n’existe pas de ressort @Transactional. Par conséquent, ma méthode de test modifie la base de données et le comportement des méthodes de test suivantes.

Je dois donc vider mon utilisateur dans la méthode de test interne de la base de données (pas à la fin) et à la fin de la méthode de test annuler toutes les modifications apportées à la base de données.

UPDATEsuggestion de préparer la méthode comme suit:

@Transactional
public void doSave(User user){
    getGenericDao().currentSession().save(user);
}

Et appeler doSave dans testSave ne fait rien. Je n'ai toujours pas d'utilisateur dans la base de données après avoir exécuté cette méthode. Je place le point d'arrêt et vérifie ma base de données en ligne de commande.

UPDATEMerci beaucoup pour votre réponse. Le problème est que la méthode flush () ne met pas mon utilisateur dans la base de données. J'ai essayé Isolation.READ_UNCOMMITTED et ne met pas mon utilisateur dans la base de données. Je peux réaliser ce que je veux, mais seulement si je désactive la transaction de printemps sur la méthode @Test et que je sauvegarde les transactions programmatiques. MAIS alors la méthode @Test n’est pas restaurée, laissant l’utilisateur enregistré pour les méthodes @Test suivantes. Ici, la méthode @Test pour enregistrer l'utilisateur n'est pas aussi dangereuse que la méthode @Test pour supprimer l'utilisateur, car elle n'est pas restaurée. Donc, il doit y avoir un support transactionnel pour la méthode @Test avec laquelle je ne peux de toute façon pas mettre mon utilisateur (ou supprimer) dans la base de données. En réalité, l'utilisateur est placé (ou supprimé) dans la base de données uniquement après la fin de la méthode @Test et après la transaction pour la méthode @Test. Je souhaite donc enregistrer mon utilisateur dans la base de données au milieu de la méthode @Test et le restaurer à la fin de la méthode @Test

Je vous remercie!

13

Enfin, je suis resté sur la solution suivante:

Premièrement, mes méthodes @Test ne s'exécutent pas dans le support print @Transactional. Voir cet article pour savoir à quel point il peut être dangereux. Ensuite, au lieu d’utiliser les beans @Repository dans les méthodes @Test, j’ai autowire des beans @Service qui utilisent l’annotation @Transactional. Le miracle est cette méthode @Test comme celle-ci 

@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testSave() {
    Answer created = createAnswer();
    Long generatedId = answerService.save(created);
//at this moment answer is already in db
    Answer actual=getAnswerById(generatedId);
... }

place mon objet Answer dans la base de données (juste après answerService.save(created);) et la méthode getAnswerById va dans la base de données et l'extrait pour vérifier si la sauvegarde était correcte.
Pour éliminer les modifications apportées à la base de données dans la méthode @Test, je recrée la base de données par JdbcTestUtils.executeSqlScript.

9
  1. Jetez un coup d'oeil ici avec un avertissement concernant les tests @Transactional ( Les pièges printaniers: les tests transactionnels considérés comme dangereux ) J'ai utilisé @org.springframework.test.context.jdbc.Sql pour re-peupler la base de données dans mes tests de service et @Transactional pour les contrôleurs.
  2. ConstraintViolationException pour le test de mise à jour du contrôleur avec des données non valides ont été levées uniquement lorsque la transaction est validée. J'ai donc trouvé 3 options:
    • 2.1 Annoter le test avec @Commit ou avec @Transactional(propagation = Propagation.NEVER). Soyez conscient du changement de base de données.
    • 2.2 Utiliser TestTransaction

Code:

     TestTransaction.flagForCommit();
     TestTransaction.end();
  • 2.3 Utilisez TransactionTemplate

Code:

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Test(expected = Exception.class)
    public void testUpdate() throws Exception {
        TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        String json = ...
        transactionTemplate.execute(ts -> {
            try {
                mockMvc.perform(put(REST_URL + USER_ID)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(json))
                    .andExpect(status().isOk());
                ...
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        });
4
GKislin

Si flush ne fonctionne pas, tout dépend du niveau d'isolation de votre base de données. 

Isolation est l'une des propriétés ACID de la base de données, qui définit comment/quand les modifications apportées par une opération deviennent visibles par d'autres opérations simultanées.

Je crois que votre niveau d'isolement est défini sur Read Committed ou Repeatable Read

1
JSS

Vous devez également vous occuper du paquet importé: Dans mon cas, j'ai importé

import javax.transaction.Transactional;

au lieu de 

import org.springframework.transaction.annotation.Transactional;

0
Meinew