Spring data and mongodb - simple roll back with sp

2019-01-18 06:49发布

问题:

I have 2 repositories, one for mongodb (DocumentRepository) and the other for hibernate entity (EntityRepository)

I have a simple service:

 @Transactional
 public doSomePersisting() {
     try {
           this.entityRepository.save(entity);
           this.documentRepository.save(document);
     }
     catch(...) {
         //Rollback mongoDB here
     }
 }

Is it possible to rollback the mongoDB on the "//Rollback mongoDB here" line? I already got a rollback from the entity part (Transactional annotation)

回答1:

MongoDB doesn't support transactions (at least not outside the scope of a single document). If you want to roll back changes you will need to handcraft that yourself. There are a few resources out there that describe ways of implementing your own transactions in Mongo if you really need them in certain circumstances. You could take a look at..

http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/

This is just an explanation of a pattern you could use. If you find that you absolutely need transactions in your application, you should consider whether MongoDB is a good fit for your needs.



回答2:

Sorry for reposting my answer.

The earlier code was allowed to insert data into MongoDB even query exceptions throwing at data insertion into PostgreSQL(Using myBatis).

I have resolved the data Transaction issue between MongoDB and Relational database and @Transactional perfectly works by making these changes in the above code.

Solution for @Transactional Management.

Mongo Config class

@Configuration
public class MongoConfig extends AbstractMongoConfiguration{
    private static final Logger LOG = LoggerFactory.getLogger(MongoConfig.class);

    @Value("${spring.data.mongodb.database}")
    private String dbName;

    @Value("${spring.data.mongodb.host}")
    private String dbHost;

    @Value("${spring.data.mongodb.port}")
    private int dbPort;

    @Override
    public String getDatabaseName() {
        return dbName;
    }

    @Bean
    public MongoClient mongoClient(){
        return new MongoClient(dbHost, dbPort);
    }

    @Bean
    public MongoDbFactory mongoDbFactory(){
        return new SimpleMongoDbFactory(mongoClient(),dbName);
    }

    @Bean
    public MongoTemplate mongoTemplate() {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        // Don't save _class to mongo
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(),mappingMongoConverter);
        mongoTemplate.setSessionSynchronization(SessionSynchronization.ON_ACTUAL_TRANSACTION);
        return mongoTemplate;
    }

    public MongoTemplate fetchMongoTemplate(int projectId) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        // Don't save _class to mongo
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        MongoDbFactory customizedDBFactory = new SimpleMongoDbFactory(mongoClient(), dbName+"_"+projectId);
        MongoTemplate mongoTemplate = new MongoTemplate(customizedDBFactory,mappingMongoConverter);
        MongoTransactionManager mongoTransactionManager = new MongoTransactionManager(customizedDBFactory);
        return mongoTemplate;
    }

    @Bean
    public MongoTransactionManager mongoTransactionManager() {
        return new MongoTransactionManager(mongoDbFactory());
    }

}

Service class for Data insertion

@Service
@Component
public class TestRepositoryImpl implements TestRepository{
    private static final Logger LOG = LoggerFactory.getLogger(TestRepositoryImpl.class);


@Autowired MongoConfig mongoConfig;
@Autowired MongoTemplate mongoTemplate;
@Autowired MongoTransactionManager mongoTransactionManager;

@Autowired UserService userService;

@Override
@Transactional
public void save(Test test){
    int projectId = 100;
    if (projectId != 0) {
        mongoTemplate = mongoConfig.fetchMongoTemplate(100);
        mongoTemplate.setSessionSynchronization(SessionSynchronization.ALWAYS);
    }
    mongoTemplate.insert(test);
    IdName idName = new IdName();
    idName.setName("test");
    mongoTemplate.insert(idName);
    User user = new User();
    user.setName("Demo");
    user.setEmail("srini@abspl.in");
    user.setPassword("sdfsdfsdf");
    userService.save(user);
    }
 }

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.abcplusd.sample.mongoapi</groupId>
  <artifactId>sample-mongo-api</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>Sample Spring Boot Mongo API</name>
  <description>Demo project for Spring Boot Mongo with Spring Data Mongo</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-mongodb</artifactId>
      <version>2.1.0.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>org.mongodb</groupId>
          <artifactId>mongo-java-driver</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-commons</artifactId>
      <version>2.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>3.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.2.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>


回答3:

MongoDB Rollback does not work with @Transactional and MongoTransactionManager. Full code implementation is here.

MongoDB 4.0, mongo-java-driver(version 3.8.2), spring-data-mongodb(version 2.1.0)

MongoConfig class

@Configuration
public class MongoConfig extends AbstractMongoConfiguration{
    private static final Logger LOG = LoggerFactory.getLogger(MongoConfig.class);

    @Value("${spring.data.mongodb.database}")
    private String dbName;

    @Value("${spring.data.mongodb.host}")
    private String dbHost;

    @Value("${spring.data.mongodb.port}")
    private int dbPort;

    @Override
    public String getDatabaseName() {
        return dbName;
    }

    @Bean
    public MongoClient mongoClient(){
        return new MongoClient(dbHost, dbPort);
    }

    @Bean
    public MongoDbFactory mongoDbFactory(){
        return new SimpleMongoDbFactory(mongoClient(),dbName);
    }

    @Bean
    public MongoTemplate mongoTemplate() {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        // Don't save _class to mongo
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(),mappingMongoConverter);
        return mongoTemplate;
    }

    public MongoTemplate fetchMongoTemplate(int projectId) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
        MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        // Don't save _class to mongo
        mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        MongoTemplate mongoTemplate = new MongoTemplate(new SimpleMongoDbFactory(mongoClient(), dbName+"_"+projectId),mappingMongoConverter);
        return mongoTemplate;
    }

    @Bean
    public MongoTransactionManager mongoTransactionManager() {
        return new MongoTransactionManager(mongoDbFactory());
    }
}

Service class to insert data in mongodb and postgreSQL(Using mybatis).

@Service
@Component
public class TestRepositoryImpl implements TestRepository{
    private static final Logger LOG = LoggerFactory.getLogger(TestRepositoryImpl.class);

    @Autowired MongoTemplate mongoTemplate;
    @Autowired MongoConfig mongoConfig;
    //@Autowired MongoClient mongoClient;

    @Autowired UserService userService;

    @Override
    @Transactional
    public void save(Test test){
        LOG.info("mongoTemplate <{}>", mongoTemplate.getDb().getName());
        int projectId = 100;
        if (projectId != 0) {
            mongoTemplate = mongoConfig.fetchMongoTemplate(100);
            LOG.info("mongoTemplate <{}>", mongoTemplate.getDb().getName());
        }
        //Inserting data to mongodb
        mongoTemplate.insert(test);
        IdName idName = new IdName();
        idName.setName("test");
        mongoTemplate.insert(idName);
        //Inserting data to postgreSQL
        User user = new User();
        user.setName("Demo");
        user.setEmail("XXXX@XXXX.in");
        user.setPassword("sdfsdfsdf");
        userService.save(user); //This line throws query exception.
    }

Data is not getting rolledback at mongodb even exception throwing at this line userService.save(user); //This line throws invalid syntax at insert query exception.

### SQL: insert into test.user(id,name,email,password     values(?,?,?,?)
### Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "values"
  Position: 50
; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "values"
  Position: 50] with root cause