Painless Spring boot tutorial with Maven Kotlin & MongoDB

Mariano Z Lopez
Mariano Z Lopez
Published in
12 min readAug 19, 2018

In this tutorial, I’m going to do my best to show you the first steps with Spring Boot, MongoDB, Maven and Kotlin.

· The Spring Framework is an open source application framework and inversion of control container for the Java platform. The framework’s core features can be used by any Java application, but there are extensions for building web applications on top of the Java EE (Enterprise Edition) platform.

· Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

· MongoDB is a free and open-source cross-platform document-oriented database. Classified as a NoSQL database, MongoDB uses JSON-like documents with schemata.

· Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

· Kotlin is a statically typed programming language for modern multiplatform applications 100% interoperable with Java and Android.

What will you learn?

Create a Maven with Kotlin and Spring Boot project

Kotlin concepts like: data class, default value, null safety, scope functions, Lambdas

Spring’s annotations and work flow

Connect the project with MongoDB

Build API REST in Spring Boot

Code layers division (Data Access Object & Services)

I ran the example with: Linux deepin 15.5, MongoDB 4.0.1, Java openJDK 1.8.0_141 & Kotlin 1.2.41.

A full example project can be found here: https://github.com/MarianoLopez/MySpringTutorial

If you don’t have MongoDB installed, please check my tutorial getting-started-with-mongodb before going further.

Enter to https://start.spring.io/ and create a Maven project with Kotlin and Spring Boot “this example uses 2.0.4 version”

book-backend project creation

The group is how the packages (group of classes) will be organized; in the example, (com.z) “com” is the root level and “z” is a sub-package.

A Maven build produces one or more artifacts, such as a compiled JAR.

Dependencies:

· Web: Full-stack web development with Tomcat and Spring MVC

· MongoDB: Connector including spring-data-mongo core

· DevTools: Spring Boot development tools “hot reload”

Unzip and open the generated project with your favorite IDE “e.g I’m using IntelliJ”, and you will get something like this

book-backend basic structure

Where src/main/kotlin/com.z.bookbackend/BookBackendApplication.kt is the main file and src/main/resources/application.properties is the default spring boot properties file.

Let’s run our first Hello World. Open the BookBackendApplication.kt and add the code below and run the main function:

project launch

What’s happening here? Well, first the main function runs our spring boot application “by default the embedded tomcat runs on port 8080”, if you come from the java world, you can see we have 2 classes “1 without body” and a main function in the same file wtf?!. With Kotlin, we can put all that in one .kt file and it will be accessible as if they were in different files. Also, in functions explicitly declaring the return type is optional when this can be inferred by the compiler and the curly braces can be omitted and the body is specified after a = symbol.

The MainController class will map “GET /” http request into index method which will return “Hello World” as http response.

localhost:8080

How it works?

Spring Framework implements the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process in which objects define their dependencies. The Spring IoC container injects those dependencies when it creates the bean “An object that is instantiated, assembled, and managed by a Spring IoC container”.

Spring IoC container

Now let’s create some packages to get the stuff organized

project structure upon completion

Controllers where all controller classes will go

Configuration where all configuration classes will go

Services where all service classes will go (also known as business logic layer)

Models where we will put the schema classes (contains the database documents meta-data)

Util where we will put all our useful classes

Configuring our application

Every spring boot project has an application properties where we can change some object’s attributes before they go to the spring IoC container. Here’s the spring boot properties appendix: https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

Configure MongoDB & Jackson “high-performance JSON processor for Java”. Remember to check MongoDB host, port, username & password “change if necessary, e.g. mongo running outside of localhost”

By default spring boot uses Jackson as JSON processor. When is it used? E.g. when a RestController get an http request and need deserialize “unmarshalling” the body’s request. Or when need serialize “marshalling” an object to be sent as http response.

Schemas

Under the “Models” folder let’s create a Shcemas.kt file that will contain the MongoDB document models

In this file we declared 2 “data class” Author and Book. If you come from the Java world,maybe, you’re now a little confused, let’s split it up

