news 2026/9/24 12:07:31

Play Framework 2.4 迁移指南:Anorm 独立化与新版本特性全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Play Framework 2.4 迁移指南:Anorm 独立化与新版本特性全解析
  • 后端
  • Web框架

【免费下载链接】playframework

The Community Maintained High Velocity Web Framework For Java and Scala.

项目地址:https://gitcode.com/gh_mirrors/pl/playframework
点击查看免费下载

本指南基于 Play Framework 2.4 迁移文档中关于 Anorm 的章节,系统讲解 Anorm 从 Play 核心中独立出来之后的依赖配置方式,以及 2.4 时代 Anorm 在BatchSql重构、行解析 API、流式处理、类型映射等方面的全部新特性。读完本文,你将掌握 Anorm 2.4 迁移所需的所有改动点,并能用索引取值、fold/foldWhile/withResult、数组与多值参数等新 API 编写更简洁、更安全的 SQL 访问代码。

背景:Anorm 从 Play 核心中独立出去

在 Play 2.4 中,Anorm 被从 Play 的核心中抽离出来,成为一个独立管理、拥有自己生命周期(可以独立发版、独立演进)的单独项目。这一变化与同版本中 Ebean、Play specs2 支持、数据库 evolutions 支持被拆分为独立模块的思路一致——让各个数据库相关组件能够按照自己的节奏演进,而不受 Play 主版本发布周期的约束。关于这次大版本迁移的整体背景,可参见 Play 2.4 迁移指南 与 Play 2.4 新特性概览(其中 Highlights24 的 Anorm 小节 以要点形式概括了本节介绍的全部新特性)。

添加 Anorm 依赖

由于 Anorm 已不再是 Play 核心的组成部分,迁移的第一步就是在构建配置中显式声明对它的依赖。在build.sbt中加入:

libraryDependencies += "org.playframework.anorm" %% "anorm" % "2.6.7"

其中%%会根据项目的 Scala 版本自动选择对应的构建产物。Anorm 的完整版本列表可以从 Maven 中央仓库的org.playframework.anorm组下查询(注意:本文不提供外部链接,请在构建工具或 Maven 仓库页面中检索该坐标)。

版本兼容性提醒:Anorm 2.4.0 起要求 Java 8。最后一个兼容 JDK 1.6 或 1.7 的版本是 Anorm 2.3.9。如果你仍在旧版 JDK 上运行,必须停留在 2.3.9 及以下版本。这与 Play 2.4 整体弃用 Java 6/7、要求 Java 8 的决策是一致的。

值得一提的是,在 Play 2.4 及之前的时代,anorm还可以作为 Play 自带依赖通过PlayImport直接引入。仓库中的 build.sbt 示例 展示了这种经典写法:

libraryDependencies ++= Seq( jdbc, anorm, ehcache )

从 Play 2.5 起官方推荐显式声明独立坐标(即上面的org.playframework.anorm写法),两种方式在你所迁移的目标版本中择一使用即可。

Changes:BatchSql 构造方式重构

新版本 Anorm 包含大量修复与改进。其中最重要的 API 破坏性变化源于 BatchSQL #3016 这一提交:SqlQuerycase class 被重构为 trait + 伴生对象的形式。随之而来的后果是,BatchSql不再接受预先构造的SqlQuery实例,而是直接传入原始 SQL 语句,由BatchSql内部完成创建与校验。

import anorm.BatchSql // 迁移前:直接传入 SqlQuery 实例(2.4 起不再被接受,无法编译) BatchSql(SqlQuery("SQL")) // 迁移后:传入原始 SQL 字符串 BatchSql("SQL") // 更简单、更安全,因为 SqlQuery 会在内部被创建并校验

从源码结构看,这一重构把"SQL 语句是否合法、参数占位符是否匹配"的校验逻辑收敛到了BatchSql内部统一执行,从而避免了调用方绕过校验直接构造SqlQuery的隐患。迁移时只需把所有BatchSql(SqlQuery(...))写法改为BatchSql("...")即可。

Parsing:行解析能力增强

按列索引取值

现在可以从Row上通过列索引直接获取值,适合列名冗长或列位置固定的场景:

