Datastax java驱动程序,将scala集合转换为java错误

时间:2015-07-14 19:07:33

标签: scala cassandra datastax-java-driver

我正在尝试将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 个答案:

答案 0 :(得分:5)

在这里你需要克服一些事情:

  1. 将地图转换为java.util.Map类型。 (您已经通过使用mapAsJavaMap覆盖了这一点)
  2. 将Set [Tuple3]转换为java.util.Set类型。
  3. 将Tuple3转换为TupleValue
  4. 不幸的是,驱动程序返回的错误(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,设置为java.util.Set并映射到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
    

答案 1 :(得分:3)

我远不是Scala专家,但错误看起来像Scala“转换”到Java Map意味着将其封装到扩展 scala.collection.convert.Wrappers $ MapWrapper 中> java.util.AbstractMap ,它反过来实现 java.util.Map 。当Cassandra尝试从Java的类型映射到它自己的类型时,它不会遇到MapWrapper作为有效类型。

我建议你编写自己的方法来转换整个事物(MailContent,对吗?)来自Map [String - &gt;使用Java的等价物将[Tuple [String,String,String]]]设置为类似的结构:HashMap和HashSet(耸肩)。此外,您应该遇到TupleType问题,因此在转换过程中应该使用Cassandra的TupleType。

答案 2 :(得分: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类型,然后将整个集合更改为java.util.Set然后将整个事物更改为java.util.Map