摘要:編寫測(cè)試用例代碼打開框架自動(dòng)生成的測(cè)試代碼文件編寫測(cè)試用例,測(cè)試增刪改查效果,測(cè)試代碼如下注釋,它可以對(duì)類成員變量方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作。
文章系列
【從零入門系列-0】Sprint Boot 之 Hello World
【從零入門系列-1】Sprint Boot 之 程序結(jié)構(gòu)設(shè)計(jì)說明
【從零入門系列-2】Sprint Boot 之 數(shù)據(jù)庫(kù)實(shí)體類
前言前一章簡(jiǎn)述了如何設(shè)計(jì)實(shí)現(xiàn)數(shù)據(jù)庫(kù)實(shí)體類,本篇文章在此基礎(chǔ)上進(jìn)行開發(fā),完成對(duì)該數(shù)據(jù)庫(kù)表的常用操作,主要包括使用Spring Data JPA進(jìn)行簡(jiǎn)單的增刪改查和復(fù)雜查詢操作。
Spring Data JPA是Spring提供的一套簡(jiǎn)化JPA開發(fā)的框架,按照約定好的【方法命名規(guī)則】寫dao層接口,就可以在不寫接口實(shí)現(xiàn)的情況下,實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的訪問和操作,同時(shí)提供了很多除了CRUD之外的功能,如分頁、排序、復(fù)雜查詢等等,Spring Data JPA 可以理解為 JPA 規(guī)范的再次封裝抽象,底層還是使用了 Hibernate 的 JPA 技術(shù)實(shí)現(xiàn)。通過引入Spring Data JPA后,我們可以基本不用寫代碼就能實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增刪改查操作。
此外,由于Spring Data JPA自帶實(shí)現(xiàn)了很多內(nèi)置的后臺(tái)操作方法,因此在調(diào)用方法時(shí)必須根據(jù)其規(guī)范使用,深刻理解規(guī)范和約定。
表的基本操作實(shí)現(xiàn)(CRUD)在這里,先介紹一下JpaRepository,這是類型為interface的一組接口規(guī)范,是基于JPA的Repository接口,能夠極大地減少訪問數(shù)據(jù)庫(kù)的代碼編寫,是實(shí)現(xiàn)Spring Data JPA技術(shù)訪問數(shù)據(jù)庫(kù)的關(guān)鍵接口。
編寫數(shù)據(jù)操作接口
在使用時(shí),我們只需要定義一個(gè)繼承該接口類型的接口即可實(shí)現(xiàn)對(duì)表的基本操作方法,在此我們需要對(duì)實(shí)體類Book進(jìn)行操作,因此在Dao目錄上右鍵New->Java Class,然后設(shè)置名稱為BookJpaRepository,kind類型選Interface即可,然后添加注解及繼承自JpaRepository,文件BookJpaRepository.java內(nèi)容如下所示:
package com.arbboter.demolibrary.Dao; import com.arbboter.demolibrary.Domain.Book; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface BookJpaRepository extends JpaRepository{ }
@Repository持久層組件,用于標(biāo)注數(shù)據(jù)訪問組件,即DAO組件,此時(shí)配合上上一篇文章中的JPA配置,我們就可以進(jìn)行增刪改查啦,不用添加任何其他代碼,因?yàn)?b>JpaRepository已經(jīng)幫我們實(shí)現(xiàn)好了。
編寫測(cè)試用例代碼
打開框架自動(dòng)生成的測(cè)試代碼文件DemoLibraryApplicationTests.java編寫測(cè)試用例,測(cè)試增刪改查效果,測(cè)試代碼如下:
@RunWith(SpringRunner.class) @SpringBootTest public class DemoLibraryApplicationTests { /** * @Autowired 注釋,它可以對(duì)類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作。 * 通過 @Autowired的使用來消除 set ,get方法,簡(jiǎn)化程序代碼 * 此處自動(dòng)裝配我們實(shí)現(xiàn)的BookJpaRepository接口,然后可以直接使用bookJpaRepository操作數(shù)據(jù)庫(kù) * 如果不加@Autowired,直接使用bookJpaRepository,程序運(yùn)行會(huì)拋出異常 */ @Autowired private BookJpaRepository bookJpaRepository; @Test public void contextLoads() { Book book = new Book(); // 增 book.setName("Spring Boot 入門學(xué)習(xí)實(shí)踐"); book.setAuthor("arbboter"); book.setImage("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2656353677,2997395625&fm=26&gp=0.jpg"); bookJpaRepository.save(book); System.out.println("保存數(shù)據(jù)成功:" + book); // 查 book = bookJpaRepository.findById(book.getId()).get(); System.out.println("新增后根據(jù)ID查詢結(jié)果:" + book); // 修改 book.setName("Spring Boot 入門學(xué)習(xí)實(shí)踐(修改版)"); bookJpaRepository.save(book); System.out.println("修改后根據(jù)ID查詢結(jié)果:" + book); // 刪除 bookJpaRepository.deleteById(book.getId()); } }
注意在測(cè)試代碼用需要對(duì)屬性bookJpaRepository使用@Autowired 自動(dòng)注入實(shí)現(xiàn)初始化,@Autowired 注解,它可以對(duì)類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作。
測(cè)試結(jié)果
通過測(cè)試結(jié)果我們可以看到,程序已經(jīng)能夠?qū)Ρ頂?shù)據(jù)進(jìn)行增刪改查,且我們通過刪除SQL可以觀察到,獲取生成記錄ID的SQL語句為:
Hibernate: select next_val as id_val from hibernate_sequence with (updlock, holdlock, rowlock) Hibernate: update hibernate_sequence set next_val= ? where next_val=?
因此可推斷出,JpaRepository對(duì)默認(rèn)的自增ID均使用表hibernate_sequence作為ID生成器,所有默認(rèn)的ID表公用此ID生成器。
通過上述例子,我們可以發(fā)現(xiàn),雖然我們沒有寫任何一條SQL語句,但是程序已經(jīng)可以正常操作數(shù)據(jù)庫(kù)了,這對(duì)苦逼的C++程序員手寫SQL來說真是不要說太幸福哈。不過上述示例也存在一些問題,數(shù)據(jù)查詢均是通過ID操作的,但是實(shí)際使用中,數(shù)據(jù)查詢還需要根據(jù)其他條件,比如書名或作者,是不是需要手寫SQL實(shí)現(xiàn)?答案是否定的,JpaRepository支持接口規(guī)范方法名查詢,意思是如果在接口中定義的查詢方法符合它的命名規(guī)則,就可以不用寫實(shí)現(xiàn),框架自動(dòng)提供實(shí)現(xiàn)的方法,只需要聲明無需自己實(shí)現(xiàn)即可使用。
JpaRepository的規(guī)范方法名查詢在我們實(shí)現(xiàn)的接口中,可以只定義查詢方法,如果是符合規(guī)范的,可以不用寫實(shí)現(xiàn),就可以直接使用。
JpaRepository會(huì)對(duì)方法名進(jìn)行校驗(yàn),不符合規(guī)范會(huì)報(bào)錯(cuò),除非添加@Query注解。
在本示例中,我們希望通過書名和作者的常用場(chǎng)景提供查詢方案,可按下述實(shí)現(xiàn):
@Repository public interface BookJpaRepository extends JpaRepository{ /** * 根據(jù)書名精準(zhǔn)查詢書籍列表 * @param name 查詢的書名 * @return 名字為name的書籍列表 */ List findByName(String name); /** * * 根據(jù)書名模糊查詢書籍列表 * @param name 查詢的書名 * @return 查詢結(jié)果 */ List findByNameLike(String name); /** * 根據(jù)書名和作者查詢,注意參數(shù)列表順序和名字順序保持一致(約定!) * @param name 查詢的書名 * @param author 查詢的作者名 * @return 查詢結(jié)果 */ List findByNameAndAuthor(String name, String author); /** * 根據(jù)書名或作者查詢,注意參數(shù)列表順序和名字順序保持一致(約定!) * @param name 查詢的書名 * @param author 查詢的作者名 * @return 查詢結(jié)果 */ List findByNameOrAuthor(String name, String author); /** * 根據(jù)作者集合查詢 * @param authors 書列表名 * @return */ List findByAuthorIn(Collection authors); }
上述代碼通,我們實(shí)現(xiàn)了模糊、精準(zhǔn)、And和Or以及In的查詢定義,都是根據(jù)JPA的命名規(guī)范定義方法,此時(shí)我們不用自己去實(shí)現(xiàn)方法,直接可以調(diào)用。
測(cè)試代碼如下:
// 模擬數(shù)據(jù) for (int i=0; i<20; i++){ Book b = new Book(); b.setName("書名_" + i); b.setAuthor("作者_(dá)" + i%5); b.setImage("img" + i); bookJpaRepository.save(b); } ListbookList; // 根據(jù)書名精準(zhǔn)查詢 bookList = bookJpaRepository.findByName("書名_2"); System.out.println("根據(jù)書名精準(zhǔn)查詢:" + bookList); // 根據(jù)書名模糊查詢 bookList = bookJpaRepository.findByNameLike("書名_2"); System.out.println("根據(jù)書名模糊查詢:" + bookList); // 根據(jù)書名和作者名查詢 bookList = bookJpaRepository.findByNameAndAuthor("書名_2", "作者_(dá)2"); System.out.println("根據(jù)書名和作者名查詢:" + bookList); // 根據(jù)書名或作者名查詢 bookList = bookJpaRepository.findByNameOrAuthor("書名_2", "作者_(dá)2"); System.out.println("根據(jù)書名或作者名查詢:" + bookList); // 根據(jù)作者名集合查詢 Collection c = new ArrayList(); c.add("作者_(dá)1"); c.add("作者_(dá)3"); bookList = bookJpaRepository.findByAuthorIn(c); System.out.println("根據(jù)作者名集合查詢:" + bookList);
運(yùn)行結(jié)果為:
Hibernate: select book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.name=? 根據(jù)書名精準(zhǔn)查詢:[Book{id=9, name="書名_2", author="作者_(dá)2", image="img2"}] Hibernate: select book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.name like ? escape ? 根據(jù)書名模糊查詢:[Book{id=9, name="書名_2", author="作者_(dá)2", image="img2"}] Hibernate: select book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.name=? and book0_.author=? 根據(jù)書名和作者名查詢:[Book{id=9, name="書名_2", author="作者_(dá)2", image="img2"}] Hibernate: select book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.name=? or book0_.author=? 根據(jù)書名或作者名查詢:[Book{id=9, name="書名_2", author="作者_(dá)2", image="img2"}, Book{id=14, name="書名_7", author="作者_(dá)2", image="img7"}, Book{id=19, name="書名_12", author="作者_(dá)2", image="img12"}, Book{id=24, name="書名_17", author="作者_(dá)2", image="img17"}] Hibernate: select book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.author in (? , ?) 根據(jù)作者名集合查詢:[Book{id=8, name="書名_1", author="作者_(dá)1", image="img1"}, Book{id=10, name="書名_3", author="作者_(dá)3", image="img3"}, Book{id=13, name="書名_6", author="作者_(dá)1", image="img6"}, Book{id=15, name="書名_8", author="作者_(dá)3", image="img8"}, Book{id=18, name="書名_11", author="作者_(dá)1", image="img11"}, Book{id=20, name="書名_13", author="作者_(dá)3", image="img13"}, Book{id=23, name="書名_16", author="作者_(dá)1", image="img16"}, Book{id=25, name="書名_18", author="作者_(dá)3", image="img18"}]
JpaRepository規(guī)范方法名查詢規(guī)約說明:JpaRepository框架在進(jìn)行方法名解析時(shí),會(huì)先把方法名多余的前綴截取掉,比如 find、findBy、read、readBy、get、getBy,然后對(duì)剩下部分進(jìn)行解析。
方法關(guān)鍵字必須遵循完全的駝峰形式,因?yàn)镴PA的方法名稱解析引擎算法是通過駝峰來解析的
下劃線可以被用來中斷解析算法的語義,但是它是一個(gè)保留字,不建議使用
In和NotIn也可以將Collection的任何子類作為參數(shù)以及數(shù)組或可變參數(shù)。
JpaRepository的復(fù)雜查詢在我們的圖書管理系統(tǒng)中要提供查詢功能,可以根據(jù)書籍ID、書名或者作者中的三個(gè)任意組合查詢,且支持查詢結(jié)果自定義分頁和排序,這樣的話,使用JpaRepository規(guī)范方法名查詢可能就變得很復(fù)雜了,由于組合后方案很多,不可能每種方案區(qū)分對(duì)待,此時(shí)應(yīng)該提供一種通用可自適應(yīng)的方法來實(shí)現(xiàn),具備動(dòng)態(tài)構(gòu)建相應(yīng)的查詢語句的能力。
Sppring Boot JPA通過JpaSpecificationExecutor提供復(fù)雜查詢的能力,繼承該接口后,重寫接口Predicate toPredicate(Root
1.繼承JpaSpecificationExecutor
@Repository public interface BookJpaRepository extends JpaRepository, JpaSpecificationExecutor { /*內(nèi)容省略*/ }
在原有的BookJpaRepository補(bǔ)充繼承JpaSpecificationExecutor即可。
2.創(chuàng)建Service接口
由于需要自實(shí)現(xiàn)toPredicate方法,所以這里把搜索查詢功能實(shí)現(xiàn)放到Service層,在Service目錄上右鍵New->Java Class創(chuàng)建BookService.java文件:
@Service public class BookService { @Autowired BookJpaRepository bookJpaRepository; /** * 搜索查詢接口 * @param para: 鍵值對(duì)包含name,id,author,pageSize,pageNumber,ordName,ordDir * @return */ Pagesearch(Map para){ return null; } }
上述代碼中@Service注解該類為服務(wù)類。
3.重寫toPredicate方法
// 構(gòu)造查詢條件 Specificationspecification = new Specification () { @Override public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) { List predicate = new ArrayList<>(); // 根據(jù)支持參數(shù)列表獲取查詢參數(shù) String matchMode = para.getOrDefault("matchMode", "AND"); List bookFields = Arrays.asList("id", "name", "author", "image"); for (String p : bookFields){ String buf = para.get(p); if(buf != null){ if(matchMode == "LIKE") { predicate.add(cb.like(root.get(p).as(String.class), "%" + buf + "%")); } else { predicate.add(cb.equal(root.get(p).as(String.class), buf)); } } } Predicate[] pre = new Predicate[predicate.size()]; return query.where(predicate.toArray(pre)).getRestriction(); } };
該方法返回的Predicate即為查詢條件。
4.分頁排序及查詢
// 分頁排序 Integer pageNumber = para.get("pageNumber") == null ? 0:Integer.valueOf(para.get("pageNumber")); Integer pageSize = para.get("pageSize") == null ? 10:Integer.valueOf(para.get("pageSize")); Sort.Direction sortDir = para.getOrDefault("sortDir", "DESC") == "DESC" ? Sort.Direction.DESC : Sort.Direction.ASC; String ordName = para.getOrDefault("ordName", "id"); Pageable pageable = PageRequest.of(pageNumber, pageSize, sortDir, ordName); return bookJpaRepository.findAll(specification, pageable);
最后bookJpaRepository.findAll(specification, pageable)返回的結(jié)果即為查詢結(jié)果
5.完整的搜索方法
@Service public class BookService { @Autowired private BookJpaRepository bookJpaRepository; /** * 搜索查詢接口 * 默認(rèn)值:pageSize-10 pageNumber-0 ordName-id sortDir-ASC matchMode-EQUAL * @param para: 鍵值對(duì)包含name,id,author,pageSize,pageNumber,ordName,sortDir,matchMode * @return */ public Pagesearch(Map para){ // 構(gòu)造查詢條件 Specification specification = new Specification () { @Override public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) { List predicate = new ArrayList<>(); // 根據(jù)支持參數(shù)列表獲取查詢參數(shù) String matchMode = para.getOrDefault("matchMode", "AND"); List bookFields = Arrays.asList("id", "name", "author", "image"); for (String p : bookFields){ String buf = para.get(p); if(buf != null){ if(matchMode == "LIKE") { predicate.add(cb.like(root.get(p).as(String.class), "%" + buf + "%")); } else { predicate.add(cb.equal(root.get(p).as(String.class), buf)); } } } Predicate[] pre = new Predicate[predicate.size()]; return query.where(predicate.toArray(pre)).getRestriction(); } }; // 分頁排序 Integer pageNumber = para.get("pageNumber") == null ? 0:Integer.valueOf(para.get("pageNumber")); Integer pageSize = para.get("pageSize") == null ? 10:Integer.valueOf(para.get("pageSize")); Sort.Direction sortDir = para.getOrDefault("sortDir", "DESC") == "DESC" ? Sort.Direction.DESC : Sort.Direction.ASC; String ordName = para.getOrDefault("ordName", "id"); Pageable pageable = PageRequest.of(pageNumber, pageSize, sortDir, ordName); return bookJpaRepository.findAll(specification, pageable); } }
6.測(cè)試代碼
@Autowired private BookService bookService; @Test public void search(){ Mappara = new HashMap<>(); Page books = bookService.search(para); System.out.println("分頁3-1降序查詢:" + books.getTotalElements() + ",頁元素?cái)?shù)目:" + books.getNumberOfElements()); para.put("sortDir", "DESC"); para.put("pageSize", "3"); para.put("pageNumber", "1"); books = bookService.search(para); System.out.println("分頁3-1降序查詢:" + books.getTotalElements() + ",頁元素?cái)?shù)目:" + books.getNumberOfElements()); para.put("author", "作者_(dá)2"); para.put("pageNumber", "0"); books = bookService.search(para); System.out.println("作者名為[作者_(dá)2]查詢結(jié)果:" + books.getTotalElements() + ",頁元素?cái)?shù)目:" + books.getNumberOfElements()); para.put("id", "9"); books = bookService.search(para); System.out.println("作者為[作者_(dá)2] id為9的查詢結(jié)果:" + books.getTotalElements() + ",頁元素?cái)?shù)目:" + books.getNumberOfElements()); para.put("matchMode", "LIKE"); books = bookService.search(para); System.out.println("作者為[作者_(dá)2] id為9的模糊查詢結(jié)果:" + books.getTotalElements() + ",頁元素?cái)?shù)目:" + books.getNumberOfElements()); }
此處BookService對(duì)象采用@Autowired注解自動(dòng)裝配初始化,然后再測(cè)試代碼中針對(duì)分頁、查詢模式都分別測(cè)試。
7.測(cè)試執(zhí)行結(jié)果
2019-05-14 18:44:43.422 INFO 131236 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select TOP(?) book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where 1=1 order by book0_.id desc Hibernate: select count(book0_.id) as col_0_0_ from library_book book0_ where 1=1 分頁3-1降序查詢:23,頁元素?cái)?shù)目:10 Hibernate: WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( select TOP(?) book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where 1=1 order by book0_.id desc ) inner_query ) SELECT id1_0_, author2_0_, image3_0_, name4_0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ? Hibernate: select count(book0_.id) as col_0_0_ from library_book book0_ where 1=1 分頁3-1降序查詢:23,頁元素?cái)?shù)目:3 Hibernate: select TOP(?) book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where book0_.author=? order by book0_.id desc Hibernate: select count(book0_.id) as col_0_0_ from library_book book0_ where book0_.author=? 作者名為[作者_(dá)2]查詢結(jié)果:4,頁元素?cái)?shù)目:3 Hibernate: select TOP(?) book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where cast(book0_.id as varchar(255))=? and book0_.author=? order by book0_.id desc 作者為[作者_(dá)2] id為9的查詢結(jié)果:1,頁元素?cái)?shù)目:1 Hibernate: select TOP(?) book0_.id as id1_0_, book0_.author as author2_0_, book0_.image as image3_0_, book0_.name as name4_0_ from library_book book0_ where (cast(book0_.id as varchar(255)) like ?) and (book0_.author like ?) order by book0_.id desc 作者為[作者_(dá)2] id為9的模糊查詢結(jié)果:2,頁元素?cái)?shù)目:2結(jié)束語
本章節(jié)篇幅較長(zhǎng),簡(jiǎn)單介紹了下JPA的基本增刪改查功能,并進(jìn)一步介紹定義了JPA的規(guī)范方法名查詢,最后引入JpaSpecificationExecutor通過搜索查詢接口,闡述了復(fù)雜場(chǎng)景下的查詢搜索。
下一篇內(nèi)容將整合當(dāng)前方法服務(wù),編寫控制層的接口,提供WEB服務(wù)接口,請(qǐng)繼續(xù)關(guān)注。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77689.html
摘要:結(jié)束語非常智能化,為開發(fā)者提供大量的默認(rèn)配置細(xì)節(jié),因此在的幫助下可以快速完成項(xiàng)目的運(yùn)行,極簡(jiǎn)入門繼續(xù)看從零入門系列程序結(jié)構(gòu)設(shè)計(jì)說明 環(huán)境準(zhǔn)備 java 開發(fā)環(huán)境 JDK1.8 安裝 Maven 安裝,jar自動(dòng)依賴及包管理工具 IDE編輯器:IntelliJ IDEA 2019 說明 本項(xiàng)目為從零入門示例,目標(biāo)為構(gòu)建一個(gè)書籍增刪改查管理頁,力爭(zhēng)記錄一個(gè)無java基礎(chǔ)的程序員學(xué)習(xí)筆...
摘要:務(wù)必在之前引入最新的核心文件為了偷懶,我們這里引入的第三方庫(kù)文件都是采用的方式,也可以選擇把庫(kù)下載到本地然后再引用。 文章系列 【從零入門系列-0】Spring Boot 之 Hello World 【從零入門系列-1】Spring Boot 之 程序結(jié)構(gòu)設(shè)計(jì)說明 【從零入門系列-2】Spring Boot 之 數(shù)據(jù)庫(kù)實(shí)體類 【從零入門系列-3】Spring Boot 之 數(shù)據(jù)庫(kù)操作...
摘要:務(wù)必在之前引入最新的核心文件為了偷懶,我們這里引入的第三方庫(kù)文件都是采用的方式,也可以選擇把庫(kù)下載到本地然后再引用。 文章系列 【從零入門系列-0】Spring Boot 之 Hello World 【從零入門系列-1】Spring Boot 之 程序結(jié)構(gòu)設(shè)計(jì)說明 【從零入門系列-2】Spring Boot 之 數(shù)據(jù)庫(kù)實(shí)體類 【從零入門系列-3】Spring Boot 之 數(shù)據(jù)庫(kù)操作...
摘要:結(jié)束語本章預(yù)先提供了項(xiàng)目實(shí)際效果圖以及項(xiàng)目的整體結(jié)構(gòu)設(shè)計(jì),后續(xù)文章會(huì)根據(jù)本篇章設(shè)計(jì)依次實(shí)現(xiàn)各個(gè)模塊,請(qǐng)持續(xù)關(guān)注。 文章系列 【從零入門系列】Sprint Boot 之 Hello World 設(shè)計(jì)效果圖 頁面展示showImg(https://raw.githubusercontent.com/arbboter/resource/master/segmentfault/image/...
閱讀 3085·2021-10-27 14:15
閱讀 3084·2021-09-07 10:18
閱讀 1390·2019-08-30 15:53
閱讀 1653·2019-08-26 18:18
閱讀 3442·2019-08-26 12:15
閱讀 3523·2019-08-26 10:43
閱讀 719·2019-08-23 16:43
閱讀 2286·2019-08-23 15:27