0

I have this table in H2:

CREATE TABLE computer (id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, introduced TIMESTAMP, discontinued TIMESTAMP, company_id BIGINT, CONSTRAINT pk_computer PRIMARY KEY (id));
CREATE SEQUENCE computer_seq START WITH 1000;

Slick auto generated class for it:

  case class ComputerRow(id: Long, name: String, introduced: Option[java.sql.Timestamp], discontinued: Option[java.sql.Timestamp], companyId: Option[Long])
  /** GetResult implicit for fetching ComputerRow objects using plain SQL queries */
  implicit def GetResultComputerRow(implicit e0: GR[Long], e1: GR[String], e2: GR[Option[java.sql.Timestamp]], e3: GR[Option[Long]]): GR[ComputerRow] = GR{
    prs => import prs._
    ComputerRow.tupled((<<[Long], <<[String], <<?[java.sql.Timestamp], <<?[java.sql.Timestamp], <<?[Long]))
  }
  /** Table description of table COMPUTER. Objects of this class serve as prototypes for rows in queries. */
  class Computer(tag: Tag) extends Table[ComputerRow](tag, "COMPUTER") {
    def * = (id, name, introduced, discontinued, companyId) <> (ComputerRow.tupled, ComputerRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    def ? = (id.?, name.?, introduced, discontinued, companyId).shaped.<>({r=>import r._; _1.map(_=> ComputerRow.tupled((_1.get, _2.get, _3, _4, _5)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

    /** Database column ID PrimaryKey */
    val id: Column[Long] = column[Long]("ID", O.PrimaryKey)
    /** Database column NAME  */
    val name: Column[String] = column[String]("NAME")
    /** Database column INTRODUCED  */
    val introduced: Column[Option[java.sql.Timestamp]] = column[Option[java.sql.Timestamp]]("INTRODUCED")
    /** Database column DISCONTINUED  */
    val discontinued: Column[Option[java.sql.Timestamp]] = column[Option[java.sql.Timestamp]]("DISCONTINUED")
    /** Database column COMPANY_ID  */
    val companyId: Column[Option[Long]] = column[Option[Long]]("COMPANY_ID")

    /** Foreign key referencing Company (database name FK_COMPUTER_COMPANY_1) */
    lazy val companyFk = foreignKey("FK_COMPUTER_COMPANY_1", companyId, Company)(r => r.id, onUpdate=ForeignKeyAction.Restrict, onDelete=ForeignKeyAction.Restrict)
  }
  /** Collection-like TableQuery object for table Computer */
  lazy val Computer = new TableQuery(tag => new Computer(tag))

I can't seem to be able to insert a new row, however. The attempt below

val row = ("name", new Timestamp((new Date).getTime), new Timestamp((new Date).getTime), 123)

DB.withDynSession {
    Computer.map( r =>
        (r.name, r.introduced, r.discontinued, r.companyId)
    ) += row
}

Throws an error

[JdbcSQLException: NULL not allowed for column "ID"; SQL statement: INSERT INTO "COMPUTER" ("NAME","INTRODUCED","DISCONTINUED","COMPANY_ID") VALUES (?,?,?,?) [23502-175]]

The same approach works with MySQL and PostgreSQL, so I'm guessing that H2 doesn't have the same primary ID auto increment functionality? How do I make my insert work with slick then?

This is a working example of the same table with Anorm:

DB.withConnection { implicit connection =>
    SQL(
        """
            insert into computer values (
                (select next value for computer_seq), 
                {name}, {introduced}, {discontinued}, {company_id}
            )
        """
    ).on(
        'name -> "name",
        'introduced -> new Timestamp((new Date).getTime),
        'discontinued -> new Timestamp((new Date).getTime),
        'company_id -> 123
    ).executeUpdate()
}
Caballero
  • 11,546
  • 22
  • 103
  • 163

2 Answers2

1

As far as I can tell this is not a Slick problem, but your DDL statement doesn't specify auto increment for H2. Check the play-slick SQL code for the sample project: https://github.com/playframework/play-slick/blob/master/samples/computer-database/conf/evolutions/default/1.sql

Also check the H2 docs: http://www.h2database.com/html/grammar.html#column_definition

cvogt
  • 11,260
  • 30
  • 46
0

Try giving the case class a default value for id, like this:

case class ComputerRow(id: Long = 0, name: String, introduced: Option[java.sql.Timestamp], discontinued: Option[java.sql.Timestamp], companyId: Option[Long])

I'm not sure for H2 but when I'm working with Postgres I usually specify the default for the id field to comply to eventual checks form Slick, then on the database side the value insert is handled automatically by the sequence.

Edit:

I may be wrong on this but I noticed that you create a sequence without assigning it:

CREATE TABLE computer (id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, introduced TIMESTAMP, discontinued TIMESTAMP, company_id BIGINT, CONSTRAINT pk_computer PRIMARY KEY (id));
CREATE SEQUENCE computer_seq START WITH 1000;

In Postgres you create sequence and then assign them Like this:

create table users (
  id  bigint not null
);

create sequence users_seq;
alter table users alter column id set default nextval('users_seq');

in H2 as far as I can see this is how you assign an auto increment:

create table test(id bigint auto_increment, name varchar(255));

Taken form this SO question.

Community
  • 1
  • 1
Ende Neu
  • 15,581
  • 5
  • 57
  • 68
  • I'd prefer not to touch that, because it's auto generated by Slick. Surely, there should be a more consistent way of doing this. – Caballero Jun 13 '14 at 11:21
  • I added something that could be relevant. – Ende Neu Jun 13 '14 at 11:24
  • The create query is not written by me, it's taken from this sample: https://github.com/playframework/playframework/tree/master/samples/scala/computer-database – Caballero Jun 13 '14 at 11:36
  • One other thing is, if a column has auto increment the slick code generator adds a `O.AutoInc` to the field, your id columns should look like `val id: Column[Long] = column[Long]("ID", O.PrimaryKey, O.AutoInc)` at least in Postgres, unfortunately I'm no `H2` user and I cannot help you further, probably you could try if the autoinc works using plain SQL, doing some inserts for example. Also the sql statement to insert has `(select next value for computer_seq)` which manually selects the next value form the sequence and uses it as id. – Ende Neu Jun 13 '14 at 11:42