本文提綱
一困后、簡(jiǎn)介
二、實(shí)例
三闺魏、最后
本文運(yùn)行環(huán)境
Ubuntu 16.04 LTS
JDK 8 +
IntelliJ IDEA ULTIMATE 2017.2
Maven 3.5.0
Spring Boot 1.5.8.RELEASE
一非洲、簡(jiǎn)介
相信大家對(duì)MySQL
都很熟悉了鸭限,就不多言。
JPA
可以簡(jiǎn)單的理解為一種保存數(shù)據(jù)的規(guī)范两踏,而hibernate
是實(shí)現(xiàn)這種規(guī)范的具體操作败京。所以本文主要講解在Spring Boot
項(xiàng)目中,如何使用JPA
即hibernate
將項(xiàng)目中的數(shù)據(jù)保存到MySQL
數(shù)據(jù)庫(kù)中梦染。
二赡麦、實(shí)例
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 #使用生成環(huán)境配置
datasource:
#MySQL數(shù)據(jù)庫(kù)驅(qū)動(dòng)
driver-class-name: com.mysql.jdbc.Driver
#數(shù)據(jù)庫(kù)連接地址
url: jdbc:mysql://localhost:3306/demo?useSSL=false
#數(shù)據(jù)庫(kù)連接名
username: root
#數(shù)據(jù)庫(kù)連接密碼
password: 123456
在application-pro.yml
中添加jpa
的配置:
spring:
jpa:
#hibernate實(shí)現(xiàn)jpa
hibernate:
#數(shù)據(jù)表的創(chuàng)建方式
ddl-auto: create-drop
#是否在控制臺(tái)顯示SQL語(yǔ)句
show-sql: true
ddl-auto
:自動(dòng)創(chuàng)建表的方式,共有5種:update
帕识、create-drop
泛粹、create
、none
肮疗、validate
晶姊,分別表示:
-
update
-創(chuàng)建表,如表結(jié)構(gòu)有變則更新伪货; -
create-drop
-每次項(xiàng)目啟動(dòng)刪除之前表并新建们衙,項(xiàng)目停止時(shí)刪除所有表,本文選擇此項(xiàng)碱呼,方便測(cè)試蒙挑; -
create
-每次項(xiàng)目啟動(dòng)刪除之前表并新建; -
none
-不使用自動(dòng)創(chuàng)建功能愚臀; -
validate
-驗(yàn)證表結(jié)構(gòu)等忆蚀,但不對(duì)數(shù)據(jù)庫(kù)做修改。
此處只配置了最基本的使用,可根據(jù)實(shí)際情況配置其它需要馋袜。
2.3 新建實(shí)體類
package com.zyr.demo.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
@Entity //表示該類是一個(gè)實(shí)體類男旗,在配置中寫了 ddl-auto,jpa會(huì)將類名作為表名自動(dòng)生成表
public class Account {
@Id //主鍵約束
@GeneratedValue //自增
private Integer id;
@NotNull
private String name;
@NotNull
private String nickName;
@NotNull
private Double money;
public Account() { //必須寫無(wú)參構(gòu)造方法桃焕,否則報(bào)錯(cuò)
}
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
:表示該類是一個(gè)實(shí)體類剑肯,在配置中寫了 ddl-auto捧毛,jpa會(huì)將類名作為表名自動(dòng)生成表; -
@Id
:表示該字段作為主鍵观堂,等同于SQL
中的PRIMARY KEY
; -
@GeneratedValue
:表示Integer
字段自增呀忧,等同于SQL
中的AUTO INCREMENT
师痕; -
@Entity
標(biāo)注的類,必須有一個(gè)無(wú)參構(gòu)造方法而账,否則會(huì)報(bào)錯(cuò)胰坟; -
@NotNull
:表示該字段在表中不能為空。
2.4 Service層
通常業(yè)務(wù)邏輯都放在Service
層中泞辐,新建AccountService
笔横,其中包含增刪改查,對(duì)應(yīng)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);
}
接口實(shí)現(xiàn)如下:
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
:寫在類上表示該類中的所有方法都進(jìn)行事務(wù)管理锯茄; -
@Service
:將該類放入Spring
容器中厢塘,并表示它是一個(gè)Service
;
2.5 DAO層
使用jpa
的話肌幽,DAO
層很“薄”晚碾,即內(nèi)容很簡(jiǎn)單,放在包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
容器中格嘁,并表示它是一個(gè)Repository
; - 必須是
interface
,并繼承JpaRepository
廊移; - 繼承
JpaRepository
后糕簿,即可直接使用其中已有的方法,如save
画机、findAll
來保存或查找數(shù)據(jù)等冶伞; - 可在該接口中,自定義操作數(shù)據(jù)庫(kù)的方法步氏,方法名有一定的規(guī)范响禽,新增方法時(shí),輸入
void
空格,然后Ctrl + 空格
會(huì)提示方法名規(guī)范芋类。也可完全自定義隆嗅,即自己寫HQL
語(yǔ)句進(jìn)行操作數(shù)據(jù)庫(kù),網(wǎng)上有很多更好的介紹侯繁,本文不過多介紹胖喳;
2.6 測(cè)試Service
新建測(cè)試類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);
}
}
運(yùn)行測(cè)試類,用例全部通過贮竟,說明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 測(cè)試AccountController
新建測(cè)試類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(""));
}
}
運(yùn)行測(cè)試類丰刊,用例全部通過辩蛋,說明API
無(wú)誤,同時(shí)也可以使用REST Client
進(jìn)行測(cè)試浪汪,這里就不再贅述惰拱。
三雌贱、最后
本文介紹了在Spring Boot
中如何使用JPA
,并演示了其基礎(chǔ)用法偿短。文中有不全之處欣孤,比如沒有service
層或Controller
出錯(cuò)之后的處理、請(qǐng)求有錯(cuò)的處理等等昔逗,將在下一篇文章:統(tǒng)一異常處理降传,全部彌補(bǔ)。
本文代碼已上傳至我的GitHub倉(cāng)庫(kù)纤子,進(jìn)入以后將branches切換為6-jpa即可看見搬瑰。
前篇:
Spring Boot實(shí)際應(yīng)用講解(一):Hello World
Spring Boot實(shí)際應(yīng)用講解(二):配置詳解
Spring Boot實(shí)際應(yīng)用講解(三):表單驗(yàn)證
Spring Boot實(shí)際應(yīng)用講解(四):RESTful API
Spring Boot實(shí)際應(yīng)用講解(五):AOP之請(qǐng)求日志
后續(xù)將推出以下文章,敬請(qǐng)關(guān)注控硼!
Spring Boot實(shí)際應(yīng)用講解(七):統(tǒng)一異常處理
Spring Boot實(shí)際應(yīng)用講解(八):MySQL + Mybatis
Spring Boot實(shí)際應(yīng)用講解(九):MySQL + Mybatis + Redis
文中若有錯(cuò)之處泽论,還請(qǐng)各位批評(píng)指正,謝謝卡乾!
原文作者/ZYRzyr
原文鏈接:http://www.reibang.com/p/b204472d8126