Primitive types to AnyRef in Scala - scala

I am adding new features to an open source project(pillar) to migrate Cassandra tables. I have a problem in operation that insert values a new table.
There is a table in Cassandra:
create table customer(
name text,
age int,
point int,
primary key(name, age)
)
I want to migrate from this table to test_person table.
create table test_person (
name text,
surname text,
point int,
city text,
primary key(name)
)
Here is an operation:
var s: PreparedStatement = session.prepare("insert into test_person (name, age, point) values (?, ?, ?)");
var r: Row = session.execute("select * from customer").one()
var arr: Array[AnyRef] = new Array[AnyRef](3)
arr(0) = row.getObject("name")
arr(1) = row.getObject("age")
arr(2) = row.getObject("point")
session.execute(s.bind(arr))
This is error message:
Type mismatch Can't assign primitive value to object.
I got as object and assign an array typed of AnyRef. What is wrong?
How can I handle this

This is happening because there is an implicit conversion happening from java.lang.Integer to Int. And Int is of type AnyVal not an AnyRef. Try using Array[Any] instead of Array[AnyRef] OR You can disable the implicit conversion by import scala.Predef.{Integer2int => _}
// This method in Predef.scala is causing the conversion
implicit def Integer2int(x: java.lang.Integer): Int

That is because, AnyRef is for objects and AnyVal is for primitives. You can use an Array[Any] in your case:
var s: PreparedStatement = session.prepare("insert into test_person (name, age, point) values (?, ?, ?)");
var r: Row = session.execute("select * from customer").one()
val arr = Array(r.getString("name"), r.getInt("age"), r.getInt("point"))

Related

How to update table query in Slick

How can I convert Query[MappedProjection[Example, (Option[String], Int, UUID, UUID)], Example, Seq] to Query[Examples, Example, Seq]?
Details
I am trying to drop a column from an existing table(Examples in this case) and move the data to another table (Examples2 in this case). I don't want to change all the existing code base, so I plan to join these two tables and map the results to Example.
import slick.lifted.Tag
import slick.driver.PostgresDriver.api._
import java.util.UUID
case class Example(
field1: Option[String] = None,
field2: Int,
someForeignId: UUID,
id: UUID,
)
object Example
class Examples(tag: Tag) extends Table[Example](tag, "entityNotes") {
def field1 = column[Option[String]]("field1")
def field2 = column[Int]("field2")
def someForeignId = column[UUID]("someForeignId")
def id = column[UUID]("id", O.PrimaryKey)
def someForeignKey = foreignKey(
"someForeignIdToExamples2",
someForeignId,
Examples2.query,
)(
_.id.?
)
def * =
(
field1.?,
field2,
someForeignId,
id,
) <> ((Example.apply _).tupled, Example.unapply)
}
object Examples{
val query = TableQuery[Examples]
}
Basically, all the functions in the codebase call Examples.query. If I update that query by joining two tables, the problem will be solved (of course with a performance shortcoming because of one extra join for each call).
To use the query with the existing code base, we need to keep the type the same. For example, we we can use filter as follows:
val query_ = TableQuery[Examples]
val query: Query[Examples, Example, Seq] = query_.filter(_.field2 > 5)
Everything will work without a problem since we keep the type of the query as it is supposed to be.
However, I cannot do that with a join if I want to use data from the second table.
val query_ = TableQuery[Examples]
val query = query
.join(Examples2.query_)
.on(_.someForeignId === _.id)
.map({
case (e, e2) =>
((
e2.value.?,
e1.field2,
e2.id
e.id,
) <> ((Example.apply _).tupled, Example.unapply))
})
This is where I got stuck. Its type is Query[MappedProjection[Example, (Option[String], Int, UUID, UUID)], Example, Seq].
Can anyone help? Btw, we don't have to use map. This is just what I got so far.

postgresql jdbc, pgobject available types, array type

