Spring Boot實(shí)際應(yīng)用講解(六):MySQL + Spring-data-jpa(Hibernate)

文/ZYRzyr
原文鏈接:http://www.reibang.com/p/b204472d8126

本文提綱
一困后、簡(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)目中,如何使用JPAhibernate將項(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泛粹、createnone肮疗、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 +
                '}';
    }
}
  1. @Entity:表示該類是一個(gè)實(shí)體類剑肯,在配置中寫了 ddl-auto捧毛,jpa會(huì)將類名作為表名自動(dòng)生成表;
  2. @Id:表示該字段作為主鍵观堂,等同于SQL中的PRIMARY KEY
  3. @GeneratedValue:表示Integer字段自增呀忧,等同于SQL中的AUTO INCREMENT师痕;
  4. @Entity標(biāo)注的類,必須有一個(gè)無(wú)參構(gòu)造方法而账,否則會(huì)報(bào)錯(cuò)胰坟;
  5. @NotNull:表示該字段在表中不能為空。

2.4 Service層

通常業(yè)務(wù)邏輯都放在Service層中泞辐,新建AccountService笔横,其中包含增刪改查,對(duì)應(yīng)RESTful API中的POST咐吼、DELETE吹缔、PUTGET

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);
    }
}
  1. @Transactional:寫在類上表示該類中的所有方法都進(jìn)行事務(wù)管理锯茄;
  2. @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);
}
  1. @Repository:將該接口放入Spring容器中格嘁,并表示它是一個(gè)Repository;
  2. 必須是interface,并繼承JpaRepository廊移;
  3. 繼承JpaRepository后糕簿,即可直接使用其中已有的方法,如save画机、findAll來保存或查找數(shù)據(jù)等冶伞;
  4. 可在該接口中,自定義操作數(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末翼悴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幔妨,更是在濱河造成了極大的恐慌鹦赎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件误堡,死亡現(xiàn)場(chǎng)離奇詭異古话,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锁施,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門陪踩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杖们,“玉大人,你說我怎么就攤上這事肩狂≌辏” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵傻谁,是天一觀的道長(zhǎng)孝治。 經(jīng)常有香客問我,道長(zhǎng)审磁,這世上最難降的妖魔是什么谈飒? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮力图,結(jié)果婚禮上步绸,老公的妹妹穿的比我還像新娘掺逼。我一直安慰自己吃媒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布吕喘。 她就那樣靜靜地躺著赘那,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氯质。 梳的紋絲不亂的頭發(fā)上募舟,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音闻察,去河邊找鬼拱礁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辕漂,可吹牛的內(nèi)容都是我干的呢灶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼钉嘹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鸯乃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起跋涣,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缨睡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后陈辱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奖年,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年沛贪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陋守。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揍堰。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗅义,靈堂內(nèi)的尸體忽然破棺而出屏歹,到底是詐尸還是另有隱情,我是刑警寧澤之碗,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布蝙眶,位于F島的核電站,受9級(jí)特大地震影響褪那,放射性物質(zhì)發(fā)生泄漏幽纷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一博敬、第九天 我趴在偏房一處隱蔽的房頂上張望友浸。 院中可真熱鬧,春花似錦偏窝、人聲如沸收恢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伦意。三九已至,卻和暖如春硼补,著一層夾襖步出監(jiān)牢的瞬間驮肉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工已骇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留离钝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓褪储,卻偏偏與公主長(zhǎng)得像卵渴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乱豆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345