Assertion Failure when trying to create OneToMany Relation using Squeryl - scala

I have two classes in a database, and wish to set up a one to many relation. Nothing complicated. However, I'm getting an assertion failure in squeryl's _splitEquality (on line 576). Squeryl is version 0.9.5
So I have a schema
object Tables extends Schema {
val foo = table[Foo]("foo_table")
val bar = table[Bar]("bar_table")
val fooBar = oneToManyRelation(foo,bar).via((f,b) => f.id === bar.foo_fk)
}
Where foo is
class Foo (val foo_id: String, val useful_info: String)
extends KeyedEntity[String] {
override def id: String = foo_id
}
and bar is
class bar (val foo_fk) {
def useful_info = Tables.fooBar.right(this).head.useful_info
}
However, this fails at runtime with the previously mentioned assertion failure, specifically that:
assert(ee.right._fieldMetaData.isIdFieldOfKeyedEntity)
fails

I fixed it by using a column annotation on Foo instead of overriding id.
So foo became
class Foo (
#Column("foo_id")
val id: String,
val useful_info: String)
extends KeyedEntity[String] {
}
I'm not totally sure why this worked, but I'm annoyed that it did.

Related

Structural equality affected by location of case class definition after deserialisation

Why is structural equality comparison affected, after deserialisation to case class instance, by the location of case class definition being inside or outside another class. For example, the assertion in the following snippet
package example
import org.json4s.DefaultFormats
import org.json4s.native.JsonMethods.parse
class Foo {
case class Person(name: String)
def bar = {
implicit val formats = DefaultFormats
val expected = Person(name = "picard")
val actual = parse("""{"name": "picard"}""").extract[Person]
assert(expected == actual, s"$expected == $actual")
}
}
object Main extends App {
(new Foo).bar
}
fails with
`java.lang.AssertionError: assertion failed: Person(picard) == Person(picard)`
whilst it passes if we move Person definition outside class Foo like so
case class Person(name: String)
class Foo {
def bar = {
...
assert(expected == actual, s"$expected == $actual")
}
}
Note, in both cases, deserialisation seems to be successful, for example,
assert(expected.name == actual.name)
is satisfied irrespective of case class Person definition location.
Perhaps it is somehow affected by the implicit Manifest passed in to extract?
This is a bug.
https://github.com/json4s/json4s/issues/564
"Deserialized inner case classes cannot be compared with case classes initialized in code"
Looks like inner classes can't be meaningfully checked for equality since every instance holds a reference to the outer object. And these references are a part of equality check:
class Outer {
case class Inner(s: String)
}
val outer = new Outer()
val a = outer.Inner("x")
val b = outer.Inner("x")
println(a==b) //true
val c = new Outer().Inner("x")
println(a==c) //false

Using Phantom 2 with an existing Cassandra session

I am trying to migrate our current implementation from Phantom 1.28.16 to 2.16.4 but I'm running into problems with the setup.
Our framework is providing us with the Cassandra session object during startup which doesn't seem to fit with Phantom. I am trying to get Phantom to accept that already instantiated session instead of going through the regular CassandraConnection object.
I'm assuming that we can't use the Phantom Database class because of this but I am hoping that there still is some way to set up and use the Tables without using that class.
Is this doable?
I ended up doing the following to be able to use Phantom with an existing connection:
Defined a new trait PhantomTable to be used instead of Phantoms 'Table' trait. They are identical except for removal of the RootConnector
trait PhantomTable[T <: PhantomTable[T, R], R] extends CassandraTable[T, R] with TableAliases[T, R]
Defined my tables by extending the PhantomTable trait and also made it to an object. Here I had to import all of the TableHelper macro to get it to compile
...
import com.outworkers.phantom.macros.TableHelper._
final case class Foo(id: String, name: Option[String])
sealed class FooTable extends PhantomTable[FooTable, Foo] {
override val tableName = "foo"
object id extends StringColumn with PartitionKey
object name extends OptionalStringColumn
}
object FooTable extends FooTable
After that it is possible to use all the wanted methods on the FooTable object as long as an implicit Keyspace and Session exists in the scope.
This is a simple main program that shows how the tables can be used
object Main extends App {
val ks = "foo_keyspace"
val cluster = Cluster.builder().addContactPoints("127.0.0.1").build()
implicit val keyspace: KeySpace = KeySpace(ks)
implicit val session: Session = cluster.connect(ks)
val res = for {
_ <- FooTable.create.ifNotExists.future
_ <- FooTable.insert.value(_.id, "1").value(_.name, Some("data")).future
row <- FooTable.select.where(_.id eqs "1").one
} yield row
val r = Await.result(res, 10.seconds)
println(s"Row: $r")
}

How to organize Slick code into separately testable units with a configurable database?

