本章主要介紹如何在Spring Boot的Web應用中使用Mysq數據庫,也充分展現Spring Boot的優點(儘量少的代碼和配置).java
數據訪問層咱們將使用Spring Data JPA和Hibernate(JPA的實現之一).mysql
##Maven pom.xml文件web
lightsword/pom.xmlspring
在項目中增長以下依賴文件sql
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
##配置文件application.properties數據庫
在src/main/resources/application.properties中設置數據源和jpa配置:json
#mysql spring.datasource.url = jdbc:mysql://localhost:3306/lightsword?useUnicode=true&characterEncoding=UTF8 spring.datasource.username = root #root@localhost ::TZaMojg3ntd spring.datasource.password = root spring.datasource.driverClassName = com.mysql.jdbc.Driver spring.datasource.max-active=0 spring.datasource.max-idle=0 spring.datasource.min-idle=0 spring.datasource.max-wait=10000 spring.datasource.max-wait-millis=31536000 # Specify the DBMS spring.jpa.database = MYSQL # Show or not log for each sql query spring.jpa.show-sql = true # Hibernate ddl auto (create, create-drop, update) spring.jpa.hibernate.ddl-auto = update # Naming strategy spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them to the entity manager) spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
所有的配置都在如上的文件中了,不須要另外的XML配置和Java配置。api
上文中的數據庫配置,你須要換成你的數據庫的地址和用戶名密碼。瀏覽器
hibernate的ddl-auto=update配置表名,數據庫的表和列會自動建立(根據Java實體類,在scala中,只要在實體類上標註@Entity,成員變量上標註@BeanProperty),這裏 能夠看到更多得hibernate配置。springboot
##實體類
建立一個HttpApi實體類,實體和Mysql數據庫的http_api表相對應(這個表字段會在應用啓動的時候,自動生成)。
package com.springboot.in.action.entity import java.util.Date import javax.persistence.{ Entity, GeneratedValue, GenerationType, Id } import scala.language.implicitConversions import scala.beans.BeanProperty @Entity class HttpApi { @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Integer = _ @BeanProperty var httpSuiteId: Integer = _ //用例名稱 @BeanProperty var name: String = _ //用例狀態: -1未執行 0失敗 1成功 @BeanProperty var state: Integer = _ //接口 @BeanProperty var url: String = _ //方法GET,POST @BeanProperty var method: String = _ //post參數json string @BeanProperty var paramJsonStr: String = _ //指望輸出 @BeanProperty var expectOutput: String = _ //實際輸出 @BeanProperty var actualOutput: String = _ @BeanProperty var runTimes: Integer = _ @BeanProperty var owner: String = _ @BeanProperty var gmtCreate: Date = _ @BeanProperty var gmtModify: Date = _ }
##實體的數據訪問層HttpApiDao
實體的數據訪問層HttpApiDao很是簡單,只須要繼承CrudRespositroy便可,CrudRespositroy已經實現了save,delete,deleteAll,findOne和findAll. (比較神奇的時這些方法其實CrudRespositroy中其實並無實現,而且經過對dao層的方法的命名還能夠實現新的方法).
固然,若是基本的CRUD方法知足不了咱們稍微複雜一些的sql查詢,咱們能夠直接定義sql查詢語句,綁定dao層的方法.實例在以下代碼中能夠看到:
package com.springboot.in.action.dao import java.util.List import com.springboot.in.action.entity.HttpApi import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.CrudRepository import scala.language.implicitConversions trait HttpApiDao extends CrudRepository[HttpApi, Integer] { def findAll(): List[HttpApi] // JavaConversions def save(t: HttpApi): HttpApi def findOne(id: Integer): HttpApi @Query(value = "SELECT * FROM http_api where http_suite_id = ?1", nativeQuery = true) def listByHttpSuiteId(id: Integer): List[HttpApi] @Query(value = "SELECT id FROM http_api where http_suite_id = ?1", nativeQuery = true) def listTestCaseId(httpSuiteId: Integer): List[Integer] // 隱式轉換,直接用scala的List會報錯:javax.persistence.NonUniqueResultException: result returns more than one elements] with root cause @Query(value = "SELECT * FROM http_api where name like %?1% ", nativeQuery = true) // like '%?%' def findByName(name: String): List[HttpApi] @Query(value = "select count(*) from http_api where http_suite_id = ?1 and state = 1", nativeQuery = true) def countPass(httpSuiteId: Integer): Int @Query(value = "select count(*) from http_api where http_suite_id = ?1 and state = 0", nativeQuery = true) def countFail(httpSuiteId: Integer): Int }
重點看一下
like '%?%'
可是在@Query的value字符串中, 這樣寫
SELECT * FROM http_api where name like %?1%
javax.persistence.NonUniqueResultException: result returns more than one elements] with root cause.
能夠顯示聲明:
import java.util.List
也可使用隱式轉換:
import scala.collection.JavaConversions._
##控制器HttpApiController
新建控制器HttpApiController.scala代碼
package com.springboot.in.action.controller import java.util.Date import java.util.concurrent.CountDownLatch import com.alibaba.fastjson.JSON import com.springboot.in.action.dao.{HttpApiDao, HttpReportDao, HttpSuiteDao} import com.springboot.in.action.engine.OkHttp import com.springboot.in.action.entity.{HttpApi, HttpReport} import org.springframework.beans.factory.annotation.Autowired import org.springframework.ui.Model import org.springframework.web.bind.annotation.{PathVariable, RequestMapping, RequestMethod, RequestParam, ResponseBody, RestController} import org.springframework.web.servlet.ModelAndView import scala.collection.JavaConversions._ @RestController @RequestMapping(Array("/httpapi")) class HttpApiController @Autowired() ( val HttpSuiteDao: HttpSuiteDao, val HttpApiDao: HttpApiDao, val HttpReportDao: HttpReportDao) { @RequestMapping(value = { Array("", "/") }, method = Array(RequestMethod.GET)) def list(model: Model) = { model.addAttribute("httpapis", HttpApiDao.findAll()) new ModelAndView("/httpapi/list") } @RequestMapping(value = { Array("/json") }, method = Array(RequestMethod.GET)) def listJson() = HttpApiDao.findAll() @RequestMapping(value = { Array("/listHttpSuiteTestCase") }, method = Array(RequestMethod.GET)) def listHttpSuiteTestCase(model: Model, @RequestParam(value = "httpSuiteId") httpSuiteId: Integer) = { var httpapis = HttpApiDao.listByHttpSuiteId(httpSuiteId) model.addAttribute("httpapis", httpapis) model.addAttribute("httpSuiteId", httpSuiteId) model.addAttribute("httpSuiteName", HttpSuiteDao.findOne(httpSuiteId).name) new ModelAndView("/httpapi/listHttpSuiteTestCase") } @RequestMapping(value = { Array("/listHttpSuiteTestCaseJson") }, method = Array(RequestMethod.GET)) @ResponseBody def listHttpSuiteTestCaseJson(model: Model, @RequestParam(value = "httpSuiteId") httpSuiteId: Integer) = { HttpApiDao.listByHttpSuiteId(httpSuiteId) } @RequestMapping(Array("/newPage/{httpSuiteId}")) def goNewPage(@PathVariable(value = "httpSuiteId") httpSuiteId: Integer, model: Model) = { model.addAttribute("httpSuiteId", httpSuiteId) model.addAttribute("httpSuiteName", HttpSuiteDao.findOne(httpSuiteId).name) new ModelAndView("/httpapi/new") } /** * 項目下面的用例編輯 */ @RequestMapping(Array("/editPage/{caseId}")) def goEditPage(model: Model, @PathVariable(value = "caseId") caseId: Integer, @RequestParam(value = "httpSuiteId") httpSuiteId: Integer) = { val httpapi = HttpApiDao.findOne(caseId) model.addAttribute("httpapi", httpapi) model.addAttribute("httpSuiteId", httpSuiteId) model.addAttribute("httpSuiteName", HttpSuiteDao.findOne(httpSuiteId).name) new ModelAndView("/httpapi/edit") } @RequestMapping(Array("/copyPage/{caseId}")) def goCopyPage(model: Model, @PathVariable(value = "caseId") caseId: Integer, @RequestParam(value = "httpSuiteId") httpSuiteId: Integer) = { val httpapi = HttpApiDao.findOne(caseId) model.addAttribute("httpapi", httpapi) model.addAttribute("httpSuiteId", httpSuiteId) model.addAttribute("httpSuiteName", HttpSuiteDao.findOne(httpSuiteId).name) new ModelAndView("/httpapi/copy") } @RequestMapping(Array("/detailPage/{id}")) def goDetailPage(model: Model, @PathVariable(value = "id") id: Integer) = { val httpapi = HttpApiDao.findOne(id) model.addAttribute("httpapi", httpapi) new ModelAndView("/httpapi/detail") } @RequestMapping(value = Array("/postnew"), method = Array(RequestMethod.POST)) @ResponseBody def newOne(@RequestParam(value = "httpSuiteId") httpSuiteId: Integer, @RequestParam(value = "name") name: String, @RequestParam(value = "url") url: String, @RequestParam(value = "method") method: String, @RequestParam(value = "paramJsonStr") paramJsonStr: String, @RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "actualOutput") actualOutput: String, @RequestParam(value = "owner") owner: String) = { val httpapi = new HttpApi() httpapi.httpSuiteId = httpSuiteId httpapi.name = name httpapi.url = url httpapi.method = method httpapi.paramJsonStr = paramJsonStr httpapi.expectOutput = expectOutput httpapi.actualOutput = actualOutput httpapi.runTimes = 0 httpapi.state = -1 httpapi.owner = owner httpapi.gmtCreate = new Date() httpapi.gmtModify = new Date() HttpApiDao.save(httpapi) } @RequestMapping(value = Array("/postedit"), method = Array(RequestMethod.POST)) @ResponseBody def editOne(@RequestParam(value = "id") id: Integer, @RequestParam(value = "name") name: String, @RequestParam(value = "url") url: String, @RequestParam(value = "method") method: String, @RequestParam(value = "paramJsonStr") paramJsonStr: String, @RequestParam(value = "expectOutput") expectOutput: String) = { val httpapi = HttpApiDao.findOne(id) httpapi.name = name httpapi.url = url httpapi.method = method httpapi.paramJsonStr = paramJsonStr httpapi.expectOutput = expectOutput httpapi.gmtModify = new Date() HttpApiDao.save(httpapi) } /** * 在新建用例頁面,調試用例用 */ @RequestMapping(value = Array("/debugTest"), method = Array(RequestMethod.GET)) @ResponseBody def debugTest(@RequestParam(value = "url") url: String, @RequestParam(value = "method") method: String, @RequestParam(value = "paramJsonStr") paramJsonStr: String) = { OkHttp.run(url, method, paramJsonStr) } /** * 執行用例 */ @RequestMapping(value = Array("/runTest"), method = Array(RequestMethod.GET)) @ResponseBody def debugTest(@RequestParam(value = "id") id: Integer) = { runTestCase(id) } /** * 迴歸項目所有用例,每一個用例單獨起一個線程跑 */ @RequestMapping(value = Array("/testHttpSuite"), method = Array(RequestMethod.GET)) @ResponseBody def testProject(@RequestParam(value = "httpSuiteId") httpSuiteId: Integer) = { val caseIds = HttpApiDao.listTestCaseId(httpSuiteId) val threads = caseIds.size val countDownLatch = new CountDownLatch(threads) for (cid <- caseIds) { val t = new TestCaseRunner(cid, countDownLatch) t.start } println("迴歸測試開始......") countDownLatch.await // now waiting sub thread done. println("迴歸測試結束!") val HttpReport = getHttpReport(httpSuiteId) // 保存測試結果 HttpReportDao.save(HttpReport) HttpReport } def getHttpReport(httpSuiteId: Integer) = { println("自動化迴歸測試報告:") val p = HttpSuiteDao.findOne(httpSuiteId) val httpSuiteName = p.name val pass = HttpApiDao.countPass(httpSuiteId) val fail = HttpApiDao.countFail(httpSuiteId) val HttpReport = new HttpReport HttpReport.httpSuiteId = httpSuiteId HttpReport.httpSuiteName = httpSuiteName HttpReport.pass = pass HttpReport.fail = fail HttpReport.time = new Date println(JSON.toJSONString(HttpReport, true)) HttpReport } /** * 執行caseId這個用例 */ def runTestCase(id: Integer) = { val tc = HttpApiDao.findOne(id) val url = tc.url val method = tc.method val paramJsonStr = tc.paramJsonStr println("接口url:" + url) println("方法:" + method) println("輸入參數:" + paramJsonStr) val result = OkHttp.run(url, method, paramJsonStr) //執行次數+1 tc.runTimes = tc.runTimes + 1 println("實際輸出:" + result) tc.actualOutput = result // 結果斷言 val expectOutput = tc.expectOutput val contains = result.contains(expectOutput) tc.state = if (contains) 1 else 0 // 執行事件 tc.gmtModify = new Date HttpApiDao.save(tc) } /** * TestCaseRunner */ class TestCaseRunner(val caseId: Integer, val countDownLatch: CountDownLatch) extends Thread { override def run() { runTestCase(caseId) countDownLatch.countDown } } }
自動裝配@Autowired Dao層代碼,在Controller實現業務邏輯.
##運行測試
啓動腳本lightsword/run.sh
mvn clean scala:compile scala:run -Dlauncher=app
運行lightsword/run.sh,啓動應用.
新建用例集,而後在此用例集中新建一個測試用例,如圖所示:
![](螢幕快照 2016-06-27 13.23.34.png) 能夠直接運行,能夠看到測試結果.
也能夠瀏覽器訪問:
http://localhost:8888/httpapi/listHttpSuiteTestCaseJson?httpSuiteId=1
看到Restful接口的json返回:
[{"id":1,"httpSuiteId":1,"name":"HelloSB測試","state":1,"url":"http://localhost:8888/hello","method":"GET","paramJsonStr":"{}","expectOutput":"LightSword","actualOutput":"{\"conent\":\"Hello, LightSword! Now is: Mon Jun 27 13:23:20 CST 2016\"}","runTimes":1,"owner":"陳光劍","gmtCreate":1467004998000,"gmtModify":1467005001000}]