浅谈Slick(2)- Slick101:第一个动手尝试的项目

   看完Slick官方网站上关于Slick3.1.1技术文档后决定开始动手建一个项目来尝试一下Slick功能的具体使用方法。我把这个过程中的一些了解和想法记录下来和大家一起分享。首先我用IntelliJ-Idea创建了一个scala项目。下一步就是如何选择数据库了。Slick是集成jdbc的更高层的Query编程语言,可以通过jdbc的url、DataSource等来指定目标数据库类型及相关的参数。对应Slick中的具体函数有:

val db = Database.forConfig("mydb")
val db = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver="org.h2.Driver")
val db = Database.forDataSource(dataSource: slick.jdbc.DatabaseUrlDataSource)

在Slick的Database配置方面forConfig("confItem")是比较灵活、方便实用的。confItem是resources/application.conf文件里的一个配置项目。Slick是通过typesafe-config来解析配置文件的。forConfig函数用typesafe-config库里的函数载入application.conf文件解析confItem并获取项目里的数据库配置参数,下面是项目中resources/application.conf文件内容:

h2mem {
    url = "jdbc:h2:mem:slickdemo"
    driver = "org.h2.Driver"
    connectionPool = disabled
    keepAliveConnection = true
}

h2 = {
    url = "jdbc:h2:~/slickdemo;mv_store=false;MODE=MSSQLServer;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE"
    driver = org.h2.Driver
    connectionPool = disabled
    keepAliveConnection = true
}

mysql {
  driver = "slick.driver.MySQLDriver$"
  db {
    url = "jdbc:mysql://localhost/slickdemo"
    driver = com.mysql.jdbc.Driver
    keepAliveConnection = true
    user="root"
    password="123"
    numThreads=10
    maxConnections = 12
    minConnections = 4
  }
}

mysqldb = {
  dataSourceClass = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
  properties {
    user = "root"
    password = "123"
    databaseName = "slickdemo"
    serverName = "localhost"
  }
  numThreads = 10
  maxConnections = 12
  minConnections = 4
}

postgres {
  driver = "slick.driver.PostgresDriver$"
  db {
    url = "jdbc:postgresql://127.0.0.1/slickdemo"
    driver = "org.postgresql.Driver"
    connectionPool = HikariCP
    user = "slick"
    password = "123"
    numThreads = 10
    maxConnections = 12
    minConnections = 4
  }
}

postgressdb = {
  dataSourceClass = "org.postgresql.ds.PGSimpleDataSource"
  properties = {
    databaseName = "slickdemo"
    user = "slick"
    password = "123"
  }
  connectionPool = HikariCP
  numThreads = 10
  maxConnections = 12
  minConnections = 4
}

mssql {
  driver = "com.typesafe.slick.driver.ms.SQLServerDriver$"
  db {
    url = "jdbc:sqlserver://host:port"
    driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
    connectionTimeout = 30 second
    connectionPool = HikariCP
    user = "slick"
    password = "123"
    numThreads = 10
    maxConnections = 12
    minConnections = 4
    keepAliveConnection = true
  }
}

tsql {
  driver = "slick.driver.H2Driver$"
  db = ${h2mem}
}

在我使用的application.conf文件中汇集了一些常用数据库的配置,我一并提供出来。除h2之外其它都没进行测试验证,具体配置参数和方法要参考数据库开发商提供的技术文档。我在这个示范里选用了h2配置:它会在我的用户根目录下创建一个slickdemo.h2.db数据库文件。 好了,选择了数据库,下面我们就来试试使用它。基本流程是这样的:首先在数据库里创建表,跟着写入一些数据,然后再读出显示。整个过程会涉及:表结构schema定义,数据插写Insert,数据读取Query及简单的Query运算方法和数据显示方法。

