Anorm用法
更新日期:
什麼是Anrom
用作寫SQL,但免去JDBC 麻煩的煩惱,典型的JDBC需要create statement/PreparedStatement 師很多try catch,Anorm還簡化了煩人的resultset looping 以及如何將resultset的內容提取
From Java Tutorial1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}
使用方法
在build.sbt 加入1
2
3
4libraryDependencies ++= Seq(
jdbc,
"com.typesafe.play" %% "anorm" % "2.4.0"
)
在program中加入1
2
3
4
5
6import anorm._
import play.api.db.DB
DB.withConnection { implicit c =>
val result: Boolean = SQL("Select 1").execute()
}
1 | 如果你擔心有exception 可以wrap 在try catch內 |
DB是playframework的datasource
執行query
SQL("Select 1")
常用以下各種method
execute()
: 用作any query, 回傳一個Boolean, true 代表提取了resultsetexecuteUpdate()
: 用作update, insert, delete, 回傳受影響的行數executeInsert()
: 用作插入資料, 回傳一個Option[Long] 作用generated IDexecuteQuery()
: 用作提取資料, 回傳SqlQueryResult (資料褲內容)
詳見Reference 2
在SQL 插入數值
Anrom提供方法簡化 PreparedStatement煩複的setXXXXupdateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
方法1: 使用on
1 | SQL( |
{countryCode}
的數值變為 “FRA”
方法2: 使用String Interpolation (Scala > 2.10)
1 | val name = "Cambridge" |
將name
的數值插中$name
當中
提取資料
這裡的API有點難理解, 請先import import anorm.SqlParser._
以使用built in parser
其中一項SQL最常用的功能為提取資料, Anorm 抽取了將SQLQueryResult (ResultSet)變為所需資料的過程, 我們最終需要ResultSetParser
,但ResultSetParser
由RowParser
轉換而來
RowParser
將column data變為我們所需的dataResultSetParser
定義如何parse resultSet,例如不同行數會回傳不同data
例子
1 | val count: Long = |
as
會自動執行executeQuery()
然後將結果給parser用作提取資料
當中RowParser是 scalar[Long]
, scalar是anorm中用作唯一column的parser,將count(*)變為Long
當中ResultSetParser是scalar[Long].single
api 中定義為def single: ResultSetParser[A]
, 因此return Long
利用column name的parser
1 | val populations: List[String ~ Int] = |
str("name")
將name 轉換String, int("population")
將population 轉換為int, 每行結果為String ~ Int
*
代表很多行, 所以總結果為List[String ~ Int]
String ~ Int
是anorm定義的type, 可用pattern matching 進行轉換1
val parser = str("name") ~ int("population") map { case n ~ p => (n, p) }
將RowParser轉換為ResultSetParser
single
: ResultSetParser[A], Returns a result set parser expecting exactly one row to parse.singleOpt
: ResultSetParser[Option[A]], Returns a result set parser for none or one parsed row.*
: ResultSetParser[List[A]], Returns possibly empty list parsed from result.+
: ResultSetParser[List[A]], Returns non empty list parse from result, or raise error if there is no result.?
: RowParser[Option[A]], Returns a row parser for optional column, that will turn missing or null column as None.