I use postgresql 9.5 with jdbc driver 9.4.1208.jre7 and scalikejdbc wrapper
My table is:
CREATE TABLE refs
(
name character varying NOT NULL,
custom json,
prices integer[]
)
I can insert json values using org.postgresql.util.PGobject:
val res = new PGobject()
res.setType("json")
res.setValue("{'name': 'test'}")
res
I also want to insert arrays. How can i do this? I thought that this would work:
def toArrStr(a: Iterable[String]): PGobject = {
val res = new PGobject()
res.setType("ARRAY")
res.setValue(s"{${a.map(i => '"' + i + '"').mkString(",")}}")
res
}
But it gives me exception: org.postgresql.util.PSQLException: Unknown type ARRAY
May be i'm missing smth but i can't find good docs about PGObject class. I think that PGObject class was designed exactly for purposes like mine but it doesn't behaves as expected
POSTGRES has many types, not only array but date, datetime, daterange, timestamprange, so on. I believe that there should be type names for its corresponding types.
I understood how to save character list[] using PGObject:
def toArrStr(a: Iterable[String]): PGobject = {
val res = new PGobject()
res.setType("varchar[]")
res.setValue(s"{${a.map(i => '"' + i + '"').mkString(",")}}")
res
}
To save array of numbers:
where size is 2,4,8 (smallint, int, bigint)
def toArrNum(a: Iterable[AnyVal], size: Int): PGobject = {
val res = new PGobject()
res.setType(s"int$size[]")
res.setValue(s"{${a.mkString(",")}}")
res
}

Join on two foreign keys from same table in scalikejdbc

So i have a one table that has two FK that points at same table.
For example:
Message table with columns sender and receiver that both references id in user table.
When i'm writing query to fetch message and join on both the result is same use for both, the first one.
Here is how i'm trying to do it.
import scalikejdbc._
Class.forName("org.h2.Driver")
ConnectionPool.singleton("jdbc:h2:mem:hello", "user", "pass")
implicit val session = AutoSession
sql"""
create table members (
id serial not null primary key,
name varchar(64),
created_at timestamp not null
)
""".execute.apply()
sql"""
create table message (
id serial not null primary key,
msg varchar(64) not null,
sender int not null,
receiver int not null
)
""".execute.apply()
Seq("Alice", "Bob", "Chris") foreach { name =>
sql"insert into members (name, created_at) values (${name}, current_timestamp)".update.apply()
}
Seq(
("msg1", 1, 2),
("msg2", 1, 3),
("msg3", 2, 1)
) foreach { case (m, s, r) =>
sql"insert into message (msg, sender, receiver) values (${m}, ${s}, ${r})".update.apply()
}
import org.joda.time._
case class Member(id: Long, name: Option[String], createdAt: DateTime)
object Member extends SQLSyntaxSupport[Member] {
override val tableName = "members"
def apply(mem: ResultName[Member])(rs: WrappedResultSet): Member = new Member(
rs.long("id"), rs.stringOpt("name"), rs.jodaDateTime("created_at"))
}
case class Message(id: Long, msg: String, sender: Member, receiver: Member)
object Message extends SQLSyntaxSupport[Message] {
override val tableName = "message"
def apply(ms: ResultName[Message], s: ResultName[Member], r: ResultName[Member])(rs: WrappedResultSet): Message = new Message(
rs.long("id"), rs.string("msg"), Member(s)(rs), Member(r)(rs))
}
val mem = Member.syntax("m")
val s = Member.syntax("s")
val r = Member.syntax("r")
val ms = Message.syntax("ms")
val msgs: List[Message] = sql"""
select *
from ${Message.as(ms)}
join ${Member.as(s)} on ${ms.sender} = ${s.id}
join ${Member.as(r)} on ${ms.receiver} = ${r.id}
""".map(rs => Message(ms.resultName, s.resultName, r.resultName)(rs)).list.apply()
Am I doing something wrong or is it bug?
Sorry for late reply. We have the Google Group ML and I actively read notifications from the group.
When you're in a hurry, please post stackoverflow URLs there. https://groups.google.com/forum/#!forum/scalikejdbc-users-group
In this case, you need to write select ${ms.result.*}, ${s.result.*} instead of select *. Please read this page for details. http://scalikejdbc.org/documentation/sql-interpolation.html

How to obtain a PrimaryKey projection of a TableElementType tuple?

