Informacje o tworzeniu zestawu replik znajdziesz tutaj
Moje rozwiązanie Kotlin:
import com.mongodb.BasicDBList
import com.mongodb.BasicDBObjectBuilder
import com.mongodb.DBObject
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import de.flapdoodle.embed.mongo.MongodExecutable
import de.flapdoodle.embed.mongo.MongodProcess
import de.flapdoodle.embed.mongo.MongodStarter
import de.flapdoodle.embed.mongo.config.MongoCmdOptionsBuilder
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder
import de.flapdoodle.embed.mongo.config.Net
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.process.runtime.Network
import org.assertj.core.api.Assertions.assertThat
import org.bson.Document
import org.junit.jupiter.api.Test
import org.springframework.data.mongodb.MongoDatabaseFactory
import org.springframework.data.mongodb.MongoTransactionManager
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory
import org.springframework.test.context.ActiveProfiles
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.TransactionCallbackWithoutResult
import org.springframework.transaction.support.TransactionTemplate
import java.io.IOException
@ActiveProfiles("test")
class EmbeddedMongoDbTransactionTest {
private val CONNECTION_STRING = "mongodb://%s:%d/"
private var node1MongodExe: MongodExecutable? = null
private var node1Mongod: MongodProcess? = null
private var mongo: MongoClient? = null
private var node2MongodExe: MongodExecutable? = null
private var node2Mongod: MongodProcess? = null
@Test
@Throws(IOException::class)
fun testSmth() {
val runtime = MongodStarter.getDefaultInstance()
val node1Port = 57023
val node2Port = 57024
try {
node1MongodExe = runtime.prepare(
MongodConfigBuilder().version(Version.Main.PRODUCTION)
.withLaunchArgument("--replSet", "rs0")
.cmdOptions(MongoCmdOptionsBuilder().useNoJournal(false).build())
.net(Net(node1Port, Network.localhostIsIPv6())).build()
)
node1Mongod = node1MongodExe?.start()
node2MongodExe = runtime.prepare(
MongodConfigBuilder().version(Version.Main.PRODUCTION)
.withLaunchArgument("--replSet", "rs0")
.cmdOptions(MongoCmdOptionsBuilder().useNoJournal(false).build())
.net(Net(node2Port, Network.localhostIsIPv6())).build()
)
node2Mongod = node2MongodExe?.start()
mongo = MongoClients.create(CONNECTION_STRING.format("localhost", node1Port))
val adminDatabase: MongoDatabase = mongo!!.getDatabase("admin")
val config = Document("_id", "rs0")
val members = BasicDBList()
members.add(Document("_id", 0).append("host", "localhost:$node1Port"))
members.add(Document("_id", 1).append("host", "localhost:$node2Port"))
config.put("members", members)
adminDatabase.runCommand(Document("replSetInitiate", config))
println(">>>>>> wait")
println(">>>>>>>>" + adminDatabase.runCommand(Document("replSetGetStatus", 1)))
Thread.sleep(15_000) // without waiting fails with error : 'not master' on server
val funDb: MongoDatabase = mongo?.getDatabase("fun")!!
// insert test 1
val testCollection: MongoCollection<Document> = funDb.getCollection("test")
println(">>>>>>>> inserting data")
testCollection.insertOne(Document("fancy", "value"))
println(">>>>>>>> finding data")
assertThat(testCollection.find().first()!!.get("fancy")).isEqualTo("value")
// insert test 2 (with transaction)
val mongoTemplate = MongoTemplate(mongo!!, "test")
// Without creating collection in advance fails with error:
// Cannot create namespace in multi-document transaction
// (https://stackoverflow.com/questions/52585715/cannot-create-namespace-in-multi-document-transactionmongodb-4-0-spring-data-2)
mongoTemplate.createCollection("collection")
val mongoDatabaseFactory: MongoDatabaseFactory = SimpleMongoClientDatabaseFactory(mongo!!, "test")
val mongoTransactionManager = MongoTransactionManager(mongoDatabaseFactory)
val transactionTemplate = TransactionTemplate(mongoTransactionManager)
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
val objectToSave = BasicDBObjectBuilder.start()
.add("key", "value")
.get()
// when
mongoTemplate.save(objectToSave, "collection")
// then
assertThat(mongoTemplate.findAll(DBObject::class.java, "collection"))
.extracting("key")
.containsOnly("value")
}
})
// after transaction
assertThat(mongoTemplate.findAll(DBObject::class.java, "collection"))
.extracting("key")
.containsOnly("value")
} finally {
println(">>>>>> shutting down")
mongo?.close()
node1MongodExe?.stop()
node1Mongod?.stop()
node2MongodExe?.stop()
node2Mongod?.stop()
}
}
}