I have a codebase of several applications with a shared database that use Slick for database access. Parts of the code like common table mappings are in a common library. I want to be able to run two kinds of tests on those projects:
tests that use the real database
other tests that use an in-memory H2 database
I have tried several ways to organize my code to support this, but have always hit a wall at some point. My latest attempt is using the DatabaseConfig approach and passes around a database definition (including database, profile and table definitions) to the objects that do database operations (let's call them Services). This way, I can - in theory - easily test a service by passing in the database definition I want to test it with. In practice, I get problems in the interaction between services because types don't fit properly.
I have created a simplified example that shows the kind of problems I get:
import slick.basic.DatabaseConfig
import slick.jdbc.JdbcProfile
import slick.lifted.Rep
import scala.concurrent.Future
trait HasProfile {
val profile: JdbcProfile
}
trait HasId {
val id: Rep[Long]
}
trait Table_Person {
this: HasProfile =>
case class PersonRow(personId: Long, lastName: String)
import profile.api._
class Person(tableTag: Tag) extends Table[PersonRow](tableTag, "person") with HasId {
val personId = column[Long]("person_id", O.PrimaryKey, O.AutoInc)
val lastName = column[String]("last_name")
override val id = personId
val * = (personId, lastName).mapTo[PersonRow]
}
lazy val Person = TableQuery[Person]
}
class DatabaseWrapper {
private val databaseConfig: DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig("PersonDatabase")
def db = databaseConfig.db
object Schema extends HasProfile with Table_Person {
override val profile = databaseConfig.profile
}
}
class PersonService(val databaseWrapper: DatabaseWrapper) {
import databaseWrapper.Schema._
val loadService = new LoadService(databaseWrapper)
def load(id: Long): Future[Option[PersonRow]] = loadService.load[PersonRow](Person, id)
}
class LoadService(val databaseWrapper: DatabaseWrapper) {
import databaseWrapper.Schema.profile.api._
def load[R](tableQuery: TableQuery[_ <: Table[R] with HasId], id: Long): Future[Option[R]] = databaseWrapper.db.run(tableQuery.filter(_.id === id).result.headOption)
}
This code gives me the following compile error:
Error:(51, 79) type mismatch;
found : slick.lifted.TableQuery[PersonService.this.databaseWrapper.Schema.Person]
required: PersonService.this.loadService.databaseWrapper.Schema.profile.api.TableQuery[_ <: PersonService.this.loadService.databaseWrapper.Schema.profile.api.Table[PersonService.this.databaseWrapper.Schema.PersonRow] with HasId]
(which expands to) slick.lifted.TableQuery[_ <: PersonService.this.loadService.databaseWrapper.Schema.profile.Table[PersonService.this.databaseWrapper.Schema.PersonRow] with HasId]
def load(id: Long): Future[Option[PersonRow]] = loadService.load[PersonRow](Person, id)
It seems the type checker does not realize that PersonService.this.loadService.databaseWrapper is the same as PersonService.this.databaseWrapper.
Is there a way around this? Does this approach even make sense, or are there any better approaches to structure the code?

"not found: value account" While using squeryl and Scala

I'm trying to make it so I can get a value from one model into the creation of another model. This is very simplified but gives the error I'm having with a model instance extending a squeryl model. In reality I'm doing some calculations specific to this model, but need a value from another model.
I'm getting an error doing all sorts of versions of this:
class Bar(
val bazId: String
val someValue: String
) extends db.BaseModel {
val foo: Foo = db.MyDb.foos.lookup(bazId).head
def this(
bazId: Baz
) = {
this (
Baz.id,
foo.someValue,
)
}
The error is:
not found: value foo
[error] foo.someValue,
^
I've tried having some kind of lookup in the Object/singleton, inside of the def this() (that gives a "Application does not take parameters" error), and other things but nothing is working. Seems so simple, but having no luck at all.
The issue is you are trying to access a member of the class in the constructor, and since this is before the object has been constructed - it is not accessible. Doing it in the companion should help you get around that. Something like:
object Bar {
val foo(bazId:String): Foo = db.MyDb.foos.lookup(bazId).head
}
class Bar(
val bazId: String,
val someValue: String
) extends db.BaseModel {
val foo: Foo = Bar.foo(bazId)
def this(
bazId: String
) = {
this (
bazId,
Bar.foo(bazId).someValue
)
}
}
Or, if you did intend to have the constructor accept the class Baz, then you could also add the lookup method directly in Baz - then you can just call it directly to look up Foo.

Can I override a scala class method with a method from a trait?

class PasswordCaseClass(val password:String)
trait PasswordTrait { self:PasswordCaseClass =>
override def password = "blue"
}
val o = new PasswordCaseClass("flowers") with PasswordTrait
Is it possible to override PasswordCaseClass's password with what is provided in PasswordTrait? Right now, I receive this error:
e.scala:6: error: overriding value password in class PasswordCase
Class of type String;
method password in trait PasswordTrait of type => java.lang.String needs to be a stable,
immutable value
val o = new PasswordCaseClass("flowers") with PasswordTrait
^
one error found
I would like to be able to have something like this:
class User(val password:String) {
}
trait EncryptedPassword { u:User =>
def password = SomeCriptographyLibrary.encrypt(u.password)
}
val u = new User("random_password") with EncryptedPassword
println(u.password) // see the encrypted version here
You can override a def with a val, but you can't do it the other way around. A val implies a guarantee -- that it's value is stable and immutable -- that a def does not.
This worked for me (with some modifications):
trait PasswordLike {
val password: String
}
class PasswordCaseClass(val password:String) extends PasswordLike
trait PasswordTrait extends PasswordLike {
override val password: String = "blue"
}
and then:
scala> val o = new PasswordCaseClass("flowers") with PasswordTrait
o: PasswordCaseClass with PasswordTrait = $anon$1#c2ccac
scala> o.password
res1: String = blue
You are trying to override the value with the method definition. It simply makes no sense - they have different semantics. Values supposed to be calculated once per object lifecycle (and stored within a final class attribute) and methods can be calculated multiple times. So what you are trying to do is to brake the contract of the class in a number of ways.
Anyway there is also compiler's fault - the error explanation is totally unclear.