diff --git a/README.md b/README.md index 6898161..e387800 100644 --- a/README.md +++ b/README.md @@ -590,6 +590,21 @@ case class Address( ) derives DbCodec ``` +#### UUID DbCodec doesn't work for my database + +Some databases directly support the UUID type; these include Postgres, Clickhouse, and H2. When using the built-in `DbCodec[UUID]`, defined in `DbCodec.scala`, serialization and deserialization of `java.util.UUID` will work as expected. + +Other databases like MySql, Oracle, and Sqlite, however, do not natively support UUID columns. Users have to choose an alternate datatype to store the UUID: most commonly `varchar(36)` or `binary(16)`. The JDBC drivers for these databases do not support direct serialization and deserialization of `java.util.UUID`, therefore the default `DbCodec[UUID]` will not be sufficient. Instead, import the appropriate codec from `com.augustnagro.magnum.UUIDCodec`. For example, + +```scala +import com.augustnagro.magnum.* +import com.augustnagro.magnum.UUIDCodec.VarCharUUIDCodec +import java.util.UUID + +@Table(MySqlDbType) +case class Person(@Id id: Long, name: String, tracking_id: Option[UUID]) derives DbCodec +``` + ## Todo * JSON / XML support * Support MSSql diff --git a/magnum/src/main/scala/com/augustnagro/magnum/DbCodec.scala b/magnum/src/main/scala/com/augustnagro/magnum/DbCodec.scala index 3b80414..02cfde2 100644 --- a/magnum/src/main/scala/com/augustnagro/magnum/DbCodec.scala +++ b/magnum/src/main/scala/com/augustnagro/magnum/DbCodec.scala @@ -291,7 +291,7 @@ object DbCodec: given UUIDCodec: DbCodec[UUID] with def queryRepr: String = "?" - val cols: IArray[Int] = IArray(Types.JAVA_OBJECT) + val cols: IArray[Int] = IArray(Types.OTHER) def readSingle(rs: ResultSet, pos: Int): UUID = rs.getObject(pos, classOf[UUID]) def writeSingle(entity: UUID, ps: PreparedStatement, pos: Int): Unit = diff --git a/magnum/src/main/scala/com/augustnagro/magnum/UUIDCodec.scala b/magnum/src/main/scala/com/augustnagro/magnum/UUIDCodec.scala new file mode 100644 index 0000000..9df8783 --- /dev/null +++ b/magnum/src/main/scala/com/augustnagro/magnum/UUIDCodec.scala @@ -0,0 +1,15 @@ +package com.augustnagro.magnum + +import java.sql.{PreparedStatement, ResultSet, Types} +import java.util.UUID + +object UUIDCodec: + given VarCharUUIDCodec: DbCodec[UUID] with + def queryRepr: String = "?" + val cols: IArray[Int] = IArray(Types.VARCHAR) + def readSingle(rs: ResultSet, pos: Int): UUID = + rs.getString(pos) match + case null => null + case uuidStr => UUID.fromString(uuidStr) + def writeSingle(entity: UUID, ps: PreparedStatement, pos: Int): Unit = + ps.setString(pos, entity.toString) diff --git a/magnum/src/test/resources/clickhouse-person.sql b/magnum/src/test/resources/clickhouse-person.sql index 9b01fd3..e1fe050 100644 --- a/magnum/src/test/resources/clickhouse-person.sql +++ b/magnum/src/test/resources/clickhouse-person.sql @@ -5,7 +5,8 @@ create table person ( first_name Nullable(String), last_name String not null, is_admin Bool not null, - created DateTime not null default now() + created DateTime not null default now(), + social_id Nullable(UUID) ) engine = MergeTree() order by created; @@ -16,54 +17,62 @@ insert into person values 'George', 'Washington', true, - toDateTime('2023-03-05 02:26:00') + toDateTime('2023-03-05 02:26:00'), + toUUID('d06443a6-3efb-46c4-a66a-a80a8a9a5388') ), ( toUUID('12970806-606d-42ff-bb9c-3187bbd360dd'), 'Alexander', 'Hamilton', true, - toDateTime('2023-03-05 02:27:00') + toDateTime('2023-03-05 02:27:00'), + toUUID('529b6c6d-7228-4da5-81d7-13b706f78ddb') ), ( toUUID('834a2bd2-6842-424f-97e0-fe5ed02c3653'), 'John', 'Adams', true, - toDateTime('2023-03-05 02:28:00') + toDateTime('2023-03-05 02:28:00'), + null ), ( toUUID('60492bb2-fe02-4d02-9c6d-ae03fa6f2243'), 'Benjamin', 'Franklin', true, - toDateTime('2023-03-05 02:29:00') + toDateTime('2023-03-05 02:29:00'), + null ), ( toUUID('2244eef4-b581-4305-824f-efe8f70e6bb7'), 'John', 'Jay', true, - toDateTime('2023-03-05 02:30:00') + toDateTime('2023-03-05 02:30:00'), + null ), ( toUUID('fb3c479a-0521-4f06-b6ad-218db867518c'), 'Thomas', 'Jefferson', true, - toDateTime('2023-03-05 02:31:00') + toDateTime('2023-03-05 02:31:00'), + null ), ( toUUID('8fefe1d8-20eb-44a0-84da-d328334e1e11'), 'James', 'Madison', true, - toDateTime('2023-03-05 02:32:00') + toDateTime('2023-03-05 02:32:00'), + null ), ( toUUID('8a2d842a-0d03-463c-8c34-43b38120f9e4'), null, 'Nagro', false, - toDateTime('2023-03-05 02:33:00') + toDateTime('2023-03-05 02:33:00'), + null ); \ No newline at end of file diff --git a/magnum/src/test/resources/h2-person.sql b/magnum/src/test/resources/h2-person.sql index 83d87b1..3abf147 100644 --- a/magnum/src/test/resources/h2-person.sql +++ b/magnum/src/test/resources/h2-person.sql @@ -5,15 +5,16 @@ create table person ( first_name varchar(50), last_name varchar(50) not null, is_admin boolean not null, - created timestamp default current_timestamp + created timestamp default current_timestamp, + social_id UUID ); -insert into person (first_name, last_name, is_admin, created) values -('George', 'Washington', true, now()), -('Alexander', 'Hamilton', true, now()), -('John', 'Adams', true, now()), -('Benjamin', 'Franklin', true, now()), -('John', 'Jay', true, now()), -('Thomas', 'Jefferson', true, now()), -('James', 'Madison', true, now()), -(null, 'Nagro', false, now()); +insert into person (first_name, last_name, is_admin, created, social_id) values +('George', 'Washington', true, now(), 'd06443a6-3efb-46c4-a66a-a80a8a9a5388'), +('Alexander', 'Hamilton', true, now(), '529b6c6d-7228-4da5-81d7-13b706f78ddb'), +('John', 'Adams', true, now(), null), +('Benjamin', 'Franklin', true, now(), null), +('John', 'Jay', true, now(), null), +('Thomas', 'Jefferson', true, now(), null), +('James', 'Madison', true, now(), null), +(null, 'Nagro', false, now(), null); diff --git a/magnum/src/test/resources/mysql-person.sql b/magnum/src/test/resources/mysql-person.sql index 83d87b1..a469a91 100644 --- a/magnum/src/test/resources/mysql-person.sql +++ b/magnum/src/test/resources/mysql-person.sql @@ -5,15 +5,16 @@ create table person ( first_name varchar(50), last_name varchar(50) not null, is_admin boolean not null, - created timestamp default current_timestamp + created timestamp default current_timestamp, + social_id varchar(36) ); -insert into person (first_name, last_name, is_admin, created) values -('George', 'Washington', true, now()), -('Alexander', 'Hamilton', true, now()), -('John', 'Adams', true, now()), -('Benjamin', 'Franklin', true, now()), -('John', 'Jay', true, now()), -('Thomas', 'Jefferson', true, now()), -('James', 'Madison', true, now()), -(null, 'Nagro', false, now()); +insert into person (first_name, last_name, is_admin, created, social_id) values +('George', 'Washington', true, now(), 'd06443a6-3efb-46c4-a66a-a80a8a9a5388'), +('Alexander', 'Hamilton', true, now(), '529b6c6d-7228-4da5-81d7-13b706f78ddb'), +('John', 'Adams', true, now(), null), +('Benjamin', 'Franklin', true, now(), null), +('John', 'Jay', true, now(), null), +('Thomas', 'Jefferson', true, now(), null), +('James', 'Madison', true, now(), null), +(null, 'Nagro', false, now(), null); diff --git a/magnum/src/test/resources/pg-person.sql b/magnum/src/test/resources/pg-person.sql index ddb197d..cdcbc12 100644 --- a/magnum/src/test/resources/pg-person.sql +++ b/magnum/src/test/resources/pg-person.sql @@ -5,15 +5,16 @@ create table person ( first_name varchar(50), last_name varchar(50) not null, is_admin boolean not null, - created timestamptz not null default now() + created timestamptz not null default now(), + social_id UUID ); -insert into person (first_name, last_name, is_admin, created) values -('George', 'Washington', true, now()), -('Alexander', 'Hamilton', true, now()), -('John', 'Adams', true, now()), -('Benjamin', 'Franklin', true, now()), -('John', 'Jay', true, now()), -('Thomas', 'Jefferson', true, now()), -('James', 'Madison', true, now()), -(null, 'Nagro', false, timestamp '1997-08-12'); +insert into person (first_name, last_name, is_admin, created, social_id) values +('George', 'Washington', true, now(), 'd06443a6-3efb-46c4-a66a-a80a8a9a5388'), +('Alexander', 'Hamilton', true, now(), '529b6c6d-7228-4da5-81d7-13b706f78ddb'), +('John', 'Adams', true, now(), null), +('Benjamin', 'Franklin', true, now(), null), +('John', 'Jay', true, now(), null), +('Thomas', 'Jefferson', true, now(), null), +('James', 'Madison', true, now(), null), +(null, 'Nagro', false, timestamp '1997-08-12', null); diff --git a/magnum/src/test/scala/ClickHouseTests.scala b/magnum/src/test/scala/ClickHouseTests.scala index 7f4e211..ff90c9f 100644 --- a/magnum/src/test/scala/ClickHouseTests.scala +++ b/magnum/src/test/scala/ClickHouseTests.scala @@ -163,7 +163,8 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[Person, Person, UUID] @@ -201,7 +202,8 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( @@ -210,7 +212,8 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = None, lastName = "Prince", isAdmin = true, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = None ) ) assertEquals(personRepo.count, 10L) @@ -225,14 +228,16 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ), Person( id = UUID.randomUUID, firstName = None, lastName = "Prince", isAdmin = true, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = None ) ) ) @@ -247,7 +252,8 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ) ) assertEquals(personRepo.count, 9L) @@ -261,14 +267,16 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ), Person( id = UUID.randomUUID, firstName = None, lastName = "Prince", isAdmin = true, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = None ) ) val people = personRepo.insertAllReturning(ps) @@ -283,7 +291,8 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = None, lastName = null, isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = None ) personRepo.insert(invalidP) @@ -310,14 +319,16 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ), Person( id = UUID.randomUUID, firstName = None, lastName = "Prince", isAdmin = true, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = None ) ) personRepo.insertAll(newPeople) @@ -347,13 +358,14 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures: firstName = Some("John"), lastName = "Smith", isAdmin = false, - created = OffsetDateTime.now + created = OffsetDateTime.now, + socialId = Some(UUID.randomUUID()) ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (id, first_name, last_name, is_admin, created) values (?, ?, ?, ?, ?)" + "insert into person (id, first_name, last_name, is_admin, created, social_id) values (?, ?, ?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) diff --git a/magnum/src/test/scala/H2Tests.scala b/magnum/src/test/scala/H2Tests.scala index e554ee5..65bc04f 100644 --- a/magnum/src/test/scala/H2Tests.scala +++ b/magnum/src/test/scala/H2Tests.scala @@ -5,6 +5,7 @@ import org.h2.jdbcx.JdbcDataSource import java.nio.file.{Files, Path} import java.sql.{Connection, DriverManager} import java.time.OffsetDateTime +import java.util.UUID import javax.sql.DataSource import scala.util.Properties.propOrNone import scala.util.Using @@ -123,7 +124,8 @@ class H2Tests extends FunSuite: case class PersonCreator( firstName: Option[String], lastName: String, - isAdmin: Boolean + isAdmin: Boolean, + socialId: Option[UUID] ) derives DbCodec @Table(H2DbType, SqlNameMapper.CamelToSnakeCase) @@ -132,7 +134,8 @@ class H2Tests extends FunSuite: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[PersonCreator, Person, Long] @@ -146,7 +149,7 @@ class H2Tests extends FunSuite: test("delete invalid"): connect(ds()): - personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now)) + personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now, None)) assertEquals(8L, personRepo.count) test("deleteById"): @@ -186,14 +189,16 @@ class H2Tests extends FunSuite: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( PersonCreator( firstName = None, lastName = "Prince", - isAdmin = true + isAdmin = true, + socialId = None ) ) assertEquals(personRepo.count, 10L) @@ -205,7 +210,8 @@ class H2Tests extends FunSuite: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) assertEquals(person.id, 9L) @@ -217,17 +223,20 @@ class H2Tests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) val people = personRepo.insertAllReturning(newPc) @@ -238,7 +247,7 @@ class H2Tests extends FunSuite: test("insert invalid"): intercept[SqlException]: connect(ds()): - val invalidP = PersonCreator(None, null, false) + val invalidP = PersonCreator(None, null, false, None) personRepo.insert(invalidP) test("update"): @@ -261,17 +270,20 @@ class H2Tests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) personRepo.insertAll(newPeople) @@ -299,7 +311,8 @@ class H2Tests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) personRepo.insert(p) personRepo.count @@ -310,7 +323,8 @@ class H2Tests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = None ) try transact(dataSource): @@ -327,13 +341,14 @@ class H2Tests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = None ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (first_name, last_name, is_admin) values (?, ?, ?)" + "insert into person (first_name, last_name, is_admin, social_id) values (?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) @@ -349,7 +364,8 @@ class H2Tests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) val newIsAdmin = true diff --git a/magnum/src/test/scala/MySqlTests.scala b/magnum/src/test/scala/MySqlTests.scala index dfa2e05..630da3a 100644 --- a/magnum/src/test/scala/MySqlTests.scala +++ b/magnum/src/test/scala/MySqlTests.scala @@ -12,9 +12,11 @@ import org.testcontainers.utility.DockerImageName import java.nio.file.{Files, Path} import java.sql.Connection import java.time.OffsetDateTime +import java.util.UUID import javax.sql.DataSource import scala.util.Using import scala.util.Using.Manager +import com.augustnagro.magnum.UUIDCodec.VarCharUUIDCodec class MySqlTests extends FunSuite, TestContainersFixtures: @@ -132,7 +134,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: case class PersonCreator( firstName: Option[String], lastName: String, - isAdmin: Boolean + isAdmin: Boolean, + socialId: Option[UUID] ) derives DbCodec @Table(MySqlDbType, SqlNameMapper.CamelToSnakeCase) @@ -141,7 +144,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[PersonCreator, Person, Long] @@ -155,7 +159,7 @@ class MySqlTests extends FunSuite, TestContainersFixtures: test("delete invalid"): connect(ds()): - personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now)) + personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now, None)) assertEquals(8L, personRepo.count) test("deleteById"): @@ -195,14 +199,16 @@ class MySqlTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( PersonCreator( firstName = None, lastName = "Prince", - isAdmin = true + isAdmin = true, + socialId = None ) ) @@ -215,7 +221,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) assertEquals(person.id, 9L) @@ -227,17 +234,20 @@ class MySqlTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) val people = personRepo.insertAllReturning(newPc) @@ -248,7 +258,7 @@ class MySqlTests extends FunSuite, TestContainersFixtures: test("insert invalid"): intercept[SqlException]: connect(ds()): - val invalidP = PersonCreator(None, null, false) + val invalidP = PersonCreator(None, null, false, None) personRepo.insert(invalidP) test("update"): @@ -271,17 +281,20 @@ class MySqlTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) @@ -310,7 +323,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) personRepo.insert(p) personRepo.count @@ -321,7 +335,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) try transact(dataSource): @@ -338,13 +353,14 @@ class MySqlTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (first_name, last_name, is_admin) values (?, ?, ?)" + "insert into person (first_name, last_name, is_admin, social_id) values (?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) @@ -360,7 +376,8 @@ class MySqlTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) val newIsAdmin = true diff --git a/magnum/src/test/scala/OracleTests.scala b/magnum/src/test/scala/OracleTests.scala index ed25a04..1ddb37e 100644 --- a/magnum/src/test/scala/OracleTests.scala +++ b/magnum/src/test/scala/OracleTests.scala @@ -1,4 +1,5 @@ import com.augustnagro.magnum.* +import com.augustnagro.magnum.UUIDCodec.VarCharUUIDCodec import com.dimafeng.testcontainers.OracleContainer import com.dimafeng.testcontainers.munit.fixtures.TestContainersFixtures import munit.FunSuite @@ -8,6 +9,7 @@ import oracle.jdbc.datasource.impl.OracleDataSource import java.nio.file.{Files, Path} import java.sql.Connection import java.time.OffsetDateTime +import java.util.UUID import javax.sql.DataSource import scala.util.Using @@ -132,7 +134,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: case class PersonCreator( firstName: Option[String], lastName: String, - isAdmin: String + isAdmin: String, + socialId: Option[UUID] ) derives DbCodec @Table(OracleDbType, SqlNameMapper.CamelToSnakeCase) @@ -141,7 +144,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: firstName: Option[String], lastName: String, isAdmin: String, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[PersonCreator, Person, Long] @@ -155,7 +159,7 @@ class OracleTests extends FunSuite, TestContainersFixtures: test("delete invalid"): connect(ds()): - personRepo.delete(Person(23L, None, "", "N", OffsetDateTime.now)) + personRepo.delete(Person(23L, None, "", "N", OffsetDateTime.now, None)) assertEquals(8L, personRepo.count) test("deleteById"): @@ -195,14 +199,16 @@ class OracleTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( PersonCreator( firstName = None, lastName = "Prince", - isAdmin = "Y" + isAdmin = "Y", + socialId = None ) ) assertEquals(personRepo.count, 10L) @@ -214,7 +220,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) ) assertEquals(person.id, 9L) @@ -226,17 +233,20 @@ class OracleTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = "Y" + isAdmin = "Y", + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = "N" + isAdmin = "N", + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = "Y" + isAdmin = "Y", + socialId = None ) ) val people = personRepo.insertAllReturning(newPc) @@ -247,7 +257,7 @@ class OracleTests extends FunSuite, TestContainersFixtures: test("insert invalid"): intercept[SqlException]: connect(ds()): - val invalidP = PersonCreator(None, null, "N") + val invalidP = PersonCreator(None, null, "N", None) personRepo.insert(invalidP) test("update"): @@ -270,17 +280,20 @@ class OracleTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = "Y" + isAdmin = "Y", + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = "N" + isAdmin = "N", + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = "Y" + isAdmin = "Y", + socialId = None ) ) personRepo.insertAll(newPeople) @@ -308,7 +321,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) personRepo.insert(p) personRepo.count @@ -319,7 +333,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) try transact(dataSource): @@ -336,13 +351,14 @@ class OracleTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (first_name, last_name, is_admin) values (?, ?, ?)" + "insert into person (first_name, last_name, is_admin, social_id) values (?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) @@ -357,7 +373,8 @@ class OracleTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = "N" + isAdmin = "N", + socialId = Some(UUID.randomUUID()) ) ) val newIsAdmin = "Y" @@ -427,40 +444,41 @@ class OracleTests extends FunSuite, TestContainersFixtures: | first_name varchar2(50), | last_name varchar2(50) not null, | is_admin varchar2(1) not null, - | created timestamp default current_timestamp + | created timestamp default current_timestamp, + | social_id varchar2(36) |)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('George', 'Washington', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('George', 'Washington', 'Y', current_timestamp, 'd06443a6-3efb-46c4-a66a-a80a8a9a5388')""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('Alexander', 'Hamilton', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('Alexander', 'Hamilton', 'Y', current_timestamp, '529b6c6d-7228-4da5-81d7-13b706f78ddb')""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('John', 'Adams', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('John', 'Adams', 'Y', current_timestamp, null)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('Benjamin', 'Franklin', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('Benjamin', 'Franklin', 'Y', current_timestamp, null)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('John', 'Jay', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('John', 'Jay', 'Y', current_timestamp, null)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('Thomas', 'Jefferson', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('Thomas', 'Jefferson', 'Y', current_timestamp, null)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('James', 'Madison', 'Y', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('James', 'Madison', 'Y', current_timestamp, null)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |(null, 'Nagro', 'N', current_timestamp)""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |(null, 'Nagro', 'N', current_timestamp, null)""".stripMargin ) ) .get diff --git a/magnum/src/test/scala/PgTests.scala b/magnum/src/test/scala/PgTests.scala index d296657..f9c664c 100644 --- a/magnum/src/test/scala/PgTests.scala +++ b/magnum/src/test/scala/PgTests.scala @@ -12,6 +12,7 @@ import org.testcontainers.utility.DockerImageName import java.nio.file.{Files, Path} import java.sql.Connection import java.time.OffsetDateTime +import java.util.UUID import javax.sql.DataSource import scala.util.Using import scala.util.Using.Manager @@ -136,7 +137,8 @@ class PgTests extends FunSuite, TestContainersFixtures: case class PersonCreator( firstName: Option[String], lastName: String, - isAdmin: Boolean + isAdmin: Boolean, + socialId: Option[UUID] ) derives DbCodec @Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase) @@ -145,7 +147,8 @@ class PgTests extends FunSuite, TestContainersFixtures: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[PersonCreator, Person, Long] @@ -159,7 +162,7 @@ class PgTests extends FunSuite, TestContainersFixtures: test("delete invalid"): connect(ds()): - personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now)) + personRepo.delete(Person(23L, None, "", false, OffsetDateTime.now, None)) assertEquals(8L, personRepo.count) test("deleteById"): @@ -199,14 +202,16 @@ class PgTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( PersonCreator( firstName = None, lastName = "Prince", - isAdmin = true + isAdmin = true, + socialId = None ) ) assertEquals(personRepo.count, 10L) @@ -218,7 +223,8 @@ class PgTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = None ) ) assertEquals(person.id, 9L) @@ -230,17 +236,20 @@ class PgTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) val people = personRepo.insertAllReturning(newPc) @@ -251,7 +260,7 @@ class PgTests extends FunSuite, TestContainersFixtures: test("insert invalid"): intercept[SqlException]: connect(ds()): - val invalidP = PersonCreator(None, null, false) + val invalidP = PersonCreator(None, null, false, None) personRepo.insert(invalidP) test("update"): @@ -274,17 +283,20 @@ class PgTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) personRepo.insertAll(newPeople) @@ -312,7 +324,8 @@ class PgTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = None ) personRepo.insert(p) personRepo.count @@ -323,7 +336,8 @@ class PgTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = None ) try transact(dataSource): @@ -340,13 +354,14 @@ class PgTests extends FunSuite, TestContainersFixtures: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = None ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (first_name, last_name, is_admin) values (?, ?, ?)" + "insert into person (first_name, last_name, is_admin, social_id) values (?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) @@ -362,7 +377,8 @@ class PgTests extends FunSuite, TestContainersFixtures: PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) val newIsAdmin = true @@ -410,7 +426,8 @@ class PgTests extends FunSuite, TestContainersFixtures: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: OffsetDateTime + created: OffsetDateTime, + socialId: Option[UUID] ) derives DbCodec val customPersonRepo = Repo[PersonCreator, CustomPerson, Long] diff --git a/magnum/src/test/scala/SqliteTests.scala b/magnum/src/test/scala/SqliteTests.scala index dba09fa..561a8b5 100644 --- a/magnum/src/test/scala/SqliteTests.scala +++ b/magnum/src/test/scala/SqliteTests.scala @@ -1,10 +1,12 @@ import com.augustnagro.magnum.* +import com.augustnagro.magnum.UUIDCodec.VarCharUUIDCodec import munit.FunSuite import org.sqlite.SQLiteDataSource import java.nio.file.{Files, Path} import java.sql.Connection import java.time.{LocalDateTime, OffsetDateTime} +import java.util.UUID import javax.sql.DataSource import scala.util.Using import scala.util.Using.Manager @@ -131,7 +133,8 @@ class SqliteTests extends FunSuite: case class PersonCreator( firstName: Option[String], lastName: String, - isAdmin: Boolean + isAdmin: Boolean, + socialId: Option[UUID] ) derives DbCodec @Table(SqliteDbType, SqlNameMapper.CamelToSnakeCase) @@ -140,7 +143,8 @@ class SqliteTests extends FunSuite: firstName: Option[String], lastName: String, isAdmin: Boolean, - created: String + created: String, + socialId: Option[UUID] ) derives DbCodec val personRepo = Repo[PersonCreator, Person, Long] @@ -155,7 +159,7 @@ class SqliteTests extends FunSuite: test("delete invalid"): connect(ds()): personRepo.delete( - Person(23L, None, "", false, LocalDateTime.now.toString) + Person(23L, None, "", false, LocalDateTime.now.toString, None) ) assertEquals(8L, personRepo.count) @@ -190,14 +194,16 @@ class SqliteTests extends FunSuite: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) personRepo.insert( PersonCreator( firstName = None, lastName = "Prince", - isAdmin = true + isAdmin = true, + socialId = None ) ) assertEquals(personRepo.count, 10L) @@ -209,7 +215,8 @@ class SqliteTests extends FunSuite: PersonCreator( firstName = Some("John"), lastName = "Smith", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) assertEquals(person.id, 9L) @@ -222,17 +229,20 @@ class SqliteTests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) val people = personRepo.insertAllReturning(newPc) @@ -271,7 +281,7 @@ class SqliteTests extends FunSuite: test("insert invalid"): intercept[SqlException]: connect(ds()): - val invalidP = PersonCreator(None, null, false) + val invalidP = PersonCreator(None, null, false, None) personRepo.insert(invalidP) test("update"): @@ -294,17 +304,20 @@ class SqliteTests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Johnsored", - isAdmin = true + isAdmin = true, + socialId = Some(UUID.randomUUID()) ), PersonCreator( firstName = None, lastName = "Odysseus", - isAdmin = false + isAdmin = false, + socialId = None ), PersonCreator( firstName = Some("Jorge"), lastName = "Masvidal", - isAdmin = true + isAdmin = true, + socialId = None ) ) personRepo.insertAll(newPeople) @@ -329,7 +342,8 @@ class SqliteTests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) personRepo.insert(p) personRepo.count @@ -340,7 +354,8 @@ class SqliteTests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) try transact(dataSource): @@ -357,13 +372,14 @@ class SqliteTests extends FunSuite: val p = PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) val update = sql"insert into $person ${person.insertColumns} values ($p)".update assertNoDiff( update.frag.sqlString, - "insert into person (first_name, last_name, is_admin) values (?, ?, ?)" + "insert into person (first_name, last_name, is_admin, social_id) values (?, ?, ?, ?)" ) val rowsInserted = update.run() assertEquals(rowsInserted, 1) @@ -379,7 +395,8 @@ class SqliteTests extends FunSuite: PersonCreator( firstName = Some("Chandler"), lastName = "Brown", - isAdmin = false + isAdmin = false, + socialId = Some(UUID.randomUUID()) ) ) val newIsAdmin = true @@ -424,19 +441,20 @@ class SqliteTests extends FunSuite: | first_name text, | last_name text not null, | is_admin integer not null, - | created text default(datetime()) + | created text default(datetime()), + | social_id varchar(36) |)""".stripMargin ) stmt.execute( - """insert into person (first_name, last_name, is_admin, created) values - |('George', 'Washington', true, datetime()), - |('Alexander', 'Hamilton', true, datetime()), - |('John', 'Adams', true, datetime()), - |('Benjamin', 'Franklin', true, datetime()), - |('John', 'Jay', true, datetime()), - |('Thomas', 'Jefferson', true, datetime()), - |('James', 'Madison', true, datetime()), - |(null, 'Nagro', false, datetime())""".stripMargin + """insert into person (first_name, last_name, is_admin, created, social_id) values + |('George', 'Washington', true, datetime(), 'd06443a6-3efb-46c4-a66a-a80a8a9a5388'), + |('Alexander', 'Hamilton', true, datetime(), '529b6c6d-7228-4da5-81d7-13b706f78ddb'), + |('John', 'Adams', true, datetime(), null), + |('Benjamin', 'Franklin', true, datetime(), null), + |('John', 'Jay', true, datetime(), null), + |('Thomas', 'Jefferson', true, datetime(), null), + |('James', 'Madison', true, datetime(), null), + |(null, 'Nagro', false, datetime(), null)""".stripMargin ) ).get ds