为什么 astyanax (java) 不能在我的 Scala 案例类参数列表中识别我的 @Id 注释值? [英] Why won't astyanax (java) recognize my @Id annotated values in my scala case class parameter list?

查看:19
本文介绍了为什么 astyanax (java) 不能在我的 Scala 案例类参数列表中识别我的 @Id 注释值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以这是我的困境:我有一个域模型,其中包含一堆 Scala 中的案例类,例如 UserOrganization.在我的数据访问层(dao、存储库等)中,我使用 astyanax(来自 netflix 的 Java 库),它是实体持久化程序,用于将对象保存到 cassandra 列族.

这是我的 cassandra/astyanax 支持的 DAO 的一些示例代码(是的,我知道我需要做一些更像 Scala 的事情,但我仍在学习 =))

在阅读完这个冗长的描述之后,我基本上是想看看为什么当 java 在 Field 我不想回去重构所有东西,这样我就可以使用持久器,这使得保存实体变得非常简单(即 manager.put(entity)).如果我想继续使用案例类,以便我可以使用更多不可变样式的 scala 和来自 scalaz 的 Lens,那么我将不得不更新 DAO 并手动完成所有持久化,这真的会消磨时间.

所以,如果有人知道我没有看到的东西,请告诉我!提前感谢您抽出时间阅读本文.

场景 1 - 案例类

Astyanax 无法在 val 上获取注释 @Id

<前>@实体case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,@Column(name = "created_on") 覆盖 val createdOn: Option[Date] = None,@Column(name = "modified_on") 覆盖 val modifiedOn: Option[Date] = None,@Column(name = "name") name: Option[String] = None,@Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)

场景 2 - 有伴生对象的类或没有伴生对象的类

Astyanax 无法在 val

上获取 @Id 注释<前>@实体class Organization(@Id @Column(name = "id") 覆盖 val id: Option[UUID] = None,@Column(name = "created_on") 覆盖 val createdOn: Option[Date] = None,@Column(name = "modified_on") 覆盖 val modifiedOn: Option[Date] = None,@Column(name = "name") name: Option[String] = None,@Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)对象组织{def apply(id: Option[UUID] = None,createdOn: Option[Date] = None,修改:选项 [日期] = 无,名称:选项[字符串] = 无,isPaidAccount: Boolean = false) = new Organization(id, createdOn, modifiedOn, name, isPaidAccount)}

场景 3 - Case 类或在块内定义了 val 的类

这很好用,因为它选择 theId 作为用 @Id 注释,但我不想这样做,因为 IdBaseEntity已经定义和 id val 并且破坏了继承的整个目的并且能够将 id 传递给超类

<前>@实体case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,@Column(name = "created_on") 覆盖 val createdOn: Option[Date] = None,@Column(name = "modified_on") 覆盖 val modifiedOn: Option[Date] = None,@Column(name = "name") name: Option[String] = None,@Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn) {@Id @Column(name = "id") val theId: Option[UUID] = id}

数据访问部分

在 manager 中,您将看到对 build() 的调用.Astyanax 检查传入 withEntityType() 的类,在本例中为 classOf[Organization]

当我在类块内声明了一个 val 而不是 case 类或带有伴随对象的常规类/常规类的参数列表时,除了 #3 之外,我的每个场景都失败了.Astyanax 表示该类的已知成员使用 @Id 进行注释,因此它会引发异常.在我进一步挖掘之前,我想我会向社区询问注释 scala 类并将其发送到执行反射的 java 库的细微差别.来源没什么特别的.事实上,这里是失败的相关行:https://github.com/Netflix/astyanax/blob/master/astyanax-entity-mapper/src/main/java/com/netflix/astyanax/entitystore/EntityMapper.java#L89-120

