SpringBoot精选程序员Spring Boot

浅出Spring Boot系列(二)代码组织及CRUD

2017-03-25  本文已影响757人  WilliamWei

前言

Spring Boot项目中的代码该如何进行有效组织?本文以Bookstore项目为例,进行一个简易的CRUD系统开发。

目录

建模

由于是一个简易的书店系统,建模如下:

建模

系统中主要存在4个对象,即用户、订单、商品、种类。一个用户对应0个或多个订单,每个订单至少包含一件商品。且每个商品都属于某个种类。

Model

新建model package,并在其中创建上图中的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中每个属性会创建额外的Getter Setter方法,这里通过lombok包,引入@Data注解,省略了手动写这些方法,项目编译时lombok自动地为我们生成对应方法。

使用时只需在build.gradle文件中添加对lombok的依赖即可:

compile("org.projectlombok:lombok")

Repository

新建repository package,由于数据我们这里选用的是mongodb,所以首先引入mongo的依赖

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

这里需要注意的是,我们选择建立上述POJO对应的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,且模版列表中第一个参数为POJO的类型,第二个参数为主键的类型

为什么我们在这里只写接口而不做实现呢?归功于Spring强大的依赖注入能力,当项目运行时,Spring会自动为我们注入该接口的实现。如果有使用过Mybatis,它的Mapper实际上也是类似的。

注意到上述还包含一个findByCategoryId的方法,这个也是不需要实现的。

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会自动根据方法名转换成对应SQL语句。
更多的query method可以查看官方文档

Service

新建serviceservice.impl package,前者放Interface文件,后者为对应的实现。由于项目不包含过多的业务逻辑,所以这一层会显得略有些淡薄,基本只需要调用repostory中对应方法即可。
CategoryService的实现为例:

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;
    }
}

实现需要添加@Service的Annotation

Controller

新建controller package,我们依然以资源作为分类标准,创建对应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用于标记这是一个基于Restful API的controller,response将通过response body发送。

@RequestMapping用于映射对应URL,并且可显性指定请求的方法。

关于Restful API的设计可以参考阮一峰老师的博客

至此一个经典的分层架构的API后台就开发完成了。完整目录结构如图:


目录结构

效果

创建一个Category资源,并添加几个对应的Product

添加商品

GET方式访问/categories/{category_id},即可看到该类别下的所有商品了。

上一篇下一篇

猜你喜欢

热点阅读