Skip to content

Instantly share code, notes, and snippets.

@jeroenknoef
Last active September 1, 2021 16:34
Show Gist options
  • Save jeroenknoef/3814cab50ec6aaa387048cc6707c0fbf to your computer and use it in GitHub Desktop.
Save jeroenknoef/3814cab50ec6aaa387048cc6707c0fbf to your computer and use it in GitHub Desktop.
Micronaut - KMongo - Jackson
// Example controller
@Controller
@Validated
class DemoBeanController(
private val service: Service // Injected by Micronaut
) {
@Get("/{id}")
// Micronaut 1.3 supports coroutines, this also works in earlier versions.
fun find(@ValidObjectId id: String): CompletableFuture<DemoBean?> = CoroutineScope(Dispatchers.Default).future {
service.findById(ObjectId(id).toId())
}
}
// Place this class somewhere in your project. Micronaut will wire this together automatically.
import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.mongodb.MongoClientSettings
import com.mongodb.reactivestreams.client.MongoClient
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import org.bson.codecs.configuration.CodecRegistry
import org.litote.kmongo.reactivestreams.KMongo
import org.litote.kmongo.coroutine.CoroutineClient
import org.litote.kmongo.coroutine.coroutine
import org.litote.kmongo.id.jackson.IdJacksonModule
import org.litote.kmongo.service.ClassMappingType
import org.litote.kmongo.util.CollectionNameFormatter
import javax.inject.Singleton
@Factory
@Requires(classes = [KMongo::class])
class KMongoFactory {
init {
// KMongo: construct lower case collection names from bean classes.
CollectionNameFormatter.useLowerCaseCollectionNameBuilder()
}
/**
* Inject KMongo's [CodecRegistry] for mapping data classes.
*/
@Singleton
fun codecRegistry(): CodecRegistry = ClassMappingType.codecRegistry(MongoClientSettings.getDefaultCodecRegistry())
/**
* Inject KMongo's [CoroutineClient] instead of the default [MongoClient].
*/
@Singleton
fun coroutineClient(client: MongoClient): CoroutineClient = client.coroutine
/**
* Jackson: convert Mongo ObjectId's to string and vice versa.
*/
@Singleton
fun idJacksonModule(): Module = IdJacksonModule()
/**
* Jackson: convert [java.time.LocalDate] to and from strings.
*/
@Singleton
fun javaTimeJacksonModule(): Module = JavaTimeModule()
}
// A service that accesses a collection of DemoBean objects.
data class DemoBean(
@BsonId
val id: Id<DemoBean>,
...
)
@Singleton
class DemoBeanService(
client: CoroutineClient, // Injected by Micronaut, defined in KMongoFactory.
@Property(name = "mongodb.database") database: String
) {
private val collection: CoroutineCollection<DemoBean> by lazy {
// We can't use `getCollection<T>` on the coroutine database, because reified types are not supported on classes.
// Therefore, get a handle to the collection via the MongoDatabase and the bean class.
val db = client.getDatabase(database).database
db.getCollection(KMongoUtil.defaultCollectionName(DemoBean::class), DemoBean::class.java).coroutine
}
suspend fun findById(id: Id<DemoBean>): DemoBean? = collection.find().first() // ...
suspend fun save(bean: DemoBean) = collection.save(bean)
}
// Custom validation for incoming Mongo ObjectIds
import javax.validation.Constraint
import javax.validation.Payload
import javax.validation.constraints.NotNull
import javax.validation.constraints.Pattern
import kotlin.reflect.KClass
@NotNull
@Pattern(regexp = "[a-f0-9]{24}", message = "objectid")
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [])
annotation class ValidObjectId(val message: String = "objectid", val groups: Array<KClass<*>> = [], val payload: Array<KClass<out Payload>> = [])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment