IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot与mybatis plus开发瑞吉外卖项目实战 -> 正文阅读

[Java知识库]springboot与mybatis plus开发瑞吉外卖项目实战

请求返回类封装

package com.liu.reggie.common;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

@Data
public class R <T>{
    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
}

使用

/**
* 根据 id 查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id){
    log.info("根据id查员工信息....");
    Employee employee = employeeService.getById(id);
    if (employee!=null){
        return R.success(employee);
    }
    return R.error("没有查询到相对应的信息!");
}

Spring Boot 中使用 Servlet

在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。

  • 使用 Filter 拦截器

    • 在启动类里加上 @ServletComponentScan

      package com.liu.reggie;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.boot.web.servlet.ServletComponentScan;
      import org.springframework.transaction.annotation.EnableTransactionManagement;
      
      // 扫描 servlet 注解
      @ServletComponentScan
      // 开启事务
      @EnableTransactionManagement
      @SpringBootApplication
      public class ReggieTakeOutApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(ReggieTakeOutApplication.class, args);
          }
      
      }
      
    • 启用@WebFilter进行路径拦截

      package com.liu.reggie.filter;
      
      import com.alibaba.fastjson.JSON;
      import com.liu.reggie.common.R;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.util.AntPathMatcher;
      
      import javax.servlet.*;
      import javax.servlet.annotation.WebFilter;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      
      @Slf4j
      @WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
      public class LoginCheckFilter implements Filter {
          public static final AntPathMatcher PATH_MATCHER=new AntPathMatcher();
      
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              HttpServletRequest request=(HttpServletRequest)servletRequest;
              HttpServletResponse response=(HttpServletResponse)servletResponse;
              //1、获取本次请求的URI
              String requestURI = request.getRequestURI();
              log.info("拦截到请求:{}",requestURI);
      
              // 定义不需要处理的请求路径
              String[] urls=new String[]{
                      "/employee/login",
                      "/employee/logout",
                      "/backend/**",
                      "/front/**"
              };
              //2、判断本次请求是否需要处理
              boolean check = check(urls, requestURI);
      
      
              //3、如果不需要处理,则直接放行
              if (check){
                  log.info("本次请求{}不需要处理",requestURI);
                  filterChain.doFilter(request,response);
                  return;
              }
      
      
              //4-1、判断登录状态,如果已登录,则直接放行
              if (request.getSession().getAttribute("employee") !=null){
                  log.info("用户已经登录,用户id为:{}",request.getSession().getAttribute("employee"));
                  filterChain.doFilter(request,response);
                  return;
              }
      
              log.info("用户未登录");
              //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
              response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
      
              return;
          }
      
          /**
           * 路径匹配,检查本次请求是否需要放行
           * @param urls
           * @param requestURL
           * @return
           */
          public boolean check(String[] urls,String requestURL){
              for (String url : urls) {
                  boolean match = PATH_MATCHER.match(url, requestURL);
                  if (match){
                      return true;
                  }
              }
              return false;
          }
      }
      
      

全局异常拦截

每次都在方法里使用 try/catch 太麻烦了,使用全局异常拦截,通过报错的类型就行分类处理能大大的减少代码的,和提高编码效率

package com.liu.reggie.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常处理器
 */
// 拦截调用 RestController注解和Controller注解 抛出异常的方法
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 数据库操作异常处理方法
     * @param ex
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.info(ex.getMessage());

        if (ex.getMessage().contains("Duplicate entry")){
            String[] s = ex.getMessage().split(" ");
            String msg = s[2] + "已存在";

            return R.error(msg);
        }

        return R.error("未知错误!");
    }
}

