Java从零单排 -- 小马哥商城实战(2)

364 阅读5分钟

前言

上一章我们已经基本把小马哥商城的基本表设计好,这一章节,我们开始编写我们的项目代码。首先一个商城应该要有商品,那么我们先把商品上传到数据库里面。

商品上架

spring boot的官网上快速的初始化项目,下载压缩包后解压,通过idea导入项目。点击file->new->module from existing sources,选择我们刚刚解压的项目。 这次的项目,我们的将application.properties重命名为application.yml。对应的yml格式的写法跟properties有一些不一样,不过yml的写法结构,会让我们更加清楚的了解spring的配置。yml是结构树的形式

spring:
  datasource:
    #配置驱动
    driver-class-name: com.mysql.jdbc.Driver
    #配置数据库地址
    url: jdbc:mysql://localhost:3306/xiaoma_mall?serverTimezone=UTC
    #配置mysql账号
    username: root
    #配置mysql密码
    password: 123456

#xml文件的位置
mybatis:
    mapperLocations: classpath:/mapper/**/*.xml
#输出日志
    configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

为了让我们的idea更加强大,我们需要安装一个mybatis插件。点file->setting,选择Plugins,在marketplace搜索mybatis,现在安装free mybatis plugin。安装完后重启idea才能生效的。 idea重启后,我们就开始新建包和类。

相应的代码

//商品
public class Good {
    //自增id
    private int id;
    //商品名称
    private String name;
    //商品描述
    private String remark;
    //商品价格
    private BigDecimal price;
    //商品图片
    private String image;
    //商品类型
    private int nameType;
    //创建时间
    private Date createTime;
    //状态
    private int status;
    //创建者
    private int createUser;
    ...省略 set get 方法
}
@Component
@Mapper
public interface GoodDao {

    void add(Good good);
}

刚才我们安装了mybatis插件,在add方法会出现红色的波浪线,将鼠标放在add上后,用键盘alt+enter,选择 generate statement,将会在goodMapper生成<insert id="add"></insert>,我们在insert里面编写sql就可以了。

之前我们在写动态sql的时候,字段判断我们都是 field != null and field != '' 这样的写法,所以我们不希望存到数据库中的字段为null或空字符串,所以我们要进行判断,在重复写上面的表达式的时候,其实我们可以封装一个工具类来替代这种写。

在com.xiaoma.mall下新建一个utils包,创建一个DaoParamsUtil类

/**
 * 字段检测工具类
 *
 */
public class DaoParamsUtil {


    /**
     * 判断字段是否为空字符串
     *
     * @param o 将传进来的字段提升为Object类型
     * @return true 为null或空字符串
     *         false 否
     */
    public static boolean isBlank(Object o) {
        if (o == null) {
            return true;
        } else {
            if (o instanceof String) {
                if ("".equals(o.toString().trim())) {
                    return true;
                }
            }
            return false;
        }

    }
}

工具类编写完后,我们可以通过@包名.类名@方法名

<insert id="add">
        insert into good (
        <trim suffixOverrides=",">
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(name)">
                name,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(remark)">
                remark,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(price)">
                price,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(image)">
                image,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(nameType)">
                name_type,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(createTime)">
                create_time,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(status)">
                status,
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(createUser)">
                create_user
            </if>
        </trim>
        )
        values(
        <trim suffixOverrides=",">
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(name)">
                #{name},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(remark)">
                #{remark},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(price)">
                #{price},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(image)">
                #{image},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(nameType)">
                #{nameType},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(createTime)">
                #{createTime},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(status)">
                #{status},
            </if>
            <if test="!@com.xiaoma.mall.utils.DaoParamsUtil@isBlank(createUser)">
                #{createUser}
            </if>
        </trim>
        )
    </insert>
   <!-- 
    <trim suffixOverrides=","> 表示当拼接的时候结尾符号为逗号时将自动帮我去除,感叹号取反的意思。
    -->	

动态sql拼接完后,我们先开始写我们的测试类。

//service 写简单的逻辑

public interface GoodService {
    void add(Good good);
}

@Service
public class GoodServiceImpl implements GoodService {
    @Autowired
    private GoodDao goodDao;

    @Override
    @Transactional
    public void add(Good good) {
        goodDao.add(good);
    }
}