Class declaration: The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor etc.) and the class body, surrounded by curly braces. Both the header and the body are optional; if the class has no body, curly braces can be omitted.

Primary constructor: A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is part of the class header: it goes after the class name (and optional type parameters).

Data class: In Kotlin with the mark “data” in a class, the compiler automatically derives the following members (equals/hashCode, toString, component and copy) from all properties declared in the primary constructor.

Declaring Properties: Classes in Kotlin can have properties. These can be declared as mutable, using the var keyword or read-only using the val keyword.

Default values: Kotlin supports default arguments declarations. You can specify a default value for a parameter. The default value “denoted with the = symbol” is used when the corresponding argument is omitted.

Null Safety: In Kotlin, the type system distinguishes between references that can hold null (nullable references) and those that cannot (non-null references). E.g., a regular variable of type String cannot hold null “will provoke compilation error”. To allow nulls, we can declare a variable as nullable string, written “String?”

Annotations used

Document: Identifies a domain object to be persisted to MongoDB

Id: Demarcates an identifier

So… we have an Author domain object that has a read-only String identifier which allows nullable references and its default value (the id will be generated by MongoDB), name and birth date. A Book domain object has a String identifier which cannot hold nullable references, name, a mutable Author (if we need to change the author’s book “e.g. input mistake” so then the property must be mutable) and published year.

Data Access Object

Under the package “services” let’s create a “DaoLayer.kt” file

“A data access object (DAO) is an object that provides an abstract interface to some type of database or other persistence mechanism. By mapping application calls to the persistence layer, the DAO provides some specific data operations without exposing details of the database.”

Interface: Like a class, an interface can have methods and variables, but the methods declared in interface are by default abstract (only method signature, no body). Specify what a class must do and not how (unless you add the method body as “default” like jdk8). It is the blueprint of the class. Important points about interface:

An interface can’t be instantiated but we can make reference of it that refers to the Object of its implementing class.

A class can implement more than one interface.

An interface can derive from other interfaces and thus both provide implementations for their members and declare new functions and properties.

A class that implements interface must implements all the methods in interface

It is used to achieve loose coupling.

MongoRepository <T,ID>: Mongo specific Repository interface from the springframework data core, provides methods like: findAll, count, delete, deleteAll, deleteAll, deleteById, existsById, findAllById, findById, save.

Out-of-the-box: You can define other queries as needed by simply declaring their method signature. E.g., you add findByAuthorId, which essentially seeks documents of type Book and finds the one that matches on “Author._id”.

Supported keywords for query methods: https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html#d0e3811

Generics: As in Java, classes in Kotlin may have type parameters: (T: we need to provide the type “T” argument of a Class. ID: we need to provide a serializable type “ID”)

So… we have two interfaces (AuthorDAO & BookDAO) that implements spring’s data MongoRepository interface. The implementation & injection to the IoC container of both classes will be provided by the spring’s data core framework.

Useful resources

Now, we are going to create some utility classes under the “Util” package

CrudBasic: An interface for CRUD (create, read, update and delete) basic operations, it will be implemented by our services classes.

Objects: Singleton pattern “restricts the instantiation of a class to one object” may be useful in several cases, in Kotlin is called an object declaration, and it always has a name following the object keyword. Object declaration’s initialization is thread-safe “applicable to multi-threaded code”. I declared a singleton object called “Objects” that has a read-only DateTimeFormatter, and its used by the “toLocalDate” extension function to avoid a new DateTimeFormatter instance on every time that the function its invoked.

Extensions: Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as Decorator. This is done via special declarations called extensions. In this case, I wrote an extension “toLocalDate” for the String class to parse a LocalDate from a String “dd-MM-yyyy”.

Services

Going back to “services” package, let’s create Book & Author services:

BookService

Implements our BasicCrud interface and add the “Business Logic” to each overridden method.

It has a @Service annotation to mark this class a Service, the Spring will instance the class and added it as a bean to the IoC container.

The primary constructor has 2 parameters “book & author” data access objects, where do they come from? And who instance them if they are interfaces?