Long类型id传输前端,id值改变处理方法

  • 原因:由于后端long长度为19位,而前端接受整型的长度为16位,多余的3位会被js自动处理,导致id的改变,导致与数据库id对应不上

  • 解决方法:

    使用 fastjson 中间件 和 spring MVC 的 消息转换器 (以后有这个问题直接复制两个类就行了)

    • 创建JacksonObjectMapper转换类

      package com.liu.reggie.common;
      
      import com.fasterxml.jackson.databind.DeserializationFeature;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.fasterxml.jackson.databind.module.SimpleModule;
      import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
      import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
      import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
      import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
      import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
      import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
      import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
      
      import java.math.BigInteger;
      import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.time.LocalTime;
      import java.time.format.DateTimeFormatter;
      
      import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
      
      /**
       * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
       * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
       * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
       */
      public class JacksonObjectMapper extends ObjectMapper {
      
          public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
          public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
          public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
      
          public JacksonObjectMapper() {
              super();
              //收到未知属性时不报异常
              this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
      
              //反序列化时,属性不存在的兼容处理
              this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
      
      
              SimpleModule simpleModule = new SimpleModule()
                      .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                      .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                      .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
      
                      .addSerializer(BigInteger.class, ToStringSerializer.instance)   // 将 BigInteger类型 向前端传输时转换成 String类型
                      .addSerializer(Long.class, ToStringSerializer.instance)     // 将 long类型 向前端传输时转换成 String类型
                      .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                      .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                      .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
      
              //注册功能模块 例如,可以添加自定义序列化器和反序列化器
              this.registerModule(simpleModule);
          }
      }		
      
    • spring MVC配置文件里加入自己的消息转换器

      package com.liu.reggie.config;
      
      import com.liu.reggie.common.JacksonObjectMapper;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.http.converter.HttpMessageConverter;
      import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
      import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
      
      import java.util.List;
      
      @Slf4j
      @Configuration
      public class WebMvcConfig extends WebMvcConfigurationSupport {
      
          /**
           * 设置静态资源映射
           * @param registry
           */
          @Override
          protected void addResourceHandlers(ResourceHandlerRegistry registry) {
              log.info("开始进行静态资源映射");
              registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
              registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
          }
      
          /**
           * 扩展mvc框架的消息转换器
           * @param converters
           */
          @Override
          protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      
              log.info("扩展消息转换器!");
      
              // 创建消息转换器对象
              MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
      
              // 设置对象转换器,底层使用Jackson将Java对象转为json
              messageConverter.setObjectMapper(new JacksonObjectMapper());
      
              // 将上面的消息转换器对象追加到mvc框架的转换器集合中
              converters.add(0,messageConverter);
          }
      }
      

mybatis Plus公共字段自动填充

1、在实体类的属性上加入@TableField注解,指定自动填充的策略

package com.liu.reggie.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 员工实体
 */
@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;//身份证号码

    private Integer status;

    @TableField(fill = FieldFill.INSERT)    //插入时填充字段
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)     //插入和更新时填充字段
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)        //插入时填充字段
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)     //插入和更新时填充字段
    private Long updateUser;

}

2、按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

因为MetaObjectHandler类里面拿不到 session 所以要想办法拿到 userId 就可以用 ThreadLocal 来获取

在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:

? 1、LoginCheckFilter的doFilter方法

? 2、EmployeeController的update方法

? 3、MyMetaObjectHandler的updateFill方法
可以在上面的三个方法中分别加入下面代码(获取当前线程id ) :

long id = Thread.currentThread().getId() ;
log.info("线程id:{}",id);

什么是ThreadLocal?

ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

ThreadLocal常用方法:

  • public void set(T value) 设置当前线程的线程局部变量的值

  • public T get() 返回当前线程所对应的线程局部变量的值

我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。

