Database connectors: delete support and native SQL queries

Delete rows through TaxiQL with @DeleteOperation, and run hand-written SQL - joins, aggregations, dialect-specific statements - with @SqlQuery.

NextDue to be released in v0.38.0
Available since 0.38.0-M2

Two additions to our database connectors in this release: deleting rows through TaxiQL, and running SQL you’ve written yourself.

Deleting rows

Databases connected to Orbital have long supported reads, inserts, updates and upserts. Deletes now join the family - annotate a write operation with @DeleteOperation:

@DatabaseService(connection = "customers-database")service CustomerService {   @DeleteOperation   write operation deleteCustomer(Customer):DeleteResult
   @DeleteOperation   write operation deleteCustomers(Customer[]):DeleteResult}

Rows are matched on the model’s @Id fields (composite keys work too - rows match on the combination), and you can pass a single instance or a whole array. Invoking it looks like any other mutation:

given {   customers : Customer[] = [      { id : 123 },      { id : 456 }   ]}call CustomerService::deleteCustomers

You get back a deletedCount telling you how many rows went away. Deletes run in a single transaction, so a failure partway through rolls the whole thing back rather than leaving you half-deleted.

Native SQL queries

Orbital’s table operations cover most day-to-day querying, and keep your queries portable across databases. But sometimes you just want to write the SQL yourself - a join, an aggregation, or something dialect-specific. The new @SqlQuery annotation is that escape hatch:

@DatabaseService(connection = "films-database")service FilmStats {   @SqlQuery(sql = """      SELECT d.name AS director, COUNT(f.id) AS filmCount      FROM film f      JOIN director d ON f.director_id = d.id      GROUP BY d.name   """)   operation filmsPerDirector() : DirectorFilmSummary[]}

The SQL is passed to your database exactly as written, and result columns map back to your Taxi model’s fields by name or alias. One annotation covers both directions: a plain operation runs the SQL as a query, while a write operation runs it as a statement (UPDATE, DELETE, INSERT) and returns the number of rows affected.

Operation parameters bind into the SQL with the :parameterName syntax:

@SqlQuery(sql = "SELECT title, release_year AS releaseYear FROM film WHERE release_year > :year")operation recentFilms(year : ReleaseYear) : Film[]

Values are always bound as prepared-statement parameters - never spliced into the SQL text - so user-supplied values can’t alter your query.

Both features work across all of Orbital’s SQL database connectors. Full details are in the database docs, including native query behaviour notes.