Java Spring Boot App එක Kotlin වලට convert කරමුද? CRUD API එකක් rewrite කරමු - SC Guide

Java Spring Boot App එක Kotlin වලට convert කරමුද? CRUD API එකක් rewrite කරමු - SC Guide

ආයුබෝවන් ඩෙවලොපර්ස්ලා හැමෝටම! ✋ ඔයාලා දැනට Java Spring Boot එක්ක වැඩ කරනවා නම්, මේ දවස්වල හැමෝම කතා කරන, ගොඩක් දෙනෙක් මාරු වෙන්න හිතන language එකක් ගැන අද කතා කරමු. ඒ තමයි Kotlin!

Java කියන්නේ අපේ programming ලෝකේ රජා වගේ හිටිය කෙනෙක් වුණත්, කාලෙන් කාලෙට අලුත් දේවල් එනවනේ. ඉතින් ඒ වගේ අලුත් කෙනෙක් තමයි Kotlin කියන්නේ. Google වුණත් Android development වලට Kotlin එක official language එකක් විදිහට දාලා තියෙන එකෙන් තේරෙනවා නේද මේකේ තියෙන පවර් එක? Spring Framework එකත් Kotlin වලට full support එක දෙනවා. ඒ නිසා Spring Boot එක්ක Kotlin පාවිච්චි කරන එක මේ දවස්වල මාරම ට්‍රෙන්ඩ් එකක් වෙලා.

අද අපි කතා කරන්නේ දැනට ඔයාලා Java වලින් හදලා තියෙන Spring Boot CRUD API එකක් කොහොමද Kotlin වලට ලේසියෙන්ම convert කරගන්නේ කියලා. මේකෙන් ඔයාලට Kotlin වල තියෙන සුපිරි features වගේම, code එක කොච්චර ලස්සනට, කෙටියට ලියන්න පුලුවන්ද කියලත් අත්දකින්න පුළුවන් වෙයි. එහෙනම් වැඩේට බහිමු!

ඇයි Kotlin, Spring Boot එක්ක වැඩ කරන්න හොඳම? (Why Kotlin is Best for Spring Boot?)

Java හොඳ නැහැ කියනවා නෙවෙයි, හැබැයි Kotlin වල තියෙනවා programming ජීවිතේ ගොඩක් ලේසි කරන features ටිකක්. විශේෂයෙන්ම Spring Boot වගේ framework එකක් එක්ක වැඩ කරනකොට මේවා මාරම ප්‍රයෝජනවත්.

  • කෙටි සහ පැහැදිලි Code (Concise and Expressive Code): Java වල ලියන code lines ගානට වඩා ගොඩක් අඩුවෙන් Kotlin වලින් එකම දේ කරන්න පුළුවන්. මේකෙන් code readability එක වැඩි වෙනවා වගේම, maintain කරන්නත් ලේසියි. බොයිලර්ප්ලේට් code (boilerplate code) අඩුයි.
  • Null-Safety: NullPointerException (NPE) කියන්නේ Java programmers ලගේ සදාකාලික හිසරදයක් නේද? Kotlin වල Null-Safety කියන concept එක නිසා මේ ප්‍රශ්නේ ගොඩක් දුරට අඩු කරගන්න පුළුවන්. Compiler එකම ඔයාට උදව් කරනවා null වෙන්න පුළුවන් තැන් හඳුනාගන්න.
  • Java Interoperability: ඔයාලට Java project එකක් Kotlin වලට convert කරන එක, නැත්නම් Kotlin project එකක Java libraries පාවිච්චි කරන එක කිසිම අපහසුවක් නැතුව කරන්න පුළුවන්. මොකද Kotlin 100% Java Interoperable. ඒ කියන්නේ Java code, Kotlin code එක්ක කිසිම ගැටලුවක් නැතුව වැඩ කරනවා.
  • Coroutines for Asynchronous Programming: Non-blocking application එකක් හදනකොට Java වල Threading, Concurrency management ටිකක් සංකීර්ණ වෙන්න පුළුවන්. Kotlin Coroutines මඟින් මේ asynchronous programming එක මාරම ලේසි කරනවා. ඒක lightweight වගේම ගොඩක් efficient.
  • Data Classes: Java වල Bean, DTO (Data Transfer Object) හදන්න getter, setter, equals(), hashCode(), toString() වගේ දේවල් ගොඩක් ලියන්න වෙනවා. Kotlin වල data class එකක් පාවිච්චි කරලා මේ හැමදේම එක line එකෙන් කරන්න පුළුවන්.

මේ වගේ දේවල් නිසා Spring Boot project එකක් Kotlin වලින් හදන එක මාරම පහසුයි වගේම, productivity එකත් වැඩි කරනවා.

පළවෙනි පියවර: Project එක Setup කරගමු (First Step: Setting up the Project)

