Save
Save
Save an entity by applying data to attributes of a molecule and call save.transact on it:
Person.name("Bob").age(42).save.transactQuery the saved data:
Person.name.age.query.get.head ==> ("Bob", 42)TxReport
A TxReport containing the id of the saved row is returned:
import molecule.db.postgres.sync.*
val txReport: TxReport =
Person.name("Bob").age(42).save.transactimport molecule.db.postgres.async.*
val txReport: Future[TxReport] =
Person.name("Bob").age(42).save.transactimport molecule.db.postgres.zio.*
val txReport: ZIO[Conn, MoleculeError, TxReport] =
Person.name("Bob").age(42).save.transactimport molecule.db.postgres.io.*
val txReport: cats.effect.IO[TxReport] =
Person.name("Bob").age(42).save.transactTxReport is a simple container of ids affected by a transaction.
case class TxReport(ids: List[Long]) {
// Convenience method when we expect a single id
def id: Long = ids.head
}So we can get the id of the saved row through the txReport:
txReport.id ==> 1L
// or directly
Person.name("Bob").age(42).save.transact.id ==> 1LOptional attribute
Use optional attributes with _? suffix for dynamic optional data
val optName = Some("Bob")
val optAge = None
Person.name_?(optName).age_?(optAge).save.transact
Person.name.age_?.query.get.head ==> ("Bob", None)Collection attributes
Collection attributes can be saved with a Set, Seq and Map or any subtype of those:
// DoobieSetup2 collection type
Person.hobbies(Set("stamps", "trains")).save.transact
// Set subtypes
Person.hobbies(ListSet("stamps", "trains")).save.transact
Person.hobbies(TreeSet("stamps", "trains")).save.transact
// etc// DoobieSetup2 collection type
Person.scores(Seq(1, 2, 2)).save.transact
// Seq subtypes
Person.scores(List(1, 2, 2)).save.transact
Person.scores(Vector(1, 2, 2)).save.transact
// etc// DoobieSetup2 collection type
Person.langNames(Map("en" -> "Hello", "es" -> "Hola")).save.transact
// Map subtypes
Person.langNames(TreeMap("en" -> "Hello", "es" -> "Hola")).save.transact
Person.langNames(VectorMap("en" -> "Hello", "es" -> "Hola")).save.transact
// etcRelationships
Additional related data can be added, and Molecule will transparently create the relationship by inserting the Address and add the address id as a foreign key to the Person row:
Person.name("Bob").age(42)
.Home.street("DoobieSetup2 st. 17").save.transact
Person.name.age
.Home.street.query.get.head ==>
("Bob", 42, "DoobieSetup2 st. 17")Even though we would likely save Countries separately in a database, for tests it can be valuable to be able to create a chain of relationships:
Person.name("Bob").age(42)
.Home.street("DoobieSetup2 st. 17")
.Country.name("USA")
.save.transact
Person.name.age
.Home.street
.Country.name
.query.get.head ==>
("Bob", 42, "DoobieSetup2 st. 17", "USA")Stepping back
We can use _Person to "step back" to Person and save additional relationships or "branches" from Person to other entities:
Person.name("Bob").age(42)
.Home.street("DoobieSetup2 st. 17") // add relationship
._Person // step back to Person
.Education.shortName("Harvard") // add another relationship from Person
.save.transact
// Likewise we can query multiple relationships from Person:
Person.name.age
.Home.street._Person
.Education.shortName
.query.get.head ==>
("Bob", 42, "DoobieSetup2 st. 17", "Harvard")Foreign key
As mentioned above, Country is likely a separate entity that we don't want to create for each new address. In that case we can instead get the country id and apply it to the lower-case named foreign key attribute country:
val usaId = Country.id.name_("USA").query.get.head
Person.name("Bob").age(42)
.Home.street("DoobieSetup2 st. 17")
.country(usaId) // save country id `usaId` as a foreign key
.save.transact
Person.name.age
.Home.street
.Country.name
.query.get.head ==>
("Bob", 42, "DoobieSetup2 st. 17", "USA")