Vo 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
先来做一个简单的Vo例子
model:SpuEntity实体类的代码
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.Objects;
@Getter
@Setter
@Entity
@Table(name = "spu")
public class SpuEntity extends BaseEntity{
@Id
@Column(name = "id")
private Long id;
private String title;
private String subtitle;
private Long categoryId;
private Integer rootCategoryId;
private byte online;
private String price;
private Integer sketchSpecId;
private Integer defaultSkuId;
private String img;
private String discountPrice;
private String description;
private String tags;
private Byte isTest;
private String spuThemeImg;
private String forThemeImg;
}
如果我们需要在展示的时候,只展示id/title/subtitle/online如何通过vo处理呢?
1.1创建一个SpuSimplifyVO
在项目中创建一个package,并命名为vo
在vo下面创建一个SpuSimplifyVO的类,把需要显示的列上去。切记添加上getter和setter,不然无法序列化
package com.fcors.fcors.vo;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class SpuSimplifyVO {
private Long id;
private String title;
private String subtitle;
private byte online;
/*将在baseEntity不显示的属性显示出来*/
private Date createTime;
private Date updateTime;
private Date deleteTime;
}
1.2在SpuController中追加一个入口测试
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.exception.NotFoundException;
import com.fcors.fcors.model.SpuEntity;
import com.fcors.fcors.service.SpuService;
import com.fcors.fcors.vo.SpuSimplifyVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;
import java.util.List;
@RestController
@RequestMapping("/spu/")
@Validated
public class SpuController {
@Autowired
private SpuService spuService;
@GetMapping("/id/{id}/detailTest")
public SpuSimplifyVO findOneByIdFiltter(@PathVariable @Positive Long id){
SpuSimplifyVO spu = spuService.findOneByIdFiltter(id);
if(spu ==null){
throw new NotFoundException(30003);
}
return spu;
}
}
从结果我们可以得到只想要的部分,并且之前因为添加@ JsonIgnore 注解不显示的成员变量也显示出来了。但如上例子有一个问题,他无法进行List的序列化。
demo2:vo的一对多输出单一对象
根据逻辑Spu与Sku、SpuImgList、SpuDetailImg都是存在一对多的关系,我们修改model。
2.1model下的Spu代码。为了不影响性能,多方都是通过懒加载方法。
package com.fcors.fcors.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
@Entity
@Table(name = "spu")
public class SpuEntity extends BaseEntity{
@Id
@Column(name = "id")
private Long id;
private String title;
private String subtitle;
private Long categoryId;
private Integer rootCategoryId;
private byte online;
private String price;
private Integer sketchSpecId;
private Integer defaultSkuId;
private String img;
private String discountPrice;
private String description;
private String tags;
private Byte isTest;
private String spuThemeImg;
private String forThemeImg;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="spuId")
private List<SkuEntity> skuEntityList;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="spuId")
@JsonIgnore
private List<SpuImgEntity> spuImgEntities;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="spuId")
@JsonIgnore
private List<SpuDetailImgEntity> spuDetailImgEntityList;
}
如果使用懒加载的话,需要注意:
2.2在service层添加@Transactional注解
2.3pom.xml引入组件
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.8</version>
</dependency>
2.3.2 yml 配置
spring:
jpa:
properties:
hibernate:
enable_lazy_load_no_trans: true
2.4vo下面的SpuSimplifyVO代码
package com.fcors.fcors.vo;
import com.fcors.fcors.model.SkuEntity;
import com.fcors.fcors.model.SpuDetailImgEntity;
import com.fcors.fcors.model.SpuImgEntity;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
@Getter
@Setter
public class SpuSimplifyVO {
private Long id;
private String title;
private String subtitle;
private byte online;
/*将在baseEntity不显示的属性显示出来*/
// private Date createTime;
// private Date updateTime;
// private Date deleteTime;
private List<SkuEntity> skuEntityList;
private List<SpuImgEntity> spuImgEntities;
//
//
// private List<SpuDetailImgEntity> spuDetailImgEntities;
}
如果接口出现以下错误
Resolved [org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class gani.vankek3api.vo.VankeSaloutStockSimplifyVO]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No s
就是vo的类没有添加上@Getter和@Setter
此时执行接口,我们发现会提醒报错
WARN 2956 --- [nio-8082-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved
[org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.fcors.fcors.model.SpuEntity.spuImgEntities, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.fcors.fcors.model.SpuEntity.spuImgEntities, could not initialize proxy - no Session (through reference chain: com.fcors.fcors.vo.SpuSimplifyVO["SpuImgEntities"])]
该如何处理呢?
2.5引入dozer-core库
修改pom.xml文件
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
<version>6.5.0</version>
</dependency>
2.6service的代码:findOneByIdFiltter()
package com.fcors.fcors.service;
import com.fcors.fcors.model.SkuEntity;
import com.fcors.fcors.model.SpuEntity;
import com.fcors.fcors.model.SpuImgEntity;
import com.fcors.fcors.repository.SpuRepository;
import com.fcors.fcors.vo.SpuSimplifyVO;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Transactional
@Service
public class SpuServiceImpl implements SpuService{
@Autowired
SpuRepository spuRepository;
@Override
public SpuEntity findOneById(Long id) {
SpuEntity spuEntity = this.spuRepository.findOneById(id);
return spuEntity;
}
/** 返回一个SpuSimplifyVO **/
@Override
public SpuSimplifyVO findOneByIdFiltter(Long id) {
SpuEntity spuEntity = this.spuRepository.findOneById(id);
SpuSimplifyVO vo = new SpuSimplifyVO();
BeanUtils.copyProperties(spuEntity,vo);
return vo;
}
@Override
public List<SpuEntity> getLastPagingSpu() {
return this.spuRepository.findAll();
}
}
2.7修改SpuController:findOneByIdFiltter()
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.exception.NotFoundException;
import com.fcors.fcors.model.SpuEntity;
import com.fcors.fcors.service.SpuService;
import com.fcors.fcors.vo.SpuSimplifyVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;
import java.util.List;
@RestController
@RequestMapping("/spu/")
@Validated
public class SpuController {
@Autowired
private SpuService spuService;
@GetMapping("/id/{id}/detail")
public SpuEntity getOneByName(@PathVariable @Positive Long id){
SpuEntity spu = spuService.findOneById(id);
if(spu ==null){
throw new NotFoundException(30003);
}
return spu;
}
/** 返回一个SpuSimplifyVO **/
@GetMapping("/id/{id}/detailTest")
public SpuSimplifyVO findOneByIdFiltter(@PathVariable @Positive Long id){
SpuSimplifyVO spu = spuService.findOneByIdFiltter(id);
if(spu ==null){
throw new NotFoundException(30003);
}
return spu;
}
@GetMapping("/latest")
public List<SpuEntity> getLastPagingSpu(){
List<SpuEntity> SpuList = this.spuService.getLastPagingSpu();
return SpuList;
}
}
被 @JsonIgnore 注释的两个List不序列化。
测试Test接口,我们不难发现通过VO层可以把@JsonIgnore spuImgEntities也能成功输出。
demo3:vo输出分页List
之前的案例,我们都是findone,返回一条数据,如果返回时多条数据的时候,vo该如何处理?
3.1、修改SpuservicesImpl:getLastPagingSpu()
package com.fcors.fcors.service;
import com.fcors.fcors.model.SkuEntity;
import com.fcors.fcors.model.SpuEntity;
import com.fcors.fcors.model.SpuImgEntity;
import com.fcors.fcors.repository.SpuRepository;
import com.fcors.fcors.vo.SpuSimplifyVO;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Transactional
@Service
public class SpuServiceImpl implements SpuService{
@Autowired
SpuRepository spuRepository;
@Override
public SpuEntity findOneById(Long id) {
SpuEntity spuEntity = this.spuRepository.findOneById(id);
return spuEntity;
}
@Override
public SpuSimplifyVO findOneByIdFiltter(Long id) {
SpuEntity spuEntity = this.spuRepository.findOneById(id);
SpuSimplifyVO vo = new SpuSimplifyVO();
BeanUtils.copyProperties(spuEntity,vo);
return vo;
}
@Override
public List<SpuSimplifyVO> getLastPagingSpu() {
Mapper mapper = DozerBeanMapperBuilder.buildDefault();
List<SpuEntity> spuEntities = this.spuRepository.findAll();
List<SpuSimplifyVO> vos = spuEntities.stream()
.map(s->{
return mapper.map(s,SpuSimplifyVO.class);
})
.collect(Collectors.toList());
// List<SpuSimplifyVO> vos = new ArrayList<>();
// spuEntities.forEach(s->{
// SpuSimplifyVO vo = mapper.map(s,SpuSimplifyVO.class);
// vos.add(vo);
// });
return vos;
}
}
3.2、修改controller:getLastPagingSpu()
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.exception.NotFoundException;
import com.fcors.fcors.model.SpuEntity;
import com.fcors.fcors.service.SpuService;
import com.fcors.fcors.vo.SpuSimplifyVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;
import java.util.List;
@RestController
@RequestMapping("/spu/")
@Validated
public class SpuController {
@Autowired
private SpuService spuService;
@GetMapping("/id/{id}/detail")
public SpuEntity getOneByName(@PathVariable @Positive Long id){
SpuEntity spu = spuService.findOneById(id);
if(spu ==null){
throw new NotFoundException(30003);
}
return spu;
}
@GetMapping("/id/{id}/detailTest")
public SpuSimplifyVO findOneByIdFiltter(@PathVariable @Positive Long id){
SpuSimplifyVO spu = spuService.findOneByIdFiltter(id);
if(spu ==null){
throw new NotFoundException(30003);
}
return spu;
}
@GetMapping("/latest")
public List<SpuSimplifyVO> getLastPagingSpu(){
List<SpuSimplifyVO> SpuList = this.spuService.getLastPagingSpu();
return SpuList;
}
}
上述代码存在一个问题,list的分页和排序问题。
4、Vo层更高级的用法:处理数据
Vo层还有一个更高级的用法,用于处理我们想要的数据。下面以category为例子。
以下是 category的数据表结构。is_root=1代表是一级分类;
根据上面的数据结构,如果我们现在需要一个json,格式{“main”:[manidList],”children”:[{chlidrenList}]}
4.1、创建一个CategoryController
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.service.CategoryService;
import com.fcors.fcors.vo.CategoriesAllVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("category")
@RestController
@ResponseBody
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping("/all")
public CategoriesAllVO getAll(){
CategoriesAllVO categories = categoryService.getAll();
return categories;
}
}
4.2、创建一个Category的model
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@Entity
@Table(name = "category")
public class CategoryEntity extends BaseEntity{
@Id
@Column(name = "id")
private Long id;
private String name;
private String description;
private Date createTime;
private Date updateTime;
private Date deleteTime;
private Boolean isRoot;
private Long parentId;
private String img;
private Long index;
private Long online;
private Long level;
}
4.2、在VO下面创建一个CategoryPureVO
我们之前创建的Vo过滤是通过在service中处理的,其实也可以在Vo中直接处理。
package com.fcors.fcors.vo;
import com.fcors.fcors.model.CategoryEntity;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeanUtils;
@Getter
@Setter
public class CategoryPureVO {
private Long id;
private String name;
private Boolean isRoot;
private String img;
private Long parentId;
private Long index;
public CategoryPureVO(CategoryEntity category) {
BeanUtils.copyProperties(category, this);
}
}
4.3、在repository创建categoryRepository
package com.fcors.fcors.repository;
import com.fcors.fcors.model.CategoryEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CategoryRepository extends JpaRepository<CategoryEntity,Long> {
List<CategoryEntity> findAllByIsRootOrderByIndexAsc(boolean is_root);
}
4.3、在vo下面创建一个CategoriesAllVO
通过VO的构造函数,进行数据的处理。
package com.fcors.fcors.vo;
import com.fcors.fcors.model.CategoryEntity;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@Getter
@Setter
public class CategoriesAllVO {
private List<CategoryPureVO> roots;
private List<CategoryPureVO> subs;
public CategoriesAllVO(Map<Integer, List<CategoryEntity>> map) {
/*
this.roots = map.get(1).stream().map(r-> {
return new CategoryPureVO(r);
}).collect(Collectors.toList());
*/
this.roots = map.get(1).stream()
.map(CategoryPureVO::new)
.collect(Collectors.toList());
this.subs = map.get(2).stream()
.map(CategoryPureVO::new)
.collect(Collectors.toList());
}
}
如果不熟悉stream可以快速学习
4.5、 创建service
接口:CategoryService
package com.fcors.fcors.service;
import com.fcors.fcors.vo.CategoriesAllVO;
public interface CategoryService {
CategoriesAllVO getAll();
}
方法:CategoryServiceImpl
package com.fcors.fcors.service;
import com.fcors.fcors.model.CategoryEntity;
import com.fcors.fcors.repository.CategoryRepository;
import com.fcors.fcors.vo.CategoriesAllVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryRepository categoryRepository;
public CategoriesAllVO getAll() {
List<CategoryEntity> roots = categoryRepository.findAllByIsRootOrderByIndexAsc(true);
List<CategoryEntity> subs = categoryRepository.findAllByIsRootOrderByIndexAsc(false);
Map<Integer, List<CategoryEntity>> categories = new HashMap<>();
categories.put(1, roots);
categories.put(2, subs);
CategoriesAllVO categoriesAllVO = new CategoriesAllVO(categories);
return categoriesAllVO;
}
}
测试结果
成功返回!
下面开始讲解关于JPA的分页和排序