මුලින්ම අපි Spring Initializr (start.spring.io) එකට ගිහින් අලුත් Spring Boot project එකක් හදාගමු. මෙතනදී අපි language එක විදිහට Kotlin තෝරගන්න ඕනේ. Dependencies විදිහට මේවා එකතු කරගන්න:

  • Spring Web
  • Spring Data JPA
  • H2 Database (අපි මේක පාවිච්චි කරන්නේ simple in-memory database එකක් විදිහට)
  • Kotlin Coroutines (optional, හැබැයි asynchronous operations වලට ගොඩක් හොඳයි)

ඔයාලා Maven පාවිච්චි කරනවා නම් pom.xml එකේ, Gradle පාවිච්චි කරනවා නම් build.gradle.kts (Kotlin DSL) එකේ අවශ්‍ය dependencies add වෙලා තියෙන්න ඕනේ. උදාහරණයක් විදිහට build.gradle.kts එක මේ වගේ වෙන්න ඕනේ:

plugins {
    id("org.springframework.boot") version "3.2.5"
    id("io.spring.dependency-management") version "1.1.4"
    kotlin("jvm") version "1.9.20"
    kotlin("plugin.spring") version "1.9.20"
    kotlin("plugin.jpa") version "1.9.20" // for @Entity
    kotlin("plugin.allopen") version "1.9.20" // for Spring annotations
}

group = "com.scguide"
version = "0.0.1-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    runtimeOnly("com.h2database:h2")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xjsr305=strict")
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

මේක තමයි Kotlin project එකක් setup කරගන්න මූලික පියවර. හැබැයි අපි අද කතා කරන්නේ අලුත් project එකක් ගැන නෙවෙයි, දැනට තියෙන Java project එකක් convert කරන හැටි. ඒ නිසා මේ dependencies දැනට තියෙන Java project එකේ pom.xml එකට හරි build.gradle එකට හරි add කරලා, අවශ්‍ය Kotlin plugins configure කරගන්න ඕනේ.

Java CRUD API එකක් Kotlin වලට Rewrite කරමු (Rewriting a Java CRUD API to Kotlin)

හරි, දැන් අපි අපේ ප්‍රධාන වැඩේට බහිමු. අපි හිතමු අපිට Product management වලට CRUD API එකක් තියෙනවා කියලා. ඒක Java වලින් මෙහෙම වෙන්න පුළුවන්:

// Java Product.java
@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Double price;
    private Integer quantity;

    // Getters, Setters, Constructors, toString, equals, hashCode...
}

// Java ProductRepository.java
public interface ProductRepository extends JpaRepository<Product, Long> {
}

// Java ProductService.java
@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }

    public Product getProductById(Long id) {
        return productRepository.findById(id).orElseThrow(() -> new RuntimeException("Product not found"));
    }

    public Product createProduct(Product product) {
        return productRepository.save(product);
    }

    public Product updateProduct(Long id, Product productDetails) {
        Product product = getProductById(id); // Use existing method
        product.setName(productDetails.getName());
        product.setPrice(productDetails.getPrice());
        product.setQuantity(productDetails.getQuantity());
        return productRepository.save(product);
    }

    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
}

// Java ProductController.java
@RestController
@RequestMapping("/api/products")
public class ProductController {
    @Autowired
    private ProductService productService;

