Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documents #22

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,35 @@

## Introduction

By using the following SQL builder, you can easily build and execute SQL.
### Features

- **Love SQL ♥**
- While ORM libraries in the world are convenient, they often require learning their own DSL, which I believe has a
high learning cost. Kuery Client emphasizes writing SQL as it is.
- **Based on spring-data-r2dbc and spring-data-jdbc**
- Kuery Client is implemented based on spring-data-r2dbc and spring-data-jdbc. Use whichever you prefer. You can use
Spring's ecosystem as it is, such as `@Transactional`.
- **Observability**
- It supports Micrometer Observation, so Metrics/Tracing/Logging can also be customized.
- **Highly extensible**
- When dealing with complex data schemas, there are often cases where you want to write common query logic. Thanks
to Kotlin's extension functions, this becomes easier.


### Motivation

I have used numerous ORM libraries, but in the end, I preferred libraries like MyBatis that allow writing SQL directly.

To construct SQL dynamically, custom template syntax (such as if/foreach) is often used, but I prefer to write logic
using the syntax provided by the programming language as much as possible.
I want to write dynamic SQL using Kotlin syntax, similar to [kotlinx.html](https://github.com/Kotlin/kotlinx.html).

To meet these needs, I implemented Kuery Client.

### Overview

By using the following SQL builder, you can easily build and execute SQL. Whether using R2DBC or JDBC, the way of
writing is almost the same.

```kotlin
data class User(...)
Expand Down Expand Up @@ -40,7 +68,7 @@ class UserRepository(private val kueryClient: KueryClient) {
return kueryClient
.sql {
+"INSERT INTO users (username, email) VALUES"
+users.joinToString(", ") { "(${bind(it.username)}, ${bind(it.email)})" }
+values(users) { listOf(it.username, it.email) }
}
.rowsUpdated()
}
Expand All @@ -53,7 +81,7 @@ This SQL builder is very simple. There are only two things you need to remember:
- You can also directly express logic such as if statements in Kotlin.
- Use the `bind` function for dynamic values.
- Be careful not to evaluate variables directly as strings, as this will obviously lead to SQL injection.
- Kuery Client provides Detekt custom rules that detect such dangerous cases.
- Kuery Client provides [Detekt custom rules](/docs/detekt) that detect such dangerous cases.

### Based on spring-data-r2dbc and spring-data-jdbc

Expand Down
2 changes: 2 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export default defineConfig({
{text: "Type Conversion", link: '/type-conversion'},
{text: "Observation", link: '/observation'},
{text: "Detekt Custom Rules", link: '/detekt'},
{text: "Helpers", link: '/helpers'},
{text: "Examples", link: '/examples'},
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ kueryClient
+"""
SELECT * FROM users
WHERE user_id = 1
"""
""".trimIndent()
}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/detekt.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Detekt Custom Rules

If you use dynamic values without bind, there is a possibility of causing SQL Injection. To prevent this, we provide
Detekt custom ruled.
Detekt custom rules.

## How to use

Expand Down
9 changes: 9 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Examples

## Spring WebFlux and `kuery-client-spring-data-r2dbc`

https://github.com/be-hase/kuery-client/tree/main/examples/spring-data-r2dbc

## Spring WebMVC and `kuery-client-spring-data-jdbc`

https://github.com/be-hase/kuery-client/tree/main/examples/spring-data-jdbc
35 changes: 26 additions & 9 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,62 @@

### Gradle

```kotlin
::: code-group

```kotlin [kuery-client-spring-data-r2dbc]
implementation("dev.hsbrysk.kuery-client:kuery-client-spring-data-r2dbc:{{version}}")
// or, implementation("dev.hsbrysk.kuery-client:kuery-client-spring-data-jdbc:{{version}}")
```

```kotlin [kuery-client-spring-data-jdbc]
implementation("dev.hsbrysk.kuery-client:kuery-client-spring-data-jdbc:{{version}}")
```

:::

### Maven

```xml
::: code-group

```xml [kuery-client-spring-data-r2dbc]
<dependency>
<groupId>dev.hsbrysk.kuery-client</groupId>
<artifactId>kuery-client-spring-data-r2dbc</artifactId>
<!-- or, <artifactId>kuery-client-spring-data-jdbc</artifactId> -->
<version>{{version}}</version>
</dependency>
```

```xml [kuery-client-spring-data-jdbc]
<dependency>
<groupId>dev.hsbrysk.kuery-client</groupId>
<artifactId>kuery-client-spring-data-jdbc</artifactId>
<version>{{version}}</version>
</dependency>
```

:::

## Build KueryClient

### for `kuery-client-spring-data-r2dbc`
::: code-group

```kotlin
```kotlin [kuery-client-spring-data-r2dbc]
val connectionFactory: ConnectionFactory = ...

val kueryClient = SpringR2dbcKueryClient.builder()
.connectionFactory(connectionFactory)
.build()
```

### for `kuery-client-spring-data-jdbc`

```kotlin
```kotlin [kuery-client-spring-data-jdbc]
val dataSource: DataSource = ...

val kueryClient = SpringJdbcKueryClient.builder()
.dataSource(dataSource)
.build()
```

:::

## Let's Use It

```kotlin
Expand Down
86 changes: 86 additions & 0 deletions docs/helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Helpers

## Functions

### `values`

This is a helpful function for performing multi-row inserts.

```kotlin
@Test
fun `test with transformer`() = runTest {
data class UserParam(val username: String, val email: String?, val age: Int)

val input = listOf(
UserParam("user1", "user1@example.com", 1),
UserParam("user2", null, 2),
UserParam("user3", "user3@example.com", 3),
)

kueryClient.sql {
+"INSERT INTO users (username, email, age)"
+values(input) { listOf(it.username, it.email, it.age) }
}.rowsUpdated()
}
```

```kotlin
@Test
fun `test with transformer in string interpolation`() = runTest {
data class UserParam(val username: String, val email: String?, val age: Int)

val input = listOf(
UserParam("user1", "user1@example.com", 1),
UserParam("user2", null, 2),
UserParam("user3", "user3@example.com", 3),
)

kueryClient.sql {
+"INSERT INTO users (username, email, age) ${values(input) { listOf(it.username, it.email, it.age) }}"
}.rowsUpdated()
}
```

## You can also write your own helper

For example, the above `values` function is implemented as follows.

```kotlin
fun SqlBuilder.values(input: List<List<Any?>>): String {
require(input.isNotEmpty()) { "inputted list is empty" }
val firstSize = input.first().size
require(input.all { it.size == firstSize }) { "All inputted child lists must have the same size." }
require(firstSize > 0) { "inputted child list is empty" }

val placeholders = input.joinToString(", ") { list ->
list.joinToString(separator = ", ", prefix = "(", postfix = ")") {
bind(it)
}
}
return "VALUES $placeholders"
}

fun <T> SqlBuilder.values(
input: List<T>,
transformer: (T) -> List<Any?>,
): String {
return values(input.map { transformer(it) })
}
```

Feel free to extend it as you wish.

However, if you provide your own helper functions, they might violate the detekt custom rule. To avoid this, please add
allowRegexes to the detekt custom rule.

```yaml
kuery-client:
StringInterpolation:
active: true
allowRegexes:
- ^yourFunction\(.+\)$
UseStringLiteral:
active: true
allowRegexes:
- ^yourFunction\(.+\)$
```
10 changes: 6 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ hero:
link: /introduction

features:
- title: Simple & Easy
details: As long as you know SQL, Kuery Client is easy to use.
- title: Support R2DBC & JDBC
details: Currently, Kuery Client is implemented based on spring-data-jdbc and spring-data-r2dbc. Use whichever you prefer.
- title: Love SQL
details: While ORM libraries in the world are convenient, they often require learning their own DSL, which I believe has a high learning cost. Kuery Client emphasizes writing SQL as it is.
- title: Based on spring-data-r2dbc and spring-data-jdbc
details: Kuery Client is implemented based on spring-data-r2dbc and spring-data-jdbc. Use whichever you prefer. You can use Spring's ecosystem as it is, such as @Transactional.
- title: Observability
details: It supports Micrometer Observation, so Metrics/Tracing/Logging can also be customized.
---
73 changes: 69 additions & 4 deletions docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
# Introduction

By using the following SQL builder, you can easily build and execute SQL.
## Features

```kotlin
- **Love SQL ♥**
- While ORM libraries in the world are convenient, they often require learning their own DSL, which I believe has a
high learning cost. Kuery Client emphasizes writing SQL as it is.
- **Based on spring-data-r2dbc and spring-data-jdbc**
- Kuery Client is implemented based on spring-data-r2dbc and spring-data-jdbc. Use whichever you prefer. You can use
Spring's ecosystem as it is, such as `@Transactional`.
- **Observability**
- It supports Micrometer Observation, so Metrics/Tracing/Logging can also be customized.
- **Highly extensible**
- When dealing with complex data schemas, there are often cases where you want to write common query logic. Thanks
to Kotlin's extension functions, this becomes easier.

## Motivation

I have used numerous ORM libraries, but in the end, I preferred libraries like MyBatis that allow writing SQL directly.

To construct SQL dynamically, custom template syntax (such as if/foreach) is often used, but I prefer to write logic
using the syntax provided by the programming language as much as possible.
I want to write dynamic SQL using Kotlin syntax, similar to [kotlinx.html](https://github.com/Kotlin/kotlinx.html).

To meet these needs, I implemented Kuery Client.

## Overview

By using the following SQL builder, you can easily build and execute SQL. Whether using R2DBC or JDBC, the way of
writing is almost the same.

::: code-group

```kotlin [kuery-client-spring-data-r2dbc]
data class User(...)

class UserRepository(private val kueryClient: KueryClient) {
Expand All @@ -29,20 +58,56 @@ class UserRepository(private val kueryClient: KueryClient) {
return kueryClient
.sql {
+"INSERT INTO users (username, email) VALUES"
+users.joinToString(", ") { "(${bind(it.username)}, ${bind(it.email)})" }
+values(users) { listOf(it.username, it.email) }
}
.rowsUpdated()
}
}
```

```kotlin [kuery-client-spring-data-jdbc]
data class User(...)

class UserRepository(private val kueryClient: KueryBlockingClient) {
fun findById(userId: Int): User? {
return kueryClient
.sql { +"SELECT * FROM users WHERE user_id = ${bind(userId)}" }
.singleOrNull()
}

fun search(status: String, vip: Boolean?): List<User> {
return kueryClient
.sql {
+"SELECT * FROM users"
+"WHERE"
+"status = ${bind(status)}"
if (vip != null) {
+"vip = ${bind(vip)}"
}
}
.list()
}

fun insertMany(users: List<User>): Long {
return kueryClient
.sql {
+"INSERT INTO users (username, email) VALUES"
+values(users) { listOf(it.username, it.email) }
}
.rowsUpdated()
}
}
```

:::

This SQL builder is very simple. There are only two things you need to remember:

- You can concatenate SQL strings using `+`(unaryPlus).
- You can also directly express logic such as if statements in Kotlin.
- Use the `bind` function for dynamic values.
- Be careful not to evaluate variables directly as strings, as this will obviously lead to SQL injection.
- Kuery Client provides Detekt custom rules that detect such dangerous cases.
- Kuery Client provides [Detekt custom rules](/detekt) that detect such dangerous cases.

## Based on spring-data-r2dbc and spring-data-jdbc

Expand Down
Loading