3

При переносе в Play 2.5 я принимаю шаблоны проектирования инъекций зависимостей, в том числе для доступа к базе данных (JDBC).Инъекция зависимостей Scala при использовании шаблона объекта класса/сопутствующего объекта

На уровне класса, я понимаю понятие:

class Users @Inject() (db: Database) 

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

package models 

import anorm._ 
import anorm.SqlParser._ 
import javax.inject._ 

import play.api.db._ 
import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 


case class User @Inject() (db: Database) (
    id: Option[Long] = None, 
    email: String 
) { 
    def save = { 
     id.map { id => User.findById(id) } match { 
      case None => create 
      case _ => update 
     } 
    } 

    def create = db.withConnection { implicit conn => 
     SQL(
      """INSERT INTO users (email) VALUES ({email})""" 
     ).on(
      'email -> email 
     ).executeUpdate() 

     this 
    } 

    def update = ... 
} 

object User {  
    val simple = { 
     get[Option[Long]]("id") ~ 
     get[String]("email") map { 
      case id ~ email => 
       User(id, email) 
     } 
    } 

    def findById(id: Long) = db.withConnection { implicit conn => 
     SQL("""SELECT * FROM users WHERE id = {id}""").on('id -> id).as(User.simple.singleOpt) 
    } 
} 

Это изменяет подпись класса случае (что делает его непригодным для использования в val simple = { ... }), и я не могу понять, как придать/доступ к БД в объекте компаньона. Попытка @Inject() var db: Database _ в объекте приводит к миру NullPointerExceptions, которого я бы хотел избежать.

Каков рекомендуемый шаблон дизайна для этого распространенного варианта использования в мире инъекций зависимостей?

+4

Класс корпуса не предназначен для инкапсуляции такой функции «обслуживания», и DI не предназначен для работы с объектом – cchantep

ответ

6

Общая схема заключается в том, чтобы поместить функциональность базы данных в отдельный класс. В вашем случае оставить только данные с пользователем:

case class User(id: Option[Long] = None, email: String) 

и поставить функциональность базы данных в отдельный класс:

class UserRepository @Inject()(db: Database) { 
    def save(user: User) = { ... } 
    def create() : User = { ... } 
    def findById(id: Long) : Option[User] = { ... } 
} 

Не знаю, как вы будете использовать в User объекты в код. Но с этим шаблоном вы не носите ссылку на базу данных с каждым пользовательским объектом, в основном протекающим реализацию персистентности везде, где используются пользовательские объекты. Возможно, вы хотите иметь это, но способ создания объекта User в val simple = ... указывает мне, что вы хотите создавать пользовательские объекты, содержащие только данные.

Теперь вы проезжаете объект пользователя и только тогда, когда вам нужна функциональность базы данных, вы вводите UserRepository.

Это точно не дает ответа на ваш вопрос относительно впрыскивания зависимостей в сопутствующие объекты, но может быть в любом случае.

+0

Спасибо! Это хорошая модель - я должен был также упомянуть, что я работаю против этой предыдущей версии Play <2.5 из книги «Learning Play! Framework 2»: https://github.com/andypetrella/play2-book-chapters/blob/ master/chap4/play-sbook/app/models/User.scala –

+0

Я собираюсь пойти и отметить это как принятый ответ, поскольку я думаю, что это самый близкий рефакторинг к намеченному вопросу. Кажется, что образец, представленный в книге выше, не самый функциональный. –