    @GetMapping
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }

    @GetMapping("/{id}")
    public Product getProductById(@PathVariable Long id) {
        return productService.getProductById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product createProduct(@RequestBody Product product) {
        return productService.createProduct(product);
    }

    @PutMapping("/{id}")
    public Product updateProduct(@PathVariable Long id, @RequestBody Product productDetails) {
        return productService.updateProduct(id, productDetails);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteProduct(@PathVariable Long id) {
        productService.deleteProduct(id);
    }
}

දැන් අපි මේක Kotlin වලට convert කරමු. බලන්න මේක කොච්චර ලේසිද කියලා!

Entity සහ Repository (Entity and Repository)

Kotlin වල data class කියන්නේ මේ වගේ entities වලට කියාපු දෙයක්. ඒකෙන් boilerplate code ගොඩක් අඩු වෙනවා.

// Kotlin Product.kt
package com.scguide.springbootkotlin.entity

import jakarta.persistence.*

@Entity
@Table(name = "products")
data class Product(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null, // 'val' for immutable, 'var' for mutable. Nullable ID.
    var name: String,
    var price: Double,
    var quantity: Int
)

නොට් කරන්න:

  • data class එකක් පාවිච්චි කරලා තියෙනවා. මේකෙන් getters, setters, equals(), hashCode(), toString() වගේ දේවල් auto-generate වෙනවා.
  • val id: Long? = null – මෙතන id එක nullable (?) කියලා කියනවා. ඒ වගේම default value එක null කියලා දීලා තියෙනවා. අලුත් Product එකක් හදනකොට id එක null වෙන්න පුළුවන් නිසා මේක වැදගත්. val (value) කියන්නේ immutable. var (variable) කියන්නේ mutable.

Repository එක Java වල වගේමයි, හැබැයි Kotlin syntax එකට අනුව.

// Kotlin ProductRepository.kt
package com.scguide.springbootkotlin.repository

import com.scguide.springbootkotlin.entity.Product
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ProductRepository : JpaRepository<Product, Long>

මෙහිදී ලොකු වෙනසක් නැහැ, මොකද Spring Data JPA interfaces, Kotlin එක්ක හොඳට වැඩ කරනවා.

Service Layer (Service Layer)

Service layer එකේදී Kotlin වල තියෙන null-safety, default arguments, සහ expression body functions වගේ දේවල් පාවිච්චි කරන්න පුළුවන්.

// Kotlin ProductService.kt
package com.scguide.springbootkotlin.service

import com.scguide.springbootkotlin.entity.Product
import com.scguide.springbootkotlin.repository.ProductRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class ProductService(private val productRepository: ProductRepository) { // Constructor Injection is preferred in Kotlin

    fun getAllProducts(): List<Product> = productRepository.findAll()

    fun getProductById(id: Long): Product {
        return productRepository.findByIdOrNull(id) ?: throw RuntimeException("Product not found with id: $id")
    }

    fun createProduct(product: Product): Product = productRepository.save(product)

    fun updateProduct(id: Long, productDetails: Product): Product {
        val product = getProductById(id) // Re-use method for fetching product
        product.name = productDetails.name
        product.price = productDetails.price
        product.quantity = productDetails.quantity
        return productRepository.save(product)
    }

    fun deleteProduct(id: Long) {
        if (!productRepository.existsById(id)) {
            throw RuntimeException("Product not found with id: $id")
        }
        productRepository.deleteById(id)
    }
}

නොට් කරන්න:

  • constructor injection – Spring beans inject කරන්න @Autowired වෙනුවට constructor injection (private val productRepository: ProductRepository) පාවිච්චි කරන එක Kotlin වලදී recommend කරනවා.
  • findByIdOrNull() – Spring Data JPA වල Kotlin extension function එකක්. මේකෙන් Optional එකක් වෙනුවට Product? (nullable Product) එකක් return කරනවා.
  • ?: (Elvis operator) – මේක null-safety වලට මාරම ප්‍රයෝජනවත්. findByIdOrNull(id) එක null නම්, throw RuntimeException එක execute කරනවා.
  • = (expression body functions) – getAllProducts(), createProduct() වගේ methods වල එක line එකක විතරක් code තියෙනකොට මේ විදිහට ලියන්න පුළුවන්. ඒක code එක ගොඩක් කෙටි කරනවා.

REST Controller (REST Controller)

Controller එකේදීත් code එක ගොඩක් කෙටි වෙනවා. Kotlin වලදී annotations Java වල වගේම පාවිච්චි කරන්න පුළුවන්.

// Kotlin ProductController.kt
package com.scguide.springbootkotlin.controller

import com.scguide.springbootkotlin.entity.Product
import com.scguide.springbootkotlin.service.ProductService
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/products")
class ProductController(private val productService: ProductService) { // Constructor Injection

    @GetMapping
    fun getAllProducts(): List<Product> = productService.getAllProducts()

    @GetMapping("/{id}")
    fun getProductById(@PathVariable id: Long): Product = productService.getProductById(id)

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun createProduct(@RequestBody product: Product): Product = productService.createProduct(product)

    @PutMapping("/{id}")
    fun updateProduct(@PathVariable id: Long, @RequestBody productDetails: Product): Product {
        return productService.updateProduct(id, productDetails);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    fun deleteProduct(@PathVariable id: Long) = productService.deleteProduct(id)
}

නොට් කරන්න:

  • Constructor Injection මෙතනත් පාවිච්චි කරලා තියෙනවා.
  • Methods වලටත් expression body functions පාවිච්චි කරලා code එක කෙටි කරලා තියෙනවා.

Data Transfer Objects (DTOs)

ඔයාලට requests/responses වලට වෙනම DTOs හදන්න ඕන නම්, ඒවටත් Kotlin වල data class එක පාවිච්චි කරන්න පුළුවන්. ඒක Java වල හදන POJOs (Plain Old Java Objects) වලට වඩා ගොඩක් පහසුයි.

// Kotlin ProductDto.kt (Example for request/response)
package com.scguide.springbootkotlin.dto

data class ProductDto(
    val name: String,
    val price: Double,
    val quantity: Int
)

මේ ProductDto එක ProductController එකේ @RequestBody එකට, නැත්නම් @ResponseBody එකට පාවිච්චි කරන්න පුළුවන්.

Kotlin වලින් වැඩ කරනකොට මතක තියාගන්න දේවල් (Things to Remember When Working with Kotlin)

Kotlin වලට මාරු වෙනකොට ඔයාලට අලුතෙන් මතක තියාගන්න වෙන, හැබැයි වැඩ ගොඩක් ලේසි කරන concepts ටිකක් තියෙනවා.

  • Nullability (? සහ !!):
    • String? කියන්නේ මේ String එක null වෙන්න පුළුවන් කියන එක. මේකට access කරනකොට safe call operator (?.) පාවිච්චි කරන්න ඕනේ. උදාහරණ: someString?.length
    • String!! කියන්නේ "මට මේක null වෙන්නේ නැහැ කියලා sure, ඒ නිසා check කරන්න එපා!" කියලා කියන එක. හැබැයි මේක risky, මොකද null වුණොත් NullPointerException එකක් එනවා. පුළුවන් තරම් safe calls පාවිච්චි කරන්න.
  • val vs var:
    • val කියන්නේ immutable variables (අගය වෙනස් කරන්න බැරි ඒවා).
    • var කියන්නේ mutable variables (අගය වෙනස් කරන්න පුළුවන් ඒවා).
    • පුළුවන් තරම් val පාවිච්චි කරන්න. ඒක code එක වඩාත් thread-safe කරනවා වගේම, side effects අඩු කරනවා.
  • Extension Functions:Kotlin වල තියෙන සුපිරිම features වලින් එකක් තමයි extension functions. මේකෙන් පුළුවන් දැනට තියෙන class එකකට අලුතෙන් functions add කරන්න, ඒ class එකේ code එක edit නොකරම. Spring Data JPA වල findByIdOrNull() කියන එකත් මේ වගේ extension function එකක්.
  • Named Arguments and Default Arguments:functions call කරනකොට arguments වලට නම් දීලා call කරන්න පුළුවන්. මේක code readability එක වැඩි කරනවා. ඒ වගේම functions වලට default argument values දෙන්න පුළුවන්, Java වලදී overload කරනවා වගේ නෙවෙයි, මේක ගොඩක් ලේසියි.
  • Type Inference:Kotlin compiler එකට variable එකක data type එක auto detect කරන්න පුළුවන් නම්, අපිට ඒක specify කරන්න ඕනේ නැහැ. මේකත් code එක කෙටි කරන්න උදව් වෙනවා.

මේ දේවල් හුරු වුණාට පස්සේ ඔයාලට තේරෙයි Kotlin කියන්නේ කොච්චර powerful, පහසු, සහ ආතල් language එකක්ද කියලා. Java එක්ක වැඩ කරපු ඔයාලට Kotlin වලට මාරු වෙන එක හිතන තරම් අමාරු වෙන්නේ නැහැ. මොකද concepts ගොඩක් සමානයි.

අවසාන වශයෙන් (In Conclusion)

ඉතින් යාලුවනේ, ඔයාලට දැන් තේරෙනවා ඇති Java Spring Boot API එකක් Kotlin වලට convert කරන එක කොච්චර ලේසිද කියලා. Kotlin වල තියෙන conciseness, null-safety, සහ අනෙකුත් features නිසා ඔයාලගෙ code එක වඩාත් clean, maintainable, සහ efficient වෙනවා. විශේෂයෙන්ම Spring Boot වගේ framework එකක් එක්ක Kotlin පාවිච්චි කරන එක ඔයාලගෙ productivity එකත් වැඩි කරනවා. ඒ වගේම, මේක ඔයාලගෙ skill set එකට අලුත් දෙයක් එකතු කරගන්නත් හොඳ අවස්ථාවක්.

අනිවාර්යයෙන්ම මේ conversion එක ඔයාලගෙ project එකකට apply කරලා බලන්න. පොඩි project එකකින් පටන් ගන්න. ප්‍රශ්න ආවොත් stackoverflow.com එකේ හරි, Kotlin community එකේ හරි ගොඩක් උදව් ගන්න පුළුවන්. අලුත් දේවල් ඉගෙනගන්න එක නතර කරන්න එපා!

මේ article එක ගැන ඔයාලගෙ අදහස් පහලින් comment කරන්න. ඔයාලට මේ වගේ අලුත් topic එකක් ගැන දැනගන්න ඕන නම් ඒකත් කියන්න. හැමදාම වගේ අපි එකට ඉගෙන ගනිමු!

සැලකිය යුතුයි: මේ ලිපියේ ඇති code snippets හුදෙක් උදාහරණ සඳහා පමණක් වන අතර, නියම production application එකකදී error handling, validation, logging, testing වැනි දේවල් අනිවාර්යයෙන්ම ඇතුළත් කළ යුතුය.