2010-06-02 2 views
3

Мне было интересно, что лучше всего подходит для модели JPA в лифте? I заметил, что в демонстрационном приложении jpa есть только объект Model , который похож на супер-объект, который делает все. Я не думаю, что это может быть самым масштабируемым подходом, нет?Что такое хорошая архитектура для приложения Lift-JPA?

Неправильно ли еще сделать рисунок DAO в лифте? Например, есть некоторый код, который выглядит немного раздутым и может быть упрощен по всем объектам модели:

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

Может быть:

AuthorDao.remove(someId) 

Я бы признателен за любые советы по настройке то, что будет работать с тем, как Lift хочет работать, а также легко организовать и поддерживать. Желательно от кого-то, кто фактически использовал JPA на среднем и крупном лифте, а не просто постулировал, что делает Весна (мы знаем, как это сделать);)

Первый этап разработки будет составлять около 30-40 таблиц и будет в конечном итоге получить более 100 ... нам нужен масштабируемый, аккуратный подход.

ответ

2

Повторно из списка рассылки Lift для потомков (source here):

я могу пролить немного света на то, как мы используем JPA. Я не уверен, какой контейнер вы используете, но мы используем JBoss 4.2.2 и , используя средства пула соединений.

Мы используем библиотеку scalajpa для инициализации материала JPA и сохраняем ссылку на диспетчер сущности в локальной переменной потока. Мы специально не используем Request RequestVarEM, потому что жизненный цикл RequestVar несколько сложнее обычного HTTP-запроса, , и это может привести к тому, что соединения не будут возвращены в пул в своевременно.

Первый шаг заключается в создании «модели», и направьте его на имя блока из вашего persistence.xml:

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

И мы создали немного кода, чтобы сделать некоторые операции просто. Каждый из наших постоянных классов смесей в том, что обеспечивает некоторые основные JPA операции:

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

} 

Например,

@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. 

} 

Мы в первую очередь использовать отображенные коллекции для навигации по объектной модели, и положить больше сложные методы базы данных на сопутствующем объекте, так что у нас нет ссылок на MyDBModel, разбросанных по всему коду (как вы отметили, нежелательная практика).Например:

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

// etc. 

} 

Наконец, наша интеграция с Лифтом в виде фрагмента кода, который компрессы каждый запрос:

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

}) 

Я оставил некоторую обработку ошибок здесь, чтобы сделать Идея понятна, но состоит в том, что каждый HTTP-запрос выполняется в транзакции, которая либо преуспевает, либо полностью отказывается. Поскольку MyDBModel инициализируется при первом касании, в вашем тестовом коде вы можете установить EM, как вы сочтете нужным, и объекты данных изолированы от этой конфигурации .

Надеюсь, это полезно.

Шон

+0

Как вы проверить код, который зависит так сильно от глобальных переменных (например, 'MyDBModel' в вашем примере). Не сложно ли обойтись без использования всего стека? – Theo