Scope funkce v Kotlinu

Cílem block functions je vykonat blok kódu v kontextu daného kódu. To znamená, že pokud zavoláte takovou funkci na objektu s lambdou, vytvoří se rozsah (scope), ve kterém můžete k objektu přistupovat bez uvedení jeho jména. Tyto funkce se mezi sebou liší tím, jak je možné k objektu v rámci scope přistoupit a jaký je výsledek celého výrazu.

Číst dálScope funkce v Kotlinu

Kotlin delegated properties

Properties jsou většinou představeny odpovídajícím polem ve třídě (např. val x: Int). Mohou ale být i delegovány. Jejich hodnota je získána od toho, na koho byla tato práce přenesena (delegována). Jinak řečeno getValue a setValue funce zajišťuje delegovaný kód. Delegované vlastnosti se používají deklarováním vlastnosti a delegáta, kterého používají.

Příklad 1

class Example {
    var property: Int by Delegate()
}

class Delegate {
    var random = Random(0)

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("$javaClass - getting random value | thisRef -> $thisRef, property -> $property")
        return random.nextInt()
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
        println("$javaClass creating new random with seed value: $value | thisRef -> $thisRef, property -> $property")
        random = Random(value)
    }
}

fun main() {
    val e = Example()
    println(e.property)
    e.property = 10
    println(e.property)
}

Číst dálKotlin delegated properties

Rozdíl mezi year-of-era a week-based-year

V rámci java.time.format.DateTimeFormatter je možné použít pro formátování roku malá y a velká Y. Špatně zvolené y může znamenat špatně dohledatelnou chybu. Malé y znamená year-of-era, což vytvoří očekávaný výstup, kdy 31.12. bude vždy jako poslední den v daném roce. Velké Y znamená week-based-year, což v případě týdnu, který spadá do více roků, vytvoří neočekávaný výstup. Pokud Silvestr (či pár předchozích dnů) připadnou do týdne, který nemá 31.12. v sobotu, budou dny poslední dny roku z přelomového týdne patřit do následujího roku. Příklad bude nejlepší. Takto to funguje na Linuxu, ale např. na Window byl výstup s velkým Y pro rok 2021 stejný jako pro malé y.

Číst dálRozdíl mezi year-of-era a week-based-year

Anotace @Version v JPA

Sloupec s touto anotací bude navýšen (jeho hodnota) v případě update entity. Pokud hodnoty zůstávají stejné, po update se verze nezvyšuje. Pokud načteme entitu v jednom vlákně a v jiném vlákně ji updatujeme, tak poté v tom prvním vlákně při ukládání dostaneme

Exception in thread "main" javax.persistence.OptimisticLockException: 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

Číst dálAnotace @Version v JPA

Nastavení sloupce v JPA tak, aby jej nešlo updatovat

Anotace @Column umožňuje nastavit sloupec jako neupdatovatelný pomocí updatable = false. V následujícím příkladu je takto nastaven sloupec title.

@Column(unique = true, updatable = false)
var title: String = "",

V kódu je sice možné nastavit novou hodnotu

book.title = "New title"
book.description = "New description"

sloupec se ale neobjeví ve vygenerovaném sql.

update
    t_book 
set
    description=?
where
    id=? 

Chyba: TransactionRequiredException Executing an update/delete query

Pro repository test na Micronautovi jsem dostával chybu TransactionRequiredException Executing an update/delete query při exekuci sql skriptu ze souboru.

@Inject
lateinit var entityManager: EntityManager
...
val fileNameString = "/db/script/$fileName"
val sql = this::class.java.getResource(fileNameString)?.readText()
entityManager.createNativeQuery(sql).executeUpdate()

Řešením bylo použití SessionFactory a transakcí

@Inject
lateinit var sessionFactory: SessionFactory
...
val fileNameString = "/db/script/$fileName"
val sql = this::class.java.getResource(fileNameString)?.readText()
sessionFactory.openSession()
    ?.let { session ->
        session.entityManagerFactory.createEntityManager()
            ?.let { entityManager ->
                entityManager.transaction.begin()
                entityManager.createNativeQuery(sql).executeUpdate()
                entityManager.transaction.commit()
            }
    }

Vararg a spread operátory v Kotlinu

Hvězdička (*) je spread operátor, který rozbalí pole do seznamu hodnot.
Parametr vararg umožňuje předat do funkce libovolné množství argumentů. Každá funkce může mít maximálně jeden vararg operátor.

fun main() {
    val lines = arrayOf("First line", "Second line")
    
    printOnLines("")
    
    printOnLines(header = "Header")
    
    printOnLines(header = "Header", lines = lines)

    printOnLines(header = "Header", lines = arrayOf("First line", "Second line"))
    
    printOnLines("Header", *lines)

    printOnLines(header = "Header", *lines)
    
    printOnLines("Header", "First line", "Second line")

    printLines(*lines)
}

fun printOnLines(header: String, vararg lines: String) {
    println(header)
    println("----------------------------")
    lines.forEach {
        println(it)
    }
}

fun printLines(vararg lines: String) {
    lines.forEach { 
        println(it)
    }
}

Měření času v Kotlinu

Pro změření jak dlouho trvá vykonání určitého bloku existují v Kotlinu funkce measureTimeMillis a measureNanoTime. Měřený kód stačí vložit do bloku

val timeMillis = measureTimeMillis {
    ...
}
println("Completed in $timeMillis ms")

val timeNano = measureNanoTime {
    ...
}
println("Completed in $timeNano ns")

Webflux a BlockHound

BlockHound je java agent (software nahraný JVM před zavoláním main metody), který způsobí to, že pokud v rámci neblokujícího vlákna (vlákno, které nemá volat blokující volání) zavoláte metodu, která blokuje vlákno, vyhodí se chyba.

Závislost

implementation("io.projectreactor.tools:blockhound:1.0.4.RELEASE")

Pokud si chceme BlockHound přizpůsobit, vytvoříme třídu, která implementuje BlockHoundIntegration

Číst dálWebflux a BlockHound