Database transactions with Mapper

495 views
Skip to first unread message

GA

unread,
Nov 6, 2009, 10:49:12 AM11/6/09
to Lift
Hello guys,

Is it possible to manage database transactions in Mapper? or does it
manage them automatically?

For instance, I have a Restful service that performs some database
actions. If the last action fails for some reason in some point and I
respond to the caller with an error, is it possible to rollback the
previous database changes?

Thanks in advance,

GA

David Pollak

unread,
Nov 6, 2009, 11:19:24 AM11/6/09
to lif...@googlegroups.com
If you have the following line in your Boot.scala file:

    S.addAround(DB.buildLoanWrapper)

Then all RDBMS statements executed during a given HTTP request are part of a single transaction.  S.addAround runs the HTTP request inside a given function.  The DB.buildLoanWrapper function begins transactions before the request processing begins and commits/rolls back after the processing has ended (well, this is no long technically true... it marks each DB Connection as potentially being part of a transaction, but once the connection is actually used, it is committed/rolled back at the end).

So, if you want to roll back the transaction:

DB.rollback(DefaultConnectionIdentifier)

Is this what you're looking for?
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

GA

unread,
Nov 6, 2009, 11:22:18 AM11/6/09
to lif...@googlegroups.com
That's exactly what I am looking for. Thanks David.

oshyshko

unread,
Nov 7, 2009, 1:14:15 AM11/7/09
to Lift
Looks like "S.addAround(DB.buildLoanWrapper)" has no effect.


2 Query SET autocommit=0
2 Query SET autocommit=0
2 Query SELECT accounts.id, accounts.name, accounts.user_c FROM
accounts WHERE id = 205
2 Query commit
2 Query SET autocommit=0
2 Query SET autocommit=0
2 Query SELECT accounts.id, accounts.name, accounts.user_c FROM
accounts WHERE id = 206
2 Query commit
2 Query SET autocommit=0
2 Query SET autocommit=0
2 Query SELECT accounts.id, accounts.name, accounts.user_c FROM
accounts WHERE id = 207


Any ideas what can be wrong?


class Boot extends Bootable {
def boot {
DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor)
S.addAround(DB.buildLoanWrapper)
...
}
}

- scala 2.7.6
- liftweb 1.1-M6
- MySQL 5.1

David Pollak

unread,
Nov 7, 2009, 10:10:00 AM11/7/09
to lif...@googlegroups.com
On Fri, Nov 6, 2009 at 10:14 PM, oshyshko <oshy...@gmail.com> wrote:

Looks like "S.addAround(DB.buildLoanWrapper)" has no effect.


What code are you using to make the query?

Please create a fully running example (making a GitHub project is the best way to do this) so that I can see exactly what is happening.
 

oshyshko

unread,
Nov 7, 2009, 1:05:15 PM11/7/09
to Lift
I have found the problem.

This is what I have in web.xml:
<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>

By default it should be like this:

<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

"S.addAround(DB.buildLoanWrapper)" does work, if the request path is
covered by LiftFilter.
And it does not work, if not covered (my case) -- in this case it will
create separate transactions.

So its okay.


Now I see thatreal problem is because I use BlazeDS servet which
should not be covered by LiftFilter. I wrote about it:
http://groups.google.com/group/liftweb/browse_thread/thread/33a4d1d111f54ef0/ea4c08393ce76c1d
Looks like the old problem has reappeared in a new place.


On Nov 7, 5:10 pm, David Pollak <feeder.of.the.be...@gmail.com> wrote:

Igor

unread,
Feb 21, 2012, 10:24:08 AM2/21/12
to lif...@googlegroups.com
Hello David, 

unfortunately I can not make use of your advise. I uploaded sample (and quite simple) project to git-hub
the link is: g...@github.com:inbrain/Lift-Mapper-Transaction-Test.git but just to have some background here I'll copy-paste a bit of code as well

I'm doing S.addAround(DB.buildLoanWrapper()) in Boot and I have simple rest service

object TestRest extends RestHelper with Loggable {

