scala:一种类似于地图的结构,它不会';获取值时是否不需要强制转换?
我正在编写一个转换数据库查询结果的数据结构。原始结构是一个java结果集,它将被转换为一个映射或类,该映射或类允许通过命名方法调用或将字符串传递到apply()来访问该数据结构上的不同字段。显然,不同的值可能有不同的类型。为了减轻此数据结构的客户机的负担,我的偏好是不需要强制转换数据结构的值,但获取的值仍然具有正确的类型 例如,假设我正在执行一个获取两个列值的查询,一个是Int,另一个是String。结果列的名称分别为“a”和“b”。一些理想的语法可能如下所示:scala:一种类似于地图的结构,它不会';获取值时是否不需要强制转换?,scala,Scala,我正在编写一个转换数据库查询结果的数据结构。原始结构是一个java结果集,它将被转换为一个映射或类,该映射或类允许通过命名方法调用或将字符串传递到apply()来访问该数据结构上的不同字段。显然,不同的值可能有不同的类型。为了减轻此数据结构的客户机的负担,我的偏好是不需要强制转换数据结构的值,但获取的值仍然具有正确的类型 例如,假设我正在执行一个获取两个列值的查询,一个是Int,另一个是String。结果列的名称分别为“a”和“b”。一些理想的语法可能如下所示: val javaResultSe
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实例上“按名称提取列值”,您可能可以:
//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版本呢?我发布的代码功能齐全…我正在考虑这个选项。然而,它似乎仍然不允许我做某事