ScalaQuery not creating DDL? - scala

I have an example DB in a scala object, but the tables are not created.
If I call the initialize and then I call the addEntries, I get a Table not existing exception by the JDBC layer. Where am I wrong?
object ExampleCompanyH2TestDb {
val Companies = new Table[(Int, Int, String, String, String, Double,Double)]("COMPANIES") {
def id = column[Int]("COMPANY_ID", O.PrimaryKey)
// This is the primary key column
def id2 = column[Int]("COMPANY_ID2")
def name = column[String]("COMPANY_NAME")
def country = column[String]("COUNTRY")
def city = column[String]("CITY")
def latitude = column[Double]("LATITUDE")
def longitude = column[Double]("LONGITUDE")
// Every table needs a * projection with the same type as the
table's type parameter
def * = id ~ id2 ~ name ~ country ~ city ~ latitude ~ longitude
}
// Connect to the database and execute the following block within a session
val db = Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver")
def initialize = {
db withSession {
// The session is never named explicitly. It is bound to the current
// thread as the threadLocalSession that we imported
(Companies.ddl).create
}
}
// Create the tables, including primary and foreign keys
def addEntries(entries: Iterable[ExampleCompany]) {
val asTuples = entries.map {
ExampleCompany.unapply
}.collect {
case Some(entry) => entry
}.toSeq
db withSession {
Companies.insertAll(asTuples: _*)
}
}

I have found the problem: the h2 in memory driver does not store data among the session, unless:
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.
is specified
(See http://www.h2database.com/html/features.html)

Related

Future and Option in for comprehension in slick

I pretty new in using slick and now I faced with the issue how to retrieve some data from two tables.
I have one table
class ExecutionTable(tag: Tag) extends Table[ExecTuple](tag, "execution") {
val id: Rep[String] = column[String]("id")
val executionDefinitionId: Rep[Long] = column[Long]("executionDefinitionId")
// other fields are omitted
def * = ???
}
and another table
class ServiceStatusTable(tag: Tag)
extends Table[(String, Option[String])](tag, "serviceStatus") {
def serviceId: Rep[String] = column[String]("serviceId")
def detail: Rep[String] = column[String]("detail")
def * = (serviceId, detail.?)
}
In Dao I convert data from this two tables to a business object
case class ServiceStatus(
id: String,
detail: Option[String] = None, //other fields
)
like this
private lazy val getServiceStatusCompiled = Compiled {
(id: Rep[String], tenantId: Rep[String]) =>
for {
exec <- getExecutionById(id, tenantId)
status <- serviceStatuses if exec.id === status.serviceId
} yield mapToServiceStatus(exec, status)
}
and later
def getServiceStatus(id: String, tenantId: String)
: Future[Option[ServiceStatus]] = db
.run(getServiceStatusCompiled(id, tenantId).result.transactionally)
.map(_.headOption)
The problem is that not for all entries from table execution exists entry in table serviceStatus. I cannot modify table execution and add to it field details as it is only service specific.
When I run query in case when for entry from execution exists entry in serviceStatus all works as expected. But if there is no entry in serviceStatus, Future[None] is returned.
Question: Is there any option to obtain status in for comprehension as Option depending on existing entry in table serviceStatus or some else workaround?
Usually, in case when join condition does not find corresponding record in the "right" table but the result should still contain the row from "left" table, left join is used.
In your case you can do something like:
Execution
.filter(...execution table filter...)
.joinLeft(ServiceStatus).on(_.id===_.serviceId)
This gives you pair of
(Execution, Rep[Option[ServiceStatus]])
and after query execution:
(Execution, Option[ServiceStatus])

slick - creating schema with optional blob getting ERROR: type "lo" does not exist

I am using Play Evaluation with postgreSQL and slick
This is the Model :
case class Employee(id: Int,first:String,last:String,title:String,pic: Option[Blob])
class Employees(tag:Tag) extends Table[Employee](tag,"EMPLOYEES"){
def id = column[Int]("ID",O.PrimaryKey, O.NotNull)
def first = column[String]("LAST", O.NotNull)
def last = column[String]("LAST", O.NotNull)
def title = column[String]("TITLE")
def pic = column[Blob]("PICTURE",O.Nullable)
override def * = (id,first,last,title,pic.?)<> (Employee.tupled,Employee.unapply)
}
But I am getting this
ERROR: type "lo" does not exist
the sql created shows :
create table "EMPLOYEES" ("ID" INTEGER NOT NULL PRIMARY KEY,"LAST" VARCHAR(254) NOT NULL,"TITLE" VARCHAR(254) NOT NULL,"PICTURE" lo);
where is this "lo" type came from and how can I fix it ?
can you please put below two implicit method in your class Employees and try?
private implicit def blob2Bytes(blob: java.sql.Blob): Array[Byte] = {
if (blob.length == 0)
return Array[Byte]()
val r = blob.getBytes(1, blob.length.toInt)
blob.free
r
}
private implicit def bytes2Blob(bytes: Array[Byte]) = {
new SerialBlob(bytes)
}

Slick custom codegen output empty?

I have previously used the codegen for slick like this:
scala.slick.codegen.SourceCodeGenerator.main(
Array("com.typesafe.slick.driver.ms.SQLServerDriver",
"net.sourceforge.jtds.jdbc.Driver",
"jdbc:jtds:sqlserver://10.0.1.12:1433;databaseName=master;",
"/home/bipin/slickcode", "demo", "user", "pass") )
It generates the classes which can connect to the database. But I don't need all the tables just a few of them. So I used a custom slick code generator as shown below (taken from https://github.com/slick/slick-codegen-customization-example/blob/master/codegen/CustomizedCodeGenerator.scala and modified):
import scala.slick.model.Model
import scala.slick.jdbc.meta.createModel
import com.typesafe.slick.driver.ms.SQLServerDriver
object CustomizedCodeGenerator{
def main(args: Array[String]) = {
codegen.writeToFile(
"com.typesafe.slick.driver.ms.SQLServerDriver",
"/home/bipin/slickcode",
"demo"
)
}
val db = SQLServerDriver.simple.Database.forURL(
"jdbc:jtds:sqlserver://10.0.1.12:1433;databaseName=master;user=user;password=pass;",
driver="net.sourceforge.jtds.jdbc.Driver")
// filter out desired tables
val included = Seq("Table1","Table2","T3","T4")
val model = db.withSession{ implicit session =>
val tables = SQLServerDriver.getTables.list.filter(t => included contains t.name.name)
tables.foreach(println);
createModel( tables, SQLServerDriver )
}
val codegen = new scala.slick.codegen.SourceCodeGenerator(model){
// customize Scala entity name (case class, etc.)
override def entityName = dbTableName => dbTableName match {
case "COFFEES" => "Coffee"
case _ => super.entityName(dbTableName)
}
// customize Scala table name (table class, table values, ...)
override def tableName = dbTableName => dbTableName match {
case "COF_INVENTORY" => "CoffeeInventory"
case _ => super.tableName(dbTableName)
}
// override generator responsible for tables
override def Table = new Table(_){
table =>
// customize table value (TableQuery) name (uses tableName as a basis)
override def TableValue = new TableValue{
override def rawName = super.rawName.uncapitalize
}
// override generator responsible for columns
override def Column = new Column(_){
// customize Scala column names
override def rawName = (table.model.name.table,this.model.name) match {
case ("T3","#Column1") => "Column1"
case _ => super.rawName
}
}
}
}
}
But when I run this, the output file does not have any table ddl.
/** DDL for all tables. Call .create to execute. */
lazy val ddl =
Can anyone tell what am I doing wrong and how to fix it. Thanks

Slick: How does autoInc work in the MultiDBCakeExample example?

I'm trying to understand how Slick works and how to use it... and looking at their examples in GitHub I ended up with this code snippet in MultiDBCakeExample.scala:
trait PictureComponent { this: Profile => //requires a Profile to be mixed in...
import profile.simple._ //...to be able import profile.simple._
object Pictures extends Table[(String, Option[Int])]("PICTURES") {
...
def * = url ~ id
val autoInc = url returning id into { case (url, id) => Picture(url, id) }
def insert(picture: Picture)(implicit session: Session): Picture = {
autoInc.insert(picture.url)
}
}
}
I suppose the * method returns a row in the table, while autoInc should somehow provide functionality for automatically incrementing the entity id... but to be honest I've some trouble in understanding this piece of code. What does returning refer to? What does autoInc return?
I looked at the Slick documentation but I was unable to find helpful information. Any help would be really appreciated ;-)
Because that autoInc can be confusing I will provide you a working example (please note that my DB is PostgreSQL so I need that hack with forInsert in order to make Postgresql driver increment auto-inc values).
case class GeoLocation(id: Option[Int], latitude: Double, longitude: Double, altitude: Double)
/**
* Define table "geo_location".
*/
object GeoLocations extends RichTable[GeoLocation]("geo_location") {
def latitude = column[Double]("latitude")
def longitude = column[Double]("longitude")
def altitude = column[Double]("altitude")
def * = id.? ~ latitude ~ longitude ~ altitude <> (GeoLocation, GeoLocation.unapply _)
def forInsert = latitude ~ longitude ~ altitude <> ({ (lat, long, alt) => GeoLocation(None, lat, long, alt) },
{ g: GeoLocation => Some((g.latitude, g.longitude, g.altitude)) })
}
My RichTable is an abstract class in order to not declare ids for each Table I have but just extend this:
abstract class RichTable[T](name: String) extends Table[T](name) {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
val byId = createFinderBy(_.id)
}
and use it like:
GeoLocations.forInsert.insert(GeoLocation(None, 22.23, 25.36, 22.22))
Since you pass None for id, it will be generated automatically by PostgreSql driver when Slick inserts this new entity.
I have a few weeks since I started with Slick and I really recommend it!
UPDATE: If you want to not use forInsert projections, another approach is the following - in my case the entity is Address.
Create sequence for each table on schema creation:
session.withTransaction {
DBSchema.tables.drop
DBSchema.tables.create
// Create schemas to generate ids too.
Q.updateNA("create sequence address_seq")
}
Define a method to generate the ids using sequences (I defined this once in RichTable class:
def getNextId(seqName: String) = Database { implicit db: Session =>
Some((Q[Int] + "select nextval('" + seqName + "_seq') ").first)
}
and in the mapper override insert method like:
def insert(model : Address) = Database { implicit db: Session =>
*.insert(model.copy(id = getNextId(classOf[Address].getSimpleName())))
}
And now, you can pass None when you do an insert and this methods will do a nice work for you...

Using Auto Incrementing fields with PostgreSQL and Slick

How does one insert records into PostgreSQL using AutoInc keys with Slick mapped tables? If I use and Option for the id in my case class and set it to None, then PostgreSQL will complain on insert that the field cannot be null. This works for H2, but not for PostgreSQL:
//import scala.slick.driver.H2Driver.simple._
//import scala.slick.driver.BasicProfile.SimpleQL.Table
import scala.slick.driver.PostgresDriver.simple._
import Database.threadLocalSession
object TestMappedTable extends App{
case class User(id: Option[Int], first: String, last: String)
object Users extends Table[User]("users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def first = column[String]("first")
def last = column[String]("last")
def * = id.? ~ first ~ last <> (User, User.unapply _)
def ins1 = first ~ last returning id
val findByID = createFinderBy(_.id)
def autoInc = id.? ~ first ~ last <> (User, User.unapply _) returning id
}
// implicit val session = Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver").createSession()
implicit val session = Database.forURL("jdbc:postgresql:test:slicktest",
driver="org.postgresql.Driver",
user="postgres",
password="xxx")
session.withTransaction{
Users.ddl.create
// insert data
print(Users.insert(User(None, "Jack", "Green" )))
print(Users.insert(User(None, "Joe", "Blue" )))
print(Users.insert(User(None, "John", "Purple" )))
val u = Users.insert(User(None, "Jim", "Yellow" ))
// println(u.id.get)
print(Users.autoInc.insert(User(None, "Johnathan", "Seagul" )))
}
session.withTransaction{
val queryUsers = for {
user <- Users
} yield (user.id, user.first)
println(queryUsers.list)
Users.where(_.id between(1, 2)).foreach(println)
println("ID 3 -> " + Users.findByID.first(3))
}
}
Using the above with H2 succeeds, but if I comment it out and change to PostgreSQL, then I get:
[error] (run-main) org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
This is working here:
object Application extends Table[(Long, String)]("application") {
def idlApplication = column[Long]("idlapplication", O.PrimaryKey, O.AutoInc)
def appName = column[String]("appname")
def * = idlApplication ~ appName
def autoInc = appName returning idlApplication
}
var id = Application.autoInc.insert("App1")
This is how my SQL looks:
CREATE TABLE application
(idlapplication BIGSERIAL PRIMARY KEY,
appName VARCHAR(500));
Update:
The specific problem with regard to a mapped table with User (as in the question) can be solved as follows:
def forInsert = first ~ last <>
({ (f, l) => User(None, f, l) }, { u:User => Some((u.first, u.last)) })
This is from the test cases in the Slick git repository.
I tackled this problem in an different way. Since I expect my User objects to always have an id in my application logic and the only point where one would not have it is during the insertion to the database, I use an auxiliary NewUser case class which doesn't have an id.
case class User(id: Int, first: String, last: String)
case class NewUser(first: String, last: String)
object Users extends Table[User]("users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def first = column[String]("first")
def last = column[String]("last")
def * = id ~ first ~ last <> (User, User.unapply _)
def autoInc = first ~ last <> (NewUser, NewUser.unapply _) returning id
}
val id = Users.autoInc.insert(NewUser("John", "Doe"))
Again, User maps 1:1 to the database entry/row while NewUser could be replaced by a tuple if you wanted to avoid having the extra case class, since it is only used as a data container for the insert invocation.
EDIT:
If you want more safety (with somewhat increased verbosity) you can make use of a trait for the case classes like so:
trait UserT {
def first: String
def last: String
}
case class User(id: Int, first: String, last: String) extends UserT
case class NewUser(first: String, last: String) extends UserT
// ... the rest remains intact
In this case you would apply your model changes to the trait first (including any mixins you might need), and optionally add default values to the NewUser.
Author's opinion: I still prefer the no-trait solution as it is more compact and changes to the model are a matter of copy-pasting the User params and then removing the id (auto-inc primary key), both in case class declaration and in table projections.
We're using a slightly different approach. Instead of creating a further projection, we request the next id for a table, copy it into the case class and use the default projection '*' for inserting the table entry.
For postgres it looks like this:
Let your Table-Objects implement this trait
trait TableWithId { this: Table[_] =>
/**
* can be overriden if the plural of tablename is irregular
**/
val idColName: String = s"${tableName.dropRight(1)}_id"
def id = column[Int](s"${idColName}", O.PrimaryKey, O.AutoInc)
def getNextId = (Q[Int] + s"""select nextval('"${tableName}_${idColName}_seq"')""").first
}
All your entity case classes need a method like this (should also be defined in a trait):
case class Entity (...) {
def withId(newId: Id): Entity = this.copy(id = Some(newId)
}
New entities can now be inserted this way:
object Entities extends Table[Entity]("entities") with TableWithId {
override val idColName: String = "entity_id"
...
def save(entity: Entity) = this insert entity.withId(getNextId)
}
The code is still not DRY, because you need to define the withId method for each table. Furthermore you have to request the next id before you insert an entity which might lead to performance impacts, but shouldn't be notable unless you insert thousands of entries at a time.
The main advantage is that there is no need for a second projection what makes the code less error prone, in particular for tables having many columns.
The simplest solution was to use the SERIAL type like this:
def id = column[Long]("id", SqlType("SERIAL"), O.PrimaryKey, O.AutoInc)
Here's a more concrete block:
// A case class to be used as table map
case class CaseTable( id: Long = 0L, dataType: String, strBlob: String)
// Class for our Table
class MyTable(tag: Tag) extends Table[CaseTable](tag, "mytable") {
// Define the columns
def dataType = column[String]("datatype")
def strBlob = column[String]("strblob")
// Auto Increment the id primary key column
def id = column[Long]("id", SqlType("SERIAL"), O.PrimaryKey, O.AutoInc)
// the * projection (e.g. select * ...) auto-transforms the tupled column values
def * = (id, dataType, strBlob) <> (CaseTable.tupled, CaseTable.unapply _)
}
// Insert and get auto incremented primary key
def insertData(dataType: String, strBlob: String, id: Long = 0L): Long = {
// DB Connection
val db = Database.forURL(jdbcUrl, pgUser, pgPassword, driver = driverClass)
// Variable to run queries on our table
val myTable = TableQuery[MyTable]
val insert = try {
// Form the query
val query = myTable returning myTable.map(_.id) += CaseTable(id, dataType, strBlob)
// Execute it and wait for result
val autoId = Await.result(db.run(query), maxWaitMins)
// Return ID
autoId
}
catch {
case e: Exception => {
logger.error("Error in inserting using Slick: ", e.getMessage)
e.printStackTrace()
-1L
}
}
insert
}
I've faced the same problem trying to make the computer-database sample from play-slick-3.0 when I changed the db to Postgres. What solved the problem was to change the id column (primary key) type to SERIAL in the evolution file /conf/evolutions/default/1.sql (originally was in BIGINT). Take a look at https://groups.google.com/forum/?fromgroups=#%21topic/scalaquery/OEOF8HNzn2U
for the whole discussion.
Cheers,
ReneX
Another trick is making the id of the case class a var
case class Entity(var id: Long)
To insert an instance, create it like below
Entity(null.asInstanceOf[Long])
I've tested that it works.
The solution I've found is to use SqlType("Serial") in the column definition. I haven't tested it extensively yet, but it seems to work so far.
So instead of
def id: Rep[PK[SomeTable]] = column[PK[SomeTable]]("id", O.PrimaryKey, O.AutoInc)
You should do:
def id: Rep[PK[SomeTable]] = column[PK[SomeTable]]("id", SqlType("SERIAL"), O.PrimaryKey, O.AutoInc)
Where PK is defined like the example in the "Essential Slick" book:
final case class PK[A](value: Long = 0L) extends AnyVal with MappedTo[Long]