  serve {
    "test" :: "transaction" :: Nil prefix {
      case Get(_, _) =>
        User.create.name("user").save()
        interruptExecution()
        Contact.create.name("contact").save()
        OkResponse()
    }
  }

  def interruptExecution() {
    throw new IllegalStateException()
  }
}

User is created while expected to be not

Some additional considerations (: 
1. If to add DB.use() wrapping inside TestRest and LEAVE S.addAround(DB.buildLoanWrapper()) in Boot - it doesn't work either
2. S.addAround(DB.buildLoanWrapper()) in Boot and REMOVE S.addAround(DB.buildLoanWrapper()) in Boot - works as i suppose it should (0 user 0 contact created)

Code to demonstrate these additional nuances is checked in under 2 additional branches: BothLoanWrapperAndDbUse & DbUseInPlace  

I'll be very thankful for any help here 

David Pollak

unread,
Feb 21, 2012, 12:47:48 PM2/21/12
to lif...@googlegroups.com
Please see the console when you run the app:

09:46:49.300 [main] ERROR net.liftweb.util.Props - Failed to find a properties file (but properties were accessed).  Searched: /props/dpp.raptor.in.smb-sfo1.stack.mob.props, /props/dpp.props, /props/raptor.in.smb-sfo1.stack.mob.props, /props/default.props, /dpp.raptor.in.smb-sfo1.stack.mob.props, /dpp.props, /raptor.in.smb-sfo1.stack.mob.props, /default.props
09:46:49.307 [main] ERROR n.liftweb.http.provider.HTTPProvider - Failed to Boot! Your application may not run properly
java.lang.NullPointerException: Trying to open an empty Box
at net.liftweb.common.EmptyBox.open_$bang(Box.scala:572) ~[lift-common_2.9.1-2.4.jar:2.4]
at net.liftweb.common.EmptyBox.open_$bang(Box.scala:557) ~[lift-common_2.9.1-2.4.jar:2.4]
at bootstrap.liftweb.Boot.boot(Boot.scala:20) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_29]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_29]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_29]
at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_29]
at net.liftweb.util.ClassHelpers$$anonfun$createInvoker$1.apply(ClassHelpers.scala:361) ~[lift-util_2.9.1-2.4.jar:2.4]
at net.liftweb.util.ClassHelpers$$anonfun$createInvoker$1.apply(ClassHelpers.scala:359) ~[lift-util_2.9.1-2.4.jar:2.4]


If you get an exception in Boot, the rest of boot is not run and in this case it's the addAround stuff that's not run.

--
Lift, the simply functional web framework: http://liftweb.net



--
Visi.Pro, Cloud Computing for the Rest of Us http://visi.pro
Lift, the simply functional web framework http://liftweb.net

Igor

unread,
Feb 22, 2012, 2:51:42 AM2/22/12
to lif...@googlegroups.com
Hi David, 

this is just because I attached my own property files (named by my machine) and without a password as well so it can not work
this is one of things (I mean personal prop file) probably to be created by you containing not mine but your own db access properties - I don't believe I can provide meaningful defaults

David Pollak

unread,
Feb 22, 2012, 12:31:35 PM2/22/12
to lif...@googlegroups.com
If I can't run the project out of the box, I don't spend my time on it.  Many of the Lift examples work out of the box against an H2 database.  If you make it work out of the box, I'll take a look at it.

--

Igor

unread,
Feb 23, 2012, 7:30:57 AM2/23/12
to lif...@googlegroups.com
Yes, it's quite clear. 

I updated the project on git hub, switched to H2 & added a README file. I hope it's now more convenient to try it

David Pollak

unread,
Mar 1, 2012, 12:57:41 PM3/1/12
to lif...@googlegroups.com
Sorry it's taking me so long to get to this.  I will try to get to it before Monday.

On Thu, Feb 23, 2012 at 4:30 AM, Igor <inb...@gmail.com> wrote:
Yes, it's quite clear. 

I updated the project on git hub, switched to H2 & added a README file. I hope it's now more convenient to try it

--

David Pollak

unread,
Mar 5, 2012, 4:54:27 PM3/5/12
to lif...@googlegroups.com
Igor,

