I recently spent way too much time tracking down the source of an error in the Squeryl integration to the Record persistence layer in Lift. In the hopes that it may be useful to others encountering the same error, here are the details:
Background
After reading the Squeryl-Record documentation and following the test-squerylrecord and Basic-SquerylRecord-User-Setup examples, I set out to make use of Record with Squeryl. After coding up a very simple test table and schema, I launched the website and was presented with the following exception and stack trace (excerpted):
Message: java.lang.NoSuchMethodException: net.liftweb.record.field.OptionalStringField.<init>(scala.Option)
java.lang.Class.getConstructor0(Class.java:2723)
java.lang.Class.getConstructor(Class.java:1674)
org.squeryl.internals.FieldMetaData$$anonfun$org$squeryl$internals$FieldMetaData$$_createCustomTypeFactory$1.apply(FieldMetaData.scala:511)
org.squeryl.internals.FieldMetaData$$anonfun$org$squeryl$internals$FieldMetaData$$_createCustomTypeFactory$1.apply(FieldMetaData.scala:504)
scala.Option.flatMap(Option.scala:146)
org.squeryl.internals.FieldMetaData$.org$squeryl$internals$FieldMetaData$$_createCustomTypeFactory(FieldMetaData.scala:504)
org.squeryl.internals.FieldMetaData$$anon$1.build(FieldMetaData.scala:425)
org.squeryl.internals.PosoMetaData$$anonfun$3.apply(PosoMetaData.scala:111)
org.squeryl.internals.PosoMetaData$$anonfun$3.apply(PosoMetaData.scala:80)
scala.collection.immutable.HashMap$HashMap1.foreach(HashMap.scala:176)
scala.collection.immutable.HashMap$HashTrieMap.foreach(HashMap.scala:345)
org.squeryl.internals.PosoMetaData.<init>(PosoMetaData.scala:80)
org.squeryl.View.<init>(View.scala:58)
org.squeryl.Table.<init>(Table.scala:27)
org.squeryl.Schema$class.table(Schema.scala:338)
[...]
Why the OptionalStringField class (which is part of record) is missing a constructor expected by Squeryl was beyond me. Something odd was going on.
Finding the Source
First, I tried copying my code into the (working) examples and running it. Everything worked without issue. The model code was not the issue.
After a bit more digging, I found that
net.liftweb.squerylrecord.RecordMetaDataFactory
was not being set on
org.squeryl.internals.FieldMetaData
because the session initialization and
transaction setup (in my application’s Lift Boot class) were not being
called because Lift continues after Boot throws an exception, which was
occurring before the initialization code was reached. Ugh!
Conclusion
Takeaways:
- If you receive the above exception, check that record and Squeryl are being initialized correctly.
- Setup logging for Lift very early in the development and watch for errors from Boot. This is the only way you will be aware of them (other than the mysterious errors and lack of configuration that will result).