The answer is the Spring IoC container. When BookService will be instanced “spring’s responsibility” , it realize that need an instance of BookDAO & AuthorDAO (they were created previously when our DAO interfaces implements the MongoRepository from spring-data. It has some out-of-the-box magic spring injects the implementation of both to the IoC container), then it takes them “references (by default the beans are singleton)” from the IoC container and injects them to the BookService class.

@Autowired annotation (Marks a constructor, field, setter method or getter method as to be autowired by Spring’s dependency injection facilities), but “If a bean has one constructor, you can omit the @Autowired” that’s why I have chosen a constructor dependency injection.

Otherwise if you will use another dependency injection, e.g. by field: do @Autowired lateinit var bookDAO:BookDAO. Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class. To handle this case, you can mark the property with the lateinit modifier.

Business Logic

Why do I need to re-insert the author on insert and update? Well, if we take a look at how our documents are being saved. MongoDB uses JSON-like documents, you can see that there are no references from book to author, it is just a nested object. There’s no foreign key to check if the author exits. So, if you want to keep integrity, an option is re-insert. The author shouldn’t avoid author changes from a book’s insert or update.

view documents with robo3t

You can store a reference with @DBRef “An annotation that indicates the annotated field is to be stored using a DBRef”, according to Mongo database’s reference: DBRefs are references from one document to another using the value of the first document’s _id field, collection name, and, optionally, its database name. To resolve DBRefs, your application must perform additional queries to return the referenced documents. Unless you have a compelling reason to use DBRefs, use manual references instead. But spring data does not allow to query the nested objects if you use DBRef that’s is why I do not use it.

Why do I need the book’s isbn on update? Because the save method will create a new entity if the Id is null or does not exist

AuthorService

Just like we did before, the AuthorService implements CrudBasic interface, has a @Service annotation and has its own business logic for each overridden method.

Kotlin things

Scoping functions: run, with, T.run, T.let, T.also and T.apply are scoping functions and their main functionality is provide an inner scope for the caller function.

Safe call operator “?”: E.g. if Bob, an Employee, may be assigned to a Department (or not), that in turn may have another Employee as a department head, then to obtain the name of Bob’s department head (if any), we write the following: “bob?.department?.head?.name”. Such a chain returns null if any of the properties in it is null. A safe call can also be placed on the left side of an assignment. Then, if one of the receivers in the safe calls chain is null, the assignment is skipped, and the expression on the right is not evaluated at all.

Higher-Order Functions and Lambdas: Kotlin functions are first-class, which means that they can be stored in variables and data structures, passed as arguments to and returned from other higher-order functions “function that takes functions as parameters, or returns a function”. A lambda expression or anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions, or used for constructing the result of a higher-order function that needs to return a function.

Implicit name of a single parameter: It’s very common that a lambda expression has only one parameter. It is allowed not to declare the only parameter and omit “->”. The parameter will be implicitly declared under the name “it”.

Map: Returns a list containing the results of applying the given transform function to each element in the original collection.

Controllers

Under “controllers” package let’s create Author & Book controllers

Annotations used

RestController: declare the class as rest controller able to catch http requests

RequestMapping: mapping web requests onto methods

Get, Put, Post & Delete -Mapping: HTTP method specific variants of RequestMapping

PathVariable: indicates that a method parameter should be bound to a URI template variable

RequestBody: indicates that a method parameter should be bound to the body of the web request

OnBoot

Spring Boot provides ApplicationRunner interface to run specific pieces of code when an application is fully started. This interface get called just before run() once SpringApplication completes. So let’s modify our BookBackendApplication.kt in order to upload sample data into the database.

Run & Testing

let’s run it again & make some API testing with postman

GET books: localhost:8080/api/book
GET book by isbn: localhost:8080/api/book/{isbn}
POST insert book: localhost:8080/api/book
PUT update book: localhost:8080/api/book
DELETE book by isbn: localhost:8080/api/book/{isbn}

It’s took me a lot of time to understand: springboot, kotlin, maven, mongodb & how to make connections between them “but it was worth it”. So, I thought it will be a good idea to write this tutorial about it, hope this has helped you!

--

--