Thanks for finding this nasty little (okay, big) bug.

I've created a ticket and a patch: https://github.com/lift/framework/pull/1235

Turns out the issue was broader than your example and I've updated the example code to test both stateless and stateful issues as well as S.addAround and the new LiftRule.allAround: https://github.com/dpp/Lift-Mapper-Transaction-Test

The fix will be in 2.5-SNAPSHOT in the next few days (once the patch has been reviewed)

On Thu, Feb 23, 2012 at 4:30 AM, Igor <inb...@gmail.com> wrote:
Yes, it's quite clear. 

I updated the project on git hub, switched to H2 & added a README file. I hope it's now more convenient to try it

--

Igor

unread,
Mar 6, 2012, 4:05:05 AM3/6/12
to lif...@googlegroups.com
David, 

thank you a lot for your feedback!
is there a chance for the fix to appear in some bugfix release of lift 2.4? Just to have some stable version of lift to use as soon as fix is done. 

понедельник, 5 марта 2012 г. 23:54:27 UTC+2 пользователь David Pollak написал:
Igor,

Thanks for finding this nasty little (okay, big) bug.

I've created a ticket and a patch: https://github.com/lift/framework/pull/1235

Turns out the issue was broader than your example and I've updated the example code to test both stateless and stateful issues as well as S.addAround and the new LiftRule.allAround: https://github.com/dpp/Lift-Mapper-Transaction-Test

The fix will be in 2.5-SNAPSHOT in the next few days (once the patch has been reviewed)

David Pollak

unread,
Mar 6, 2012, 9:34:18 AM3/6/12
to lif...@googlegroups.com


2012/3/6 Igor <inb...@gmail.com>

David, 

thank you a lot for your feedback!
is there a chance for the fix to appear in some bugfix release of lift 2.4?

In the open source project, we do not backport bug fixes.  Lift Co (http://liftweb.com) may offer backports as a service for a fee.
 
Just to have some stable version of lift to use as soon as fix is done. 

2.5-M1 should come out soon.  Indrajit can discuss the timing.  Granted it's a milestone release, but all but 2 of Lift's milestone releases have been suitable for production.

Andriy Plokhotnyuk

unread,
Mar 6, 2012, 1:16:06 PM3/6/12
to lif...@googlegroups.com
Hi all,

Can prepending of custom error handler, that rolling back transaction for current connection, be safe workaround for 2.4 and older versions?

  private def transactionRollbackWA() {

    def showException(le: Throwable): String = {
      val ret = "Message: " + le.toString + "\n\t" + le.getStackTrace.map(_.toString).mkString("\n\t") + "\n"
      val also = le.getCause match {
        case null => ""
        case sub: Throwable => "\nCaught and thrown by:\n" + showException(sub)
      }
      ret + also
    }

    LiftRules.exceptionHandler.prepend {
      case (Props.RunModes.Development, r, e) =>
        DB.currentConnection.open_!.rollback()
        logger.error("Exception being returned to browser when processing " + r.uri.toString + ": " + showException(e))
        XhtmlResponse((<html> <body>Exception occured while processing {r.uri}<pre>{showException(e)}</pre> </body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.ieMode)
      case (_, r, e) =>
        DB.currentConnection.open_!.rollback()
        logger.error("Exception being returned to browser when processing " + r, e)
        XhtmlResponse((<html> <body>Something unexpected happened while serving the page at {r.uri}</body> </html>), S.htmlProperties.docType, List("Content-Type" -> "text/html; charset=utf-8"), Nil, 500, S.ieMode)
    }
  }

Thanks,
Andriy

David Pollak

unread,
Mar 6, 2012, 1:32:25 PM3/6/12
to lif...@googlegroups.com
Nice elegant engineering solution. ;-)

May I suggest:

LiftRules.exceptionHandler.prepend {
  case _ if { DB.currentConnection.foreach(_.rollback()); false} => throw new Exception("You should never get here")
}

It's the evil side-effecting false returning guard.


2012/3/6 Andriy Plokhotnyuk <plokh...@gmail.com>
Reply all
Reply to author
Forward
0 new messages