文章目录

概述

完成了商品的添加Dao层到View层功能之后,

​实战SSM_O2O商铺_28【商品】商品添加之Dao层的实现​

​实战SSM_O2O商铺_29【商品】商品添加之Service层的实现及重构​

​实战SSM_O2O商铺_30【商品】商品添加之Controller层的实现​

​实战SSM_O2O商铺_31【商品】商品添加之View层的实现​

我们先来看下商品的编辑,最后做商品列表展示,当然了,可根据个人习惯,调整开发顺序。


说到商品编辑,

  • 首先肯定要根据productId查到对应Product相关的信息,既然这里是Dao层的开发,所以需要在Dao层需要开发一个 selectProductById 方法
  • 商品信息有商品缩略图和详情图片,这里我们先约定好:如果用户传入了新的商品缩略图和详情图片,就将原有的商品缩略图和详情图片删除掉。


  • 商品缩略图的地址存放在tb_product的img_addr字段,所以只需要更新改表即可。 所以对应Dao层应该有个方法updateProduct
  • 图片缩略图还涉及磁盘上的文件的删除,需要根据productId获取到Product ,然后获取Product的imgAddr属性,复用selectProductById 解渴
  • 详情图片的地址存放在tb_product_img中,根据product_id可以查找到对应商品下的全部详情图片,所以对应Dao层应该有个方法deleteProductImgById
  • 图片详情还涉及磁盘上的文件的删除,需要根据productId获取到​​List<ProductImg>​​ ,然后遍历该list,获取集合中每个ProductImg的imgAddr地址,所以还需要有个selectProductImgList方法



Dao层接口

ProductDao#selectProductById

/**
*
*
* @Title: selectProductById
*
* @Description: 根据productId查询product
*
* @param productId
*
* @return: Product
*/
Product selectProductById(long productId);

ProductDao#updateProduct

/**
*
*
* @Title: updateProduct
*
* @Description: 修改商品
*
* @param product
*
* @return: int
*/
int updateProduct(Product product);

ProductImgDao#deleteProductImgById

/**
*
*
* @Title: deleteProductImgById
*
* @Description: 删除商品对应的商品详情图片
*
* @param productId
*
* @return: int
*/
int deleteProductImgById(long productId);

ProductImgDao#selectProductImgList

/**
*
*
* @Title: selectProductImgList
*
* @Description: 根据productId查询商铺对应的图片详情信息
*
* @param productId
*
* @return: List<ProductImg>
*/
List<ProductImg> selectProductImgList(long productId);

Mapper映射文件

ProductDao.xml

说明见注释

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.artisan.o2o.dao.ProductDao">

<resultMap id="productMap" type="com.artisan.o2o.entity.Product" >
<id column="product_id" property="productId"/>
<!-- property对应实体类中的属性名 column 对应库表中的字段名 -->
<result column="product_name" property="productName"/>
<result column="product_desc" property="productDesc"/>
<result column="img_addr" property="imgAddr" />
<result column="normal_price" property="normalPrice" />
<result column="promotion_price" property="promotionPrice" />
<result column="priority" property="priority" />
<result column="create_time" property="createTime" />
<result column="last_edit_time" property="lastEditTime" />
<result column="enable_status" property="enableStatus" />

<!-- 一对一使用association
product中的属性为productCategory, 通过数据库中的product_category_id关联起来的
类型为 com.artisan.o2o.entity.ProductCategory-->
<association property="productCategory" column="product_category_id"
javaType="com.artisan.o2o.entity.ProductCategory">
<!-- 对应ProductCategory中的属性 tb_product_category的字段 -->
<id column="product_category_id" property="productCategoryId" />
<result column="product_category_name" property="productCategoryName" />
</association>

<!-- 一对一使用association
product中的属性为shop, 通过数据库中的shop_id关联起来的
类型为com.artisan.o2o.entity.Shop-->
<association property="shop" column="shop_id"
javaType="com.artisan.o2o.entity.Shop">
<id column="shop_id" property="shopId" />
<!-- 对应Shop中的属性 tb_shop的字段 ,如果是符合对象,使用xx.xxx的方式-->
<result column="owner_id" property="owner.userId" />
<result column="shop_name" property="shopName" />
</association>
<!-- 一对多使用collection
product中的属性为productImgList,并且是通过库表中的product_id关联起来的,
保存的类型为com.imooc.myo2o.entity.ProductImg -->
<collection property="productImgList" column="product_id"
ofType="com.artisan.o2o.entity.ProductImg">
<id column="product_img_id" property="productImgId" />
<result column="img_addr" property="imgAddr" />
<result column="img_desc" property="imgDesc" />
<result column="priority" property="priority" />
<result column="create_time" property="createTime" />
<result column="product_id" property="productId" />
</collection>


</resultMap>

