Spring Bootを利用して、Azure CosmosDB(MongoDBコア)にアクセスしたので、その記録です。
コード書くのが怠かったので、https://spring.pleiades.io/guides/gs/accessing-data-mongodb/ を参考にしました。
まずは、普通にサンプルコードをgit cloneします。
$ git clone https://github.com/spring-guides/gs-accessing-data-mongodb.git
チュートリアル通りにローカルにMongoDBを立てて、
$ sudo apt install mongodb
$ mkdir -p data/db
$ mongod --dbpath=./data/db
Gradleで実行します。
$ gradle bootRun
Welcome to Gradle 6.5.1!
Here are the highlights of this release:
- Experimental file-system watching
- Improved version ordering
- New samples
For more details see https://docs.gradle.org/6.5.1/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)
2020-07-02 03:12:00.912 INFO 32643 --- [ main] c.e.a.AccessingDataMongodbApplication : Starting AccessingDataMongodbApplication on DESKTOP-N9T4CN3 with PID 32643 (XXX/gs-accessing-data-mongodb/complete/build/classes/java/main started by XXX in XXX/gs-accessing-data-mongodb/complete)
2020-07-02 03:12:00.917 INFO 32643 --- [ main] c.e.a.AccessingDataMongodbApplication : No active profile set, falling back to default profiles: default
2020-07-02 03:12:01.274 INFO 32643 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2020-07-02 03:12:01.315 INFO 32643 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 36ms. Found 1 repository interfaces.
2020-07-02 03:12:01.589 INFO 32643 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
2020-07-02 03:12:01.674 INFO 32643 --- [localhost:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:1}] to localhost:27017
2020-07-02 03:12:01.678 INFO 32643 --- [localhost:27017] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=ServerVersion{versionList=[3, 6, 8]}, minWireVersion=0, maxWireVersion=6, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2881100}
2020-07-02 03:12:01.745 WARN 32643 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.LocalDateTime to class java.time.Instant as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 03:12:01.745 WARN 32643 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.Instant to class java.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 03:12:01.771 WARN 32643 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.LocalDateTime to class java.time.Instant as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 03:12:01.771 WARN 32643 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.Instant to class java.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 03:12:02.134 INFO 32643 --- [ main] c.e.a.AccessingDataMongodbApplication : Started AccessingDataMongodbApplication in 1.552 seconds (JVM running for 1.853)
2020-07-02 03:12:02.157 INFO 32643 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:2}] to localhost:27017
Customers found with findAll():
-------------------------------
Customer[id=5efcd1f2b15c223a7dc2a9ce, firstName='Alice', lastName='Smith']
Customer[id=5efcd1f2b15c223a7dc2a9cf, firstName='Bob', lastName='Smith']
Customer found with findByFirstName('Alice'):
--------------------------------
Customer[id=5efcd1f2b15c223a7dc2a9ce, firstName='Alice', lastName='Smith']
Customers found with findByLastName('Smith'):
--------------------------------
Customer[id=5efcd1f2b15c223a7dc2a9ce, firstName='Alice', lastName='Smith']
Customer[id=5efcd1f2b15c223a7dc2a9cf, firstName='Bob', lastName='Smith']
2020-07-02 03:12:02.270 INFO 32643 --- [extShutdownHook] org.mongodb.driver.connection : Closed connection [connectionId{localValue:2, serverValue:2}] to localhost:27017 because the pool has been closed.
BUILD SUCCESSFUL in 53s
2 actionable tasks: 2 executed
mongoコマンドで中を覗いてみます。
$ mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("2648d101-f236-4f56-bc3d-7250d32ef9a2") }
MongoDB server version: 3.6.8
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
Server has startup warnings:
2020-07-02T02:54:56.366+0900 I STORAGE [initandlisten]
2020-07-02T02:54:56.366+0900 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2020-07-02T02:54:56.366+0900 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten]
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten]
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** WARNING: This server is bound to localhost.
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** Remote systems will be unable to connect to this server.
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** Start the server with --bind_ip <address> to specify which IP
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** addresses it should serve responses from, or with --bind_ip_all to
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** bind to all interfaces. If this behavior is desired, start the
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten] ** server with --bind_ip 127.0.0.1 to disable this warning.
2020-07-02T02:54:57.070+0900 I CONTROL [initandlisten]
2020-07-02T02:54:57.071+0900 I CONTROL [initandlisten]
2020-07-02T02:54:57.071+0900 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2020-07-02T02:54:57.071+0900 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2020-07-02T02:54:57.071+0900 I CONTROL [initandlisten]
> db.customer.find()
{ "_id" : ObjectId("5efcd1f2b15c223a7dc2a9ce"), "firstName" : "Alice", "lastName" : "Smith", "_class" : "com.example.accessingdatamongodb.Customer" }
{ "_id" : ObjectId("5efcd1f2b15c223a7dc2a9cf"), "firstName" : "Bob", "lastName" : "Smith", "_class" : "com.example.accessingdatamongodb.Customer" }
>
入ってますね。
この時点で、チュートリアルでは接続先のMongoDBへのアクセスは行っていません。実装されているデータアクセスに関するものも、以下の2つのみです。
package com.example.accessingdatamongodb;
import org.springframework.data.annotation.Id;
public class Customer {
@Id
public String id;
public String firstName;
public String lastName;
public Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%s, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}
package com.example.accessingdatamongodb;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CustomerRepository extends MongoRepository<Customer, String> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}
高レベルAPIであるRepositoryを使用しているので、実装は非常に簡単です。実際にはSpring Data MongoDBによってCustomerRepojitoryインターフェースを実装するクラスが自動生成されて、saveやfindなどのメソッドも利用できるようになっています。便利ですね。(低レベルAPIであるTemplateMongoを使用すると、もっと複雑なこともできます。)
今回は、MongoDBコアで作成したCosmosDBにアクセスするのが主目的なので、高レベルAPIを利用したこのサンプルを利用します。
Azure側に、CosmosDBを作成します。こちらは https://docs.microsoft.com/ja-jp/azure/cosmos-db/create-mongodb-java#create-a-database-account を参考にしてサクサクとPortalから作成します。コレクションの作成は不要です。
作成されたら、Portalから接続先のURIを確認します。今回はJavaですので、"クイックスタート"の"Java"タブに表示されているプライマリ接続文字列をコピーします。
サンプルコードに、main/resources/application.propertisを追加し、"spring.data.mongodb.uri"を追加して、先ほどコピーした値を貼り付け、Mongo DB Driverが3.6以降の場合は末尾に"&retrywrites=false"を追加します。Cosmos DBが現時点でRetryable Writesに対応していないためです。
spring.data.mongodb.uri=<your_connection_string>&retrywrites=false
定義したURLを使用するため、Configurationを作成します。
package com.example.accessingdatamongodb;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomerRepositoryConfig {
@Value("${spring.data.mongodb.uri:mongodb://localhost:27017}")
public String connectionString;
public @Bean MongoClient mongoClient() {
return MongoClients.create(this.connectionString);
}
}
Gradleで実行します。
$ gradle bootRun -Pargs=--debug
> Task :bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)
2020-07-02 04:54:56.451 INFO 3822 --- [ main] c.e.a.AccessingDataMongodbApplication : Starting AccessingDataMongodbApplication on DESKTOP-N9T4CN3 with PID 3822 (XXX/gs-accessing-data-mongodb/complete/build/classes/java/main started by XXX in XXX/gs-accessing-data-mongodb/complete)
2020-07-02 04:54:56.456 INFO 3822 --- [ main] c.e.a.AccessingDataMongodbApplication : No active profile set, falling back to default profiles: default
2020-07-02 04:54:57.370 INFO 3822 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2020-07-02 04:54:57.497 INFO 3822 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 118ms. Found 1 repository interfaces.
2020-07-02 04:54:58.011 INFO 3822 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[XXX:YYY], mode=MULTIPLE, requiredClusterType=REPLICA_SET, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500, requiredReplicaSetName='globaldb'}
2020-07-02 04:54:58.011 INFO 3822 --- [ main] org.mongodb.driver.cluster : Adding discovered server XXX:YYY to client view of cluster
2020-07-02 04:54:58.244 WARN 3822 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.LocalDateTime to class java.time.Instant as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 04:54:58.245 WARN 3822 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.Instant to class java.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 04:54:58.316 WARN 3822 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.LocalDateTime to class java.time.Instant as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 04:54:58.317 WARN 3822 --- [ main] o.s.data.convert.CustomConversions : Registering converter from class java.time.Instant to class java.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
2020-07-02 04:54:58.912 INFO 3822 --- [ main] c.e.a.AccessingDataMongodbApplication : Started AccessingDataMongodbApplication in 3.129 seconds (JVM running for 3.84)
2020-07-02 04:54:58.947 INFO 3822 --- [ main] org.mongodb.driver.cluster : No server chosen by com.mongodb.client.internal.MongoClientDelegate$1@210386e0 from cluster description ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=XXX:YYY, type=UNKNOWN, state=CONNECTING}]}. Waiting for 30000 ms before timing out
2020-07-02 04:54:59.082 INFO 3822 --- [azure.com:10255] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:1162392059}] to XXX:YYY
2020-07-02 04:54:59.097 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=XXX:YYY, type=REPLICA_SET_PRIMARY, state=CONNECTED, ok=true, version=ServerVersion{versionList=[3, 6, 0]}, minWireVersion=0, maxWireVersion=6, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=12402600, setName='globaldb', canonicalAddress=XXX:YYY, hosts=[XXX:YYY], passives=[], arbiters=[], primary='XXX:YYY', tagSet=TagSet{[Tag{name='region', value='Japan East'}]}, electionId=null, setVersion=1, lastWriteDate=null, lastUpdateTimeNanos=134393982328436}
2020-07-02 04:54:59.100 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Adding discovered server XXX:YYY to client view of cluster
2020-07-02 04:54:59.103 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Server XXX:YYY is no longer a member of the replica set. Removing from client view of cluster.
2020-07-02 04:54:59.105 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Canonical address XXX:YYY does not match server address. Removing XXX:YYY from client view of cluster
2020-07-02 04:54:59.338 INFO 3822 --- [azure.com:10255] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:689958399}] to XXX:YYY
2020-07-02 04:54:59.348 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=XXX:YYY, type=REPLICA_SET_PRIMARY, state=CONNECTED, ok=true, version=ServerVersion{versionList=[3, 6, 0]}, minWireVersion=0, maxWireVersion=6, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=8755400, setName='globaldb', canonicalAddress=XXX:YYY, hosts=[XXX:YYY], passives=[], arbiters=[], primary='XXX:YYY', tagSet=TagSet{[Tag{name='region', value='Japan East'}]}, electionId=null, setVersion=1, lastWriteDate=null, lastUpdateTimeNanos=134394233832236}
2020-07-02 04:54:59.349 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Setting max set version to 1 from replica set primary XXX:YYY
2020-07-02 04:54:59.349 INFO 3822 --- [azure.com:10255] org.mongodb.driver.cluster : Discovered replica set primary XXX:YYY
2020-07-02 04:54:59.603 INFO 3822 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:3, serverValue:1139365834}] to XXX:YYY
Customers found with findAll():
-------------------------------
Customer[id=5efcea13cac85c79009dba55, firstName='Alice', lastName='Smith']
Customer[id=5efcea15cac85c79009dba56, firstName='Bob', lastName='Smith']
Customer found with findByFirstName('Alice'):
--------------------------------
Customer[id=5efcea13cac85c79009dba55, firstName='Alice', lastName='Smith']
Customers found with findByLastName('Smith'):
--------------------------------
Customer[id=5efcea13cac85c79009dba55, firstName='Alice', lastName='Smith']
Customer[id=5efcea15cac85c79009dba56, firstName='Bob', lastName='Smith']
2020-07-02 04:55:01.231 INFO 3822 --- [extShutdownHook] org.mongodb.driver.connection : Closed connection [connectionId{localValue:3, serverValue:1139365834}] to XXX:YYY because the pool has been closed.
BUILD SUCCESSFUL in 8s
3 actionable tasks: 2 executed, 1 up-to-date
成功しました。Azureポータルのデータエクスプローラーでcustomerコレクションが追加されていることを確認しましょう。
データが作成されていることが確認できますね。
Cosmos DBは最近Free Tierが提供されたので、手軽に機能を試すことができます。
是非、触ってみてください。