Question about architecture of a JPA-based model in Lift

40 views
Skip to first unread message

egervari

unread,
Jun 2, 2010, 6:37:30 AM6/2/10
to Lift
Hello,

I was wondering what is the best practice for a JPA model in Lift? I
noticed that in the jpa demo application, there is just a Model object
that is like a super object that does everything. I don't think this
can be the most scalable approach, no?

Is it is wise to still do the DAO pattern in Lift? For example,
there's some code that looks a tad bloated and could be simplified
across all model objects:

Model.remove(Model.getReference(classOf[Author], someId))

Could be:

AuthorDao.remove(someId)

I'd appreciate any tips for setting up something that will work with
the way Lift wants to work and is also easy to organize and maintain.
The first phase of development will be around 30-40 tables, and will
eventually get to over 100

DChenBecker

unread,
Jun 2, 2010, 4:42:24 PM6/2/10
to Lift
It's been a long time since I've looked at the JPA demo code, but I
would be the first to admit that it could use some cleanup. The
current Model is basically just a direct ScalaJPA EntityManager, so if
you're doing a lot of ops I think it would make sense to make a more
specific engine for what you're doing.

Out of curiosity, why would the current "Model" not scale? It's
basically a proxy for EntityManager, and since it uses a RequestVar as
a backing store every request gets its own EM to work with. EMs really
aren't designed to be shared between threads/requests, so I'm not sure
what else we could do there.

On a related note, I've been looking forward to 2.8 and Manifests,
since that should allow the ScalaJPA EntityManager impl to simplify
some of the methods that currently require class arguments.

Derek

egervari

unread,
Jun 2, 2010, 5:36:07 PM6/2/10
to Lift
Hi Derek,

I didn't mean scale from a performance point of view, I meant from a
code maintenaince point of view.

You see, the nit-picker in me wants to wrap a lot of these methods and
organize them and their respective queries. With a lot of tables, and
a lot of queries, this is going to be tough to manage.

I don't mind using named queries as a best practice (I guess... I
don't care much either way honestly.. I hope IDEA can just take me to
the query in the xml with a single click), but i'd still want to wrote
them in method calls just because it'll make it easier to see which
queries are available inside of the IDE when using code completion.
That way people don't have to dick around looking in a giant orm.xml
to find the query they want to invoke.

Also, a Dao layer might help us too because we might want to move some
tables to couche/mongo/whatever at some point, unless this pattern
isn't going to fly with lift all that much.

The reason I ask these questions is because I am at a point where I
want to start testing/writing my domain model's persistence stuff...
but I don't know what the heck to invoke when I'm testing ;) I am
using EntityManager right now. I made a test base class to rollback
everything after the test case is done. But if I am going to have some
kind of architecture or layering, this isn't the right thing to do at
all.

egervari

unread,
Jun 2, 2010, 8:34:39 PM6/2/10
to Lift
Yeah, the setup is kind of weird to me, so this really suggests that
I'm doing something wrong.

In a test class, I'm going to want to roll back the scala entity
manager after I do a bunch of stuff. So in order to do this, we need
to be able to know the entity manager, not some abstraction over it.

Ideally in the real application, we want to manage transactions for
us... but in the tests, we kind of want control over that.

So to make a DAO layer that can make it possible to do these kind of
things, you need some kind of dependency injection. ScalaEntityManager
needs to be injected into the Dao... or it needs to reuse the same
scala entity manager that the current test is using.

And then you run into the problem that all the daos themselves need to
be dependency injected where ever they are used... in tests, or
whatever.

Of course, maybe all my old school thinking is just totally clouding
me to see the real solution, or maybe there's something available that
I just don't know about. That's why I'm asking ;)

egervari

unread,
Jun 3, 2010, 3:59:11 AM6/3/10
to Lift
Okay, I decided to use a DI pattern to make the dao tier:

trait MyProjectComponents
extends UserDaoComponent
with RoleDaoComponent
with LanguageDaoComponent
with PersistentComponent
{
val localEMF = new LocalEMF("myProject")

val userDao = new UserDao
val languageDao = new LanguageDao
val roleDao = new RoleDao
}

Setting it up this way has a lot of nice advantages compared to that
Model approach.

Testing also becomes a joke. You just mix this guy in with your test
case, and you're ready to start testing:

