淺出Spring Boot系列(二)代碼組織及CRUD

前言

Spring Boot項(xiàng)目中的代碼該如何進(jìn)行有效組織?本文以Bookstore項(xiàng)目為例,進(jìn)行一個簡易的CRUD系統(tǒng)開發(fā)。

目錄

建模

由于是一個簡易的書店系統(tǒng),建模如下:

建模

系統(tǒng)中主要存在4個對象,即用戶、訂單、商品、種類。一個用戶對應(yīng)0個或多個訂單,每個訂單至少包含一件商品。且每個商品都屬于某個種類。

Model

新建model package,并在其中創(chuàng)建上圖中的4個類,另外額外多一個OrderProduct類,該類繼承自Product,增加一個quantity屬性。

User為例:

package com.william.model;

import lombok.Data;
import org.springframework.data.annotation.Id;

import java.util.List;


/**
 * Created by william on 17/3/23.
 */
@Data
public class User {

    @Id
    private String id;
    private String username;
    private String password;
    private String salt;
    private String photo;
    private List<String> roles;
}

一般POJO中每個屬性會創(chuàng)建額外的Getter Setter方法,這里通過lombok包,引入@Data注解,省略了手動寫這些方法,項(xiàng)目編譯時lombok自動地為我們生成對應(yīng)方法。

使用時只需在build.gradle文件中添加對lombok的依賴即可:

compile("org.projectlombok:lombok")

Repository

新建repository package,由于數(shù)據(jù)我們這里選用的是mongodb,所以首先引入mongo的依賴

compile("org.springframework.boot:spring-boot-starter-data-mongodb")

這里需要注意的是,我們選擇建立上述POJO對應(yīng)的repository 的Interface,而不是Class。這里以ProductRepositoy為例:

package com.william.repository;

import com.william.model.Product;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;

import java.util.List;

/**
 * Created by william on 17/3/24.
 */
public interface ProductRepository extends MongoRepository<Product,String> {

    List<Product> findByCategoryId(@Param("categoryId") String categoryId);
}

這里我們選擇繼承MongoRepository,且模版列表中第一個參數(shù)為POJO的類型,第二個參數(shù)為主鍵的類型

為什么我們在這里只寫接口而不做實(shí)現(xiàn)呢?歸功于Spring強(qiáng)大的依賴注入能力,當(dāng)項(xiàng)目運(yùn)行時,Spring會自動為我們注入該接口的實(shí)現(xiàn)。如果有使用過Mybatis,它的Mapper實(shí)際上也是類似的。

注意到上述還包含一個findByCategoryId的方法,這個也是不需要實(shí)現(xiàn)的。

The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.

由于遵循約定大于配置,Spring會自動根據(jù)方法名轉(zhuǎn)換成對應(yīng)SQL語句。
更多的query method可以查看官方文檔

Service

新建service、service.impl package,前者放Interface文件,后者為對應(yīng)的實(shí)現(xiàn)。由于項(xiàng)目不包含過多的業(yè)務(wù)邏輯,所以這一層會顯得略有些淡薄,基本只需要調(diào)用repostory中對應(yīng)方法即可。
CategoryService的實(shí)現(xiàn)為例:

package com.william.service.impl;

import com.william.model.Category;
import com.william.repository.CategoryRepository;
import com.william.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Created by william on 17/3/25.
 */
@Service
public class MongoCategoryServiceImpl implements CategoryService {

    @Autowired
    private CategoryRepository repository;
    @Override
    public Category create(Category category) {
        return repository.insert(category);
    }

    @Override
    public Category show(String id) {
        return repository.findOne(id);
    }

    @Override
    public Category update(Category category) {
        return repository.save(category);
    }

    @Override
    public List<Category> findAll() {
        Sort sort = new Sort(Sort.Direction.ASC,"order");
        return repository.findAll(sort);
    }

    @Override
    public Category destroy(String id) {
        Category category = repository.findOne(id);
        repository.delete(id);
        return category;
    }
}

實(shí)現(xiàn)需要添加@Service的Annotation

Controller

新建controller package,我們依然以資源作為分類標(biāo)準(zhǔn),創(chuàng)建對應(yīng)controller。以CategoryController為例:

package com.william.controller;

import com.william.model.Category;
import com.william.model.Product;
import com.william.service.CategoryService;
import com.william.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Created by william on 17/3/24.
 */
@RestController
@RequestMapping("/categories")
public class CategoryController {

    @Autowired
    private ProductService productService;
    @Autowired
    private CategoryService service;

    @RequestMapping(method = RequestMethod.POST)
    public Category create(@RequestBody Category category)
    {
        return service.create(category);
    }

    @RequestMapping(method = RequestMethod.GET)
    public List<Category> getAllCategories()
    {
        return service.findAll();
    }

    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public Category show(@PathVariable String id)
    {
        return service.show(id);
    }

    @RequestMapping(value = "/{id}",method = RequestMethod.PUT)
    public Category update(@PathVariable String id, @RequestBody Category category)
    {
        category.setId(id);
        return service.create(category);
    }

    @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
    public Category destroy(@PathVariable String id)
    {
        return service.destroy(id);
    }

    @RequestMapping("/{id}/products")
    public List<Product> findAllProducts(@PathVariable String id)
    {
        return productService.findAll(service.show(id));
    }

}

@RestController用于標(biāo)記這是一個基于Restful API的controller,response將通過response body發(fā)送。

@RequestMapping用于映射對應(yīng)URL,并且可顯性指定請求的方法。

關(guān)于Restful API的設(shè)計(jì)可以參考阮一峰老師的博客

至此一個經(jīng)典的分層架構(gòu)的API后臺就開發(fā)完成了。完整目錄結(jié)構(gòu)如圖:


目錄結(jié)構(gòu)

效果

創(chuàng)建一個Category資源,并添加幾個對應(yīng)的Product。

添加商品

GET方式訪問/categories/{category_id},即可看到該類別下的所有商品了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容