Git Product home page Git Product logo

pearl's Introduction

Pearl

Build Status codecov

A toy data mapper I've build as an assignment for Java programming course.

It is heavily inspired by Ecto, and I purposefully attempted to steer away from the ActiveRecord/Hibernate way of managing persistence, because I've been burned by it in the past.

Interactions with the database are explicit and performed strictly through the repository (Repo). The records are represented by immutable data classes, creation and updates are handled through changesets.

Example usage

As mentioned above, this is not really intended to be used anywhere, but I've still compiled basic usage instructions to give a more concrete overview of how the library functions.

The dependency is com.github.thymelous:pearl:master-SNAPSHOT.

Connecting to a database (PostgreSQL 9.6+) is as simple as

import org.pearl.repo.Repo

Repo.connect("localhost", 5432, dbname = "...", username = "...", password = "...")

This needs to be done once, at application startup.

Models

A model is just a data class with constant (val) properties inherited from org.pearl.Model. It must have a primary key property (either Int or Long, annotated with Id) and have a default constructor without parameters.

Currently supported datatypes are Int, Long, Double, String, LocalDateTime, and ZonedDateTime. Enums are also supported out-of-the-box: you don't need to specify anything, they are automatically converted to and from their String representations.

Data manipulation

Records are created through changesets:

val changeset = Changeset.newRecord(Image(url = "img_url", size = Enum.SMALL))
// Alternatively, from a map of param name (String) to value (String):
val paramChangeset = Changeset.newRecord<Image>(
  params = mapOf("url" to "img_url", "size" to "SMALL", "secret" to "forbidden"),
  allowedParms = listOf("name", "size"))
// Note that since "secret" is not an allowed param, its value will be defaulted

val inserted = Repo.insert(changeset)
inserted.id
// => 1

Individual updates are handled similarly:

val paramChangest = Changeset.update(record, mapOf("size" to "LARGE"), listOf("size"))
// Return the updated record, including changes that were made by e.g. a database trigger
val updated = Repo.one(updateRecord(paramChangest))
// Just perform the update, don't return anything
Repo.execute(updateRecord(paramChangest))

Deletions are performed using the primary key only:

val toBeDeleted = Image(...)
// Assuming toBeDeleted's id = 4, the following two commands are equivalent:
Repo.execute(deleteRecord(toBeDeleted))
Repo.execute(deleteRecord(Image(id = 4)))

Queries

When it comes to querying data, Pearl has a (somewhat limited) DSL that supports both record instantiation and projection (selecting a subset of columns).

val record = Repo.one(from<Image>().where { it["id"] eq 1 })
val records = Repo.many(from<Image>().where { it["size"] eq Enum.LARGE })
val projected = Repo.rows(from<Image>().where { (it["width"] lt 200) and (it["height"] gt 100) }.select("id", "size"))

A nifty feature you can use is conditional data manipulation:

// Insert an image if there are none in the database
var record = Repo.one(insert(Changeset.newRecord(Image())).where { not(exists(from<Image>())) })
record == null
// => false
record = Repo.one(insert(Changeset.newRecord(Image())).where { not(exists(from<Image>())) })
record == null
// => true

// Set "hasPreview" to true for all records with "size" = LARGE or "width" more than 800
Repo.execute(updateAll(Changeset.update(Image(), Image(hasPreview = true))).where { (it["size"] eq Enum.LARGE) or (it["width"] gt 800) })

// Delete all images disliked by at least one administrator and return them
val removedRecords = Repo.many(delete<Image>().where { it["dislikedByUser"] `in` from<User>().select("name").where { it["role"] eq "admin" })

// Only delete the record if its "size" is null or "deletedOn' is not null
Repo.execute(deleteRecord(record).where { it["size"].isNull() or it["deletedOn"].isNotNull() })

// Delete two largest by width images
Repo.execute(delete<Image>().where { it["id"] `in` from<Image>().select("id").orderBy("width", DESC).limit(2) })

pearl's People

Contributors

timlathy avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.