scala:一种类似于地图的结构,它不会';获取值时是否不需要强制转换?

scala:一种类似于地图的结构,它不会';获取值时是否不需要强制转换?,scala,Scala,我正在编写一个转换数据库查询结果的数据结构。原始结构是一个java结果集,它将被转换为一个映射或类,该映射或类允许通过命名方法调用或将字符串传递到apply()来访问该数据结构上的不同字段。显然,不同的值可能有不同的类型。为了减轻此数据结构的客户机的负担,我的偏好是不需要强制转换数据结构的值,但获取的值仍然具有正确的类型 例如,假设我正在执行一个获取两个列值的查询,一个是Int,另一个是String。结果列的名称分别为“a”和“b”。一些理想的语法可能如下所示: val javaResultSe

我正在编写一个转换数据库查询结果的数据结构。原始结构是一个java结果集,它将被转换为一个映射或类,该映射或类允许通过命名方法调用或将字符串传递到apply()来访问该数据结构上的不同字段。显然,不同的值可能有不同的类型。为了减轻此数据结构的客户机的负担,我的偏好是不需要强制转换数据结构的值,但获取的值仍然具有正确的类型

例如,假设我正在执行一个获取两个列值的查询,一个是Int,另一个是String。结果列的名称分别为“a”和“b”。一些理想的语法可能如下所示:

val javaResultSet = dbQuery("select a, b from table limit 1")

// with ResultSet, particular values can be accessed like this:
val a = javaResultSet.getInt("a")
val b = javaResultSet.getString("b")
// but this syntax is undesirable. 

// since I want to convert this to a single data structure, 
// the preferred syntax might look something like this:
val newStructure = toDataStructure[Int, String](javaResultSet)("a", "b")

// that is, I'm willing to state the types during the instantiation
// of such a data structure.

// then,
val a: Int = newStructure("a") // OR
val a: Int = newStructure.a

// in both cases, "val a" does not require asInstanceOf[Int].
我一直在试图确定什么样的数据结构可能允许这样做,但我想不出一个解决方法

另一个要求显然是,我想定义一个用于所有db查询的单一数据结构。我意识到我可以很容易地为每次调用定义一个case类或类似的类,从而解决键入问题,但是当编写许多db查询时,这样的解决方案不能很好地扩展。我怀疑有些人会建议使用某种ORM,但在我的例子中,让我们假设最好以字符串的形式维护查询


有人有什么建议吗?谢谢

要在不强制转换的情况下执行此操作,您需要更多有关查询的信息,并且需要在完成时获得这些信息

我怀疑有些人会建议使用某种ORM,但在我的例子中,让我们假设最好以字符串的形式维护查询


你的怀疑是对的,你无法回避这一点。如果当前的ORMs或dsl(如squeryl)不适合您的喜好,您可以创建自己的一个。但是我怀疑您是否能够使用查询字符串。

基本问题是,您不知道任何给定查询中将有多少列,因此您不知道数据结构应该有多少类型参数,并且不可能抽象出类型参数的数量

然而,对于不同数量的类型参数,有一种数据结构存在于不同的变体中:元组。(例如,Tuple2、Tuple3等)您可以为不同数量的返回元组的参数定义参数化映射函数,如下所示:

def toDataStructure2[T1, T2](rs: ResultSet)(c1: String, c2: String) =
  (rs.getObject(c1).asInstanceOf[T1],
  rs.getObject(c2).asInstanceOf[T2])


def toDataStructure3[T1, T2, T3](rs: ResultSet)(c1: String, c2: String, c3: String) =
  (rs.getObject(c1).asInstanceOf[T1],
  rs.getObject(c2).asInstanceOf[T2],
  rs.getObject(c3).asInstanceOf[T3])
您必须为表中预期的列数定义这些列(最多22列)

这当然取决于使用getObject并将其强制转换为给定类型是安全的

在您的示例中,可以使用生成的元组,如下所示:

val (a, b) = toDataStructure2[Int, String](javaResultSet)("a", "b")