实现步骤:

  • 编写BaseContext工具类,基于ThreadLocal封装的工具类

    package com.liu.reggie.common;
    
    /**
     * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
     */
    public class BaseContext {
    
        private static ThreadLocal<Long> threadLocal=new ThreadLocal<>();
    
        public static void setCurrentId(Long id){
            threadLocal.set(id);
        }
    
        public static Long getCurrenId(){
            return threadLocal.get();
        }
    }
    
  • 在LogincheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id

    //4-1、判断登录状态,如果已登录,则直接放行
    if (request.getSession().getAttribute("employee") !=null){
        log.info("用户已经登录,用户id为:{}",request.getSession().getAttribute("employee"));
    
        Long empId = (Long)request.getSession().getAttribute("employee");
        BaseContext.setCurrentId(empId);
    
        long id = Thread.currentThread().getId() ;
        log.info("线程id:{}",id);
    
        filterChain.doFilter(request,response);
        return;
    }
    
  • 在 MyMetaObjectHandler 的方法中调用 BaseContext 获取登录用户的id

    package com.liu.reggie.common;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.time.LocalDateTime;
    
    /**
     * 自定义元数据对象处理器
     */
    
    @Component
    @Slf4j
    public class MyMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("公共字段自动填充 [insert] ....");
            log.info(metaObject.toString());
    
            metaObject.setValue("createTime",LocalDateTime.now());
            metaObject.setValue("updateTime",LocalDateTime.now());
    
    
    
            metaObject.setValue("createUser",BaseContext.getCurrenId());
            metaObject.setValue("updateUser",BaseContext.getCurrenId());
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("公共字段自动填充 [update] ....");
            log.info(metaObject.toString());
    
            long id = Thread.currentThread().getId() ;
            log.info("线程id:{}",id);
    
            metaObject.setValue("updateTime",LocalDateTime.now());
            metaObject.setValue("updateUser",BaseContext.getCurrenId());
        }
    }
    

自定义异常

  • 创建 CustomException 自定义异常类 继承 RuntimeException 运行异常类,用于自定义异常消息

    package com.liu.reggie.common;
    
    public class CustomException extends RuntimeException{
        public CustomException(String message){
            super(message);
        }
    }
    
  • 在全局异常捕获中捕获返回前端

    /**
     * 全局异常处理器
     */
    // 拦截调用 RestController注解和Controller注解 抛出异常的方法
    @ControllerAdvice(annotations = {RestController.class, Controller.class})
    @ResponseBody
    @Slf4j
    public class GlobalExceptionHandler {
    
        /**
         * 数据库操作异常处理方法
         * @param ex
         * @return
         */
        @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
        public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
            log.info(ex.getMessage());
    
            if (ex.getMessage().contains("Duplicate entry")){
                String[] s = ex.getMessage().split(" ");
                String msg = s[2] + "已存在";
    
                return R.error(msg);
            }
    
            return R.error("未知错误!");
        }
    
        /**
         * 自定义异常处理方法
         * @param ex
         * @return
         */
        @ExceptionHandler(CustomException.class)
        public R<String> exceptionHandler(CustomException ex){
            log.info(ex.getMessage());
            return R.error(ex.getMessage());
        }
    }
    

文件上传下载

spring-web 已经封装了,底层也是使用Apache的两个组件:

  • commons-fileupload
  • commons-io

就不用引入其他的包,只需要用MultipartFile接受文件流就行

application.yml配置文件里设置基础路径,方便文件转移

# 文件上传路径
web:
  upload-path: F:\\images

CommonController控制器里

package com.liu.reggie.controller;

