diff --git a/docs/reference.md b/docs/reference.md index f8c16a62..62b4b212 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -2241,7 +2241,7 @@ Basic `INSERT` operations or assigned to their default values ```scala -Buyer.insert.values( +Buyer.insert.columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 4 @@ -2287,7 +2287,7 @@ Buyer.select.filter(_.name `=` "test buyer") ```scala Buyer.insert - .values(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) + .columns(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) ``` @@ -3837,7 +3837,7 @@ Note that `.returning`/`RETURNING` is not supported in MySql, H2 or HsqlDB ```scala Buyer.insert - .values(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) + .columns(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) .returning(_.id) ``` @@ -3882,7 +3882,7 @@ exception if zero or multiple rows are returned. ```scala Buyer.insert - .values(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) + .columns(_.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09")) .returning(_.id) .single ``` @@ -4171,7 +4171,7 @@ MySql only supports `onConflictUpdate` but not `onConflictIgnore`. ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -4200,7 +4200,7 @@ Buyer.insert ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -4232,7 +4232,7 @@ Buyer.insert ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 4 // This should cause a primary key conflict @@ -4264,7 +4264,7 @@ ScalaSql's `.onConflictUpdate` translates into SQL's `ON CONFLICT DO UPDATE` ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -4315,7 +4315,7 @@ Buyer.select ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -4366,7 +4366,7 @@ Buyer.select ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -7925,7 +7925,7 @@ Basic operations on all the data types that ScalaSql supports mapping between Da ```scala -DataTypes.insert.values( +DataTypes.insert.columns( _.myTinyInt := value.myTinyInt, _.mySmallInt := value.mySmallInt, _.myInt := value.myInt, @@ -7974,7 +7974,7 @@ DataTypes.select ```scala -NonRoundTripTypes.insert.values( +NonRoundTripTypes.insert.columns( _.myOffsetDateTime := value.myOffsetDateTime, _.myZonedDateTime := value.myZonedDateTime ) @@ -9008,7 +9008,7 @@ Expr("Hello").rpad(10, "xy") ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -9037,7 +9037,7 @@ Buyer.insert ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict @@ -9088,7 +9088,7 @@ Buyer.select ```scala Buyer.insert - .values( + .columns( _.name := "test buyer", _.dateOfBirth := LocalDate.parse("2023-09-09"), _.id := 1 // This should cause a primary key conflict diff --git a/docs/tutorial.md b/docs/tutorial.md index bd1b3a61..c0bb85e1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1073,7 +1073,7 @@ ScalaSql supports SQL `INSERT`s with multiple styles. You can insert a single row via `.insert.values`, passing the columns you want to insert (and leaving out any that the database would auto-populate) ```scala -val query = City.insert.values( // ID provided by database AUTO_INCREMENT +val query = City.insert.columns( // ID provided by database AUTO_INCREMENT _.name := "Sentosa", _.countryCode := "SGP", _.district := "South", @@ -1317,6 +1317,51 @@ with the most commonly-used functions built in, but it is expected that you will need to build up your own library of custom `Expr[T]` functions to to access less commonly used functions that are nonetheless still needed in your application +## Custom Type Mappings + +You can define custom `TypeMapper`s to support reading and writing values to +the database which are of a type not supported by ScalaSql. The example below +demonstrates how to define a custom `CityId` type, define an implicit `TypeMapper` +for it, and then `INSERT` it into the database and `SELECT` it out after. + +```scala +case class CityId(value: Int) +object CityId { + implicit def tm: TypeMapper[CityId] = new TypeMapper[CityId] { + def jdbcType: JDBCType = JDBCType.INTEGER + def get(r: ResultSet, idx: Int): CityId = new CityId(r.getInt(idx)) + def put(r: PreparedStatement, idx: Int, v: CityId): Unit = r.setInt(idx, v.value) + } +} + +case class City2[+T[_]]( + id: T[CityId], + name: T[String], + countryCode: T[String], + district: T[String], + population: T[Long] +) + +object City2 extends Table[City2]() { + initTableMetadata() + override def tableName: String = "city" +} +``` +```scala +db.run( + City2.insert.columns( + _.id := CityId(31337), + _.name := "test", + _.countryCode := "XYZ", + _.district := "district", + _.population := 1000000 + ) +) + +db.run(City2.select.filter(_.id === 31337).single) ==> + City2[Id](CityId(31337), "test", "XYZ", "district", 1000000) +``` + ## Customizing Table and Column Names ScalaSql allows you to customize the table and column names via overriding diff --git a/scalasql/src/query/Insert.scala b/scalasql/src/query/Insert.scala index 8a40cfdf..affb0e55 100644 --- a/scalasql/src/query/Insert.scala +++ b/scalasql/src/query/Insert.scala @@ -10,7 +10,7 @@ trait Insert[Q, R] extends WithExpr[Q] with scalasql.generated.Insert[Q, R] { def table: TableRef def qr: Queryable[Q, R] def select[C, R2](columns: Q => C, select: Select[C, R2]): InsertSelect[Q, C, R, R2] -// def values(f: Q*): InsertValues[Q, R] + def columns(f: (Q => Column.Assignment[_])*): InsertColumns[Q, R] def batched[T1](f1: Q => Column.ColumnExpr[T1])(items: Expr[T1]*): InsertColumns[Q, R] diff --git a/scalasql/test/src/WorldSqlTests.scala b/scalasql/test/src/WorldSqlTests.scala index 049bc812..96d05f13 100644 --- a/scalasql/test/src/WorldSqlTests.scala +++ b/scalasql/test/src/WorldSqlTests.scala @@ -1407,7 +1407,13 @@ object WorldSqlTests extends TestSuite { // +DOCS // ## Custom Type Mappings // + // You can define custom `TypeMapper`s to support reading and writing values to + // the database which are of a type not supported by ScalaSql. The example below + // demonstrates how to define a custom `CityId` type, define an implicit `TypeMapper` + // for it, and then `INSERT` it into the database and `SELECT` it out after. + // // ```scala + // case class CityId(value: Int) // object CityId { // implicit def tm: TypeMapper[CityId] = new TypeMapper[CityId] { // def jdbcType: JDBCType = JDBCType.INTEGER @@ -1431,12 +1437,16 @@ object WorldSqlTests extends TestSuite { // ``` db.run( City2.insert.columns( - City2[Id](CityId(31337), "test", "ccode", "district", 1000000) + _.id := CityId(31337), + _.name := "test", + _.countryCode := "XYZ", + _.district := "district", + _.population := 1000000 ) ) db.run(City2.select.filter(_.id === 31337).single) ==> - City2[Id](CityId(31337), "test", "ccode", "district", 1000000) + City2[Id](CityId(31337), "test", "XYZ", "district", 1000000) // -DOCS } test("customTableColumnNames") { diff --git a/scalasql/test/src/datatypes/DataTypesTests.scala b/scalasql/test/src/datatypes/DataTypesTests.scala index 465f159d..b5709e06 100644 --- a/scalasql/test/src/datatypes/DataTypesTests.scala +++ b/scalasql/test/src/datatypes/DataTypesTests.scala @@ -28,7 +28,7 @@ case class DataTypes[+T[_]]( myLocalDateTime: T[LocalDateTime], myInstant: T[Instant], myVarBinary: T[geny.Bytes], - myUUID: T[java.util.UUID], + myUUID: T[java.util.UUID] ) object DataTypes extends Table[DataTypes] {