五、使用BeanUtils进行属性拷贝:
上面说到把从数据库查到的productInfo的属性的值赋给productInfoVo对应的属性,这里说一下赋值的问题:
ProductInfo类如下:
@Entity @Data public class ProductInfo { @Id private String productId; private String productName; private BigDecimal productPrice; private Integer productStock;//库存 private String productDescription;//描述 private String productIcon;//小图 private Integer productStatus;//商品状态(0正常,1下架) private Integer categoryType;//类目编号 }
ProductInfoVo类上面已给出,对比可以发现,ProductInfoVo与ProductInfo相比,就是少几个属性。我们现在从数据库查出来的是productInfo,而前端需要的是productInfoVo,所以需要将productInfo里的值设置到productInfoVo里去。常规做法是先从productInfo中get再set到productInfoVo中去:
ProductInfo productInfo = productService.findOne(productId); ProductInfoVo productInfoVo = new ProductInfoVo(); productInfoVo.setProductName(productInfo.getProductName()); productInfoVo.setProductPrice(productInfo.getProductPrice()); ......
如果属性少问题也不大,如果属性很多,那么就要写一大堆这样的代码。可以使用spring提供的一个属性拷贝工具,不管多少个属性,只需一行代码:
BeanUtils.copyProperties(productInfo, productInfoVo);
这就表示把productInfo的属性拷贝到productInfoVo对象中去。
注意:使用这个工具有两点要注意,第一个就是这两个对象的属性名要一致;第二就是null值也会拷贝进去,所以如果productInfo中有个属性值为null,进行拷贝后productInfoVo对应的属性值也会是null,就算拷贝之前设置了值也会覆盖掉,所以要先拷贝再赋值。
六、dto的使用:
dto全称是data transfer object,中文意思为数据传输对象。那么dto有什么作用?什么时候该用dto?如何使用呢?看下面的例子:
假如我数据库有两张表,一张学生表student,一张班级表class。它们对应的实体类如下:
@Data public class Student { @Id private String studentId; private String classId; private String name; }
@Data public class Class { @Id private String classId; private String className; }
假如我现在要查一个班级的信息,班级应该是包含了多个学生的,因为一个班级有多个学生,那么通常class实体类应该这样设计:
@Data public class Class { @Id private String classId; private String className; private List<Student> studentList; }
那么问题来了,这样实体类和表就对应不上了,因为在class表中没有与studentList这个属性对应的字段。虽然可以在studentList上加上@Transient
注解,这样jpa在与数据表对应时就会忽略这个属性。但是这样不好,感觉就是污染了这个与数据表对应的实体类,我们还是要让实体类与数据表 一 一对应,所以class类不能增加这个字段。那么我们就新建一个实体类,叫ClassDto:
@Data public class ClassDto { private String classId; private String className; private List<Student> studentList; }
这就是dto的作用,最常用的就是当某两个实体类存在关系时,而数据表对应的实体类为了跟数据表一致,没有体现这种关系,那么就可以使用dto。dto不对应数据表,所@Id注解也不需要了。
七、异常处理:
平时我们用异常可能直接throw一个exception就完事了,但是这样不好,因为这样抛出去自己也看不懂是什么异常,所以可以像下面这样处理:
自定义一个异常类继承RuntimeException:
public class GlobalException extends RuntimeException{ private Integer code; public GlobalException(ExceptionEnum exceptionEnum ){ super(resultEnum.getMessage()); this.code = resultEnum.getCode(); } public GlobalException(Integer code,String message){ super(message); this.code = code; } }
用到的枚举类:
@Getter public enum ExceptionEnum { PRODUCT_NOT_EXIST(10,"商品不存在"), PRODUCT_STOCK_ERROR(11,"库存不足"), ; private Integer code; private String message; ExceptionEnum (Integer code, String message) { this.code = code; this.message = message; } }
自定义一个异常类,搭配枚举一起使用。那么在抛异常的时候就可以这样写:
throw new GlobalException(ExceptionEnum.PRODUCT_NOT_EXIST);
这样前端就可以看到“商品不存在”这样的提示,而不是一串看不懂的异常。枚举类种我只是列举了两个例子,有异常就可以往枚举种添加,然后像上面那样用就行了。
八、生成随机数:
有时候数据表的Id没有设置自增,需要我们自己设置Id。Id要求是必须唯一,提供如下工具类:
public class KeyUtil { /** * 生成唯一主键 * 格式:当前时间+随机数 */ public static synchronized String genUniqueKey(){ Random random = new Random(); //Integer a =random.nextInt(90) + 10;//生成两位随机数 //生成六位随机数 Integer number =random.nextInt(900000) + 100000; return System.currentTimeMillis() + String.valueOf(number); } }
九、表单验证:
前端给后台传参数的时候,我们要在controller中获取前端传入的参数,一般有以下几种做法:
- HttpServletRequest:
用这个一般要编写一个工具类,用来获取指定类型的参数:
public class HttpServletRequestUtil { public static int getInt(HttpServletRequest request, String name) { try { return Integer.decode(request.getParameter(name)); } catch (Exception e) { return -1; } } public static long getLong(HttpServletRequest request, String name) { try { return Long.valueOf(request.getParameter(name)); } catch (Exception e) { return -1; } } public static Double getDouble(HttpServletRequest request, String name) { try { return Double.valueOf(request.getParameter(name)); } catch (Exception e) { return -1d; } } public static Boolean getBoolean(HttpServletRequest request, String name) { try { return Boolean.valueOf(request.getParameter(name)); } catch (Exception e) { return false; } } public static String getString(HttpServletRequest request, String name) { try { String result = request.getParameter(name); if (result != null) { result = result.trim(); } if ("".equals(result)) result = null; return result; } catch (Exception e) { return null; } } }
然后在controller中这样用:
@RequestMapping(value = "/getproductlistbyshop") public ResultVo list(HttpServletRequest request) { // 获取前台传过来的页码 int pageIndex = HttpServletRequestUtil.getInt(request, "pageIndex"); // 获取前台传过来的每页显示的数量 int pageSize = HttpServletRequestUtil.getInt(request, "pageSize"); ...... }
但是这样获取参数,如果要校验的话需要自己写if语句来判断,比如:
if (pageIndex == null || pageSize == null){ log.error(...); throw new GlobalException(...); }
看第二种获取前端参数的方法:
- @RequestParam:
@GetMapping("/list") public ResultVo list(@RequestParam(value = "page",defaultValue = "0") Integer page, @RequestParam(value = "size",defaultValue = "10") Integer size){ ...... }
用这个还可以用defaultValue指定默认值,当前端没传时默认就为defaultValue指定的值。这种方法呢其实就是第一种方法的注解形式,如果要对获取的参数判断,还是要自己写if语句。
接下来看第三种方法:
- 表单对象:
如果前端传过来的参数很多,用上面两种方法写未免有些麻烦,而且还要自己一个个的判断传过来的参数是否为空,为空的话又要给前端什么提示。我们可以把前端的参数封装成一个对象,然后在controller中直接获取该对象即可,而且对于参数的验证都可以在封装的这个对象中完成,这就是springmvc提供的表单验证。看例子:
@Data public class OrderForm { @NotEmpty(message = "姓名必填") private String name; @NotEmpty(message = "手机号必填") private String phone; @NotEmpty(message = "地址必填") private String address; @NotEmpty(message = "openid必填") private String openid; @NotEmpty(message = "购物车不能为空") private String items; }
比如从前端获取的参数有这么多,那么就可以封装成这样一个OrderForm表单对象。加上@NotEmpty
注解就表示这个参数不能为空,里面的message就是当该参数为空时给前端的提示。接下来看如何在controller中使用该对象:
@PostMapping("/create") public ResultVo create(@Valid OrderForm orderForm, BindingResult bindingResult){ //判断表单校验后有没有错误 if (bindingResult.hasErrors()) { log.error("【创建订单】参数不正确,orderForm={}",orderForm); throw new GlobalException(ExceptionEnum.PARAM_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage()); } ...... }
用@Valid
注解就可以使用该对象,bindingResult就是验证的结果,如果验证参数不正确,通过bindingResult.getFieldError().getDefaultMessage()
就可以获取到刚才@NotEmpty
注解里面message的内容,配合全局异常使用,就可以把这个message返回给前端。
总结:
上面的介绍的lombok、创建时间和更新时间的处理、BeanUtils的使用都可以减少代码量,而dto、vo、全局异常处理、表单验证等可以使代码结构更加清晰,使程序更加健壮。希望大家喜欢!