Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hizhengfu/b9d8eb76acca2c8535d6f8f58612f492 to your computer and use it in GitHub Desktop.
Save hizhengfu/b9d8eb76acca2c8535d6f8f58612f492 to your computer and use it in GitHub Desktop.
Exposed 0.30.2 CrudRepository Proposal
abstract class CrudRepository <ID, DOMAIN, TABLE : Table> {
abstract val table: TABLE
abstract fun toDomain(row: ResultRow): DOMAIN
abstract fun toRow(domain: DOMAIN): TABLE.(InsertStatement<Number>) -> Unit
abstract fun match(id: ID): SqlExpressionBuilder.() -> Op<Boolean>
abstract fun extractId(domain: DOMAIN): ID
abstract fun updateRow(domain: DOMAIN): TABLE.(UpdateStatement) -> Unit
/**
* Get a particular record by its ID.
*
* @param id The ID of the record to retrieve.
* @return The domain object which corresponds to the specified ID if operation was successful or null otherwise.
*/
fun get(id: ID): DOMAIN? = transaction {
table.select(where = match(id)).singleOrNull()?.let { toDomain(it) }
}
/**
* Simply retrieves all records.
*/
fun getAll(): List<DOMAIN> = transaction {
table.selectAll().map { toDomain(it) }
}
/**
* Creates a new record.
*
* @param domain The object to be created in its domain form.
* @return The newly created record if operation was successful or null otherwise.
*/
fun create(domain: DOMAIN): DOMAIN? = transaction {
table.insert(toRow(domain)).let {
it.resultedValues?.singleOrNull()?.let { toDomain(it) }
}
}
/**
* Updates an existing record.
*
* @param domain The domain object which will be updated.
* @return The freshly updated domain object if operation was successful or null otherwise.
*/
fun update(domain: DOMAIN): DOMAIN? = transaction {
table.update(where = match(extractId(domain)), body = updateRow(domain))
.let {
if(it == 0) null else get(extractId(domain))
}
}
/**
* Deletes the single record which corresponds to the specified ID.
*
* @param id The ID of the record to be deleted.
* @return The ID of the deleted record if operation was successful or null otherwise.
*/
fun delete(id: ID): ID? = transaction {
table.deleteWhere(op = match(id)).let {
if(it == 0) null else id
}
}
}
// Our Domain Object
data class Person(
val identityId: UUID,
val userName: String,
val givenName: String,
val familyName: String,
val email: String
)
// Our Table Definition
object Persons : Table("persons") {
val identityId = varchar("identityId", 36)
val userName = varchar("userName", 64)
val givenName = varchar("givenName", 64)
val familyName = varchar("familyName", 64)
val email = varchar("email", 64)
override val primaryKey = PrimaryKey(identityId)
}
// Exemplaratory CrudRepositry for managing Persons :-)
object PersonRepository : CrudRepository<UUID, Person, Persons>() {
override val table = Persons
override fun toDomain(row: ResultRow): Person {
return Person(
identityId = UUID.fromString(row[Persons.identityId]),
userName = row[Persons.userName],
givenName = row[Persons.givenName],
familyName = row[Persons.familyName],
email = row[Persons.email]
)
}
override fun toRow(domain: Person): Persons.(InsertStatement<Number>) -> Unit = {
it[identityId] = domain.identityId.toString()
it[userName] = domain.userName
it[givenName] = domain.givenName
it[familyName] = domain.familyName
it[email] = domain.email
}
override fun updateRow(domain: Person): Persons.(UpdateStatement) -> Unit = {
it[userName] = domain.userName
it[givenName] = domain.givenName
it[familyName] = domain.familyName
it[email] = domain.email
}
override fun match(id: UUID): SqlExpressionBuilder.() -> Op<Boolean> = {
Persons.identityId eq id.toString()
}
override fun extractId(domain: Person): UUID = domain.identityId
}
@grv87
Copy link

grv87 commented Dec 9, 2021

  1. This is not a complete Repository, but just a Data Mapper.
    It doesn't have a cache. Also, it doesn't provide any way to search for records with criteria different from id.
    But anyway it looks promising.

  2. Shouldn't get call in update be outside of transaction? For example, DB could change some data in triggers on commit.

  3. create looks invalid to me. INSERT generally doesn't return all column values.
    create should probably have get(extractId(domain)), the same as update.

    I've fixed this, and also made various improvements in https://gist.github.com/grv87/2a4969dc1277e43df4d22032919a0026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment