Anotace javax.persistence.SequenceGenerator

Anotace @SequenceGenerator se používá k určení generátoru primárních klíčů pro tabulku. Na tento generátor je odkazováno z anotace @GeneratedValue.

name – Název generátoru, který musí být unikátní.
sequenceName – Název sekvence v databázi. Je to sekvence, která již v databázi existuje.
allocationSize – Musí být stejná jako hodnota „auto incerement“ sekvence v databázi.

import cz.vitfo.entity.CustomerEntity.Companion.GENERATOR_NAME
import cz.vitfo.entity.CustomerEntity.Companion.SEQUENCE_NAME
import cz.vitfo.entity.CustomerEntity.Companion.TABLE_NAME
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.SequenceGenerator
import javax.persistence.Table

@Entity
@Table(name = TABLE_NAME)
@SequenceGenerator(name = GENERATOR_NAME, sequenceName = SEQUENCE_NAME, allocationSize = 1)
class CustomerEntity(

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = GENERATOR_NAME)
    var id: Long

) {
    companion object {
        const val TABLE_NAME = "t_customer"
        const val SEQUENCE_NAME = "${TABLE_NAME}_id_seq"
        const val GENERATOR_NAME = "${SEQUENCE_NAME}_gen"
    }
}

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ál

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ál

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ál

Posílání chyb do Sentry

Sentry je primárně zaměřeno na chyby. Je to místo, kam budou všechny pády systému reportovány. Na sentry si musíte vytvořit účet a nastavit projekt tak, aby chyby posílal do sentry a mít je na jednom místě.

build.gradle

implementation("io.sentry:sentry-spring-boot-starter:4.3.0")

application.yml
Tato data dostanete při registraci do sentry.

sentry:
    dsn: https://xxx@yyy.ingest.sentry.io/zzz

Kód, který pošle chybu do sentry.

try {
    throw Exception("This is a v1 test.")
} catch (e: Exception) {
    Sentry.captureException(e)
}

Je možné přidat tento kód do GlobalErrorHandleru.

@ControllerAdvice
@RestController
class GlobalErrorHandler {
    private val logger: Logger = LoggerFactory.getLogger(javaClass)

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler
    fun handle(e: Exception) {
        logger.error("Error", e)
        Sentry.captureException(e)
    }
}

Kotlin JavaFX Hello World

Build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.4.10"
    id("org.openjfx.javafxplugin") version "0.0.9"
}

group = "cz.vitfo.redgui"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

tasks.withType() {
    kotlinOptions.jvmTarget = "11"
}

javafx {
    version = "15.0.1"
    modules = listOf("javafx.controls")
}

Třída dědící z javafx.application.Application

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

class FXApplication : Application() {
    override fun start(stage: Stage) {
        val javaVersion = System.getProperty("java.version")
        val javafxVersion = System.getProperty("javafx.version")
        val l = Label("Hello, JavaFX $javafxVersion, running on Java $javaVersion.")
        val scene = Scene(StackPane(l), 640.0, 480.0)
        stage.scene = scene
        stage.show()
    }

    fun main(args: Array) {
        launch()
    }
}

fun main(args: Array) {
    FXApplication().main(args)
}

Výsledná aplikace

Zdroj: github.com/openjfx/samples/blob/master/HelloFX/Gradle/hellofx/src/main/java/HelloFX.java

Kotlin Swing Hello World

import java.awt.EventQueue
import javax.swing.JFrame

class Application : JFrame() {

    init {
        title = "Hello world"
        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(400, 300)
        setLocationRelativeTo(null)
    }
}

fun main() {
    EventQueue.invokeLater {
        val window = Application()
        window.isVisible = true
    }
}

Přehled jednotlivých komponent Swingu: web.mit.edu/6.005/www/sp14/psets/ps4/java-6-tutorial/components.html