Using Slick's lifted embedding, I define a class extending AbstractTable, with some primary keys spanning multiple columns. For example:
class Foo extends AbstractTable[(some, tuple, type)](tag, name)
{
def col1 = ...
def col2 = ...
def col3 = ...
def * = (col1, col2, col3)
def pk = primaryKey(name, (col1, col2))
...
}
Somewhere in the code, I hold a PrimaryKey reference that corresponds to that primary key (the code in question is generic and must not depend on the knowledge of which specific tables and which columns are defined). I also hold a reference to a TableElementType tuple corresponding to a row in this table, as defined by the * projection.
How do I programmatically obtain the primary key projection of that element? That is, given the PrimaryKey and TableElementType references as arguments, I want to obtain the (val1, val2) tuple out of the (val1, val2, val3) TableElementType tuple, in this example. I didn't find readily available methods to achieve that in the Slick documentation.
AbstractTable has a different signature, it takes 3 parameters and not 2:
abstract class AbstractTable[T](val tableTag: Tag, val schemaName: Option[String], val tableName: String) extends ColumnBase[T]
Here I'm extending Table for convenience, what you can do is twist the shape in your table type parameter:
class Foo(tag: Tag) extends Table[((String, String), String)](tag, "Foo") {
def col1 = column[String]("c1")
def col2 = column[String]("c2")
def col3 = column[String]("c3")
def * = ((col1, col2), col3)
def pk = primaryKey("pk", (col1, col2))
}
Note that if you have (String, String, String) then the projection function must be also defined so:
def * = (col1, col2, col3)
// this below doesn't match the given shape:
// def * = ((col1, col2), col3)
If you don't want to, just define a custom method:
def someF(results: List[(String, String, String)]): List[((String, String), String)] = {
results.map { case(a,b,c) => ((a,b),c) }
}
The code is untested because at the moment I don't have a database available but it compiles.
Edit:
You could use this:
def tupleKeys[T <: Table[S], S](table: TableQuery[T]): Tuple2[Option[PrimaryKey], Option[PrimaryKey]] = {
val keys: List[PrimaryKey] = table.baseTableRow.primaryKeys.toList
if(keys.length == 2) (Option(keys(0)), Option(keys(1)))
else (Option(keys(0)), None)
}
But as I said, you don't know how many keys you have before running the script while the tuple has its length fixed, I wrapped the values in option because you may or not have 2 keys and you can either return a Tuple1 or a Tuple2 since they are different types, you can use this method into the projection function to twist the keys shape but it still won't be as dynamic as you want it.

[SlickException: Read NULL value for column (USERS /670412212).LOGIN_ID]

