Custom Field Types

20 views
Skip to first unread message

Matthew

unread,
May 4, 2012, 1:14:23 AM5/4/12
to Circumflex Public Q&A
We're evaluating circumflex for use in a production scala app and like
what we've seen so far. One thing we'd like to be able to do,
however, is to define type-safe id fields, so that a record type
Address, say, would have a primary key of type Id[Address] instead of
just Long. I don't think I need to explain to scala programmers how
wonderful type-safety is, so being able to have this feature is very
important to us. We started out by defining a case class for our Id
type:

case class Id[R <: Record[_, R]](value: Long)

and a new field type:

class IdField[T <: Record[_, T], R <: Record[_, R]](name: String,
record: R)
extends XmlSerializable[Id[T], R](name, record,
ormConf.dialect.longType)
with AutoIncrementable[Id[T], R] {

def fromString(str: String): Option[Id[T]] =
try Some(Id(str.toLong)) catch { case e: Exception => None }

override def toString(value: Option[Id[T]]): String =
value.map(_.value.toString).getOrElse("")

override def read(rs: java.sql.ResultSet, alias: String):
Option[Id[T]] = {
val o = rs.getObject(alias)
if (rs.wasNull) None
else Some(Id(o.asInstanceOf[Long]))
}
}

This works reasonably well, but we are running into very strange
errors because there are places in the code where fields can be read
without triggering the read method on the field itself (for example
ormConf.dialect.identityLastIdQuery creates a query that reads the
primary key and then just casts it to the field type without calling
our overridden read method, resulting in a class cast exception at run
time, even though everything type checks fine.

Anyway, my question is whether this sort of thing is doable in
circumflex, and in particular whether anyone has experience with or
recommendations about defining custom field types. This would make a
great library even better.

Thanks,
Matthew

Boris Okunskiy

unread,
May 5, 2012, 12:48:11 PM5/5/12
to circumfl...@googlegroups.com
Greetings Matthew,

A couple of things to clarify: do you have custom datatype? As I see from the `read` method in your implementation, it just reads `Long` -- so I assume you have a foreign key which points to a different table? If so, nothing stops you from doing something like this:

class Foo extends Record[Long, Foo] {
def relation = Foo
def PRIMARY_KEY = address.field

val address = "id".BIGINT.NOT_NULL.REFERENCES(Address).ON_DELETE(CASCADE)
}

You see, PRIMARY_KEY should point to `Field`, while the `address` method is an association.

Either way, fields are designed to work with SQL-compatible data types. I believe, it is the associations concept you are looking for.

Please let me know, if it clarifies something.

Best regards,
Boris Okunskiy

Matthew Neeley

unread,
May 7, 2012, 1:38:37 PM5/7/12
to circumfl...@googlegroups.com
Hi Boris,

Thanks for the reply. The Id type does just wrap a Long in the
database, so there's no custom type there; rather I want a custom type
on the scala side. So I don't think the solution you propose does
quite what I'm looking for. Even if I have an association back to the
same record type, I couldn't use that to fetch an entity by primary
key in a typesafe way, because the primary key type is still just
Long. I'd like to have a custom type Id[Foo] instead (even if the
underlying database type is just Long). Is this supported? What
about other custom field types (for example, having an underlying
varchar field that is a Field[java.util.UUID, _] on the scala side)?
I would like that much more than, say, using validators, because of
the compile checking instead of runtime checking.

-Matthew

Boris Okunskiy

unread,
May 8, 2012, 3:46:59 AM5/8/12
to circumfl...@googlegroups.com
Hi Matthew,

As for UUID columns, yes, they can be implemented quite easily, because they are easily translated to/from JDBC types.

> I'd like to have a custom type Id[Foo] instead (even if the
> underlying database type is just Long). Is this supported?


The records, OTOH, are the whole different story. You see, the main reason we do not support such a thing rather more conceptual then technical: Circumflex ORM claims to make SELECT queries look and behave as close to plain SQL as possible. Now, if we had a field with underlying type of record, then we would need to modify each SELECT in order to fetch an association (otherwise it would not be possible to read the record from the ResultSet).

> Even if I have an association back to the
> same record type, I couldn't use that to fetch an entity by primary
> key in a typesafe way, because the primary key type is still just
> Long.

I don't think I quite understand, what fetching API you are going after. FWIW, having a method like this is perfectly fine:

object Foo
extends Foo
with Table[Long, Foo] {

def get(bar: Bar): Option[Foo] = this.get(bar.id())

}

Perhaps we could come up with something if you elaborate a bit on what do you expect from that type-safe fetching API and maybe provide some use cases.

TIA,

Best regards,
Boris Okunskiy

Reply all
Reply to author
Forward
0 new messages