import com.liu.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {

    @Value("${web.upload-path}")
    private String filePath;

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        log.info(file.toString());

        //  原始文件名
        String originalFilename = file.getOriginalFilename();

        // 截取文件后缀
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        // 使用UUID重新生成文件名,防止文件名称重复造成文件覆盖
        String fileName = UUID.randomUUID().toString() + suffix;

        // 创建一个目录对象
        File dir = new File(filePath);
        if (!dir.exists()){
            // 目录不存在需要创建
            dir.mkdirs();
        }

        try {
            file.transferTo(new File(filePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return R.success(fileName);
    }

    /**
     * 文件下载
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response){
        // 输入流,通过输入流读取文件内容
        try {
            FileInputStream fileInputStream=new FileInputStream(new File(filePath+name));

            // 输出流,通过输出流将文件写回浏览器,在浏览器展示图片
            ServletOutputStream outputStream = response.getOutputStream();

            response.setContentType("image/jpeg");

            int len=0;
            byte[] bytes=new byte[1024];
            while ((len=fileInputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }

            // 关闭资源
            outputStream.close();
            fileInputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

金额累加AtomicInteger类

i++和++i不是线程安全的,因此在高并发的情况下,需要使用synchronized等关键字来保证线程安全,但是AtomicInteger这个类则是线程安全的

常用方法

public static void main(String[] args) {

    AtomicInteger int1=new AtomicInteger();
    System.out.println("AtomicInteger的默认值为:"+int1);

    //对数据赋值
    int1.set(123);
    //获取数据值
    System.out.println("获取数据的值为:  "+int1.get());

    //先相加,再获取值
    System.out.println("先与12相加,再获取值:  "+int1.addAndGet(12));
    //先获取值,再相加
    System.out.println("先获取值,再与12相加:  "+int1.getAndAdd(12));
    //先获取值,再赋新值
    System.out.println("先获取值,再赋新值100:  "+int1.getAndSet(100));
    //自减1,再获取值
    System.out.println("自减1,再获取值:  "+int1.decrementAndGet());
    //自增1,再获取值
    System.out.println("自增1,再获取值:  "+int1.incrementAndGet());
    //先获取值,再自减1
    System.out.println("先获取值,再自减1:  "+int1.getAndDecrement());
    //先获取值,再自增1
    System.out.println("先获取值,再自增1:  "+int1.getAndIncrement());

}

运行结果

AtomicInteger的默认值为:0
获取数据的值为:  123
先与12相加,再获取值:  135
先获取值,再与12相加:  135
先获取值,再赋新值100:  147
自减1,再获取值:  99
自增1,再获取值:  100
先获取值,再自减1:  100
先获取值,再自增1:  99

使用AtomicInteger和int在高并发下的线程安全

public class Counter {
    public static AtomicInteger count=new AtomicInteger();
    public volatile static int countInt=0;
    public static void increase(){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        countInt++;
        count.getAndIncrement();  //自增
    }

    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch latch=new CountDownLatch(100);

        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.increase();
                    latch.countDown();
                }
            }).start();
        }

        latch.await();

        System.out.println("运行结果:count: "+Counter.count+",countInt: "+countInt);
    }
}

运行结果:

运行结果:count: 100,countInt: 99

使用AtomicInteger,即使不用同步锁synchronized,最后的结果也是100,可用看出AtomicInteger的作用,用原子方式更新的int值。主要用于在高并发环境下的高效程序处理。使用非阻塞算法来实现并发控制。

总结:

使用AtomicInteger是线程安全的,即使不使用synchronized关键字也能保证其是线程安全的。而且由于AtomicInteger由硬件提供原子操作指令实现,在非激烈竞争的情况下,开销更小,速度更快

项目中的简单使用

// 线程安全的数量累加器
AtomicInteger amount = new AtomicInteger(0);

// 计算总金额,封装订单明细
List<OrderDetail> orderDetails=shoppingCarts.stream().map((item)->{
    OrderDetail orderDetail = new OrderDetail();
    orderDetail.setOrderId(orderId);
    orderDetail.setNumber(item.getNumber());
    orderDetail.setDishFlavor(item.getDishFlavor());
    orderDetail.setDishId(item.getDishId());
    orderDetail.setSetmealId(item.getSetmealId());
    orderDetail.setName(item.getName());
    orderDetail.setImage(item.getImage());
    orderDetail.setAmount(item.getAmount());
    
    // 每一个订单详情中的  价格(item.getAmount())  乘于(.multiply)   数量(item.getNumber())
    amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
    return orderDetail;
}).collect(Collectors.toList());

BigDecimal说明:

  1. 简介:

    Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、"、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

  2. 构造器描述:

    BigDecimal(int) 创建一个具有参数所指定整数值的对象。

    BigDecimal(double) 创建一个具有参数所指定双精度值的对象。 //不推荐使用

    BigDecimal(long) 创建一个具有参数所指定长整数值的对象。

    BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。 //推荐使用

  3. 方法说明

    add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。

    subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。

    multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。

    divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。

    toString() 将BigDecimal将对象的数值转换成字符串。
    doubleValue() 将BigDecimal对象中的值以双精度数返回。
    floatValue() 将BigDecimal对象中的值以单精度数返回。
    longValue() 将BigDecimal对象中的值以长整数返回。
    intValue() 将BigDecimal对象中的值以整数返回。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-15 11:30:57  更:2022-05-15 11:31:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:59:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码