<insert id="insertProduct"
parameterType="com.artisan.o2o.entity.Product"
useGeneratedKeys="true" keyProperty="productId" keyColumn="product_id">
INSERT INTO
tb_product
(
product_name,
product_desc,
img_addr,
normal_price,
promotion_price,
priority,
create_time,
last_edit_time,
enable_status,
product_category_id,
shop_id
)
VALUES(
#{productName},
#{productDesc},
#{imgAddr},
#{normalPrice},
#{promotionPrice},
#{priority},
#{createTime},
#{lastEditTime},
#{enableStatus},
#{productCategory.productCategoryId},
#{shop.shopId}
)
</insert>


<select id="selectProductById" resultMap="productMap" parameterType="Long">
<!-- 具体的sql -->
SELECT
p.product_id,
p.product_name,
p.product_desc,
p.img_addr,
p.normal_price,
p.promotion_price,
p.priority,
p.create_time,
p.last_edit_time,
p.enable_status,
p.product_category_id,
p.shop_id,
pm.product_img_id,
pm.img_addr,
pm.img_desc,
pm.priority,
pm.create_time
FROM
tb_product p
<!-- 左连接LEFT JOIN,(即使该商品没有商品详情图片,也要查询出来该商铺) -->
LEFT JOIN
tb_product_img pm
ON
p.product_id =pm.product_id
WHERE
p.product_id = #{productId}
ORDER BY
pm.priority DESC
</select>


<update id="updateProduct" parameterType="com.artisan.o2o.entity.Product">
UPDATE
tb_product
<set>
<!-- 注意后面的逗号 -->
<if test="productName !=null ">product_name = #{productName},</if>
<if test="productDesc !=null ">product_desc = #{productDesc},</if>
<if test="imgAddr !=null ">img_addr = #{imgAddr},</if>
<if test="normalPrice != null ">normal_price = #{normalPrice},</if>
<if test="promotionPrice != null ">promotion_price = #{promotionPrice},</if>
<if test="priority != null">priority = #{priority},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="lastEditTime != null">last_edit_time = #{lastEditTime},</if>
<if test="enableStatus != null ">enable_status = #{enableStatus},</if>
<!-- 注意如果是引用的复杂对象的写法 -->
<if test="productCategory != null and productCategory.productCategoryId != null ">product_category_id = #{productCategory.productCategoryId},</if>
</set>
WHERE
product_id = #{productId}
AND
shop_id=#{shop.shopId}
</update>

</mapper>

ProductImgDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.artisan.o2o.dao.ProductImgDao">

<insert id="batchInsertProductImg"
parameterType="com.artisan.o2o.entity.ProductImg"
useGeneratedKeys="true" keyProperty="productImgId" keyColumn="product_img_id">
INSERT INTO
tb_product_img
(
img_addr,
img_desc,
priority,
create_time,
product_id
)
VALUES
<foreach collection="list" item="productImg" index="index" separator=",">
(
#{productImg.imgAddr},
#{productImg.imgDesc},
#{productImg.priority},
#{productImg.createTime},
#{productImg.productId}
)
</foreach>
</insert>



<delete id="deleteProductImgById">
DELETE FROM
tb_product_img
WHERE
product_id = #{produtId}
</delete>


<select id="selectProductImgList" resultType="com.artisan.o2o.entity.ProductImg">
SELECT
product_img_id,
img_addr,
img_desc,
priority,
create_time,
product_id
FROM
tb_product_img
WHERE
product_id=#{productId}
ORDER BY
product_img_id
</select>

</mapper>

单元测试

ProductDaoTest

package com.artisan.o2o.dao;

import java.util.Date;

import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;

import com.artisan.o2o.BaseTest;
import com.artisan.o2o.entity.Product;
import com.artisan.o2o.entity.ProductCategory;
import com.artisan.o2o.entity.Shop;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ProductDaoTest extends BaseTest {

@Autowired
ProductDao productDao;

@Test
public void testA_InsertProdcut() {

// 注意表中的外键关系,确保这些数据在对应的表中的存在
ProductCategory productCategory = new ProductCategory();
productCategory.setProductCategoryId(36L);

// 注意表中的外键关系,确保这些数据在对应的表中的存在
Shop shop = new Shop();
shop.setShopId(5L);

Product product = new Product();
product.setProductName("test_product");
product.setProductDesc("product desc");
product.setImgAddr("/aaa/bbb");
product.setNormalPrice("10");
product.setPromotionPrice("8");
product.setPriority(66);
product.setCreateTime(new Date());
product.setLastEditTime(new Date());
product.setEnableStatus(1);
product.setProductCategory(productCategory);
product.setShop(shop);

int effectNum = productDao.insertProduct(product);
Assert.assertEquals(1, effectNum);
}

@Test
public void testB_UpdateProduct() {

// 注意表中的外键关系,确保这些数据在对应的表中的存在
ProductCategory productCategory = new ProductCategory();
productCategory.setProductCategoryId(36L);

// 注意表中的外键关系,确保这些数据在对应的表中的存在
Shop shop = new Shop();
shop.setShopId(5L);

Product product = new Product();
product.setProductName("modifyProduct");
product.setProductDesc("modifyProduct desc");
product.setImgAddr("/mmm/ddd");
product.setNormalPrice("350");
product.setPromotionPrice("300");
product.setPriority(66);
product.setLastEditTime(new Date());
product.setEnableStatus(1);

product.setProductCategory(productCategory);
product.setShop(shop);

// 设置productId
product.setProductId(2L);

int effectNum = productDao.updateProduct(product);
Assert.assertEquals(1, effectNum);

}
}