现在我们先设计表结构schema:

 1 package com.datatech.learn.slick101
 2 import slick.driver.H2Driver.api._
 3 object slick101 {
 4 
 5 /* ----- schema  */
 6   //表字段对应模版
 7   case class AlbumModel (id: Long
 8                    ,title: String
 9                    ,year: Option[Int]
10                    ,artist: String
11                    )
12   //表结构: 定义字段类型, * 代表结果集字段
13   class AlbumTable(tag: Tag) extends Table[AlbumModel](tag, "ALBUMS") {
14     def id = column[Long]("ID",O.AutoInc,O.PrimaryKey)
15     def title = column[String]("TITLE")
16     def year = column[Option[Int]]("YEAR")
17     def artist = column[String]("ARTIST",O.Default("Unknown"))
18     def * = (id,title,year,artist) <> (AlbumModel.tupled, AlbumModel.unapply)
19   }
20   //库表实例
21   val albums = TableQuery[AlbumTable]

在这个示范里我们确定使用H2数据库,所以需要import H2Driver.api。使用了case class AlbumModel作为库表字段对应模版。这样一是可以规范代码,再就是如果遇到一个宽表有很多列的话可以节省许多重复铺垫及避免无谓错误。

现在需要从库表实例albums产生它的schema,然后转换成一个DBIOAction:

  //创建表动作
 val createTableAction = albums.schema.create

这个createTableAction就是个DBIOAction:一个效果描述。我们必须用具体的实现方式Database.run来运算产生实际效果:

1   //数据库实例化
2 val db = Database.forConfig("h2")
3 def main(args: Array[String]): Unit = {
4    val res = db.run(createTableAction).andThen {
5        case Success(_) => println("table ALBUMS created.")
6        case Failure(e) => println(e.getMessage)
7    }
8    Await.result(res, 10 seconds)
9 }

db.run返回Future类型。我们是用Future类型的andThen组件来显示运算结果的:

table ALBUMS created.

Process finished with exit code 0

如果跟着再运算一次应该会产生重复重建错误:

Exception in thread "main" org.h2.jdbc.JdbcSQLException: Table "ALBUMS" already exists; SQL statement:
Table "ALBUMS" already exists; SQL statement: ...

下面是一个插入数据的动作:

1 //插入数据动作
2   val insertAlbumsAction =
3   albums ++= Seq(
4     AlbumModel(0, "Keyboard Cat", Some(2003), "Keyboard Cat's Greatest Hits"),
5     AlbumModel(0, "Spice Girls", Some(2010), "Spice"),
6     AlbumModel(0, "Rick Astley", Some(1998), "Whenever You Need Somebody"),
7     AlbumModel(0, "Manowar", None,"The Triumph of Steel"),
8     AlbumModel(0, "Justin Bieber", Some(2011),"Believe"))

运算及显示结果:

 1    val res2 = db.run(insertAlbumsAction).andThen {
 2       case Success(_) => println("albums inserted.")
 3       case Failure(e) => println(e.getMessage)
 4     }
 5     Await.result(res2, 10 seconds)
 6 ---
 7 table ALBUMS created.
 8 albums inserted.
 9 
10 Process finished with exit code 0

下面是抽取动作和数据显示函数。我们把新插入的数据再读出来验证插入情况:

//数据抽取动作
  val selectAlbumsAction =
  albums.result
  def printResults[T](fut: Future[Iterable[T]]): Unit =
    Await.result(fut, Duration.Inf).foreach(println)

  val res3 = db.run(selectAlbumsAction)
  printResults(res3)

运算结果:

AlbumModel(1,Keyboard Cat,Some(2003),Keyboard Cat's Greatest Hits)
AlbumModel(2,Spice Girls,Some(2010),Spice)
AlbumModel(3,Rick Astley,Some(1998),Whenever You Need Somebody)
AlbumModel(4,Manowar,None,The Triumph of Steel)
AlbumModel(5,Justin Bieber,Some(2011),Believe)

Process finished with exit code 0

下面是完整的示范代码:

 1 package com.datatech.learn.slick101
 2 import scala.concurrent.ExecutionContext.Implicits.global
 3 import scala.concurrent.duration._
 4 import scala.concurrent.{Await, Future}
 5 import scala.util.{Success,Failure}
 6 
 7 import slick.driver.H2Driver.api._
 8 object slick101 {
 9 
10   /* ----- schema  */
11   //表字段对应模版
12   case class AlbumModel(id: Long
13                         , artist: String
14                         , year: Option[Int]
15                         , title: String
16                        )
17 
18   //表结构: 定义字段类型, * 代表结果集字段
19   class AlbumTable(tag: Tag) extends Table[AlbumModel](tag, "ALBUMS") {
20     def id = column[Long]("ID", O.AutoInc, O.PrimaryKey)
21 
22     def title = column[String]("TITLE")
23 
24     def year = column[Option[Int]]("YEAR")
25 
26     def artist = column[String]("ARTIST", O.Default("Unknown"))
27 
28     def * = (id, artist, year, title) <> (AlbumModel.tupled, AlbumModel.unapply)
29   }
30 
31   //库表实例
32   val albums = TableQuery[AlbumTable]
33 
34   //创建表动作
35   val createTableAction = albums.schema.create
36 
37   //数据库实例化
38   val db = Database.forConfig("h2")
39 
40   //插入数据动作
41   val insertAlbumsAction =
42   albums ++= Seq(
43     AlbumModel(0, "Keyboard Cat", Some(2003), "Keyboard Cat's Greatest Hits"),
44     AlbumModel(0, "Spice Girls", Some(2010), "Spice"),
45     AlbumModel(0, "Rick Astley", Some(1998), "Whenever You Need Somebody"),
46     AlbumModel(0, "Manowar", None,"The Triumph of Steel"),
47     AlbumModel(0, "Justin Bieber", Some(2011),"Believe"))
48 
49   //数据抽取动作
50   val selectAlbumsAction =
51   albums.result
52 
53   def printResults[T](fut: Future[Iterable[T]]): Unit =
54     Await.result(fut, Duration.Inf).foreach(println)
55 
56   def main(args: Array[String]): Unit = {
57 
58      val res = db.run(createTableAction).andThen {
59        case Success(_) => println("table ALBUMS created.")
60        case Failure(e) => println(e.getMessage)
61      }
62      Await.result(res, 10 seconds)
63 
64     val res2 = db.run(insertAlbumsAction).andThen {
65       case Success(_) => println("albums inserted.")
66       case Failure(e) => println(e.getMessage)
67     }
68     Await.result(res2, 10 seconds) 
69 
70     val res3 = db.run(selectAlbumsAction)
71     printResults(res3)
72   }
73 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏海天一树

Neo4j学习(3):操作图数据库的语言--Cypher

match是匹配规则,(n)表示所有节点,语法要求加上小括号。 return n表示返回匹配到的所有节点

1112
来自专栏一名合格java开发的自我修养

jdbc操作根据bean类自动组装sql,天啦,我感觉我实现了hibernate

场景:需要将从ODPS数仓中计算得到的大额可疑交易信息导入到业务系统的mysql中供业务系统审核。但是本系统是开放是为了产品化,要保证不同环境的可移植性,同时同...

1632
来自专栏chenssy

【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

2704
来自专栏Pythonista

Django之ORM数据库

            django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqli...

961
来自专栏潇涧技术专栏

Lint Tool Analysis (2)

本系列的几篇源码分析文档意义不大,如果你正好也在研究lint源码,或者你想知道前面自定义lint规则中提出的那几个问题,抑或你只是想大致了解下lint的源码都有...

1101
来自专栏函数式编程语言及工具

浅谈Slick(4)- Slick301:我的Slick开发项目设置

  前面几篇介绍里尝试了一些Slick的功能和使用方式,看来基本可以满足用scala语言进行数据库操作编程的要求,而且有些代码可以通过函数式编程模式来实现。我想...

21310
来自专栏算法修养

Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource

前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移。因为项目整体要迁移到ASP.NET Core ...

3267
来自专栏爱撒谎的男孩

地址管理之省市区三级联动菜单

6913
来自专栏码匠的流水账

resilience4j小试牛刀

resilience4j是一款受hystrix启发的容错组件,提供了如下几款核心组件:

4581
来自专栏pangguoming

Hibernate详细教程

一、搭建Hibernate环境 1.在src目录下创建hibernate.cfg.xml配置文件 PS:文件的名字不能改! <?xml version="1.0...

4175

扫码关注云+社区