数据斯塔克斯 Java 驱动,将 Scala 集合转换为 Java 错误

3

我正在尝试将一个Scala Map(我正试图转换为java.util.Map)存储到Cassandra 2.1.8中。

数据结构如下:

Map[String -> Set[Tuple[String, String, String]]]

我按照以下方式创建了表格:
CREATE TABLE mailing (emailaddr text PRIMARY KEY, totalmails bigint, emails map<text, frozen<set<tuple<text, text, text>>>>);

首先,我尝试将Set转换为Java Set:

def emailsToCassandra(addr: emailAddress, mail: MailContent, number: Int) = {
println("Inserting emails into cassandra")

mail.emails.foreach(result =>

  setAsJavaSet(result._2)
)

然后我建立查询并尝试将Map转换为Java Map:

val query = QueryBuilder.insertInto("emails", "mailing")
                        .value("emailAddr", addr.toString())
                        .value("totalmails", number)
                        .value("emails", mapAsJavaMap(mail.emails))
session.executeAsync(query)

我回来了:

java.lang.IllegalArgumentException: Value 1 of type class scala.collection.convert.Wrappers$MapWrapper does not correspond to any CQL3 type

我也尝试过这样做:

val lol = mail.emails.asInstanceOf[java.util.Map[String, java.util.Set[Tuple3[String, String, String]]]]

这不起作用

提前感谢你

3个回答

5

这里有几件事情需要解决:

  1. 将Map类型转换为java.util.Map类型(使用mapAsJavaMap已经完成了此操作)
  2. 将Set[Tuple3]类型转换为java.util.Set类型
  3. 将Tuple3类型转换为TupleValue类型

不幸的是,驱动程序返回的错误信息 (java.lang.IllegalArgumentException: Value 1 of type class scala.collection.convert.Wrappers$MapWrapper does not correspond to any CQL3 type) 是误导性的,因为Map类型在您的代码中已正确转换,但最终会出现Tuple3类型出现问题。我打开了 JAVA-833 来跟踪这个问题。

我对MailContent做了一些假设,但以下代码应该可以使事情正常工作。主要执行实际操作的逻辑是emailsToCql,它将Tuple3[String, String, String]映射到TupleValue,并将Set转换为java.util.Set,将Map转换为java.util.Map。

import com.datastax.driver.core.{DataType, TupleType, Cluster}
import com.datastax.driver.core.querybuilder.QueryBuilder

import scala.collection.JavaConverters._

object Scratch extends App {

  val cluster = Cluster.builder().addContactPoint("127.0.0.1").build()
  val session = cluster.connect()

  session.execute("create keyspace if not exists emails WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };")
  session.execute("create table if not exists emails.mailing (emailaddr text PRIMARY KEY, totalmails bigint, emails map<text, frozen<set<tuple<text, text, text>>>>);")

  val emailType = TupleType.of(DataType.text(), DataType.text(), DataType.text())

  case class MailContent(addr: String, emails: Map[String, Set[Tuple3[String, String, String]]]) {
    lazy val emailsToCql = emails.mapValues {
      _.map(v => emailType.newValue(v._1, v._2, v._3)).asJava
    }.asJava
  }

  val mailContent = MailContent("test@email.com", Map(
    "dest@email.com" -> Set(("field1", "field2", "field3")),
    "dest2@email.com" -> Set(("2field1", "2field2", "2field3"))))

  val query = QueryBuilder.insertInto("emails", "mailing")
                .value("emailAddr", mailContent.addr)
                .value("totalmails", mailContent.emails.size)
                .value("emails", mailContent.emailsToCql)

  session.execute(query)

  cluster.close()
}

这将生成一个在cqlsh中类似下面这样的记录:
 emailaddr      | emails                                                                                                       | totalmails
----------------+--------------------------------------------------------------------------------------------------------------+------------
 test@email.com | {'dest2@email.com': {('2field1', '2field2', '2field3')}, 'dest@email.com': {('field1', 'field2', 'field3')}} |          2

3
我不是Scala专家,但这个错误看起来像是Scala将Map转换为Java时会将其封装到scala.collection.convert.Wrappers$MapWrapper中,它扩展了java.util.AbstractMap,而后者实现了java.util.Map。当Cassandra尝试从Java的类型映射到自己的类型时,它没有遇到MapWrapper作为有效类型。
我建议您编写自己的方法,将整个内容(MailContent,对吧?)从Map [String-> Set [Tuple [String,String,String]]]转换为类似结构,使用Java的等价物:HashMap和HashSet(耸肩)。此外,在此转换过程中,您可能会遇到TupleType的问题,因此应该使用Cassandra的TupleType。

2

user2244255说得对,将其分离为自己的方法帮助我理解了发生了什么。这是最终可行的代码:

def resultToJavaMap(mail: MailContent) ={

 mapAsJavaMap(mail.emails.mapValues{result =>

  setAsJavaSet(result.map {lol =>
    val theType = TupleType.of(DataType.text(), DataType.text(), DataType.text())
    val theValue = theType.newValue()
    theValue.setString(0, lol.to.toString())
    theValue.setString(1, lol.subject)
    theValue.setString(2, lol.message)
  })

})

基本上,您可以访问Set中的元组,将其更改为DataStax类型,然后将整个Set更改为java.util.Set,最后将整个内容更改为java.util.Map


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接