{"id":50042204,"url":"https://github.com/bluetape4k/bluetape4k-exposed","last_synced_at":"2026-06-01T07:00:34.861Z","repository":{"id":356304887,"uuid":"1231834522","full_name":"bluetape4k/bluetape4k-exposed","owner":"bluetape4k","description":"Kotlin Exposed ORM extensions for bluetape4k","archived":false,"fork":false,"pushed_at":"2026-05-31T17:00:02.000Z","size":73188,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-05-31T19:04:03.945Z","etag":null,"topics":["bluetape4k","exposed","jdbc","jetbrains","r2dbc"],"latest_commit_sha":null,"homepage":"https://bluetape4k.github.io","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bluetape4k.png","metadata":{"files":{"readme":"README.ko.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":"docs/governance/kover-coverage-policy.md","roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-07T10:32:42.000Z","updated_at":"2026-05-31T17:00:05.000Z","dependencies_parsed_at":"2026-06-01T07:00:26.963Z","dependency_job_id":null,"html_url":"https://github.com/bluetape4k/bluetape4k-exposed","commit_stats":null,"previous_names":["bluetape4k/bluetape4k-exposed"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/bluetape4k/bluetape4k-exposed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluetape4k%2Fbluetape4k-exposed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluetape4k%2Fbluetape4k-exposed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluetape4k%2Fbluetape4k-exposed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluetape4k%2Fbluetape4k-exposed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bluetape4k","download_url":"https://codeload.github.com/bluetape4k/bluetape4k-exposed/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluetape4k%2Fbluetape4k-exposed/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33763655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bluetape4k","exposed","jdbc","jetbrains","r2dbc"],"created_at":"2026-05-21T03:35:20.948Z","updated_at":"2026-06-01T07:00:34.848Z","avatar_url":"https://github.com/bluetape4k.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bluetape4k-exposed\n\n[![CI](https://github.com/bluetape4k/bluetape4k-exposed/actions/workflows/ci.yml/badge.svg)](https://github.com/bluetape4k/bluetape4k-exposed/actions/workflows/ci.yml)\n[![Kotlin](https://img.shields.io/badge/Kotlin-2.3-7F52FF?logo=kotlin)](https://kotlinlang.org)\n[![JVM](https://img.shields.io/badge/JVM-21-ED8B00?logo=openjdk)](https://openjdk.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n[English](README.md)\n\n![bluetape4k Exposed 작업대 일러스트](./docs/assets/exposed-workbench.png)\n\n[JetBrains Exposed](https://github.com/JetBrains/Exposed) ORM을 위한 Kotlin 확장 라이브러리. Repository 패턴, 캐시 통합, JSON Column 직렬화, 암호화, Spring Boot 자동 설정을 제공합니다.\n\n---\n\n## 프로젝트 목적\n\n`bluetape4k-exposed`는 JetBrains Exposed를 운영 환경에 맞는 Kotlin 데이터 툴킷으로 확장합니다.\nJDBC/R2DBC Repository 패턴, cache-backed read path, JSON/암호화 Column, 데이터베이스별\ndialect 확장, Spring Boot 4 자동 설정을 Exposed DSL 스타일 안에서 제공합니다.\n\n## 주요 기능\n\n- **Repository 패턴** — Exposed DSL 기반 타입 안전한 JDBC 및 R2DBC(코루틴) Repository 추상화\n- **CTE Query DSL** — PostgreSQL/MySQL `WITH`, `WITH RECURSIVE` SELECT를 위한 JDBC/R2DBC 헬퍼\n- **캐시 통합** — Caffeine(로컬), Lettuce/Redisson(분산 Redis) 캐시 백엔드\n- **JSON Column** — Jackson 2.x, Jackson 3.x, Fastjson2 Column 직렬화\n- **암호화** — Google Tink 기반 암호화 Column\n- **DB 특화 확장** — PostgreSQL, MySQL 8, BigQuery, ClickHouse, Trino, DuckDB, Timefold persistence 헬퍼\n- **Spring Boot** — Spring Boot 4.x 자동 설정 (JDBC, R2DBC, Batch, Spring Modulith JDBC 이벤트 발행 통합)\n- **측정 단위 Column** — `bluetape4k-measured` 단위를 위한 Exposed Custom ColumnType\n\n\u003c!-- README_VISUAL_OVERVIEW:START --\u003e\n## Overview Diagram\n\n![Bluetape4k Exposed overview diagram](docs/images/readme-diagrams/root-readme-overview-01.png)\n\n## Module Relationship Diagram\n\n![Bluetape4k Exposed module relationship diagram](docs/images/readme-diagrams/root-readme-module-relationships-01.png)\n\u003c!-- README_VISUAL_OVERVIEW:END --\u003e\n\n## 아키텍처\n\n![bluetape4k-exposed architecture](docs/assets/exposed-architecture.png)\n\n## 모듈 목록\n\n| 모듈 | 설명 |\n|------|------|\n| `exposed-core` | 핵심 Column 타입, DSL 헬퍼, 확장 함수 |\n| `exposed-dao` | DAO Entity 확장, 라이프사이클 훅 |\n| `exposed-jdbc` | JDBC 기반 Repository 패턴, 트랜잭션 DSL |\n| `exposed-r2dbc` | R2DBC 코루틴 네이티브 Repository, suspend 트랜잭션 |\n| `exposed-jdbc-tests` | JDBC 통합 테스트 픽스처 |\n| `exposed-r2dbc-tests` | R2DBC 통합 테스트 픽스처 |\n| `exposed-cache` | 캐시 추상화 인터페이스 |\n| `exposed-jdbc-caffeine` | JDBC + Caffeine 로컬 캐시 |\n| `exposed-jdbc-lettuce` | JDBC + Lettuce Redis 분산 캐시 |\n| `exposed-jdbc-redisson` | JDBC + Redisson Redis 분산 캐시 |\n| `exposed-r2dbc-caffeine` | R2DBC + Caffeine 로컬 캐시 |\n| `exposed-r2dbc-lettuce` | R2DBC + Lettuce Redis 분산 캐시 |\n| `exposed-r2dbc-redisson` | R2DBC + Redisson Redis 분산 캐시 |\n| `exposed-jackson2` | Jackson 2.x JSON Column 직렬화 |\n| `exposed-jackson3` | Jackson 3.x JSON Column 직렬화 |\n| `exposed-fastjson2` | Fastjson2 JSON Column 직렬화 |\n| `exposed-tink` | Google Tink 암호화 Column |\n| `exposed-measured` | 측정 단위용 Custom ColumnType 매핑 |\n| `exposed-postgresql` | PostgreSQL 다이얼렉트 확장 |\n| `exposed-mysql8` | MySQL 8 다이얼렉트 확장 |\n| `exposed-bigquery` | BigQuery connector 지원 |\n| `exposed-clickhouse` | ClickHouse connector 지원 |\n| `exposed-trino` | Trino connector 지원 |\n| `exposed-duckdb` | DuckDB embedded analytics 지원 |\n| `exposed-timefold-solver-persistence` | Timefold Solver persistence 통합 |\n| `exposed-spring-boot-jdbc` | Spring Boot 4.x JDBC 자동 설정 |\n| `exposed-spring-boot-r2dbc` | Spring Boot 4.x R2DBC 자동 설정 |\n| `exposed-spring-boot-batch` | Spring Boot 4.x Batch 통합 |\n| `exposed-spring-modulith` | Exposed 기반 Spring Modulith JDBC 이벤트 발행 Repository |\n\n## 빠른 시작\n\n### Gradle 의존성 추가\n\n```kotlin\ndependencies {\n    // 핵심 JDBC\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-jdbc:1.10.0\")\n    // R2DBC (코루틴)\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-r2dbc:1.10.0\")\n    // Redis 캐시 (Lettuce)\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-jdbc-lettuce:1.10.0\")\n    // Jackson JSON Column\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-jackson2:1.10.0\")\n    // Spring Boot 자동 설정\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-spring-boot-jdbc:1.10.0\")\n    // Exposed 기반 Spring Modulith JDBC 이벤트 발행\n    implementation(\"io.github.bluetape4k.exposed:bluetape4k-exposed-spring-modulith:1.10.0\")\n}\n```\n\n스냅샷은 Maven Central Snapshots에 배포됩니다:\n\n```kotlin\nrepositories {\n    maven(\"https://central.sonatype.com/repository/maven-snapshots/\")\n    mavenCentral()\n}\n```\n\n### Exposed Gradle Plugin\n\nExposed table 정의에서 migration script를 생성하는 application 또는 example\nmodule에는 JetBrains 공식 Exposed Gradle plugin을 사용합니다. Plugin 버전은\n프로젝트가 사용하는 JetBrains Exposed 버전과 맞춥니다. bluetape4k repo에서는\n중앙 `bluetape4k-dependencies` catalog에서 plugin alias를 가져와, plugin 버전이\n공유 `exposed` 호환 라인을 따르도록 합니다.\n\n```kotlin\nplugins {\n    alias(bt4k.plugins.exposed.plugin)\n}\n```\n\n이 plugin은 `generateMigrations` workflow를 추가하며, `exposed.migrations`\nblock에서 table package와 대상 database 또는 Testcontainers image를 설정합니다.\n자세한 내용은\n[Exposed Gradle plugin 문서](https://www.jetbrains.com/help/exposed/exposed-gradle-plugin.html)와\n[Gradle Plugin Portal entry](https://plugins.gradle.org/plugin/org.jetbrains.exposed.plugin)를\n참고하세요.\n\nDemo migration 생성은 weekly 및 pull request smoke workflow에서 검증합니다:\n\n```bash\n./gradlew :exposed-spring-boot-jdbc-demo:generateMigrations --filename=V1__create_products.sql\n./gradlew :exposed-spring-boot-r2dbc-demo:generateMigrations --filename=V1__create_webflux_products.sql\n```\n\n### Database 예제\n\n| 예제 | 목적 | 검증 |\n|------|------|------|\n| `examples-exposed-clickhouse-oltp-olap` | PostgreSQL OLTP에서 ClickHouse OLAP으로 forwarding 후 집계 분석 | `./gradlew :examples-exposed-clickhouse-oltp-olap:test` |\n| `examples-exposed-bigquery-dry-run` | Credential 없이 BigQuery REST dry-run과 query-job option 검증 | `./gradlew :examples-exposed-bigquery-dry-run:test` |\n\n### JDBC Repository (H2 / PostgreSQL / MySQL)\n\n```kotlin\nimport org.jetbrains.exposed.sql.*\nimport org.jetbrains.exposed.sql.transactions.transaction\n\nobject UserTable : LongIdTable(\"users\") {\n    val name = varchar(\"name\", 255)\n    val email = varchar(\"email\", 255)\n    val createdAt = datetime(\"created_at\")\n}\n\nclass UserRepository(private val database: Database) {\n\n    fun findById(id: Long): ResultRow? = transaction(database) {\n        UserTable.selectAll()\n            .where { UserTable.id eq id }\n            .singleOrNull()\n    }\n\n    fun findAll(): List\u003cResultRow\u003e = transaction(database) {\n        UserTable.selectAll().toList()\n    }\n\n    fun create(name: String, email: String): Long = transaction(database) {\n        UserTable.insertAndGetId {\n            it[UserTable.name] = name\n            it[UserTable.email] = email\n            it[UserTable.createdAt] = org.joda.time.DateTime.now()\n        }.value\n    }\n\n    fun deleteById(id: Long): Boolean = transaction(database) {\n        UserTable.deleteWhere { UserTable.id eq id } \u003e 0\n    }\n}\n\n// 사용 예\nval db = Database.connect(dataSource)\nSchemaUtils.create(UserTable)\n\nval repo = UserRepository(db)\nval id = repo.create(\"홍길동\", \"gildong@example.com\")\nval user = repo.findById(id)\n```\n\n### R2DBC 코루틴 Repository\n\n```kotlin\nimport io.bluetape4k.exposed.r2dbc.transactions.suspendTransaction\n\nclass UserR2dbcRepository(private val database: R2dbcDatabase) {\n\n    suspend fun findById(id: Long): ResultRow? = suspendTransaction(database) {\n        UserTable.selectAll()\n            .where { UserTable.id eq id }\n            .singleOrNull()\n    }\n\n    suspend fun create(name: String, email: String): Long = suspendTransaction(database) {\n        UserTable.insertAndGetId {\n            it[UserTable.name] = name\n            it[UserTable.email] = email\n        }.value\n    }\n}\n```\n\n### Common Table Expression (PostgreSQL / MySQL)\n\n```kotlin\nimport io.bluetape4k.exposed.core.CteTable\nimport io.bluetape4k.exposed.jdbc.withCte\nimport org.jetbrains.exposed.v1.jdbc.select\n\nval activeUsers = CteTable(\n    name = \"active_users\",\n    query = Users.select(Users.id, Users.name).where { Users.active eq true }\n)\n\nval rows = activeUsers\n    .select(activeUsers[Users.id], activeUsers[Users.name])\n    .withCte(activeUsers)\n    .orderBy(activeUsers[Users.id])\n    .toList()\n```\n\n### JSON Column (Jackson)\n\n```kotlin\nimport io.bluetape4k.exposed.jackson2.json\n\ndata class Address(val street: String, val city: String)\n\nobject ContactTable : LongIdTable(\"contacts\") {\n    val name = varchar(\"name\", 255)\n    val address = json\u003cAddress\u003e(\"address\")  // JSON 텍스트로 저장\n}\n```\n\n### 암호화 Column (Tink)\n\n```kotlin\nimport io.bluetape4k.exposed.tink.encrypted\n\nobject SecretTable : LongIdTable(\"secrets\") {\n    val sensitiveData = encrypted(\"data\")  // AES-GCM으로 암호화 저장\n}\n```\n\n### Spring Boot 자동 설정\n\n```kotlin\n@SpringBootApplication\n@EnableExposedJdbc\nclass MyApplication\n\n// application.yml\n// spring:\n//   datasource:\n//     url: jdbc:postgresql://localhost:5432/mydb\n```\n\n### Spring Modulith 이벤트 발행\n\n`exposed-spring-modulith`는 Exposed DSL과 동일한\nExposed `DataSource`/`springTransactionManager`를 사용하는 JDBC-only Spring\nModulith `EventPublicationRepository`를 제공합니다. artifact 이름은 공식\nSpring Modulith 저장소 모듈처럼 보이지 않도록 `exposed-spring-modulith`\n형태로 둡니다.\n\n```yaml\nbluetape4k:\n  spring:\n    modulith:\n      exposed:\n        completion-mode: update\n        initialize-schema: false\n```\n\n운영 스키마는 Flyway 또는 Liquibase 사용을 권장합니다. `initialize-schema`는\n테스트와 작은 로컬 애플리케이션 용도입니다.\n\n## 요구사항\n\n- JVM 21+\n- Kotlin 2.3+\n- JetBrains Exposed 1.3+\n\n## 라이선스\n\nMIT — [LICENSE](LICENSE) 참고.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluetape4k%2Fbluetape4k-exposed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluetape4k%2Fbluetape4k-exposed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluetape4k%2Fbluetape4k-exposed/lists"}