<前>class CassandraOrganizationDAO 使用 OrganizationDAO { 扩展 BaseCassandraDAO[Organization, UUID](Astyanax.context)val ColumnFamilyOrganizations: ColumnFamily[UUID, String] = new ColumnFamily[UUID, String]("组织",TimeUUIDSerializer.get(),StringSerializer.get(),ByteBufferSerializer.get())val ColumnFamilyOrganizationMembers: ColumnFamily[UUID, UUID] = new ColumnFamily[UUID, UUID]("organization_members",TimeUUIDSerializer.get(),TimeUUIDSerializer.get(),DateSerializer.get())val manager: EntityManager[Organization, UUID] = new DefaultEntityManager.Builder[Organization, UUID]().withEntityType(classOf[组织]).withKeyspace(getKeyspace()).withColumnFamily(ColumnFamilyOrganizations).建造()//类的其余部分被省略}

解决方案

我遇到了一个类似的问题,我的注释在构造函数的字段中从未被考虑在内.确实,我使用了 SpringData 并且无法直接在字段上映射注释(如 @Indexed).

要在你的案例类的构造函数中启用注解,你应该首先创建这样的类:

object FixedScalaAnnotations {type Id = com.package containsid.Id @field//替换为正确的包类型列 = com.package containscolumn.Column @field}

然后将其导入到您的案例类中并使用它代替原来的:

import FixedScalaAnnotations._@实体case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,@Column(name = "created_on") 覆盖 val createdOn: Option[Date] = None,@Column(name = "modified_on") 覆盖 val modifiedOn: Option[Date] = None,@Column(name = "name") name: Option[String] = None,@Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)

确保未使用原始软件包.

这里有一篇关于 JPA 的相关文章:http://blog.fakod.eu/2010/07/14/constructor-arguments-with-jpa-annotations/

So here's my dilemma: I have a domain model with a bunch of case classes in scala such as User and Organization. Within my data access layer (dao, repository, etc) I am using astyanax(a java library from netflix) and it's entity persister to save the object to a cassandra column family.

Here is some sample code for my cassandra/astyanax-backed DAOs (yes i know I need to do something more scala-ish, but I'm still learning =))

After reading through this long-winded description, I'm basically looking to see why annotated vals in the param list won't work when java does getDeclaredAnnotations() on the Field I would hate to have to go back and refactor everything so I can use the persister which makes it really simple to save an entity (i.e. manager.put(entity)). If I want to keep using case classes so I can work with more immutable style scala and Lens from scalaz, I'll then have to update the DAO and do all the persisting manually which can really kill time.

So, if anyone knows something that I'm not seeing, please let me know! Thanks in advance for taking the time to read through this.

Scenario 1 - Case Class

Astyanax fails to pick up the annotation @Id on val

@Entity
case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,
                        @Column(name = "created_on") override val createdOn: Option[Date] = None,
                        @Column(name = "modified_on") override val modifiedOn: Option[Date] = None,
                        @Column(name = "name") name: Option[String] = None,
                        @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)

Scenario 2 - Class with companion object or class without companion object

Astyanax fails to pick up the @Id annotation on val

@Entity
class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,
                       @Column(name = "created_on") override val createdOn: Option[Date] = None,
                       @Column(name = "modified_on") override val modifiedOn: Option[Date] = None,
                       @Column(name = "name") name: Option[String] = None,
                       @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)

object Organization {
  def apply(id: Option[UUID] = None,
            createdOn: Option[Date] = None,
            modifiedOn: Option[Date] = None,
            name: Option[String] = None,
            isPaidAccount: Boolean = false) = new Organization(id, createdOn, modifiedOn, name, isPaidAccount)
}

Scenario 3 - Case class or class with val defined inside block

This works fine because it picks up theId as being annotated with @Id, but I don't want do do this because IdBaseEntity already defines and id val and defeats the whole purpose of inheritance and being able to pass id to the superclass

@Entity
case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,
                        @Column(name = "created_on") override val createdOn: Option[Date] = None,
                        @Column(name = "modified_on") override val modifiedOn: Option[Date] = None,
                        @Column(name = "name") name: Option[String] = None,
                        @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn) {
  @Id @Column(name = "id") val theId: Option[UUID] = id
}

The Data access portion

Way down in manager you will see a call to build(). Astyanax examines the class that was passed in to withEntityType(), which in this case is classOf[Organization]

Every one of my scenarios fails except #3 when I have a val declared inside the class block instead of the parameter list for the case class or a regular class/regular class with companion object. Astyanax says that there is know member of that class that is annotated with @Id so it throws an exception. Before I dig any further, I figured I would ask the community about the nuances of annotation a scala class and sending it to a java library that does reflection. The source is nothing special. In fact here are the relevant lines where things fail: https://github.com/Netflix/astyanax/blob/master/astyanax-entity-mapper/src/main/java/com/netflix/astyanax/entitystore/EntityMapper.java#L89-120

class CassandraOrganizationDAO extends BaseCassandraDAO[Organization, UUID](Astyanax.context) with OrganizationDAO {
  val ColumnFamilyOrganizations: ColumnFamily[UUID, String] = new ColumnFamily[UUID, String](
    "organizations",
    TimeUUIDSerializer.get(),
    StringSerializer.get(),
    ByteBufferSerializer.get())

  val ColumnFamilyOrganizationMembers: ColumnFamily[UUID, UUID] = new ColumnFamily[UUID, UUID](
    "organization_members",
    TimeUUIDSerializer.get(),
    TimeUUIDSerializer.get(),
    DateSerializer.get())

  val manager: EntityManager[Organization, UUID] = new DefaultEntityManager.Builder[Organization, UUID]()
    .withEntityType(classOf[Organization])
    .withKeyspace(getKeyspace())
    .withColumnFamily(ColumnFamilyOrganizations)
    .build()

 // the rest of the class is omitted 
}

解决方案

I came across a similar issue, where my annotation were never taken in account in my constructor's fields. Indeed, I used SpringData and it was impossible to directly map annotation (like @Indexed) on a field.

To enable annotations in your case class's constructor, you should first create this kind of class:

object FixedScalaAnnotations {
  type Id = com.packagecontainingid.Id @field //replace by the right package
  type Column = com.packagecontainingcolumn.Column @field
}

And then import it in your case class and use it instead of the original:

import FixedScalaAnnotations._

@Entity
case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None,
                        @Column(name = "created_on") override val createdOn: Option[Date] = None,
                        @Column(name = "modified_on") override val modifiedOn: Option[Date] = None,
                        @Column(name = "name") name: Option[String] = None,
                        @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)

Be sure the original packages aren't used.

Here a related article dealing with JPA: http://blog.fakod.eu/2010/07/14/constructor-arguments-with-jpa-annotations/

这篇关于为什么 astyanax (java) 不能在我的 Scala 案例类参数列表中识别我的 @Id 注释值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