"A Language" should "save properties" in {
val language = new Language("English", "en")

languageDao.save(language)

language.isPersistent should be (true)
}

I have one problem still. I need to share the entityManager inside of
the Dao with the test class. That means I can't close the
EntityManager until the test is done. Right now, I'm closing it inside
of the Dao.

Is there any way to say: "If there's already an entity manager
running, let's use that. If not, we start a new one" when the
Dao.save()/delete() is called? And when it ends, we probably want some
other guy to close it... not the dao.

I know Spring solved this problem, but we're doing JPA with Lift... so
we don't have Spring now

sean8223

unread,
Jun 3, 2010, 10:25:00 AM6/3/10
to Lift
I can shed a little light on how we use JPA. I'm not sure what kind of
container you are working with, but we are using JBoss 4.2.2, and
using its connection pool facilities.

We utilize the scalajpa library to initialize the JPA stuff and keep a
reference to the entity manager in a thread local variable. We
specifically don't use the Lift RequestVarEM, because the lifecycle of
a RequestVar is somewhat more complicated than a regular HTTP request,
and this can lead to connections not being returned to the pool in a
timely fashion.

The first step is to create the "model", and point it at the unit name
from your persistence.xml:

object MyDBModel extends LocalEMF("unitName", false) with
ThreadLocalEM

And we've created a little bit of code to make some operations simple.
Each of our persistent classes mixes in a that provides some basic JPA
operations:

trait Persistent {
def persist = DBModel.persist(this)
def merge = DBModel.merge(this)
def remove = DBModel.remove(this)
}

For example,

@Entity
@Table{val name="person"}
class Person extends Persistent {

@Id
var id:String = _

@Column {val name="first_name", val nullable = false, val
updatable=false}
var firstName:String = _

@Column {val name="last_name", val nullable = false, val
updatable=false}
var lastName:String = _

@OneToMany{ ... }
var roles:Set[Role] = new HashSet[Role]()

// etc.

}

We primarily use the mapped collections to navigate the object model,
and put more complex database methods on the companion object, so that
we don't have references to MyDBModel scattered throughout the code
(as you noted, an undesirable practice). For example:

object Person {

def findByLastName = MyDBModel.createQuery[Person]
("...").findAll.toList

// etc.

}

Lastly, our integration with Lift is in the form of a bit of code that
wraps each request:

S.addAround(new LoanWrapper {
def apply[T](f: => T):T = {
try {
f
}
catch {
case e => MyDBModel.getTransaction.setRollbackOnly
}
finally {
MyDBModel.cleanup
}
}
})

I've left out some error handling here to make the idea clearer, but
the intent is that each HTTP request executes in a transaction, which
either succeeds or fails in its entirety. Since MyDBModel is
initialized when it is first touched, in your test code you can rig up
the EM as you see fit, and the data objects are isolated from this
configuration.

Hope this is useful.

Sean

Eric Pabst

unread,
Jun 3, 2010, 9:07:45 AM6/3/10
to Lift
I'd recommend adding a method on object Author that delegates to
Model.

object Author {
def remove(someId: String) = {
Model.remove(Model.getReference(classOf[Author], someId))
}
}

Then, that ugliness will be encapsulated there.

Eric

On Jun 2, 4:37 am, egervari <ken.egerv...@gmail.com> wrote:

Will Sargent

unread,
Jun 3, 2010, 10:09:57 AM6/3/10
to lif...@googlegroups.com
I'm the same way -- I prefer to think about design from an IoC / DAO perspective, and I feel really happy when I see DDD type design.  That's one of the things that I wish Rails had a better handle on -- Datamapper is a step up over ActiveRecord, but it's still disturbing to me to have the domain objects tied to the persistence layer through class based finders.

I know it's possible to use Spring / Hibernate in Lift, but it's confusing digging through all the blogs and mailing list stuff.    Is there a definitive source that says what the good and bad parts are?

Will.


--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


egervari

unread,
Jun 3, 2010, 5:42:25 PM6/3/10
to Lift
Wow, thanks so much Sean. I believe there's enough in this reply to
get me started in fixing my problems, and perhaps even adjusting my
design. If I have any more problems, I'll be sure to post, but this
certainly gives me enough to chew for today. Thanks!
Reply all
Reply to author
Forward
0 new messages