mapping custom type - save joda DateTime as db int (or Long)

66 views
Skip to first unread message

samzil...@gmail.com

unread,
Mar 29, 2013, 5:06:03 PM3/29/13
to mapp...@googlegroups.com
Hi,

I just started evaluating mapperdao for usage in our app and encountered an issue I couldn't find the answer to in the docs/group.

I would like to save joda DateTime in the db using its millis value (actually millis/1000).
So :
when querying the db - conversion from joda DateTime to Long. use this long for queries.
when returning result from db - get the Long number from db and fill entity value with the matching DateTime.

Is it possible with mapperdao?
What is the correct way of doing this with mapperdao?

Thanks
-Sam

Konstantinos Kougios

unread,
Mar 29, 2013, 5:09:56 PM3/29/13
to mapp...@googlegroups.com
I believe it is possible, can you share your mapping?
> --
> You received this message because you are subscribed to the Google
> Groups "mapperdao" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to mapperdao+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

samzil...@gmail.com

unread,
Mar 29, 2013, 5:25:07 PM3/29/13
to mapp...@googlegroups.com, samzil...@gmail.com
Sure, it's pretty trivial:

case class Feedback(user: Option[User] = None, feedback: String,
created: DateTime = new DateTime) <----
 
---created is DateTime in the domain class and I want to persisted it as Long

object FeedbackEntity extends Entity[Long, SurrogateLongId, Feedback]("feedbacks") {
  val id = key("id") autogenerated (_.id)
  val feedback = column("feedback") to (_.feedback)
  val created = column("created") to (_.created)
  val user = manytoone(UserEntity) foreignkey("user_id")  option (_.user)

  def constructor(implicit m: ValuesMap) = new Feedback(user = user, feedback = feedback, created = created)
    with Stored {
    val id :Long = FeedbackEntity.id
  }

// this is an attempt i made to see if perhaps the dao would pick this up and implicitly convert my value
// obviously it didn't work :)
  implicit def Date2Long(d : DateTime) :Long = d.getMillis/1000
  implicit def Long2Date(l : Long) :DateTime = new DateTime(l*1000)
    
}

class FeedbackDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager)
  extends TransactionalSurrogateLongIdCRUD[Feedback] with SurrogateLongIdAll[Feedback] {
  val entity = FeedbackEntity

Tim Pigden

unread,
Mar 29, 2013, 6:11:57 PM3/29/13
to mapp...@googlegroups.com, samzil...@gmail.com
where I've done these sort of conversions I've not bothered with implicits - I'm not sure it adds anything to clarity or self-documentation. But I think they collide also with the fancy column builder stuff so it gets converted to something else first - which may be your problem.

Without implicits is something like
val createdL = column("created") to (_.created.getMillis)  

and in the constructor 
= {
val asDate = new DateTime(1000 * createdL)
new Feedback ...




--
You received this message because you are subscribed to the Google Groups "mapperdao" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mapperdao+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Tim Pigden
Optrak Distribution Software Limited
+44 (0)1992 517100
http://www.linkedin.com/in/timpigden
http://optrak.com
Optrak Distribution Software Ltd is a limited company registered in England and Wales.
Company Registration No. 2327613 Registered Offices: Orland House, Mead Lane, Hertford, SG13 7AT England 
This email and any attachments to it may be confidential and are intended solely for the use of the individual to whom it is addressed. Any views or opinions expressed are solely those of the author and do not necessarily represent those of Optrak Distribution Software Ltd. If you are not the intended recipient of this email, you must neither take any action based upon its contents, nor copy or show it to anyone. Please contact the sender if you believe you have received this email in error.

Konstantinos Kougios

unread,
Mar 29, 2013, 6:24:13 PM3/29/13
to mapp...@googlegroups.com
Yes, I think too that this solves the problem. createdL is not a long column and it has to be treated as such from now on, i.e. when querying too.

samzil...@gmail.com

unread,
Mar 29, 2013, 7:00:29 PM3/29/13
to mapp...@googlegroups.com, samzil...@gmail.com
It works, but the problem with this approach is that it makes you write a lot of boilerplate code.
You need to write these conversions in every entity you want to use them...
In our app, for example, we use DateTime a lot and always expect it to be Long in the db.
So it can be tiresome (and error prone...) to use this kind of approach.

Is there perhaps a more general solution for custom types?
Like defining your own type along with how it is passed to db (driver actually) and how it is built back from db? then either use it with some annotation or register it somewhere with mapperdao?

Or maybe by extending your DefaultTypeManager and overriding something?

Thanks for the help so far
-Sam

On Friday, March 29, 2013 10:06:03 PM UTC+1, samzil...@gmail.com wrote:

Konstantinos Kougios

unread,
Mar 30, 2013, 6:39:12 AM3/30/13
to mapp...@googlegroups.com
I've deployed a snapshot 1.0.0.${scala.version}-SNAPSHOT which has an impl of type conversions.
(you'll need to add the sonatype snapshot repo, please see https://code.google.com/p/mapperdao/wiki/MavenConfiguration )

The best example to look at is the test suite:

https://code.google.com/p/mapperdao/source/browse/src/test/scala/com/googlecode/mapperdao/CustomTypesSuite.scala

You'll have to manually instantiate mapperdao as the factory methods won't do (see the example above, it does just that).

So how does it work? You configure mapperdao with a UserDefinedDatabaseToScalaTypes. Then you provide 2 functions, 1 that converts the types from  scala->database and and the reverse one.

Please let me know how it goes.

Cheers
--

Tim Pigden

unread,
Mar 30, 2013, 6:49:39 AM3/30/13
to mapp...@googlegroups.com
Hi - does this have my INTERVAL stuff in it too? Will I have to change the way I instantiate mapperdao?
Thanks
Tim

Konstantinos Kougios

unread,
Mar 30, 2013, 6:54:15 AM3/30/13
to mapp...@googlegroups.com
yes, this includes the interval fix too.

No you don't need to change the way you instantiate mapperdao. The factory methods do for most occasions and only for fine-tuning the factory methods have to be bypassed.

Cheers

Tim Pigden

unread,
Mar 30, 2013, 7:13:55 AM3/30/13
to mapp...@googlegroups.com
thanks! Will let you know how it goes.

Tim Pigden

unread,
Mar 30, 2013, 7:28:56 AM3/30/13
to mapp...@googlegroups.com
Not doing exactly what I expected
error: type mismatch;
found   : com.googlecode.mapperdao.ColumnInfo[FirstPersist.this.DefaultProduct,org.joda.time.Duration]
required: org.joda.time.Duration
shelfLife

on my expanded test case:

package com.optrak.mapperdaotest

import org.specs2.mutable.Specification
import grizzled.slf4j.Logging
import com.googlecode.mapperdao._
import java.util.Properties
import org.apache.commons.dbcp.BasicDataSourceFactory
import com.googlecode.mapperdao.utils.{TransactionalSurrogateIntIdCRUD, Database, Setup}
import org.joda.time.chrono.ISOChronology
import org.springframework.transaction.PlatformTransactionManager
import org.joda.time.Duration

/**
 * Created with IntelliJ IDEA.
 * User: timpi_000
 * Date: 09/02/13
 * Time: 18:36
 * Copyright (c) Optrak Distribution Software Ltd and Tim Pigden, Hertford, UK 2012
 */
class FirstPersist extends Specification with Logging {

    val properties = new Properties
    properties.load(getClass.getResourceAsStream("/jdbc.properties"))
    val dataSource = BasicDataSourceFactory.createDataSource(properties)
    val entities = List(DefaultProductEntity)
    val (jdbc, mapperDao, queryDao, transactionManager) = Setup.create(Database.PostgreSql, dataSource, TypeRegistry(entities), None, ISOChronology.getInstance)

  class DefaultProductDao(val mapperDao: MapperDao, val queryDao: QueryDao, val txManager: PlatformTransactionManager) extends TransactionalSurrogateIntIdCRUD[DefaultProduct]
  {
    val entity = DefaultProductEntity
  }

  val productDao = new DefaultProductDao(mapperDao, queryDao, transactionManager)

  object DefaultProductEntity extends Entity[Int, SurrogateIntId, DefaultProduct]("products") {

    val id = key("id") autogenerated(_.id)
    val productCode = column("productcode") to (_.productCode)
    val shelfLife = column("shelflife") to (_.shelfLife)
    def constructor(implicit m: ValuesMap) = new DefaultProduct(
      productCode,
      shelfLife
    ) with SurrogateIntId {
      val id: Int = DefaultProductEntity.id
    }
  }

  case class DefaultProduct(productCode: String, shelfLife: Duration)

    val product1 = DefaultProduct(
      productCode = "pcode",
      shelfLife = new Duration(1000L * 24 * 60 * 60)
    )

  "a product" should {
    "persist" in {
      try {
        productDao.create(product1)

      } catch {
        case e: Exception => e.printStackTrace()
      }
      true mustEqual(true)


  }


  }

}

Tim Pigden

unread,
Mar 30, 2013, 7:32:11 AM3/30/13
to mapp...@googlegroups.com
ok just looked at your source for tests - your using Period rather than interval. I need to check differences in joda

Tim Pigden

unread,
Mar 30, 2013, 7:45:41 AM3/30/13
to mapp...@googlegroups.com
Hmm it's a tricky point. I normally want Duration in my application because it's the length of time it takes to carry out an operation - such as unload a truck. If I start unloading just when the clocks go forward or back it doesn't make any difference to the operation. On the other hand if I want to say that an order must be completed within the next 2 days I might mean 47 or 49 hours ...
My normal usage would follow jodatime here and use Duration for former and Period for latter.
I think ideally it should take both and set the seconds. I'm unclear exactly what pginterval is doing here. Because it looks like internally postgres stores interval as years, days, seconds whereas pginterval allows things like minutes. I'll see what postgres does in sql.

Sam Zilverberg

unread,
Mar 30, 2013, 8:07:11 AM3/30/13
to mapp...@googlegroups.com

I just went on holiday today until Tuesday. I'll give this a try the moment i get back. TY for the so quick response :-)
-Sam

You received this message because you are subscribed to a topic in the Google Groups "mapperdao" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mapperdao/5I-CVydjpDY/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to mapperdao+...@googlegroups.com.

samzil...@gmail.com

unread,
Apr 2, 2013, 12:11:05 PM4/2/13
to mapp...@googlegroups.com, samzil...@gmail.com
I tried the snapshot and it works nicely :)
I ditched the entity check from the example so it would work on all joda DateTime across the app. 
I'll continue working with it and i'll post back (hopefully with a fix) if i find problems.

Thanks a lot!

On Friday, March 29, 2013 10:06:03 PM UTC+1, samzil...@gmail.com wrote:

Konstantinos Kougios

unread,
Apr 2, 2013, 12:11:54 PM4/2/13
to mapp...@googlegroups.com
thanks for letting me know

I've added all these fixes to rc19 and deployed it
--
Reply all
Reply to author
Forward
0 new messages