Scala入门到精通——第二十九节 Scala数据库编程
本节主要内容
- Scala Mavenproject的创建
- Scala JDBC方式訪问MySQL
- Slick简单介绍
- Slick数据库编程实战
- SQL与Slick相互转换
本课程在多数内容是在官方教程上改动而来的,官方给的样例是H2数据库上的。经过本人改造,用在MySQL数据库上,官方教程地址:http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html
1. Scala Mavenproject的创建
本节的project项目採用的是Maven Project,在POM.xml文件里加入以下两个依赖就能够使用scala进行JDBC方式及Slick框架操作MySQL数据库:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>com.typesafe.slick</groupId>
<artifactId>slick_2.11</artifactId>
<version>2.1.0</version>
</dependency>
scala IDE for eclipse 中创建scala Maven项目的方式例如以下:
在Eclispe 中点击” File->new->other”。例如以下图
输入Maven能够看到Maven Project:
直接next。得到
再点击next,在filter中输入scala得到:
选中,然后next输入相应的groupId等。直接finish就可以。创建完项目将上述依赖加入到pom.xml文件其中。这样就完毕了scala maven Project的创建。
2. Scala JDBC方式訪问MySQL
以下给出的是scala採用JDBC訪问MySQL的代码演示样例
package cn.scala.xtwy.jdbc
import java.sql.{ Connection, DriverManager }
object ScalaJdbcConnectSelect extends App {
// 訪问本地MySQLserver,通过3306端口訪问mysql数据库
val url = "jdbc:mysql://localhost:3306/mysql"
//驱动名称
val driver = "com.mysql.jdbc.Driver"
//用户名
val username = "root"
//密码
val password = "123"
//初始化数据连接
var connection: Connection = _
try {
//注冊Driver
Class.forName(driver)
//得到连接
connection = DriverManager.getConnection(url, username, password)
val statement = connection.createStatement
//运行查询语句,并返回结果
val rs = statement.executeQuery("SELECT host, user FROM user")
//打印返回结果
while (rs.next) {
val host = rs.getString("host")
val user = rs.getString("user")
println("host = %s, user = %s".format(host, user))
}
} catch {
case e: Exception => e.printStackTrace
}
//关闭连接。释放资源
connection.close
}
3. Slick简单介绍
在前一小节中我们演示了怎样通过JDBC进行数据库訪问。相同在Scala中也能够利用JAVA中的ORM框架如Hibernate、IBatis等进行数据库的操纵,但它们都是Java风格的数据库操纵方式,Scala语言中也有着自己的ORM框架。眼下比較流行的框架包括:
1、Slick (typesafe公司开发)
2、Squeryl
3、Anorm
4、ScalaActiveRecord (基于Squeryl之上)
5、circumflex-orm
6、activate-framework(Scala版的Hibernate)
本节课程要讲的便是Slick框架,它是Scala语言创建者所成立的公司TypeSafe所开发的一个Scala风格的开源数据库操纵框架,它眼下支持以下几种主流的数据:
DB2 (via slick-extensions)
Derby/JavaDB
H2
HSQLDB/HyperSQL
Microsoft Access
Microsoft SQL Server (via slick-extensions)
MySQL
Oracle (via slick-extensions)
PostgreSQL
SQLite
当然它也支持其他数据,仅仅只是功能可能还不完好。在Slick中。能够像訪问Scala自身的集合一样对数据库进行操作。它具有例如以下几个特点:
1 数据库的訪问採用Scala风格:
//以下给出的是数据查询操作
class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
def name = column[String]("COF_NAME", O.PrimaryKey)
def price = column[Double]("PRICE")
def * = (name, price)
}
val coffees = TableQuery[Coffees]
//以下给出的数据訪问API
// Query that only returns the "name" column
coffees.map(_.name)
// Query that does a "where price < 10.0"
coffees.filter(_.price < 10.0)
从上面的代码能够看到。Slick訪问数据库就跟Scala操纵自身的集合一样.
2 Slick数据操纵是类型安全的
// The result of "select PRICE from COFFEES" is a Seq of Double
// because of the type safe column definitions
val coffeeNames: Seq[Double] = coffees.map(_.price).list
// Query builders are type safe:
coffees.filter(_.price < 10.0)
// Using a string in the filter would result in a compilation error
3 支持链式操作
// Create a query for coffee names with a price less than 10, sorted by name
coffees.filter(_.price < 10.0).sortBy(_.name).map(_.name)
// The generated SQL is equivalent to:
// select name from COFFEES where PRICE < 10.0 order by NAME
4. Slick 数据库编程实战
以下的代码演示了Slick怎样创建数据库表、怎样进行数据插入操作及怎样进行数据的查询操作(以MySQL为例):
package cn.scala.xtwy
//导入MySQL相关方法
import scala.slick.driver.MySQLDriver.simple._
object UseInvoker extends App {
// 定义一个Test表
//表中包括两列,各自是id,name
class Test(tag: Tag) extends Table[(Int, String)](tag, "Test") {
def k = column[Int]("id", O.PrimaryKey)
def v = column[String]("name")
// Every table needs a * projection with the same type as the table's type parameter
//每一个Table中都应该有*方法。它的类型必须与前面定义的类型參数(Int, String)一致
def * = (k, v)
}
//创建TableQuery对象(这里调用的是TableQuery的apply方法
//没有显式地调用new
val ts = TableQuery[Test]
//forURL注冊MySQL驱动器,传入URL,用户名及密码
//方法回返的是一个DatabaseDef对象,然后再调用withSession方法
Database.forURL("jdbc:mysql://localhost:3306/slick", "root","123",
driver = "com.mysql.jdbc.Driver") withSession {
//定义一个隐式值
//implicit session: MySQLDriverbackend.Session
//兴许方法中当做隐式參数传递
implicit session =>
// 创建Test表
//create方法中带有一个隐式參数
//def create(implicit session: JdbcBackend.SessionDef): Unit
ts.ddl.create
//插入数据
//def insertAll(values: U*)(implicit session: JdbcBackend.SessionDef): MultiInsertResult
ts.insertAll(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d", 5 -> "e")
//数据库查询(这里返回全部数据)
ts.foreach { x => println("k="+x._1+" v="+x._2) }
//这里查询返回全部主鍵 <3的
ts.filter { _.k <3 }.foreach { x => println("k="+x._1+" v="+x._2) }
}
//模式匹配方式
ts.foreach { case(id,name) => println("id="+id+" name="+name) }
}
以下我们再给一个更为复杂的样例来演示Slick中是怎样进行数据的入库与查询操作的:
package cn.scala.xtwy
import scala.slick.driver.MySQLDriver.simple._
object CoffeeExample extends App {
// Definition of the SUPPLIERS table
//定义Suppliers表
class Suppliers(tag: Tag) extends Table[(Int, String, String, String, String, String)](tag, "SUPPLIERS") {
def id = column[Int]("SUP_ID", O.PrimaryKey) // This is the primary key column
def name = column[String]("SUP_NAME")
def street = column[String]("STREET")
def city = column[String]("CITY")
def state = column[String]("STATE")
def zip = column[String]("ZIP")
// Every table needs a * projection with the same type as the table's type parameter
def * = (id, name, street, city, state, zip)
}
val suppliers = TableQuery[Suppliers]
// Definition of the COFFEES table
//定义Coffees表
class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
def name = column[String]("COF_NAME", O.PrimaryKey)
def supID = column[Int]("SUP_ID")
def price = column[Double]("PRICE")
def sales = column[Int]("SALES")
def total = column[Int]("TOTAL")
def * = (name, supID, price, sales, total)
// A reified foreign key relation that
//can be navigated to create a join
//外鍵定义,它使得supID域中的值关联到suppliers中的id
//从而保证表中数据的正确性
//它定义的事实上是一个n:1的关系,
//即一个Coffees相应仅仅有一个Suppliers,
//而一个Suppliers能够相应多个Coffees
def supplier = foreignKey("SUP_FK", supID, suppliers)(_.id)
}
val coffees = TableQuery[Coffees]
Database.forURL("jdbc:mysql://localhost:3306/slick", "root", "123",
driver = "com.mysql.jdbc.Driver") withSession {
implicit session =>
// Create the tables, including primary and foreign keys
//按顺序创建表
(suppliers.ddl ++ coffees.ddl).create
// Insert some suppliers
//插入操作,集合的方式
suppliers += (101, "Acme, Inc.", "99 Market Street", "Groundsville", "CA", "95199")
suppliers += (49, "Superior Coffee", "1 Party Place", "Mendocino", "CA", "95460")
suppliers += (150, "The High Ground", "100 Coffee Lane", "Meadows", "CA", "93966")
// Insert some coffees (using JDBC's batch insert feature, if supported by the DB)
//批量插入操作,集合方式
coffees ++= Seq(
("Colombian", 101, 7.99, 0, 0),
("French_Roast", 49, 8.99, 0, 0),
("Espresso", 150, 9.99, 0, 0),
("Colombian_Decaf", 101, 8.99, 0, 0),
("French_Roast_Decaf", 49, 9.99, 0, 0))
//返回表中全部数据,相当于SELECT * FROM COFFEES
coffees foreach {
case (name, supID, price, sales, total) =>
println(" " + name + "\t" + supID + "\t" + price + "\t" + sales + "\t" + total)
}
//返回表中全部数据。仅仅只是这次是直接让数据库帮我们进行转换
val q1 = for (c <- coffees)
yield LiteralColumn(" ") ++ c.name ++ "\t" ++ c.supID.asColumnOf[String] ++
"\t" ++ c.price.asColumnOf[String] ++ "\t" ++ c.sales.asColumnOf[String] ++
"\t" ++ c.total.asColumnOf[String]
// The first string constant needs to be lifted manually to a LiteralColumn
// so that the proper ++ operator is found
q1 foreach println
//联合查询
//採用===进行比較操作,而非==操作符。用于进行值比較
//相同的还有!=值不等比較符
//甚至其他比較操作符与scala是一致的 <, <=, >=, >
val q2 = for {
c <- coffees if c.price < 9.0
s <- suppliers if s.id === c.supID
} yield (c.name, s.name)
}
5. SQL与Slick相互转换
package cn.scala.xtwy
import scala.slick.driver.MySQLDriver.simple._
import scala.slick.jdbc.StaticQuery.interpolation
import scala.slick.jdbc.GetResult
object SQLAndSlick extends App {
type Person = (Int, String, Int, Int)
class People(tag: Tag) extends Table[Person](tag, "PERSON") {
def id = column[Int]("ID", O.PrimaryKey)
def name = column[String]("NAME")
def age = column[Int]("AGE")
def addressId = column[Int]("ADDRESS_ID")
def * = (id, name, age, addressId)
def address = foreignKey("ADDRESS", addressId, addresses)(_.id)
}
lazy val people = TableQuery[People]
type Address = (Int, String, String)
class Addresses(tag: Tag) extends Table[Address](tag, "ADDRESS") {
def id = column[Int]("ID", O.PrimaryKey)
def street = column[String]("STREET")
def city = column[String]("CITY")
def * = (id, street, city)
}
lazy val addresses = TableQuery[Addresses]
val dbUrl = "jdbc:mysql://localhost:3306/slick"
val jdbcDriver = "com.mysql.jdbc.Driver"
val user = "root"
val password = "123"
val db = Database.forURL(dbUrl, user, password, driver = jdbcDriver)
db.withSession { implicit session =>
(people.ddl ++ addresses.ddl).create
//插入address数据
addresses += (23, "文一西路", "浙江省杭州市")
addresses += (41, "紫荆花路", "浙江省杭州市")
//插入people数据
people += (1, "摇摆少年梦", 27, 23)
people += (2, "john", 28, 41)
people += (3, "stephen", 28, 23)
//以下两条语句是等同的(获取固定几个字段)
val query = sql"select ID, NAME, AGE from PERSON".as[(Int, String, Int)]
query.list.foreach(x => println("id=" + x._1 + " name=" + x._2 + " age=" + x._3))
val query2 = people.map(p => (p.id, p.name, p.age))
query2.list.foreach(x => println("id=" + x._1 + " name=" + x._2 + " age=" + x._3))
//以下两条语句是等同的(Where语句)
val query3 = sql"select * from PERSON where AGE >= 18 AND NAME = '摇摆少年梦'".as[Person]
query3.list.foreach(x => println("id=" + x._1 + " name=" + x._2 + " age=" + x._3))
val query4 = people.filter(p => p.age >= 18 && p.name === "摇摆少年梦")
query4.list.foreach(x => println("id=" + x._1 + " name=" + x._2 + " age=" + x._3))
//orderBy
sql"select * from PERSON order by AGE asc, NAME".as[Person].list
people.sortBy(p => (p.age.asc, p.name)).list
//max
sql"select max(AGE) from PERSON".as[Option[Int]].first
people.map(_.age).max
//隐式join
sql"""
select P.NAME, A.CITY
from PERSON P, ADDRESS A
where P.ADDRESS_ID = a.id
""".as[(String, String)].list
people.flatMap(p =>
addresses.filter(a => p.addressId === a.id)
.map(a => (p.name, a.city))).run
// or equivalent for-expression:
(for (
p <- people;
a <- addresses if p.addressId === a.id
) yield (p.name, a.city)).run
//join操作
sql"""select P.NAME, A.CITY from PERSON P join ADDRESS A on P.ADDRESS_ID = a.id """.as[(String, String)].list
(people join addresses on (_.addressId === _.id))
.map{ case (p, a) => (p.name, a.city) }.run
}
}
上面列出的仅仅是Slick与SQL的部分转换,还有诸如:Update、Delete等操作能够參见:http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html
加入公众微信号,能够了解很多其他最新Spark、Scala相关技术资讯
Scala入门到精通——第二十九节 Scala数据库编程的更多相关文章
- Scala入门到精通——第二十四节 高级类型 (三)
作者:摆摆少年梦 视频地址:http://blog.csdn.net/wsscy2004/article/details/38440247 本节主要内容 Type Specialization Man ...
- Scala入门到精通——第十九节 隐式转换与隐式參数(二)
作者:摇摆少年梦 配套视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 隐式參数中的隐式转换 函数中隐式參数使用概要 隐式转换问题梳理 1. 隐式參数中的 ...
- Scala入门到精通——第十六节 泛型与注解
本节主要内容 泛型(Generic Type)简单介绍 注解(Annotation)简单介绍 注解经常使用场景 1. 泛型(Generic Type)简单介绍 泛型用于指定方法或类能够接受随意类型參数 ...
- Scala入门到精通——第十五节 Case Class与模式匹配(二)
本节主要内容 模式匹配的类型 for控制结构中的模式匹配 option类型模式匹配 1. 模式的类型 1 常量模式 object ConstantPattern{ def main(args: Arr ...
- centos MySQL主从配置 ntsysv chkconfig setup命令 配置MySQL 主从 子shell MySQL备份 kill命令 pid文件 discuz!论坛数据库读写分离 双主搭建 mysql.history 第二十九节课
centos MySQL主从配置 ntsysv chkconfig setup命令 配置MySQL 主从 子shell MySQL备份 kill命令 pid文件 discuz!论坛数 ...
- 大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2)
大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2) 上一节中我们讲解了随机森林的基本概念,本节的话我们讲解随机森 ...
- 风炫安全web安全学习第二十九节课 CSRF防御措施
风炫安全web安全学习第二十九节课 CSRF防御措施 CSRF防御措施 增加token验证 对关键操作增加token验证,token值必须随机,每次都不一样 关于安全的会话管理(SESSION) 不要 ...
- Simulink仿真入门到精通(十九) 总结回顾&自我练习
从2019年12月27到2020年2月12日,学习了Simulink仿真及代码生成技术入门到精通,历时17天. 学习的比较粗糙,有一些地方还没理解透彻,全书梳理总结: Simulink的基础模块已基本 ...
- Scala入门到精通——第二十七节 Scala操纵XML
本节主要内容 XML 字面量 XML内容提取 XML对象序列化及反序列化 XML文件读取与保存 XML模式匹配 1. XML 字面量 XML是一种很重要的半结构化数据表示方式,眼下大量的应用依赖于XM ...
随机推荐
- 异常Exception
try…catch…finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的“教训”告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看 ...
- 线性同余同余方程组解法(excrt)
[问题描述] 求关于 x 的同余方程组 x%a 1 =b 1 a1=b1 x%a 2 =b 2 a2=b2 x%a 3 =b 3 a3=b3 x%a 4 =b 4 a4=b4 的大于等于 0 ...
- SCOPE_IDENTITY()和 SELECT @@IDENTITY 的用法
这俩个,是在插入数据的时候使用,返回刚刚插入数据行的ID 大家慎用@@IDENTITY,而尽量采用 SCOPE_IDENTITY() 函数替换之. SCOPE_IDENTITY() 也是得到最后一条 ...
- MVC页面常用方法
转自:https://www.cnblogs.com/baisoft/p/5839319.html @RenderBody 在Razor引擎中没有了“母版页”,取而代之的是叫做“布局”的页面(_Lay ...
- jython awt demo
jython awt demo : """\ Create a panel showing all of the colors defined in the pawt.c ...
- Ubuntu16.04添加HP Laserjet Pro M128fn打印机和驱动
一.全部设置->打印机->添加新打印机 添加打印机 二.选择自动搜索到的网络打印机HP Laserjet Pro M128fn,点击添加. 三.添加打印机完成,打印测试页进行测试. 四. ...
- selenium+python 安装使用
一.序言 selenium官网selenium简单教程selenium完整教程python基础教程 二.安装python3 https://www.python.org/downloads/relea ...
- BZOJ2690: 字符串游戏(平衡树动态维护Dfs序)
Description 给定N个仅有a~z组成的字符串ai,每个字符串都有一个权值vi,有M次操作,操作分三种: Cv x v':把第x个字符串的权值修改为v' Cs x a':把第x个字符串修改成a ...
- 机器学习实践:《Python机器学习实践指南》中文PDF+英文PDF+代码
机器学习是近年来渐趋热门的一个领域,同时Python 语言经过一段时间的发展也已逐渐成为主流的编程语言之一.<Python机器学习实践指南>结合了机器学习和Python 语言两个热门的领域 ...
- Django_模板HTML