2つのリポジトリがあります。1つはmongodb(DocumentRepository)用で、もう1つは休止状態エンティティ(EntityRepository)用です。
私は簡単なサービスを持っています:
@Transactional
public doSomePersisting() {
try {
this.entityRepository.save(entity);
this.documentRepository.save(document);
}
catch(...) {
//Rollback mongoDB here
}
}
「// mongoDBhereをロールバックする」行でmongoDBをロールバックすることは可能ですか?エンティティ部分からすでにロールバックを取得しています(トランザクションアノテーション)
MongoDBはトランザクションをサポートしていません(少なくとも単一のドキュメントの範囲外ではありません)。変更をロールバックする場合は、自分でそれを手作りする必要があります。特定の状況で本当に必要な場合に、Mongoで独自のトランザクションを実装する方法を説明するリソースがいくつかあります。あなたは見てみることができます。
http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/
これは、使用できるパターンの説明にすぎません。アプリケーションでトランザクションが絶対に必要であることがわかった場合は、MongoDBがニーズに適しているかどうかを検討する必要があります。
私の答えを再投稿してすみません。
以前のコードでは、PostgreSQLへのデータ挿入時にスローするクエリ例外でもMongoDBにデータを挿入できました(myBatisを使用)。
MongoDBとリレーショナルデータベース間のデータトランザクションの問題を解決しました。@ Transactionalは、上記のコードでこれらの変更を行うことで完全に機能します。
@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
@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("[email protected]");
user.setPassword("sdfsdfsdf");
userService.save(user);
}
}
<?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>
MongoDb 4.0.xを使用すると、トランザクションを使用できます。以下のバージョンを使用する場合は、2フェーズコミットを実装する必要があります。
注:MongoDbでは、ReplicaSetがある場合にのみトランザクションを使用できます。
JPAとMongoDbの両方でトランザクションを使用するには、 ChainedTransactionManager を使用する必要があります。プロセスは次のとおりです。
私のconfは次のようになります(私はスプリングブートを使用していませんが、同等である必要があります):
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories("com....")
public class HibernateConfig {
//define entity manager, data source and all the stuff needed for your DB
@Bean("jpaTransactionManager")
public JpaTransactionManager transactionManager() throws NamingException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
//transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
@Configuration
@EnableMongoRepositories(basePackages = "com....")
public class MongoDbConf extends AbstractMongoClientConfiguration {
private final Environment environment;
@Autowired
public MongoDbConf(Environment environment) {
this.environment = environment;
}
@Override
public MongoClient mongoClient() {
String connectionString = environment.getProperty("mongodb.connectionString");
if(StringUtils.isBlank(connectionString))
throw new IllegalArgumentException("No connection string to initialize mongo client");
return MongoClients.create(
MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(connectionString))
.applicationName("MY_APP")
.build());
}
@Override
protected String getDatabaseName() {
return environment.getProperty("mongodb.database", "myDB");
}
@Bean("mongoDbTransactionManager")
public MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
}
@Configuration
public class ChainedTransactionConf {
private MongoTransactionManager mongoTransactionManager;
private JpaTransactionManager jpaTransactionManager;
@Autowired
public ChainedTransactionConf(MongoTransactionManager mongoTransactionManager, JpaTransactionManager jpaTransactionManager) {
this.mongoTransactionManager = mongoTransactionManager;
this.jpaTransactionManager = jpaTransactionManager;
}
@Bean("chainedTransactionManager")
public PlatformTransactionManager getTransactionManager() {
ChainedTransactionManager transactionManager = new ChainedTransactionManager(jpaTransactionManager, mongoTransactionManager);
return transactionManager;
}
}
@Service
public class MongoDbRepositoryImpl implements MongoDbRepository {
private static final Logger logger = Logger.getLogger(MongoDbRepositoryImpl.class);
//MongoOperations will handle a mongo session
private final MongoOperations operations;
@Autowired
public MongoDbRepositoryImpl(MongoOperations operations) {
this.operations = operations;
}
@Override
public void insertData(Document document) {
MongoCollection<Document> collection = operations.getCollection("myCollection");
collection.insertOne(document);
}
@Service
public class DocumentServiceImpl implements DocumentService {
private final MongoDbRepository mongoDbRepository;
private final JpaRepository jpaRepository;
@Autowired
public DocumentServiceImpl(MongoDbRepository mongoDbRepository,JpaRepository jpaRepository) {
this.mongoDbRepository = mongoDbRepository;
this.jpaRepository = jpaRepository;
}
@Override
@Transactional("chainedTransactionManager")
public void insertNewDoc(Map<String,Object> rawData) {
//use org.springframework.transaction.annotation.Transactional so you can define used transactionManager
//jpaRepository.insert...
Document mongoDoc = new Document(rawData);
mongoDbRepository.insertData(mongoDoc)
//you can test like this : breakpoint and throw new IllegalStateException()
//to see that data is not commited
}
MongoDBロールバックは、@ TransactionalおよびMongoTransactionManagerでは機能しません。完全なコード実装はこちらです。
@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
@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("[email protected]");
user.setPassword("sdfsdfsdf");
userService.save(user); //This line throws query exception.
}
この行で例外がスローされても、mongodbでデータがロールバックされませんuserService.save(user); //この行は、挿入クエリ例外で無効な構文をスローします。
### 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