Joschua Bloch引入了一个异构集合,可以用Java编写。我有一次稍微采纳了它。它现在用作值寄存器。它基本上是两个地图的包装器。这是你可以使用的。但这只是供参考,因为您对Scala解决方案感兴趣

在Scala中,我将从处理元组开始。元组是一种异构集合。可以通过
\u 1、\u 2、\u 3
等字段访问结果,但不必访问结果。但你不想要那个,你想要名字。这是您为以下各项分配名称的方式:

scala> val tuple = (1, "word")
tuple: ([Int], [String]) = (1, word)

scala> val (a, b) = tuple
a: Int = 1
b: String = word

因此,如前所述,我将尝试围绕元组构建一个
ResultSetWrapper

如果您决定使用异构集合,异构类型列表上有一些非常有趣的帖子:

一个例子是

第二大系列文章以

本系列继续介绍从a部分链接而来的“b、c、d”部分

最后,丹尼尔·斯皮瓦克(Daniel Spiewak)的一次演讲谈到了霍马普斯

所以,所有这些都表明,也许你可以从这些想法中构建你的解决方案。很抱歉,我没有一个具体的例子,但我承认我自己还没有尝试过这些

如果希望在普通bean实例上“按名称提取列值”,您可能可以:

  • 使用你(和我)不喜欢的反射和投射
  • 使用大多数ORM库提供的ResultSetToJavaBeanMapper,它有点重而且耦合
  • 编写一个scala编译器插件,它太复杂而无法控制
  • 因此,我想具有以下特性的轻量级ORM可能会让您满意:

  • 支持原始SQL
  • 支持轻量级、声明性和自适应ResultSetToJavaBeanMapper
  • 没有别的了 我就这个想法做了一个实验项目,但注意到它仍然是一个ORM,我只是认为它可能对你有用,或者可以给你一些提示

    用法:

    声明模型:

    //declare DB schema
    trait UserDef extends TableDef {
      var name = property[String]("name", title = Some("姓名"))
      var age1 = property[Int]("age", primary = true)
    }
    
    //declare model, and it mixes in properties as {var name = ""}
    @BeanInfo class User extends Model with UserDef
    
    //declare a object.
    //it mixes in properties as {var name = Property[String]("name") }
    //and, object User is a Mapper[User], thus, it can translate ResultSet to a User instance.
    object `package`{
      @BeanInfo implicit object User extends Table[User]("users") with UserDef
    }
    
    然后调用原始sql,隐式映射器[User]为您工作:

    val users = SQL("select name, age from users").all[User] 
    users.foreach{user => println(user.name)}
    
    甚至构建类型安全查询:

    val users = User.q.where(User.age > 20).where(User.name like "%liu%").all[User]
    
    有关更多信息,请参见单元测试:

    计划之家:


    它大量使用“抽象类型”和“隐式”来实现这一神奇的功能,您可以查看TableDef、Table、Model的源代码以了解详细信息。

    数百万年前,我编写了一个示例来展示。过来看;它与你想做的事情非常吻合

    implicit val conn = connect("jdbc:h2:f2", "sa", "");
    implicit val s: Statement = conn << setup;
    val insertPerson = conn prepareStatement "insert into person(type, name) values(?, ?)";
    for (val name <- names) 
                insertPerson<<rnd.nextInt(10)<<name<<!;
    for (val person <- query("select * from person", rs => Person(rs,rs,rs)))
                println(person.toXML);
    for (val person <- "select * from person" <<! (rs => Person(rs,rs,rs)))
                println(person.toXML);
    
    implicit val conn=connect(“jdbc:h2:f2”、“sa”和“);
    
    隐式val s:Statement=conn这似乎是可能的,但如何按名称提取列值?使用此解决方案无法做到这一点。我希望使用类似于异类集合但用scala编写的内容。java方法在scala中不可行有什么原因吗?scala不支持原始类型,这使得我的示例“逐行”无法转换到scala中。但Scala是如此强大……我相信还有其他方式来表达您的需求。另一方面为什么不直接使用Java版本呢?我发布的代码功能齐全…我正在考虑这个选项。然而,它似乎仍然不允许我做某事