val res: (String, String) = SQL("SELECT * FROM Test").map(row => rowString -> rowString // 字符串列 #1 和 #2 )

按标签(label)统一解析

列解析现在按标签统一处理,无论该标签是列名(name)还是别名(alias),都走同一套解析逻辑,不再区分两种来源:

val res: (String, Int) = SQL"SELECT text, count AS i".map(row => rowString -> rowInt )

这里的count AS i是一个聚合函数别名列,在 2.4 之前这类标签的解析路径与普通列名不同,现在已完全统一。

新增 fold 与 foldWhile:面向结果流的折叠操作

这是 Anorm 2.4 新增的流式 API,用于对结果集逐行做累积计算,返回Either[List[Throwable], T]:左侧是所有执行过程中收集到的异常列表,右侧是折叠结果。

fold会处理结果流中的每一行:

val countryCount: Either[List[Throwable], Long] = SQL"Select count(*) as c from Country".fold(0l) { (c, _) => c + 1 }

foldWhile则允许在满足条件时提前终止,例如只读取前 100 本书名:

val books: Either[List[Throwable], List[String]] = SQL("Select name from Books").foldWhile(List[String]()) { (list, row) => if (list.size == 100) (list -> false) // 停止,返回当前 list else (list :+ rowString) -> true // 继续,追加一个书名 }

注意foldWhile的折叠函数返回值是一个二元组(acc, continue):第一元是累积值,第二元是布尔标记,false表示停止遍历。这种设计让"提前截断 + 部分处理"(如分页、限量加载)变得非常直接。

新增 withResult:自定义结果流解析器

如果fold/foldWhile仍不够灵活,可以用withResult提供完全自定义的流解析器。它把游标(Cursor)交给你的函数,由你自行决定何时停止、如何处理每一行:

import anorm.{ Cursor, Row } @annotation.tailrec def go(c: Option[Cursor], l: List[String]): List[String] = c match { case Some(cursor) => if (l.size == 100) l // 自定义上限,部分处理 else { val row = cursor.next() go(cursor.next(), l :+ rowString) } case _ => l } val books: Either[List[Throwable], List[String]] = SQL("Select name from Books").withResult(go(_, List.empty[String]))

该示例演示了与游标配合的尾递归(@annotation.tailrec)写法:遍历过程中可以施加任意自定义逻辑(如数量上限、条件过滤),到达流末尾(None)时返回累积结果。withResultfold/foldWhile之外面向高阶定制场景的通用入口。

类型映射(Type mappings):更丰富的列与参数转换

数组(Array / List)支持

当某列的类型是 JDBC 数组(java.sql.Array)时,Anorm 现在可以把它映射为数组或列表(Array[T]List[T]),前提是元素类型T本身支持列映射:

import anorm.SQL import anorm.SqlParser.{ scalar, * } // 引入数组与元素解析器 import anorm.Column.{ columnToArray, stringToArray } val res: List[Array[String]] = SQL("SELECT str_arr FROM tbl").as(scalar[Array[String]].*)

同时还提供了便捷的数组解析函数SqlParser.arrayTSqlParser.listT,用于显式声明按数组或列表解析。

反过来,如果 JDBC 语句期望一个数组参数(java.sql.Array),也可以直接传入Array[T],只要元素类型T受支持:

val arr = Array("fr", "en", "ja") SQL"UPDATE Test SET langs = $arr".execute()

多值参数(Multi-value parameter)

新增了将List[T]Set[T]SortedSet[T]Stream[T]Vector[T]作为多值参数传入的转换,配合IN ({param})写法可以避免手工拼接 SQL:

SQL("SELECT * FROM Test WHERE cat IN ({categories})") .on('categories -> List(1, 3, 4)) SQL("SELECT * FROM Test WHERE cat IN ({categories})") .on('categories -> Set(1, 3, 4)) SQL("SELECT * FROM Test WHERE cat IN ({categories})") .on('categories -> SortedSet("a", "b", "c")) SQL("SELECT * FROM Test WHERE cat IN ({categories})") .on('categories -> Stream(1, 3, 4)) SQL("SELECT * FROM Test WHERE cat IN ({categories})") .on('categories -> Vector("a", "b", "c"))

on('categories -> ...)中的符号'categories对应 SQL 中的命名占位符{categories}。传入Set/SortedSet时元素的去重与排序由集合本身语义保证,实际展开为 SQL 列表参数。

数值与布尔类型转换的修订

基础类型(数值、布尔)的列转换得到改进。一些原本不合理的转换被移除,迁移后以下写法将不再被接受:

列类型(JDBC 类型)(转为)JVM/Scala 类型
DoubleBoolean
IntBoolean

同时新增了大量数值/布尔转换,扩展了列类型支持:

列类型(JDBC 类型)(转为)JVM/Scala 类型
BigDecimalBigInteger
BigDecimalInt
BigDecimalLong
BigIntegerBigDecimal
BigIntegerInt
BigIntegerLong
BooleanInt
BooleanLong
BooleanShort
ByteBigDecimal
FloatBigDecimal
IntBigDecimal
LongInt
ShortBigDecimal

可以看出,新转换以"数值精度提升/缩窄"为主线(如IntLongBigDecimalBigInteger互相转换),并允许布尔值与数值互转(BooleanInt/Long/Short)。做迁移测试时应重点检查原先依赖Double/IntBoolean的代码。

二进制与大对象(Binary and large data)

二进制列(bytes、stream、blob)新增了映射为Array[Byte]InputStream的转换:

↓JDBC / JVM➞Array[Byte]InputStream¹
Array[Byte]YesYes
Blob²YesYes
Clob³NoNo
InputStream⁴YesYes
Reader⁵NoNo
    1. 类型java.io.InputStream
    1. 类型java.sql.Blob
    1. 类型java.sql.Clob
    1. 类型java.io.Reader(表格原文如此,指流式输入源)
    1. 类型java.io.Reader(文本型字符流,不映射为二进制)

二进制与大对象同样可以作为参数传入:

JVMJDBC
Array[Byte]Long varbinary
Blob¹Blob
InputStream²Long varbinary
Reader³Long varchar
    1. 类型java.sql.Blob
    1. 类型java.io.InputStream
    1. 类型java.io.Reader

注意Clob/Reader这类字符型大对象不会转换为Array[Byte]/InputStream(二进制视角),而是作为文本参数以Long varchar传递,二者语义不可混用。

其他改进(Misc)

  • Joda Time:新增从LongDateTimestamp列到 JodaInstantDateTime的转换。
  • UUID 解析:可将文本列解析为UUID值:SQL("SELECT uuid_as_text").as(scalar[UUID].single)
  • 可空参数:对可空参数传None已标记为废弃,应改用类型安全的Option.empty[T],以便在编译期保留明确的元素类型信息:
// 废弃写法 .on('param -> None) // 推荐写法 .on('param -> Option.empty[String])

实践提示:在 Play 应用中使用 Anorm

配合本次迁移,还有两点实践建议值得一并落实:

  1. 显式声明依赖并核对版本:确认目标 Anorm 版本满足 Java 8 要求,并将构建中的anorm依赖更新为独立坐标(或按你所迁移的 Play 版本选用PlayImportanorm条目,参考 build.sbt 示例)。

  2. 使用自定义执行上下文执行 JDBC 操作:Anorm 的 SQL 执行属于阻塞式 JDBC 操作,官方数据库指南 AccessingAnSQLDatabase 明确建议通过CustomExecutionContext(如DatabaseExecutionContext)来执行,避免占用 Play 的渲染线程池;文档中还给出了以物理核心数估算 JDBC 连接池大小的建议(连接数 ≈ 物理核心数 × 2 + 磁盘轴数)。

迁移检查清单

  • 构建中添加"org.playframework.anorm" %% "anorm"依赖(版本满足 Java 8 要求)
  • 所有BatchSql(SqlQuery(...))改为BatchSql("...")
  • 检查Double/IntBoolean的列转换代码(2.4 起已移除)
  • None可空参数改为Option.empty[T]
  • 新代码优先使用索引/标签取值、fold/foldWhile/withResult、数组与多值参数等新 API
  • 数据库操作迁移到自定义ExecutionContext上执行
  • 后端
  • Web框架

【免费下载链接】playframework

The Community Maintained High Velocity Web Framework For Java and Scala.

项目地址:https://gitcode.com/gh_mirrors/pl/playframework
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 12:05:06

入侵检测系统设计与实现:从架构选型到落地避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 12:04:06

从频段到选型:GPS/北斗/Galileo/GLONASS四大GNSS系统深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 12:02:46

基于STM32的大棚温湿度智能测控系统设计(DHT11 + 分级调控 + 多级报警)

基于STM32的大棚温湿度智能测控系统设计(DHT11 分级调控 多级报警) 一、系统功能总览二、核心模块选型对比 2.1 主控模块2.2 温湿度检测模块2.3 数据显示模块2.4 报警通知模块2.5 执行控制模块 三、系统接线总表四、系统软件设计 4.1 主程序流程设计4.…

作者头像 李华
网站建设 2026/9/24 11:58:56

微信表情怎么导出到电脑?电脑端完整步骤

想在电脑上整理微信表情的人,常常卡在第一步:表情在微信里根本不是一个「文件」,右键也另存不了。微信表情导出到电脑,完整步骤是:电脑版微信搜索并关注「表情保存助手」→ 把表情发给它 → 复制它回复的下载地址到浏览…

作者头像 李华
网站建设 2026/9/24 11:57:42

FineReport替代方案迁移实战:报表工具选型、校验与批量迁移指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 11:56:56

三相全桥MOSFET布局与死区时间:无刷电机驱动硬件设计避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华