Giter VIP home page Giter VIP logo

spring-boot-mvc-kotlin-crud's Introduction

spring boot and kotlin wedding :) testing crud operation using spring web(rest layer) and Spring Data Jpa with Kotlin

Spring boot kotlin

Build Status

This is an example project trying to learn Kotlin using Spring mvc and Spring data Jpa. When i finish the project i will give my own opinion about this new language (for me is new because I am just reading how to write code with it) and how difficult is to learn kotlin if you are a java (it’s my case)

  1. Use Spring data jpa H2 in memory Database

  2. Creating DTOs (POJOs/POCOs) is very easy (no library is needed as in Java). Properties in Kotlin classes can be declared either as mutable using the var keyword, or as read-only using the val keyword.

    @Entity
    data class UserEntity (@Id @GeneratedValue val id: Long? = null, val name:String, val age:Int, val favoriteNumber: String)
    
    @Entity
    data class PetEntity (@Id @GeneratedValue val id: Long? = null, val name:String, val age:Int)
  3. Inheritance: All classes in Kotlin have a common superclass Any, that is the default superclass for a class with no supertypes. To declare an explicit supertype, place the type after a colon in the class header:

    @Service
    class UserServiceImpl (val userRepository: UserRepository) :UserService
  4. Use mapping between layers using (https://stackoverflow.com/questions/39199426/better-way-to-map-kotlin-data-objects-to-data-objects) If you need to write a function that can be called without having a class instance but needs access to the internals of a class (for example, a factory method), you can write it as a member of an object declaration inside that class. Even more specifically, if you declare a companion object inside your class, you can access its members using only the class name as a qualifier.

        companion object {
            fun createFromUserModel(user: com.cromero.api.service.User) = User(
                    id = user.id,
                    name = user.name,
                    age = user.age,
                    favoriteNumber = user.favoriteNumber,
                    color = Color.assignColorPerAge(user.age)
            )
        }
  5. Create integration test layer using mockmvc and rest template

     @Test
        fun `controller Must return User`() {
            //save an user
            val user = UserEntity(name = "pepe",age = 33,favoriteNumber = "25")
            userRepository.save(user)
    
            mockMvc.perform(get("/user/${user.name}")).andExpect(status().isOk)
                    .andExpect(jsonPath("\$.name").value(user.name))
                    .andExpect(jsonPath("\$.age").value(user.age))
                    .andExpect(jsonPath("\$.favoriteNumber").value(user.favoriteNumber))
        }
    
    
        @Test
        fun `controller Must return User using rest template`() {
            //save an user
            val user = UserEntity(name = "manolo",age = 33,favoriteNumber = "54")
            userRepository.save(user)
    
            //get the user by id
            val result = restTemplate.getForEntity<User>("/user/${user.name}")
    
    
            //user asserts from database
            assertEquals(result.statusCode,HttpStatus.OK)
            assertTrue(result.body?.name == user.name)
            assertTrue(result.body?.age == user.age)
            assertTrue(result.body?.favoriteNumber == user.favoriteNumber)
    
        }
  6. Test when "pattern matching" and Enum classes with static methods using companion object

      // When expression is exhaustive, else is a must with integer value. It is better to use Enum or Sealed
            //classes instead if it would be possible because do not need else branch
            fun assignColorPerAge(age: Int) = when (age) {
                in Int.MIN_VALUE..0 -> BLACK
                in 1..10 -> BLUE
                in 20..40 -> GREEN
                in 40..99 -> RED
                in 100..Int.MAX_VALUE -> BLACK
                else -> BLACK
            }
    
      // Example of code using Enum. Else branch is not a must with enum and sealed class
      when (val color=createFromUserModel.color) {
                    Color.BLACK->  "Your black color description is ${color.description}"
                    Color.RED -> "Your red color description is ${color.description}"
                    Color.GREEN -> "Your green color description is ${color.description}"
                    Color.BLUE -> "Your blue color description is ${color.description}"
                }
  7. Test kotlin amazing null properties: using Safe Calls operator (?.) and the Elvis Operator (?:)

    userService.findByName(name)?.let { User.createFromUserModel(it) }?:
                                throw UserNotFoundException("user not found")
  8. Added A convenient and performant logging library wrapping slf4j with Kotlin extensions. https://github.com/MicroUtils/kotlin-logging

    private val LOGGER = KotlinLogging.logger {}
    // using Kotlin's String templates: String literals may contain template expressions,
    // i.e. pieces of code that are evaluated and whose results are concatenated into the string
    LOGGER.info("User $savedEntity  was successfully created")
  9. Testing Kotlin Collections filters and orders methods: Java .stream is not needed and it is not a must to make splicit call to .collect(Collectors.toList()); (collections are stronger comparing to Java)

         /**
         * Find all users containing {letters} in its name. It returns all users if letters is null
         */
        @GetMapping(value = ["/findAllContainsLetters", "/findAllContainsLetters/{letters}"])
        fun findAllContainsLetter(@PathVariable("letters") letters: String?) =
            userService.findAll().filter { user: com.cromero.api.service.User ->
                letters?.let { cad: String -> user.name.toUpperCase().contains(cad.toUpperCase()) } ?: true
            }.sortedBy { it.name }
  10. Ranges can be used in many cases: for, if, case etc…​

        if (createFromUserModel.age in 0..18)
                {
                    "is not legal age"
                }
                else
                {
                    "is legal age"
                }
    
       //Additional examples
        for (i in 1..100) { ... }  // closed range: includes 100
        for (i in 1 until 100) { ... } // half-open range: does not include 100
        for (x in 2..10 step 2) { ... }
        for (x in 10 downTo 1) { ... }
        if (x in 1..10) { ... }

Spring loves Kotlin :) using several features making easier our code:

  1. Injecting beans by constructor. example injecting our service bean in our controller:

    @RestController
    @RequestMapping("/user")
    class UserController (val userService: UserService)
    
    @Service
    class UserServiceImpl (val userRepository: UserRepository,val customProperties: CustomProperties) :UserService
  2. Use your data class defining your custom application properties (new in Spring boot 2.2):

    //using data class and Kotling default values inside properties.
    //mandatory properties and optional using nullable properties
    @ConstructorBinding
    @ConfigurationProperties("com.cromero.application")
    data class CustomProperties(
        val name: String?="app with no name",
        val description: String,
        val database: Database)
    {
        data class Database(
            val host: InetAddress?=InetAddress.getByName("127.0.0.1"),
            val port: Integer,
            val connectTimeout : Duration=Duration.ofMillis(1000))
    }
    
    // application.properties values
    server.port=8082
    management.endpoint.health.show-details=always
    com.cromero.application.name=users application
    com.cromero.application.description=application demo using kotlin and Spring boot 2.2
    com.cromero.application.database.host=123.23.23.2
    com.cromero.application.database.port=88
    com.cromero.application.database.connectTimeout=500ms

spring-boot-mvc-kotlin-crud's People

Contributors

cristianprofile avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.