ldbc v0.3.0-beta9 is released.
This release adds enhancements to existing features.
Note
ldbc is pre-1.0 software and is still undergoing active development. New versions are not binary compatible with prior versions, although in most cases user code will be source compatible.
The major version will be the stable version.
What's Changed
Caution
This version is not compatible with the previous version, v0.3.0-beta8.
Adding Column Annotation
Table generation using Deriving used the property name as it is as the column name.
case class City(
id: Int,
name: String,
countryCode: String,
district: String,
population: Int
) derives Table
The formatting of the column names could be adjusted by implicitly passing Naming.
given Naming = Naming.PASCAL
However, this alone could not address all cases. For example, the following column names cannot be used in the previous implementation.
CREATE TABLE city (
ID Bigint
CountryID Bigint
)
Naming supports only certain formats (CAMEL, PASCAL, and SNAKE cases) and does not support column names that are all uppercase or only uppercase in certain parts of the column name.
Such cases can be resolved by using column annotations.
case class City(
@Column("ID") id: Int,
name: String,
countryCode: String,
district: String,
population: Int
) derives Table
The overall formatting is done by Naming, and changes to only specific columns can be made using this annotation, which allows for flexible customization of column names.
Support for INSERT SELECT/JOIN
There are two ways to pass values in an INSERT statement: by VALUES or by using a SELECT statement or JOIN.
This modification supports both syntaxes.
TableQuery[City]
.insertInto(city => city. id *: city. name)
.select(
TableQuery[Country]
.select(country => country. id *: city. name)
)
The same can be defined for using the records resulting from a JOIN.
TableQuery[City]
.insertInto(city => city. id *: city. name)
.select(
TableQuery[Country]
.join(TableQuery[City])
.on((country, city) => country. id === city. countryId)
.select((country, city) => country. id *: city. name)
.where((country, city) => city. population > 1000000)
)
Breaking Change
Change column refinement method
So far, column refinement has simply grouped the columns used as Tuple.
cityTable.select(city => (city.id, city.name))
However, there was a problem with this. Column is a type with a single type parameter. Until Scala2, there was a limit to the number of Tuples that could be passed. Scala 2 had a limit on the number of Tuples, so it was necessary to create a boilerplate or something that could handle all the number of Tuples.
In this case, dynamic Tuples are treated as Tuples or Tuple.Map, so if you wanted to access a Column type, you had to cast the type using asInstanceOf
because its type can only be treated as a Tuple.
Casting the type would of course lose its type safety and complicate the code.
We decided to adopt twiddles, one of the same TypeLevel projects, to solve this problem.
Columns can be more easily composited by using twiddles.
cityTable.select(city => city.id *: city.name)
Composition itself is implementor-friendly because it can be done using *:
, and this eliminates the need for unsafe typecasting since the internal code can just use Column[T] instead of Tuple.
Twiddles also make it easy to convert the composite result to another type.
case class City(id: Long, name: String)
def id: Column[Int] = column[Int]("ID")
def name: Column[String] = column[String]("Name")
def city: Column[City] = (id *: name).to[City]
Change from Table to TableQuery
Until now, the same Table type has been used to construct queries using the Table type and the Table type with Table information from the model.
case class City(id: Long, name: String) derives Table
val cityTable = Table[City]
Because the same type was used to represent two things, it was easy to implement incorrectly.
cityTable.select(city => city.insert(???))
Development tools such as IDEs could cause no small amount of confusion to implementers because they complement all available APIs.
case class City(id: Long, name: String) derives Table
val cityTable = TableQuery[City]
To solve this problem, we created a new type called TableQuery and separated the implementation of query construction.
Changes to Insert
The Insert statement is similarly modified so that columns are composited and inserted columns are specified.
cityTable
- .insertInto(city => (city.id, city.name))
+ .insertInto(city => city.id *: city.name)
.values((1L, "Tokyo"))
Changes to Update
Previously, the Update Statement had to be set up one by one for each column to be updated. This implementation is convenient when you want to update a few columns individually, but it is very cumbersome to write set
for each additional column you want to update.
cityTable
.update("id", 1L)
.set("name", "Tokyo")
.set("population", 1, false)
With this update, columns can now be combined, allowing multiple columns to be specified together for update processing.
cityTable
.update(city => city.id *: city.name)((1L, "Tokyo"))
.set(_.population, 1, false)
With this update, columns can now be combined, allowing multiple columns to be specified together for update processing. It is still possible to update only specific columns using set
. In addition, since set
can be used to set conditions for updating, it is possible to create a query that updates additional columns only when the conditions are positive.
Changes to Join
Previously, table joins were constructed by setting the join condition as the second argument.
cityTable.join(countryTable)((c, co) => c.countryCode === co.code)
From this change, the conditions for joining tables must be set via the on
API. This change is the result of an internal implementation change.
cityTable.join(countryTable).on((c, co) => c.countryCode === co.code)
💪 Enhancement
- Enhancement/2024 09 raises errors in fields that cannot be decoded by @takapi327 in #306
- Enhancement/2024 10 added support tuple class mapping by @takapi327 in #307
- Refactor/2024 10 renewal schema by @takapi327 in #329
🪲 Bug Fixes
- Delete cardinality property by @takapi327 in #310
🔧 Refactoring
- Rename scala34 -> scala35 by @takapi327 in #309
- Refactor/2024 07 renewal of documentation by @takapi327 in #253
📖 Documentation
- Documentation/2024 06 change document liblay to laika by @takapi327 in #252
- Documentation/2024 06 fixed document by @takapi327 in #247
- Feature/2024 09 support versioned documentation by @takapi327 in #308
- Document/2024 10 migrate v0.2 document by @takapi327 in #316
⛓️ Dependency update
- Update mdoc, sbt-mdoc from 2.5.4 to 2.6.1 by @scala-steward in #294
- Dependencies/2024 09 update scala version by @takapi327 in #303
- Dependencies/2024 10 update sbt typelevel by @takapi327 in #305
- Update slick from 3.5.1 to 3.5.2 by @scala-steward in #304
- Update specs2-core, specs2-junit from 5.5.3 to 5.5.8 by @scala-steward in #311
- Update sbt-typelevel, ... from 0.7.3 to 0.7.4 by @scala-steward in #312
- Update HikariCP from 5.1.0 to 6.0.0 by @scala-steward in #297
- Update sbt, scripted-plugin from 1.10.2 to 1.10.3 by @scala-steward in #318
- Dependencies/2024 10 update scala version by @takapi327 in #315
- Update sbt, scripted-plugin from 1.10.3 to 1.10.4 by @scala-steward in #319
- Update cats-effect from 3.5.4 to 3.5.5 by @scala-steward in #320
- Update otel4s-core-trace from 0.10.0 to 0.11.0 by @scala-steward in #321
- Update sbt, scripted-plugin from 1.10.4 to 1.10.5 by @scala-steward in #323
- Update HikariCP from 6.0.0 to 6.1.0 by @scala-steward in #322
- Update otel4s-core-trace from 0.11.0 to 0.11.1 by @scala-steward in #326
- Update cats-effect from 3.5.5 to 3.5.6 by @scala-steward in #328
- Update cats-effect from 3.5.6 to 3.5.7 by @scala-steward in #330
- Update sbt, scripted-plugin from 1.10.5 to 1.10.6 by @scala-steward in #331
Full Changelog: v0.3.0-beta8...v0.3.0-beta9