MallApplicationTests 类编写我们的测试方法

        @Autowired
	GoodService goodService;
	@Test
	public void addGood(){
		Good good = new Good();
		good.setName("7号电池");
		good.setPrice(new BigDecimal(2.5));
		goodService.add(good);

	}

商品图片我们还没有上传,现在我们用spring mvc 的文件上传功能MultipartFile

@RestController
@RequestMapping("good")
public class GoodController {
    
    /**
     * 商品文件上传接口
     * 
     * @param file
     * @return
     */
    @PostMapping("shelves")
    public String shelves(MultipartFile file) {
        if (file == null) {
            return "error";
        }
        //获取上传文件的后缀
        int suffixIndex = file.getOriginalFilename().lastIndexOf(".");
        String suffix = file.getOriginalFilename().substring(suffixIndex);

        try {
            //将上传的文件做md5加密,获取唯一的md5值
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(file.getBytes());
            String md5Name = new BigInteger(md.digest()).toString(16);
            //path文件路径, md5值昨晚文件名称
            Path path = Paths.get("D:/demo/" + md5Name + suffix);
            //保存文件
            Files.write(path, file.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
            return "error";
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "ok";
    }
}

用postman上传图片测试,返回ok代表上传成功。在我们配置的文件路径就能看到上传的图片了。

通过接口获取图片。

   /**
     * 获取图片
     *
     * @param imgName 图片名称
     * @param response 响应对象
     */
    @GetMapping("getGoodImg/{imgName}")
    public void getGoodImg(@PathVariable("imgName") String imgName, HttpServletResponse response) {
        Path path = Paths.get("D:/demo/" + imgName );
        try {
            //获取图片
            byte[] img = Files.readAllBytes(path);
            //组装响应头
            response.setContentType("image/png".concat(";charset=UTF-8"));
            response.setHeader("Content-Disposition", "inline; filename=" + imgName.split("\\.")[0] + "");
            // 将图片字节写到响应输出流
            ServletOutputStream servletOutputStream = response.getOutputStream();
            servletOutputStream.write(img);
            //将流输出并且关闭流
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

现在我们尝试下通过html页面上架商品,首先在static目录下新建一个html文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>小马哥商城</title>
</head>
<body>
    <h3>商品上架</h3>
    <form action="good/shelves" method="post" enctype="multipart/form-data">
        <div>商品名称<input name="name"></div>
        <div>商品描述<input name="remark"></div>
        <div>商品价格<input name="price"></div>
        <div>商品图片<input name="file" type="file"></div>
        <div><button type="submit">上架</button></div>
    </form>
</body>
</html>

good/shelves接口稍微改动下,

  @Autowired
    private GoodService goodService;

    /**
     * 商品文件上传接口
     *
     * @param file
     * @return
     */
    @PostMapping("shelves")
    public String shelves(MultipartFile file, Good good) {
        if (file == null) {
            return "error";
        }
        //获取上传文件的后缀
        int suffixIndex = file.getOriginalFilename().lastIndexOf(".");
        String suffix = file.getOriginalFilename().substring(suffixIndex);

        try {
            //将上传的文件做md5加密,获取唯一的md5值
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(file.getBytes());
            String md5Name = new BigInteger(md.digest()).toString(16);
            //path文件路径, md5值昨晚文件名称
            Path path = Paths.get("D:/demo/" + md5Name + suffix);
            //保存文件
            Files.write(path, file.getBytes());


            good.setImage( md5Name + suffix);
            goodService.add(good);
        } catch (IOException e) {
            e.printStackTrace();
            return "error";
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "ok";
    }

重新启动项目,在浏览器输入localhost:8080即可看到我们编写的Html页面。 输入好数据,点击上传就完成商品上架功能了。

小结

  • 数据插入,动态sql工具类使用
  • 图片上传,查看
  • MultipartFile
  • HttpServletResponse
  • 输出流OutputStream 上面的知识我们需要上网搜索了解,在这里就不一一解析了。输出流在java基础部分没有讲解,在这里就简单介绍什么是流?流其实文件的一种表现形式,在我们的电脑中,看到的文件、图片等,都是window帮我们做好流的转换,这样我们才能看到图片。在java中读取文件,就是获取输入流 IntputStream,上传文件也是获取输入流,将 输入流 写到输出流OutputStream,最后将流关闭,就完成文件的写入了。流用完一定要关闭,不然就会一直占用该文件流,他们人无法使用。