【问题标题】:Datastax java driver, convert scala collections to java errorDatastax java驱动程序,将scala集合转换为java错误
【发布时间】:2015-07-14 19:07:33
【问题描述】:

我正在尝试将 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]]]]

没用

提前谢谢你

【问题讨论】:

    标签: scala cassandra datastax-java-driver


    【解决方案1】:

    这里有几件事你需要克服:

    1. 将地图转换为 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,设置为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
    

    【讨论】:

      【解决方案2】:

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

      我建议您编写自己的方法,使用 Java 的等价物将整个事物(MailContent,对吗?)从 Map[String -> Set[Tuple[String, String, String]]] 转换为类似的结构: HashMap 和 HashSet(耸耸肩)。此外,您应该对 TupleType 有问题,因此您应该在此转换期间使用 Cassandra 的 TupleType。

      【讨论】:

        【解决方案3】:

        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

        【讨论】:

          猜你喜欢
          • 2018-12-17
          • 2015-03-12
          • 2010-10-15
          • 2014-05-04
          • 2016-11-29
          • 2016-11-29
          • 1970-01-01
          • 2018-02-15
          • 1970-01-01
          相关资源
          最近更新 更多