I am using Slick 1.0.0 with play framework 2.1.0. I am getting the following error when I query my Users table. The value of LOGIN_ID is null in DB.
The query I am executing is:
val user = { for { u <- Users if u.providerId === id.id } yield u}.first
This results in the following error:
play.api.Application$$anon$1: Execution exception[[SlickException: Read NULL value for column (USERS /670412212).LOGIN_ID]]
at play.api.Application$class.handleError(Application.scala:289) ~[play_2.10.jar:2.1.0]
at play.api.DefaultApplication.handleError(Application.scala:383) [play_2.10.jar:2.1.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$24.apply(PlayDefaultUpstreamHandler.scala:314) [play_2.10.jar:2.1.0]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$24.apply(PlayDefaultUpstreamHandler.scala:312) [play_2.10.jar:2.1.0]
at play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113) [play_2.10.jar:2.1.0]
at play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113) [play_2.10.jar:2.1.0]
scala.slick.SlickException: Read NULL value for column (USERS /670412212).LOGIN_ID
at scala.slick.lifted.Column$$anonfun$getResult$1.apply(ColumnBase.scala:29) ~[slick_2.10-1.0.0.jar:1.0.0]
at scala.slick.lifted.TypeMapperDelegate$class.nextValueOrElse(TypeMapper.scala:158) ~[slick_2.10-1.0.0.jar:1.0.0]
at scala.slick.driver.BasicTypeMapperDelegatesComponent$TypeMapperDelegates$StringTypeMapperDelegate.nextValueOrElse(BasicTypeMapperDelegatesComponent.scala:146) ~[slick_2.10-1.0.0.jar:1.0.0]
at scala.slick.lifted.Column.getResult(ColumnBase.scala:28) ~[slick_2.10-1.0.0.jar:1.0.0]
at scala.slick.lifted.Projection15.getResult(Projection.scala:627) ~[slick_2.10-1.0.0.jar:1.0.0]
at scala.slick.lifted.Projection15.getResult(Projection.scala:604) ~[slick_2.10-1.0.0.jar:1.0.0]
My User table is defined as :
package models
import scala.slick.driver.MySQLDriver.simple._
case class User(userId:String,email:String,loginId:String,fullName:String,firstName:String,lastName:String,location:String,homeTown:String,providerId:String,provider:String,state:String,zip:String,accessKey:String,refreshKey:String,avatarUrl:String)
object Users extends Table[User]("USERS") {
def userId = column[String]("USER_ID", O.PrimaryKey) // This is the primary key column
def email = column[String]("EMAIL",O.NotNull)
def loginId = column[String]("LOGIN_ID",O.Nullable)
def fullName = column[String]("FULL_NAME",O.NotNull)
def firstName = column[String]("FIRST_NAME",O.Nullable)
def lastName = column[String]("LAST_NAME",O.Nullable)
def location = column[String]("LOCATION",O.Nullable)
def homeTown = column[String]("HOME_TOWN",O.Nullable)
def providerId = column[String]("PROVIDER_ID",O.Nullable)
def provider = column[String]("PROVIDER",O.Nullable)
def state = column[String]("STATE",O.Nullable)
def zip = column[String]("ZIP",O.Nullable)
def accessKey = column[String]("ACCESS_KEY",O.Nullable)
def refreshKey = column[String]("REFRESH_KEY",O.Nullable)
def avatarUrl = column[String]("AVATAR_URL",O.Nullable)
// Every table needs a * projection with the same type as the table's type parameter
def * = userId ~ email ~ loginId ~ fullName ~ firstName ~ lastName ~ location ~ homeTown ~ providerId ~ provider ~ state ~ zip ~ accessKey ~ refreshKey ~ avatarUrl <> (User,User.unapply _)
}
Please help. It looks like Slick can not handle Null values from DB?
Your case class is not ok. If you use O.Nullable, all your properties have to be Option[String].
If you get this error, you'll have to either make the properties O.Nullable, or you have to specify that your query returns an option.
For example let's say you do a rightJoin you might not want to make the properties of the right record optional. In that case you can customize the way you yield your results using .?
val results = (for {
(left, right) <- rightRecord.table rightJoin leftRecord.table on (_.xId === _.id)
} yield (rightRecord.id, leftRecord.name.?)).list
results map (r => SomeJoinedRecord(Some(r._1), r._2.getOrElse(default)))
This problem arises if a column contains a null value and at runtime it gets a null in the column response. If you see in the code below, my cust_id is nullable, but it has no null values. Since, there is a job that makes sure that is is never null. So, the below mapping works. However, it is the best practice to look at your table structure and create the class accordingly. This avoids nasty runtime exception.
If the table definition on database is like:
CREATE TABLE public.perf_test (
dwh_id serial NOT NULL,
cust_id int4 NULL,
cust_address varchar(30) NULL,
partner_id int4 NULL,
CONSTRAINT perf_test_new_dwh_id_key UNIQUE (dwh_id)
);
The corresponding class definition can be as below. But, it will be advised to have the cust_id also as Option[Int]. However, as long as it has values and no nulls, you will not encounter error.
import slick.jdbc.PostgresProfile.api._
class PerfTest(tag: Tag) extends Table[(Int, Int, Option[String], Option[Int])](tag, "perf_test") {
def dwhId = column[Int]("dwh_id")
def custId = column[Int]("cust_id")
def custAddress = column[Option[String]]("cust_address")
def partnerId = column[Option[Int]]("partner_id")
def * = (dwhId, custId, custAddress,partnerId)
}
What happened to me was that I had anomalies in the DB and some values were accidentally nulls - those that I didn't expect to be. So do not forget to check your data too :)