自社でMongoDB,SpringBootを扱う機会があり、勉強のために簡単なデモコードを作成しました。
自社のメインサーバーはフレームワークを用いておらず、社内でフレームワークを用いたコードとの比較を行うための資料として作成しています。
SpringBootでMongoDBを扱う雰囲気が伝わればと思います。
まず、アプリケーションの起動時に呼ばれるApplicationクラスです。
@SpringBootApplication
@EnableMongoRepositories
class MyApplication @Autowired constructor(val groceryItemsDao: GroceryItemsDao, val usersDao: UsersDao): CommandLineRunner {
companion object {
@JvmStatic
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
}
override fun run(vararg args: String?) {
groceryItemsDao.insertItem(GroceryItem("Whole Wheat Biscuit", "Whole Wheat Biscuit", 5, "snacks"))
usersDao.insertUser(User("Suzuki"))
}
}
SpringBootにはDI機能が備わっており、以下のようなinterfaceを宣言し、そのinterfaceを実装したクラスを自動的にinjectすることができます。
- interfaceで使いたい関数を宣言する
interface GroceryItemsDao {
fun findItemByName(name: String): GroceryItem?
fun insertItem(groceryItem: GroceryItem)
}
interface UsersDao {
fun insertUser(user: User)
}
- 上のinterfaceを実装したクラスがフレームワーク側で自動的にinsertされる
@Component
class GroceryItemsDaoImpl @Autowired constructor(
@Qualifier("myGroceryListTemplate")
val template: MongoTemplate,
): GroceryItemsDao {
private val model = GroceryItem::class.java
override fun findItemByName(name: String): GroceryItem? {
val query = Query(Criteria.where(GroceryItem.KEY.NAME).`is`(name))
return template.find(query, model).first()
}
override fun insertItem(groceryItem: GroceryItem) {
try {
template.insert(groceryItem)
} catch (me: MongoException) {
println("Unable to insert due to an error: $me")
}
}
}
@Component
class UsersDaoImpl @Autowired constructor(@Qualifier("userTemplate") val template: MongoTemplate): UsersDao {
private val model = User::class.java
override fun insertUser(user: User) {
template.insert(user)
}
}
今回はGroceryItemsDaoとUsersDaoは異なるデータベースに属するコレクションのDaoにしました。
複数のDatabaseへの接続は以下のようなConfigクラスを作成します。
@Configuration
class MongoConfig( @Value("\${spring.data.mongodb.uri}") private val connectionString: String) {
@Bean
fun mongoClient(): MongoClient {
val connectionString = ConnectionString(connectionString)
val settings = MongoClientSettings.builder().applyConnectionString(connectionString)
.serverApi(ServerApi.builder().version(ServerApiVersion.V1).build()).build()
return MongoClients.create(settings)
}
@Bean
fun userTemplate(): MongoTemplate {
return MongoTemplate(mongoClient(), "user")
}
@Bean
fun myGroceryListTemplate(): MongoTemplate {
return MongoTemplate(mongoClient(), "grocery")
}
}
spring.data.mongodb.uri=mongodb+srv://admin:☓☓☓☓☓☓☓☓@cluster0.☓☓☓☓☓☓.mongodb.net/?retryWrites=true&w=majority
spring.autoconfigure.exclude= org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
これでuserTemplate()は「user」データベースの操作を行うTemplateを提供し、myGroceryListTemplate()は「grocery」データベースの操作を行うTemplateを提供します。
どのコレクションに対してのDaoなのかはModelクラスに記述します。
@Document("groceryItem")
data class GroceryItem(
val title: String,
val name: String,
val quantity: Int,
val category: String?,
@Id
val id: ObjectId = ObjectId()
) {
object KEY {
val ID = GroceryItem::id.name
val TITLE = GroceryItem::title.name
val NAME = GroceryItem::name.name
val QUANTITY = GroceryItem::quantity.name
val CATEGORY = GroceryItem::category.name
}
}
@Document("user")
data class User(
val name: String,
@Id
val id: ObjectId = ObjectId(),
) {
object KEY {
val ID = User::id.name
val NAME = User::name.name
}
}
RestAPIを作成するときは以下のように記述して作成します
@RestController
@RequestMapping("/user")
class UserController @Autowired constructor(val usersDao: UsersDao) {
@PostMapping("/insert")
fun insertUser(@RequestBody user: User) {
usersDao.insertUser(user)
}
}
アノテーションやDI機能を用いて簡潔にサーバーを作成できることがわかりました。