Jak vytvořit like dotazy ve Spring Data

V tomto příspěvku ukáži, jak lze vytvořit sql dotazy s LIKE ve Spring Data bez pomoci @Query anotace. Budeme mít entitu kniha

@Entity
@Table(name = "t_book")
@SequenceGenerator(initialValue = 100, name = "t_book_seq_gen", sequenceName = "t_book_seq")
class BookEntity (
        @Id
        @GeneratedValue(generator = "t_book_seq_gen", strategy = GenerationType.SEQUENCE)
        var id: Long = 0L,

        var author: String = "",

        var publisher: String = "",

        var title: String = "",

        ...
)

a vytvoříme si jpa repository

Číst dálJak vytvořit like dotazy ve Spring Data

Spring CommandLineRunner

CommandLineRunner je funkční rozhraní s metodou void run(String... args) throws Exception. V této metodě můžeme uvést vše, co chceme aby se stalo a startu aplikace. Jednou z možností jak vytvořit CommandLineRunner je vlastní třída s anotací @Configuration a následně metoda s anotací @Bean vracející CommandLineRunner

@Configuration
class AppConfig {

    @Bean
    fun init(bookRepository: BookRepository): CommandLineRunner {
        return CommandLineRunner {
            bookRepository.save(BookEntity(title = "Cyber Leviathan"))
            bookRepository.save(BookEntity(title = "Last Hunt"))
        }
    }
}

Jiný způsob je extendovat CommandLineRunner a implementovat metodu run()

@SpringBootApplication
class SpringData2Application : CommandLineRunner {
	override fun run(vararg args: String?) {
		println("Running ...")
	}
}

fun main(args: Array<String>) {
	runApplication<SpringData2Application>(*args)
}

 

ISO duration a Java (ISO 8601 duration format)

Java má metodu Duration.parse("duration-string"), která vypadá, jako by pracovala s ISO duration, ale není tomu tak. Dokáže parsovat použe tyto suffixy D (day), H (hour), M (minute), S (second).

Takže toto je správně:

Duration.parse("P1D") // jeden den

ale toto je chybně

Duration.parse("P1M") // správně by to bylo "PT1M" ale neznamená to 1 měsíc, ale 1 minutu.

Číst dálISO duration a Java (ISO 8601 duration format)

Vytvoření tabulek pro spring batch

Spring Batch obsahuje inicializační SQL skripty pro většinu populárních databází (více v tomto příspěvku). Spring Boot dokáže detekovat databázi a spustit správný skript při startu aplikace. Pokud chcete, aby Spring Batch vytvořil potřebné tabulky, přidejte do konfiguračního souboru

spring.batch.initialize-schema=always

Vypnutí této funkcionality

spring.batch.initialize-schema=never

Zdroj: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-initialize-a-spring-batch-database

Zobrazení logů docker kontejneru

Pro zobrazení logů docker kontejneru se používá příkaz:

docker logs [OPTIONS] container_name

Pro zobrazení nápovědy k příkazu docker logs použijte:

docker logs --help

Usage:	docker logs [OPTIONS] CONTAINER

Fetch the logs of a container

Options:
      --details        Show extra details provided to logs
  -f, --follow         Follow log output
      --since string   Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
      --tail string    Number of lines to show from the end of the logs (default "all")
  -t, --timestamps     Show timestamps
      --until string   Show logs before a timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)

Výpis chyby při použítí vykřičníků v Kotlinu

Kotlin kompilátor kontroluje, zda hodnota výrazu může být null a pokud tomu tak je, kód nezkompiluje dokud se v daném místě neprovede kontrola na not null. Programátor si může vynutit, že chce danou hodnotu použít i bez této kontroly pokud si je jist, že hodnota není null pomocí dvou vykřičníků. Pokud hodnota je null, dojde k vyhození kotlin.KotlinNullPointerException.

Nedoporučuje se používat více !! (one celkově se !! mají používat pouze vyjímečně ve zdůvodněných případech) na jednom řádku, protože výpis v logu vám neřekne, která hodnota byla null.

Příklad

class Aa (var b: Bb?)

class Bb (var text: String?)

Výsledky volání

println(a!!.b!!.text!!.toUpperCase())

Exception in thread "main" kotlin.KotlinNullPointerException
	at cz.vitfo.NullKt.main(Null.kt:8)
	at cz.vitfo.NullKt.main(Null.kt)
println(aWithB!!.b!!.text!!.toUpperCase())

Exception in thread "main" kotlin.KotlinNullPointerException
	at cz.vitfo.NullKt.main(Null.kt:8)
	at cz.vitfo.NullKt.main(Null.kt)

Je vidět, že v logu je pouze řádek, kde došlo k vyhození výjimky, nikoliv přesně která část výrazu.

Micrometer a DistributionSummary

Pokud použijeme micrometer a objekt DistributionSummary, zde jsou ukázky výstupu na actuatoru:

Pokud máme hodnoty

500, 1500, 2500, ..., 9500

a

val summary = DistributionSummary.builder("my.summary").baseUnit("miliseconds").register(meterRegistry)

a hodnoty uložíme/nahrajeme

summary.record(hodnota)

výsledek (na endpointu /actuator/prometheus)

# HELP my_summary_miliseconds
# TYPE my_summary_miliseconds summary
my_summary_miliseconds_count 10.0
my_summary_miliseconds_sum 50000.0
# HELP my_summary_miliseconds_max
# TYPE my_summary_miliseconds_max gauge
my_summary_miliseconds_max 9500.0

Další možnosti:

Číst dálMicrometer a DistributionSummary

Projekce a Query

V předchozím příspěvku jsem ukázal základní možnost projekce ve Spring Data. Na pár věcí je ale třeba si dávat pozor. Je možné používat @Query a také nativní @Query(nativeQuery = true), ale je třeba specifikovat název výsledného sloupce pomocí klíčového slova AS.

@Query("SELECT b.title AS title, b.numberOfPages AS numberOfPages FROM BookEntity b")
fun findAllInfo(): List<BaseBookInfo>

@Query("SELECT b.title AS title, b.numberOfPages AS numberOfPages FROM t_book b", nativeQuery = true)
fun findAllInfoNative(): List<BaseBookInfo>

Pokud byste to neudělali, našel by se sice správný počet záznamů, ale všechny záznamy by měly všechny hodnoty null (nebo by došlo k vyhození výjimky v případě přístupu k primitivním datovým typům).

Spring Data a projekce

Pokud pro dotazování používáte Spring Data, v rámci výsledku dostanete root entitu (všechny její hodnoty). Pokud tedy budete mít následující entitu:

@Entity
@Table(name = "t_book")
@SequenceGenerator(initialValue = 100, name = "t_book_seq_gen", sequenceName = "t_book_seq")
class BookEntity (
        @Id
        @GeneratedValue(generator = "t_book_seq_gen", strategy = GenerationType.SEQUENCE)
        var id: Long = 0L,

        var author: String = "",

        var publisher: String = "",

        var title: String = "",

        var foreword: String = "",

        var numberOfPages: Int = 0,

        var publishingYear: Int = 0
)

vždy získáte všechny hodnoty (id, author, publisher, title, foreword, numberOfPages, …), i když je nebudete vždy potřebovat. Pokud chcete použe některé hodnoty, je zde možnost tzv. projekce. Vytvoříte si rozhraní, které bude obsahovat jen to, co potřebujete. Např. budete chtít pouze titul knihy a počet stran.

Číst dálSpring Data a projekce