-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v0.2.0 release
- Loading branch information
Showing
14 changed files
with
338 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,123 @@ | ||
# Exposed GADT mapping | ||
|
||
A library based on [Exposed](https://github.com/JetBrains/Exposed) [DSL](https://github.com/JetBrains/Exposed/wiki/DSL) providing mappings between data entities and tables with support for generic algebraic data types (nested classes, type parameters, and sealed classes) | ||
[![Maven Central](https://img.shields.io/maven-central/v/com.huanshankeji/exposed-adt-mapping)](https://search.maven.org/artifact/com.huanshankeji/exposed-adt-mapping) | ||
|
||
This project is an attemp to provide an alternative to [Exposed DAO](https://github.com/JetBrains/Exposed/wiki/DAO) while supporting some more advanced functional programming features. Also see JetBrains/Exposed#24 for more details. | ||
A library based on [Exposed](https://github.com/JetBrains/Exposed) [DSL](https://github.com/JetBrains/Exposed/wiki/DSL) providing mappings between data entities and tables with support for GADT (generalized algebraic data type), aka features including nested properties of composite class types, type parameters and their type inference, and sealed classes | ||
|
||
This project is an attempt to provide an alternative to [Exposed DAO](https://github.com/JetBrains/Exposed/wiki/DAO) while supporting some more advanced functional programming features. See JetBrains/Exposed#24 for more details. | ||
|
||
## Basic usage guide | ||
|
||
Please note that these APIs are far from stable. There are going to be refactors in future releases. | ||
|
||
### Table and data definitions | ||
|
||
#### Tables and joins | ||
|
||
```kotlin | ||
typealias DirectorId = Int | ||
|
||
class Director(val directorId: DirectorId, val name: String) | ||
|
||
class FilmDetails<DirectorT>( | ||
val sequelId: Int, | ||
val name: String, | ||
val director: DirectorT | ||
) | ||
typealias FilmDetailsWithDirectorId = FilmDetails<DirectorId> | ||
|
||
typealias FilmId = Int | ||
|
||
class Film<DirectorT>(val filmId: FilmId, val filmDetails: FilmDetails<DirectorT>) | ||
typealias FilmWithDirectorId = Film<DirectorId> | ||
typealias FullFilm = Film<Director> | ||
``` | ||
|
||
#### Data entities and attributes | ||
|
||
```kotlin | ||
typealias DirectorId = Int | ||
|
||
class Director(val directorId: DirectorId, val name: String) | ||
|
||
class FilmDetails<DirectorT>( | ||
val sequelId: Int, | ||
val name: String, | ||
val director: DirectorT | ||
) | ||
typealias FilmDetailsWithDirectorId = FilmDetails<DirectorId> | ||
|
||
typealias FilmId = Int | ||
|
||
class Film<DirectorT>(val filmId: FilmId, val filmDetails: FilmDetails<DirectorT>) | ||
typealias FilmWithDirectorId = Film<DirectorId> | ||
typealias FullFilm = Film<Director> | ||
``` | ||
|
||
A nested composite class property can either map to flattened fields or a table referenced by a foreign key: `FilmDetails` is a nested class in `Film`, but the corresponding table `Films` has the `FilmDetails` members/fields flattened directly instead of referencing a corresponding table for `FilmDetails` with a foreign key; on the contrary, a `director : Director` member of `FilmDetails<Director>` maps to the `Directors` table referenced. | ||
|
||
As laid out above in the code, a recommended approach to define data types is to make necessary use of type parameters to improve code reuse. | ||
|
||
### Create mappers | ||
|
||
You can create mappers with the overloaded `reflectionBasedClassPropertyDataMapper` functions. Pass the `propertyColumnMappingConfigMapOverride` parameter to override the default options. | ||
|
||
```kotlin | ||
object Mappers { | ||
val director = reflectionBasedClassPropertyDataMapper<Director>(Directors) | ||
val filmDetailsWithDirectorId = reflectionBasedClassPropertyDataMapper<FilmDetailsWithDirectorId>( | ||
Films, | ||
propertyColumnMappingConfigMapOverride = mapOf( | ||
// The default name is the property name "director", but there is no column property with such a name, therefore we need to pass a custom name. | ||
FilmDetailsWithDirectorId::director to PropertyColumnMappingConfig.create<DirectorId>(columnPropertyName = Films::directorId.name) | ||
) | ||
) | ||
val filmWithDirectorId = reflectionBasedClassPropertyDataMapper<FilmWithDirectorId>( | ||
Films, | ||
propertyColumnMappingConfigMapOverride = mapOf( | ||
FilmWithDirectorId::filmDetails to PropertyColumnMappingConfig.create<FilmDetailsWithDirectorId>( | ||
// You can pass a nested custom mapper. | ||
customMapper = filmDetailsWithDirectorId | ||
) | ||
) | ||
) | ||
val fullFilm = reflectionBasedClassPropertyDataMapper<FullFilm>( | ||
filmsLeftJoinDirectors, | ||
propertyColumnMappingConfigMapOverride = mapOf( | ||
FullFilm::filmDetails to PropertyColumnMappingConfig.create( | ||
adt = PropertyColumnMappingConfig.Adt.Product( | ||
mapOf( | ||
// Because `name` is a duplicate name column so a custom mapper has to be passed here, otherwise the `CHOOSE_FIRST` option maps the data property `Director::name` to the wrong column `Films::name`. | ||
FilmDetails<Director>::director to PropertyColumnMappingConfig.create<Director>(customMapper = director) | ||
) | ||
) | ||
) | ||
) | ||
) | ||
} | ||
``` | ||
|
||
### CRUD operations | ||
|
||
Call `updateBuilderSetter` to get a setter lambda to pass to `insert` or `update`. Call `selectWithMapper` to execute a query with a mapper (not available yet, available soon in 0.2.0). | ||
|
||
```kotlin | ||
val directorId = 1 | ||
val director = Director(directorId, "George Lucas") | ||
Directors.insert(Mappers.director.updateBuilderSetter(director)) | ||
|
||
val episodeIFilmDetails = FilmDetails(1, "Star Wars: Episode I – The Phantom Menace", directorId) | ||
Films.insert(Mappers.filmDetailsWithDirectorId.updateBuilderSetter(episodeIFilmDetails)) // insert without the ID since it's `AUTO_INCREMENT` | ||
|
||
val filmId = 2 | ||
val episodeIIFilmDetails = FilmDetails(2, "Star Wars: Episode II – Attack of the Clones", directorId) | ||
val filmWithDirectorId = FilmWithDirectorId(filmId, episodeIIFilmDetails) | ||
Films.insert(Mappers.filmWithDirectorId.updateBuilderSetter(filmWithDirectorId)) // insert with the ID | ||
|
||
val fullFilm = with(Mappers.fullFilm) { | ||
resultRowToData(filmsLeftJoinDirectors.select(neededColumns).where(Films.filmId eq filmId).single()) | ||
} | ||
// not available yet, available soon in 0.2.0 | ||
val fullFilms = | ||
filmsLeftJoinDirectors.selectWithMapper(Mappers.fullFilm, Films.filmId inList listOf(1, 2)).toList() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import com.huanshankeji.CommonDependencies | ||
import com.huanshankeji.CommonVersions | ||
|
||
val projectVersion = "0.1.0" | ||
val projectVersion = "0.2.0" | ||
|
||
val commonVersions = CommonVersions() | ||
val commonVersions = CommonVersions(kotlinCommon = "0.5.1") | ||
val commonDependencies = CommonDependencies(commonVersions) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
lib/src/main/kotlin/com/huanshankeji/exposed/datamapping/Table.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.huanshankeji.exposed.datamapping | ||
|
||
import org.jetbrains.exposed.sql.ColumnSet | ||
import org.jetbrains.exposed.sql.Op | ||
|
||
fun <Data : Any> ColumnSet.selectWithMapper(mapper: NullableDataQueryMapper<Data>, where: Op<Boolean>? = null) = | ||
select(mapper.neededColumns) | ||
.run { where?.let { where(it) } ?: this } | ||
.asSequence().map { mapper.resultRowToData(it) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...kotlin/com/huanshankeji/exposed/datamapping/classproperty/ReflectionFunctionInvocation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.huanshankeji.exposed.datamapping.classproperty | ||
|
||
import kotlin.reflect.KFunction | ||
|
||
class ReflectionFunctionInvocationException(constructor: KFunction<*>, vararg args: Any?, cause: Throwable) : | ||
Exception("calling the function $constructor with params ${args.toList()}", cause) | ||
|
||
// also consider catching only in debug/test mode | ||
fun <R> KFunction<R>.callWithCatch(vararg args: Any?) = | ||
try { | ||
call(args = args) | ||
} catch (e: Exception) { | ||
throw ReflectionFunctionInvocationException(this, args = args, cause = e) | ||
} |
Oops, something went wrong.