2016-09-08 4 views
2

У меня есть структура, которая имеет 2 отношения: компания и местоположение. Чтобы сохранить классификацию, мне нужно знать идентификаторы ее отношений, которые могут потребовать персистентности сущности сначала (то есть она может уже существовать в базе данных, иначе она должна быть вставлена).Slick 3.1.1: Вставьте объект со своими отношениями в одной транзакции

Идентификаторы - это UUID, назначенные приложением (то есть не автоинкрементные с помощью db), поэтому приложение назначает идентификатор, который либо окажется идентификатором объекта, либо будет заменен фактическим идентификатором в транзакции, если объект уже существует.

код, который делает это выглядит следующим образом:

def create(classified: Classified, company: Company, location: Location): Future[String] = { 
val interaction = for { 
    comp <- companies.filter(_.name === company.name).result.headOption flatMap { 
    case None => companies returning companies.map(_.id) += company 
    case Some(comp) => DBIO.successful(comp.id.get) 
    } 
    loc <- locations.filter(_.name === location.name).result.headOption flatMap { 
    case None => locations returning locations.map(_.id) += location 
    case Some(loc) => DBIO.successful(loc.id.get) 
    } 
    cl <- classifieds returning classifieds.map(_.id) += classified.copy(companyId = comp, locationId = loc) 
} yield cl 
db.run(interaction.transactionally) 

}

Вышеприведенные отлично работает, когда побежал против Postgres (который является производственной базой данных), но не для H2 (который является испытанием и dev database) с ошибкой: [SlickException: эта СУБД позволяет возвращать только один столбец AutoInc из INSERT]

Похоже, что драйвер H2 не возвращает идентификаторы, если они не являются разновидностями автоинкремента.

Итак, как эту транзакцию можно записать так, чтобы а) вставки произошли в одной транзакции б) с минимальными обратными переходами db и c) в нейтральной базе данных?

РЕДАКТИРОВАТЬ:

выше метод используется от контроллера следующим образом:

classifiedDao.create(
     Classified(Some(UUID.randomUUID().toString), c.title, Jsoup.clean(c.body, Whitelist.basic()), c.refNo, "", ""), 
     Company(Some(UUID.randomUUID().toString), c.companyName, c.companyEmail, None, None), 
     Location(Some(UUID.randomUUID().toString), c.location, None) 
    ).map(_ => 
     Redirect(routes.Classifieds.form()).flashing("success" -> "Classified submitted") 
    ) 
+1

Просто уточнить:. Есть '' company' и location' уже есть UUID, назначенный на вставке (строка 'случай None => компании возвращающиеся companies.map (_ ID) + = company')? – Roman

+0

Да.Контроллер назначает идентификаторы всем трем объектам, а затем передает объекты в DAO, которые должны быть сохранены, если объект уже сохранен, идентификатор отбрасывается, а тот, который извлекается из db, используется отдельно от классифицированного, который всегда используется. – javito

+0

@Roman этот комментарий был чистым гением. В случае «Нет» я уже знаю идентификатор, все, что мне нужно сделать, это выполнить результат с другими DBActions, и я закончен. Спасибо, это было блестяще. PS: Я отправляю ответ ниже для потомков. – javito

ответ

0

Для сликовое returning работать Id должен быть auto generated primary key

Id не должны быть только первичным но и автоинкремент.

Примечание

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

Так что напишите функцию, которая возвращает Id после вставки в базу данных следующим образом.

def getIdAfterInsert(entity: Entity): DBIO[EntityId] = { 
    (entities += entity).flatMap { _ => 
    entities.filter(_.name === entity.name).result.flatMap { 
     case Some(entity) => DBIO.successful(entity.id) 
     case None => DBIO.fail(new Exception("something terrible happened")) 
    } 
    }.transactionally 
} 
1

Оказывается, извлекая идентификатор из БД является легкой частью, так как код, который делает это уже имеет необходимый тип (DBIOAction), тяжелая часть получала идентификатор после вставки, но в этом случае Я уже знал идентификатор, потому что это мой код, который его установил. Нет необходимости использовать return() и полагаться на СУБД.

Решение:

def create(classified: Classified, company: Company, location: Location): Future[String] = { 
val interaction = for { 
    comp <- companies.filter(_.name === company.name).result.headOption flatMap { 
    case None => { 
     companies += company 
     DBIO.successful(company.id.get) 
    } 
    case Some(comp) => DBIO.successful(comp.id.get) 
    } 
    loc <- locations.filter(_.name === location.name).result.headOption flatMap { 
    case None => { 
     locations += location 
     DBIO.successful(location.id.get) 
    } 
    case Some(loc) => DBIO.successful(loc.id.get) 
    } 
    cl <- { 
    classifieds += classified.copy(companyId = comp, locationId = loc) 
    DBIO.successful(classified.id.get) 
    } 
} yield cl 
db.run(interaction.transactionally) 
} 
+0

Рад, что я мог помочь. Вы можете подумать об использовании 'DBIOAction.andThen', чтобы убедиться, что вставка успешно выполнена:' (компании + = компания) .andThen (DBIO.successful (company.id.get)) '. – Roman

Смежные вопросы