Spring Boot內嵌資料庫的使用(H2)
Spring FrameWork提供對於SQL資料庫提供了非常廣泛的支援,從直接使用JdbcTemplate的JDBC到類似Hibernate等完備的ORM技術。Spring Data提供一個額外的功能, 直接從介面建立Repository實現, 並使用約定從你的方法名生成查詢。
配置資料來源(DataSource)
Java的javax.sql.DataSource介面提供了一個標準的使用資料庫連線的方法。 傳統做法是, 一個DataSource使用一個URL以及相應的證書去構建一個數據庫連線。
內嵌資料庫的支援
在開發應用的過程中使用內嵌的記憶體資料庫是非常方便的,很明顯,記憶體資料庫不提供資料的持久化儲存;當應用啟動時你需要填充你的資料庫,當應用結束時資料將會丟棄。
Spring Boot可以自動的配置內嵌的H2、HSQL、Derby資料庫。你不需要提供任何連結URLs,只需要簡單的提供一個你需要使用的內嵌資料庫的依賴即可。
如果在測試時要使用內嵌資料庫的功能,需要注意在預設情況下無論使用多少個應用上下文在整個測試過程中相同的資料庫都是可用複用的。如果想要讓每個應用上下文使用隔離的內嵌資料庫,應該設定spring.datasource.generate-unique-name為ture。
內嵌資料庫的使用
本文通過演示H2內嵌資料庫的使用來幫助理解在Spring Boot中如何使用內嵌資料庫。
要使用H2內嵌資料庫,首先需要新增JPA和H2的依賴,但是我們從資料庫讀取資料之後,需要展示在頁面上,所以需要配置一個模板引擎,本文選擇thymeleaf作為我們應用的模板引擎,所以也需要新增對thymeleaf的依賴。
新增之後的pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springboot</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
對於我們的示例程式來說,我們需要建立一個實體類,類名我們將其命名為Journal,具體詳細資訊如下。
package com.springboot.domain;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;
@Entity
public class Journal {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String title;
private Date created;
private String summary;
@Transient
private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
public Journal(String title, String summary, String date) throws ParseException{
this.title = title;
this.summary = summary;
this.created = format.parse(date);
}
Journal(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getCreatedAsShort(){
return format.format(created);
}
public String toString(){
StringBuilder value = new StringBuilder("JournalEntry(");
value.append("Id: ");
value.append(id);
value.append(",Title: ");
value.append(title);
value.append(",Summary: ");
value.append(summary);
value.append(",Created: ");
value.append(getCreatedAsShort());
value.append(")");
return value.toString();
}
}
在上面的程式碼片段中,因為使用了JPA技術,所以你需要使用@Entity, @Id和@GeneratedValue註解,因此這個類被標記作為JPA實體並且能夠被持久化到資料庫中。你也可以看到,在我們的程式碼中也使用了@Transient註解,這個註解表示JPA引擎不會持久化被註解的屬性,因為上面的程式碼片中被@Transient註解的屬性僅僅只是用來格式化日期。這個類有兩個建構函式,一個沒有引數,JPA引擎需要這個建構函式,另外一個建構函式需要用來填充資料庫的引數。
我們覆蓋(override)了一個toString方法,主要用來列印記錄。
接下來,我們需要為資料建立一個持久化機制,那麼我們就需要使用Spring Data JPA技術了,建立一個介面,繼承JpaRepository介面。程式碼如下。
package com.springboot.repository;
import com.springboot.domain.Journal;
import org.springframework.data.jpa.repository.JpaRepository;
public interface JournalRepository extends JpaRepository<Journal,Long> {
}
上面的程式碼片所展示的是Spring Data Repository JPA技術,要使用這項技術只需要繼承JpaRepository介面即可。JpaRepository是一個標記介面,允許Spring Data Repository引擎識別它並提供必要的代理類來實現基本的CRUD (Create, Read, Update, Delete) 和一些自定義的方法。你可以通過一些類似於findByTitleLike、findBySummary、findByTitleAndSummaryIgnoringCase等等命名約定去實現你的方法。預設情況下,這些方法所實現的功能都會被設定成一個事務。JpaRepository也有一些非常便捷的操作可以對資料進行排序和分頁的操作。
接下來,我們需要建立一個Web controller。
package com.springboot.web;
import com.springboot.repository.JournalRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class JournalController {
@Autowired
JournalRepository repo;
@RequestMapping("/")
public String index(Model model){
model.addAttribute("journal", repo.findAll());
return "index";
}
}
@Controller註解、@Autowired註解、@RequestMapping註解我們不在詳細介紹,如果使用過Spring MVC開發過應用,這些註解基本都已經瞭如指掌。在我們的index方法中,有一個Model類的引數,他用來新增一個名為journal的屬性值,該屬性的值通過呼叫JournalRepository介面repo.findAll() 獲取。JournalRepository繼承自JpaRepository,所以該介面有很多不同的預設方法,findAll方法就是其中之一。該方法返回資料庫中所有的實體。
接下來我們需要構建模板檔案,在src/main/resources/templates目錄下,需要建立一個index.html檔案並寫入如下內容。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"></meta>
<meta http-equiv="Content-Type" content="text/html"></meta>
<title>Spring Boot Journal</title>
<link rel="stylesheet" type="text/css" media="all" href="css/bootstrap.min.css"></link>
</head>
<body>
<h1>Spring Boot Journal</h1>
<ul class="timeline">
<div th:each="entry,status : ${journal}">
<li th:attr="class=${status.odd}?'timeline-inverted':''">
<div class="tl-circ"></div>
<div class="timeline-panel">
<div class="tl-heading">
<h4><span th:text="${entry.title}">TITLE</span></h4>
<p><small class="text-muted"><i class="glyphicon glyphicon-time"></i>
<span th:text="${entry.createdAsShort}">CREATED</span></small></p>
</div>
<div class="tl-body">
<p><span th:text="${entry.summary}">SUMMARY</span></p>
</div>
</div>
</li>
</div>
</ul>
</body>
</html>
注意:在static目錄下新建一個css目錄,新增bootstrap.min.css檔案,該檔案自行到bootstrap去下載。
現在,就剩下最重要的一步,構建資料和實現應用入口。程式碼如下;
package com.springboot;
import com.springboot.domain.Journal;
import com.springboot.repository.JournalRepository;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
@Bean
InitializingBean saveData(JournalRepository repo){
return ()->{
repo.save(new Journal("Get to know Spring Boot","Today I will learn Spring Boot","01/01/2016"));
repo.save(new Journal("Simple Spring Boot Project","I will do my first Spring Boot Project","01/02/2016"));
repo.save(new Journal("Spring Boot Reading","Read more about Spring Boot","02/01/2016"));
repo.save(new Journal("Spring Boot in the Cloud","Spring Boot using Cloud Foundry","03/01/2016"));
};
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
注意:這裡要提及的一點就是saveData方法返回的是一個InitializingBean,這個特殊的類在Spring引擎建立序列化例項時會被呼叫。在本示例中,這個方法將會在應用完成執行的過程中被執行。
以上基本所有工作已經完成,點選Intellij IDEA的執行按鈕執行應用,應用執行成功之後在瀏覽器輸入http://localhost:8080,如果程式不出錯,將會出現如下的結果;