概述
完成了商品的添加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
,然后遍历该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); } }
单元测试通过.可以看到是按照方法名的升序顺序执行的,形成一个闭环。
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] • 24
Github地址
代码地址: https://github.com/yangshangwei/o2o