ProductImgDaoTest

package com.artisan.o2o.dao;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;

import com.artisan.o2o.BaseTest;
import com.artisan.o2o.entity.ProductImg;

/**
*
*
* @ClassName: ProductImgDaoTest
*
* @Description: 测试类的执行顺序可通过对测试类添加注解@FixMethodOrder(value) 来指定
*
* @author: Mr.Yang
*
* @date: 2018年6月30日 下午3:28:28
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ProductImgDaoTest extends BaseTest {

@Autowired
private ProductImgDao productImgDao;

/**
* 加入@Ignore 可以不执行该单元测试方法
*/
@Test
@Ignore
public void testBatchInsertProductImg() {
ProductImg productImg1 = new ProductImg();
productImg1.setImgAddr("/xiaogongjiang/xxxx");
productImg1.setImgDesc("商品详情图片1");
productImg1.setPriority(99);
productImg1.setCreateTime(new Date());
productImg1.setProductId(2L);

ProductImg productImg2 = new ProductImg();
productImg2.setImgAddr("/artisan/xxxx");
productImg2.setImgDesc("商品详情图片2");
productImg2.setPriority(98);
productImg2.setCreateTime(new Date());
productImg2.setProductId(2L);

// 添加到productImgList中
List<ProductImg> productImgList = new ArrayList<ProductImg>();
productImgList.add(productImg1);
productImgList.add(productImg2);

// 调用接口批量新增商品详情图片
int effectNum = productImgDao.batchInsertProductImg(productImgList);
Assert.assertEquals(2, effectNum);
}

@Test
public void testA_BatchInsertProductImg() {
ProductImg productImg1 = new ProductImg();
productImg1.setImgAddr("/xxx/xxx");
productImg1.setImgDesc("商品详情图片1x");
productImg1.setPriority(88);
productImg1.setCreateTime(new Date());
productImg1.setProductId(3L);

ProductImg productImg2 = new ProductImg();
productImg2.setImgAddr("/yyy/yyyy");
productImg2.setImgDesc("商品详情图片2y");
productImg2.setPriority(66);
productImg2.setCreateTime(new Date());
productImg2.setProductId(3L);

// 添加到productImgList中
List<ProductImg> productImgList = new ArrayList<ProductImg>();
productImgList.add(productImg1);
productImgList.add(productImg2);

// 调用接口批量新增商品详情图片
int effectNum = productImgDao.batchInsertProductImg(productImgList);
Assert.assertEquals(2, effectNum);
}

@Test
public void testB_DeleteProductImgById() {
Long productId = 3L;
int effectNum = productImgDao.deleteProductImgById(productId);
Assert.assertEquals(2, effectNum);
}
}

单元测试通过.可以看到是按照方法名的升序顺序执行的,形成一个闭环。

实战SSM_O2O商铺_32【商品】商品编辑之Dao层的实现_ssm实战


SQL日志如下

JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5bf8fa12] will not be managed by Spring
==> Preparing: INSERT INTO tb_product_img ( img_addr, img_desc, priority, create_time, product_id ) VALUES ( ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ? )
==> Parameters: /xxx/xxx(String), 商品详情图片1x(String), 88(Integer), 2018-06-30 21:26:34.455(Timestamp), 3(Long), /yyy/yyyy(String), 商品详情图片2y(String), 66(Integer), 2018-06-30 21:26:34.455(Timestamp), 3(Long)
<== Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a67e3c6]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1921ad94] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@30af5b6b] will not be managed by Spring
==> Preparing: SELECT product_img_id, img_addr, img_desc, priority, create_time, product_id FROM tb_product_img WHERE product_id=? ORDER BY product_img_id
==> Parameters: 3(Long)
<== Columns: product_img_id, img_addr, img_desc, priority, create_time, product_id
<== Row: 16, /xxx/xxx, 商品详情图片1x, 88, 2018-06-30 21:26:34.0, 3
<== Row: 17, /yyy/yyyy, 商品详情图片2y, 66, 2018-06-30 21:26:34.0, 3
<== Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1921ad94]
ProductImg [productImgId=16, imgAddr=/xxx/xxx, imgDesc=商品详情图片1x, priority=88, createTime=Sat Jun 30 21:26:34 BOT 2018, productId=3]
ProductImg [productImgId=17, imgAddr=/yyy/yyyy, imgDesc=商品详情图片2y, priority=66, createTime=Sat Jun 30 21:26:34 BOT 2018, productId=3]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@535779e4] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6c0d9d86] will not be managed by Spring
==> Preparing: DELETE FROM tb_product_img WHERE product_id = ?
==> Parameters: 3(Long)
<== Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@535779e4]

Github地址

代码地址: ​​https://github.com/yangshangwei/o2o​