Аннотация Riak Java Client не распознается в кейс-классе Scala?

Я использую классы case для определения различных «моделей» данных в нашем приложении. Причина в том, чтобы упростить использование Jerkson (интерфейс Scala для Jackson). Чтобы преобразовать мой User в объект домена в Riak, я использовал аннотацию @RiakKey в своем guid. У меня есть следующее:

case class User(
  @RiakKey val guid: String,

  @RiakIndex(name = "email") val email: String,

  val salt: String,

  val passwordHash: String,

  val emailHash: String,

  val firstName: String,

  val lastName: String,

  val suspended: Boolean=false,

  val created: Timestamp=now

)

Когда я собираюсь выполнить преобразование домена в классе case, @RiakKey не распознается. Он выдает NoKeySpecifedException. Вот мой преобразователь:

class UserConverter(val bucket: String) extends Converter[User] {

  def fromDomain(domainObject: User, vclock: VClock) = {
    val key = getKey(domainObject)

    if(key == null) throw new NoKeySpecifedException(domainObject)

    val kryo = new Kryo()
    kryo.register(classOf[User])

    val ob = new ObjectBuffer(kryo)
    val value = ob.writeObject(domainObject)

    RiakObjectBuilder.newBuilder(bucket, key)
        .withValue(value)
        .withVClock(vclock)
        .withContentType(Constants.CTYPE_OCTET_STREAM)
        .build()
  }
}

Это проблема в Scala с аннотациями Java? Есть ли обходной путь?

Обновлять

Здесь создается и хранится объект User, а также ссылка на преобразователь:

1)

val user = parse[User](body) // jerkson parse, body is a string of JSON
User.store(user)

2)

object User {

  val bucketName = "accounts-users"
  val bucket = DB.client.createBucket(bucketName).execute()

  def fetch(id: String) = bucket.fetch(id).execute().getValueAsString()

  def store(o: User) = bucket.store( o ).withConverter(new UserConverter(bucketName)).execute()

}

Отслеживание следов

com.basho.riak.client.convert.NoKeySpecifedException
        at com.basho.riak.client.bucket.DefaultBucket.store(DefaultBucket.java:455)
        at com.threetierlogic.AccountService.models.User$.store(User.scala:58)
        at com.threetierlogic.AccountService.controllers.Users$$anonfun$routes$3.apply(Users.scala:54)
        at com.threetierlogic.AccountService.controllers.Users$$anonfun$routes$3.apply(Users.scala:51)

person crockpotveggies    schedule 05.02.2013    source источник
comment
Должно работать, судя по тому, что я читал (я ничего не писал на scala). Я знаю, что это глупый вопрос, но я просто хочу проверить... вы установили guid на что-то отличное от null в объекте User, передаваемом в операцию сохранения, да? Используете ли вы свои собственные Mutation и ConflictResolver?   -  person Brian Roach    schedule 06.02.2013
comment
Не использовать мою собственную Mutation и ConflictResolver, это необходимо? Guid задается как строка 0u0700fs984. Я отправляю какой-то фиктивный JSON и сохраняю его в Riak, но синтаксический анализ JSON проходит нормально.   -  person crockpotveggies    schedule 06.02.2013
comment
Возможно ли, что когда Джексон анализирует входящий JSON, аннотации теряются?   -  person crockpotveggies    schedule 06.02.2013
comment
Нет - не обязательно (просто хотел проверить), и если не указано, значения по умолчанию в основном такие, как вы ожидаете (братья и сестры не поддерживаются, и все, что вы передаете вызову хранилища, сохраняется независимо от чего-либо существующего в Riak). Аннотации — это просто маркеры в Java/Scala; они не сохраняются с данными объекта (toDomain() в конвертере использует их, чтобы знать, куда поместить полученные данные).   -  person Brian Roach    schedule 06.02.2013
comment
Редактировать: если я закомментирую строку throw new NoKeySpecifed, похоже, что она фактически выбрасывается при вызове метода хранилища. Интересно, не возвращает ли RiakObjectBuilder ключ.   -  person crockpotveggies    schedule 06.02.2013
comment
Ну вроде как - конвертер не твой :) DefaultBucket.store(T o) проверяет ключ и вот что кидает. Можете ли вы опубликовать свой код, где вы создаете объект User и передаете его методу Bucket.store()? (Это задолго до включения конвертера)   -  person Brian Roach    schedule 06.02.2013
comment
Да просто в конце добавил.   -  person crockpotveggies    schedule 06.02.2013
comment
Если этот JSON содержит поле guid со значением String в нем, а объект User заполняется им... Я в конце своего опыта. Погуглив аннотации java и scala, я не могу найти ничего, кроме того, что должно просто работать.   -  person Brian Roach    schedule 06.02.2013
comment
Будет ли обходной путь использовать DomainBucket.store, и мне нужно создать сегмент домена? Что-то увидел в комментариях вверху DomainBucket.java   -  person crockpotveggies    schedule 06.02.2013


Ответы (2)


(прошу прощения за долгий разговор перед этим ответом)

Узнав немного больше о scala, я обнаружил, что с кейс-классом нужно делать это немного по-другому.

http://piotrbuda.eu/2012/10/scala-case-classes-and-annotations-part-1.html

Если вы сделаете:

@(RiakKey@field) guid: String

оно работает.

Я написал небольшую тестовую программу на scala и смог извлечь аннотированный ключ, используя статический метод getKey(), используемый в DefaultBucket, который возвращал null и вызывал исключение.

import com.basho.riak.client.convert.KeyUtil.getKey;

object Main {

  def main(args: Array[String]): Unit = {
    val u = User("my_key")
    val k = getKey(u)
    System.out.println(k);

  }
}

Пользователь.scala

/* scala 2.9.1 would be scala.annotation.target.field */
import scala.annotation.meta.field
import com.basho.riak.client.convert.RiakKey;

case class User (@(RiakKey@field) guid: String)

Выход:

мой ключ

(И, если вы измените аннотацию на прежнюю, она вернет null, как и ожидалось)

person Brian Roach    schedule 06.02.2013
comment
вау, сам сегодня узнал кое-что новое. Похоже, мне тоже нужно исправить аннотации индекса. большое спасибо за публикацию этого! scala devs на riak везде будут аплодировать! - person crockpotveggies; 07.02.2013
comment
Рад, что смог помочь :) Вы всегда можете связаться с нами напрямую на Riak- Список рассылки пользователей, а также - person Brian Roach; 07.02.2013
comment
Спасибо за ссылку. Я также отмечу, что импорт для Scala 2.9.1 import scala.annotation.target.field выглядит так, как будто структура пакета изменилась в 2.10. - person crockpotveggies; 07.02.2013

Вот мое предложенное решение проблемы. Вместо того чтобы полагаться на аннотации, я просто воспользуюсь методом DefaultBucket.store и назначу ключ вручную.

Мой объект-компаньон пользователя:

object User {

  val bucketName = "accounts-users"
  val bucket = DB.client.createBucket(bucketName).execute()

  def store(key: String, o: User) = bucket.store(key, o).withConverter(new UserConverter(bucketName)).execute()

}

И используя его:

val user = parse[User](body)
User.store(user.guid, user)
person crockpotveggies    schedule 06.02.2013
comment
Догадаться. Иначе это сведет меня с ума. Извините за отсутствие у меня знаний о scala, из-за чего это оказалось сложнее, чем должно было быть :) Опубликовал ответ. - person Brian Roach; 07.02.2013