本文提綱
一、簡介
二、實例
三、最後
本文運行環境
Ubuntu 16.04 LTS
JDK 8 +
IntelliJ IDEA ULTIMATE 2017.2
Maven 3.5.0
Spring Boot 1.5.8.RELEASE
一、簡介
相信大家對MySQL
都很熟悉了,就不多言。
JPA
可以簡單的理解爲一種保存數據的規範,而hibernate
是實現這種規範的具體操作。所以本文主要講解在Spring Boot
項目中,如何使用JPA
即hibernate
將項目中的數據保存到MySQL
數據庫中。
二、實例
2.1 添加依賴
在pom.xml
中添加如下依賴:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2.2 配置文件
在配置文件application.yml
中添加MySQL
配置:
spring:
profiles:
active: pro #使用生成環境配置
datasource:
#MySQL數據庫驅動
driver-class-name: com.mysql.jdbc.Driver
#數據庫連接地址
url: jdbc:mysql://localhost:3306/demo?useSSL=false
#數據庫連接名
username: root
#數據庫連接密碼
password: 123456
在application-pro.yml
中添加jpa
的配置:
spring:
jpa:
#hibernate實現jpa
hibernate:
#數據表的創建方式
ddl-auto: create-drop
#是否在控制檯顯示SQL語句
show-sql: true
ddl-auto
:自動創建表的方式,共有5種:update
、create-drop
、create
、none
、validate
,分別表示:
-
update
-創建表,如表結構有變則更新; -
create-drop
-每次項目啓動刪除之前表並新建,項目停止時刪除所有表,本文選擇此項,方便測試; -
create
-每次項目啓動刪除之前表並新建; -
none
-不使用自動創建功能; -
validate
-驗證表結構等,但不對數據庫做修改。
此處只配置了最基本的使用,可根據實際情況配置其它需要。
2.3 新建實體類
package com.zyr.demo.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
@Entity //表示該類是一個實體類,在配置中寫了 ddl-auto,jpa會將類名作爲表名自動生成表
public class Account {
@Id //主鍵約束
@GeneratedValue //自增
private Integer id;
@NotNull
private String name;
@NotNull
private String nickName;
@NotNull
private Double money;
public Account() { //必須寫無參構造方法,否則報錯
}
public Account(Integer id) {
this.id = id;
}
public Account(String name, String nickName, Double money) {
this.name = name;
this.nickName = nickName;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
", money=" + money +
'}';
}
}
-
@Entity
:表示該類是一個實體類,在配置中寫了 ddl-auto,jpa會將類名作爲表名自動生成表; -
@Id
:表示該字段作爲主鍵,等同於SQL
中的PRIMARY KEY
; -
@GeneratedValue
:表示Integer
字段自增,等同於SQL
中的AUTO INCREMENT
; -
@Entity
標註的類,必須有一個無參構造方法,否則會報錯; -
@NotNull
:表示該字段在表中不能爲空。
2.4 Service層
通常業務邏輯都放在Service
層中,新建AccountService
,其中包含增刪改查,對應RESTful API
中的POST
、DELETE
、PUT
、GET
:
package com.zyr.demo.service;
import com.zyr.demo.domain.Account;
import java.util.List;
public interface AccountService {
Account insertAccount(Account account);
List<Account> findByName(String name);
void deleteById(Integer id);
Account updateAccount(Account account);
}
接口實現如下:
package com.zyr.demo.service.impl;
import com.zyr.demo.domain.Account;
import com.zyr.demo.repository.AccountRepository;
import com.zyr.demo.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional
@Service
public class AccountServiceImpl implements AccountService {
private AccountRepository accountRepository;
@Autowired
public AccountServiceImpl(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Override
public Account insertAccount(Account account) {
if (account != null) {
accountRepository.save(account);
}
return account;
}
@Override
public List<Account> findByName(String name) {
return accountRepository.findByName(name);
}
@Override
public void deleteById(Integer id) {
accountRepository.delete(new Account(id));
}
@Override
public Account updateAccount(Account account) {
return accountRepository.save(account);
}
}
-
@Transactional
:寫在類上表示該類中的所有方法都進行事務管理; -
@Service
:將該類放入Spring
容器中,並表示它是一個Service
;
2.5 DAO層
使用jpa
的話,DAO
層很“薄”,即內容很簡單,放在包repository
下。
新建AccountRepository
接口:
package com.zyr.demo.repository;
import com.zyr.demo.domain.Account;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> {
List<Account> findByName(String name);
}
-
@Repository
:將該接口放入Spring
容器中,並表示它是一個Repository
; - 必須是
interface
,並繼承JpaRepository
; - 繼承
JpaRepository
後,即可直接使用其中已有的方法,如save
、findAll
來保存或查找數據等; - 可在該接口中,自定義操作數據庫的方法,方法名有一定的規範,新增方法時,輸入
void
空格,然後Ctrl + 空格
會提示方法名規範。也可完全自定義,即自己寫HQL
語句進行操作數據庫,網上有很多更好的介紹,本文不過多介紹;
2.6 測試Service
新建測試類AccountServiceImplTest
:
package com.zyr.demo.service.impl;
import com.zyr.demo.domain.Account;
import com.zyr.demo.service.AccountService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class AccountServiceImplTest {
@Autowired
private AccountService accountService;
private String name1 = "Bob";
private String nickName1 = "boooob";
private Double money1 = 50.5;
private String name2 = "Tom";
private String nickName2 = "toooom";
private Double money2 = 60.5;
@Before
public void setUp() throws Exception {
Account account1 = new Account(name1, nickName1, money1);
Account account2 = new Account(name2, nickName2, money2);
accountService.insertAccount(account1);
accountService.insertAccount(account2);
}
@Test
public void testInsert_and_Find() throws Exception {
Account account = accountService.findByName(name1).get(0);
assertEquals(name1, account.getName());
assertEquals(nickName1, account.getNickName());
assertEquals(money1, account.getMoney(), .001);
}
@Test
public void testDeleteById() throws Exception {
accountService.deleteById(1);
List<Account> accounts = accountService.findByName(name1);
for (Account account : accounts) {
assertTrue(account.getId() != 1);
}
}
@Test
public void testUpdateAccount() throws Exception {
Account account = new Account("Jack", "jaacck", 200.5);
account.setId(2);
accountService.updateAccount(account);
Account newAccount = accountService.findByName("Jack").get(0);
assertEquals("Jack", newAccount.getName());
assertEquals("jaacck", newAccount.getNickName());
assertEquals(200.5, newAccount.getMoney(), .001);
assertEquals(2, newAccount.getId(), .001);
}
}
運行測試類,用例全部通過,說明service
層和dao
層沒問題;
2.7 新建AccountController
package com.zyr.demo.controller;
import com.zyr.demo.domain.Account;
import com.zyr.demo.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/account")
public class AccountController {
private AccountService accountService;
@Autowired
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
@PostMapping("/account")
public Account insertAccount(Account account) {
return accountService.insertAccount(account);
}
@GetMapping("/accounts")
public List<Account> findByName(String name) {
return accountService.findByName(name);
}
@PutMapping("/account")
public Account updateAccount(Account account) {
return accountService.updateAccount(account);
}
@DeleteMapping("/account")
public void deleteById(Integer id) {
accountService.deleteById(id);
}
}
2.8 測試AccountController
新建測試類AccountControllerTest
如下:
package com.zyr.demo.controller;
import com.zyr.demo.domain.Account;
import com.zyr.demo.service.AccountService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class AccountControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private AccountService accountService;
private String name = "Bob";
private String nickName = "boooob";
private Double money = 50.5;
@Before
public void setUp() throws Exception {
Account account = new Account(name, nickName, money);
accountService.insertAccount(account);
}
@Test
public void testInsertAccount() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/account/account")
.param("name", "Tom")
.param("nickName", "tooom")
.param("money", "10.5"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().json("{\"id\":4,\"name\":\"Tom\",\"nickName\":\"tooom\",\"money\":10.5}"));
}
@Test
public void testFindByName() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/account/accounts")
.param("name", name))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().json("[{\"id\":1,\"name\":\"Bob\",\"nickName\":\"boooob\",\"money\":50.5}]"));
}
@Test
public void testUpdateAccount() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.put("/account/account")
.param("id", "1")
.param("name", "Tom")
.param("nickName", "tooom")
.param("money", "10.5"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().json("{\"id\":1,\"name\":\"Tom\",\"nickName\":\"tooom\",\"money\":10.5}"));
}
@Test
public void testDeleteById() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.delete("/account/account")
.param("id", "1"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string(""));
}
}
運行測試類,用例全部通過,說明API
無誤,同時也可以使用REST Client
進行測試,這裏就不再贅述。
三、最後
本文介紹了在Spring Boot
中如何使用JPA
,並演示了其基礎用法。文中有不全之處,比如沒有service
層或Controller
出錯之後的處理、請求有錯的處理等等,將在下一篇文章:統一異常處理,全部彌補。
本文代碼已上傳至我的GitHub倉庫,進入以後將branches切換爲6-jpa即可看見。
前篇:
Spring Boot實際應用講解(一):Hello World
Spring Boot實際應用講解(二):配置詳解
Spring Boot實際應用講解(三):表單驗證
Spring Boot實際應用講解(四):RESTful API
Spring Boot實際應用講解(五):AOP之請求日誌
後續將推出以下文章,敬請關注!
Spring Boot實際應用講解(七):統一異常處理
Spring Boot實際應用講解(八):MySQL + Mybatis
Spring Boot實際應用講解(九):MySQL + Mybatis + Redis
文中若有錯之處,還請各位批評指正,謝謝!
原文作者/ZYRzyr
原文鏈接:http://www.jianshu.com/p/b204472d8126