Transações do Spring Data MongoDB

Transações do Spring Data MongoDB

1. Visão geral

A partir da versão 4.0, o MongoDB suporta transações ACID para vários documentos. E,Spring Data Lovelace now provides support for these native MongoDB transactions.

Neste tutorial, discutiremos o suporte do Spring Data MongoDB para transações síncronas e reativas.

Também daremos uma olhada no Spring DataTransactionTemplate para suporte a transações não nativas.

Para uma introdução a este módulo Spring Data, dê uma olhada em nossointroductory write-up.

2. Configurar MongoDB 4.0

Primeiro, precisaremos configurar o MongoDB mais recente para experimentar o novo suporte a transações nativas.

Para começar, temos que baixar a versão mais recente deMongoDB Download Center.

A seguir, iniciaremos o serviçomongod usando a linha de comando:

mongod --replSet rs0

Por fim, inicie o conjunto de réplicas - se ainda não estiver:

mongo --eval "rs.initiate()"

Observe que o MongoDB atualmente suporta transações em um conjunto de réplicas.

3. Configuração do Maven

Em seguida, precisamos adicionar as seguintes dependências ao nossopom.xml:


    org.springframework.data
    spring-data-mongodb
    2.1.0.RELEASE



    org.springframework.data
    spring-data-releasetrain
    Lovelace-M3
    pom
    import

Observe que precisamos adicionar o repositório de marcos ao nossopom.xml também:


    
        spring-milestones
        Spring Milestones
        https://repo.spring.io/milestone
        
            false
        
    

A versão mais recente da biblioteca pode ser encontrada emCentral Repository

4. Configuração do MongoDB

Agora, vamos dar uma olhada em nossa configuração:

@Configuration
@EnableMongoRepositories(basePackages = "com.example.repository")
public class MongoTransactionConfig extends AbstractMongoConfiguration{

    @Bean
    MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
        return new MongoTransactionManager(dbFactory);
    }

    @Override
    protected String getDatabaseName() {
        return "test";
    }

    @Override
    public MongoClient mongoClient() {
        return new MongoClient("127.0.0.1", 27017);
    }
}

Observe quewe need to register MongoTransactionManager em nossa configuração para habilitar transações nativas do MongoDB, pois elas são desabilitadas por padrão.

5. Transações síncronas

Depois de terminar a configuração, tudo o que precisamos fazer para usar as transações nativas do MongoDB - éannotate our method with*@Transactional*.

Tudo dentro do método anotado será executado em uma transação:

@Test
@Transactional
public void whenPerformMongoTransaction_thenSuccess() {
    userRepository.save(new User("John", 30));
    userRepository.save(new User("Ringo", 35));
    Query query = new Query().addCriteria(Criteria.where("name").is("John"));
    List users = mongoTemplate.find(query, User.class);

    assertThat(users.size(), is(1));
}

Observe que não podemos usar o comandolistCollections dentro de uma transação de vários documentos - por exemplo:

@Test(expected = MongoTransactionException.class)
@Transactional
public void whenListCollectionDuringMongoTransaction_thenException() {
    if (mongoTemplate.collectionExists(User.class)) {
        mongoTemplate.save(new User("John", 30));
        mongoTemplate.save(new User("Ringo", 35));
    }
}

Este exemplo lança umMongoTransactionException, pois usamos o métodocollectionExists().

Também não podemos executarcount dentro de uma transação de vários documentos:

@Test(expected = MongoCommandException.class)
@Transactional
public void whenCountDuringMongoTransaction_thenException() {
    userRepository.save(new User("John", 30));
    userRepository.save(new User("Ringo", 35));
    userRepository.count();
}

Porém, podemos solucionar esse problema com uma consulta simples e obter o tamanho da lista resultante:

@Test
@Transactional
public void whenQueryDuringMongoTransaction_thenSuccess() {
    userRepository.save(new User("Jane", 20));
    userRepository.save(new User("Nick", 33));
    List users = mongoTemplate.find(new Query(), User.class);

    assertTrue(users.size() > 1);
}

6. TransactionTemplate

Vimos como o Spring Data suporta novas transações nativas do MongoDB. Além disso, o Spring Data também fornece a opção não nativa.

We can perform non-native transactions using Spring Data TransactionTemplate:

@Test
public void givenTransactionTemplate_whenPerformTransaction_thenSuccess() {
    mongoTemplate.setSessionSynchronization(SessionSynchronization.ALWAYS);

    TransactionTemplate transactionTemplate = new TransactionTemplate(mongoTransactionManager);
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            mongoTemplate.insert(new User("Kim", 20));
            mongoTemplate.insert(new User("Jack", 45));
        };
    });

    Query query = new Query().addCriteria(Criteria.where("name").is("Jack"));
    List users = mongoTemplate.find(query, User.class);

    assertThat(users.size(), is(1));
}

Precisamos definirSessionSynchronization paraALWAYS para usar transações não nativas do Spring Data.

7. Transações Reativas

Finalmente, daremos uma olhada emSpring Data support for MongoDB reactive transactions.

Precisaremos adicionar mais algumas dependências apom.xml para trabalhar com MongoDB reativo:


    org.mongodb
    mongodb-driver-reactivestreams
    1.9.2



    io.projectreactor
    reactor-test
    3.2.0.RELEASE
    test

As dependênciasmongodb-driver-reactivestreamsereactor-test estão disponíveis no Maven Central.

E, é claro, precisamos configurar nosso MongoDB reativo:

@Configuration
@EnableReactiveMongoRepositories(basePackages
  = "com.example.reactive.repository")
public class MongoReactiveConfig
  extends AbstractReactiveMongoConfiguration {

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        return "reactive";
    }
}

Para usar transações no MongoDB reativo, precisamos usar o métodoinTransaction() emReactiveMongoOperations:

@Autowired
private ReactiveMongoOperations reactiveOps;

@Test
public void whenPerformTransaction_thenSuccess() {
    User user1 = new User("Jane", 23);
    User user2 = new User("John", 34);
    reactiveOps.inTransaction()
      .execute(action -> action.insert(user1)
      .then(action.insert(user2)));
}

Mais informações sobre repositórios reativos no Spring Data estão disponíveishere.

8. Conclusão

Neste artigo, aprendemos como usar transações MongoDB nativas e não nativas usando o Spring Data.

O código-fonte completo dos exemplos está disponívelover on GitHub.