文章目录
- 概述
- Dao层接口
-
- ProductDao#selectProductById
- ProductDao#updateProduct
- ProductImgDao#deleteProductImgById
- ProductImgDao#selectProductImgList
- Mapper映射文件
-
- ProductDao.xml
- ProductImgDao.xml
- 单元测试
-
- ProductDaoTest
- ProductImgDaoTest
- Github地址
概述
完成了商品的添加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
代码语言:javascript复制/**
*
*
* @Title: selectProductById
*
* @Description: 根据productId查询product
*
* @param productId
*
* @return: Product
*/
Product selectProductById(long productId);
ProductDao#updateProduct
代码语言:javascript复制/**
*
*
* @Title: updateProduct
*
* @Description: 修改商品
*
* @param product
*
* @return: int
*/
int updateProduct(Product product);
ProductImgDao#deleteProductImgById
代码语言:javascript复制/**
*
*
* @Title: deleteProductImgById
*
* @Description: 删除商品对应的商品详情图片
*
* @param productId
*
* @return: int
*/
int deleteProductImgById(long productId);
ProductImgDao#selectProductImgList
代码语言:javascript复制 /**
*
*
* @Title: selectProductImgList
*
* @Description: 根据productId查询商铺对应的图片详情信息
*
* @param productId
*
* @return: List
*/
List selectProductImgList(long productId);
Mapper映射文件
ProductDao.xml
说明见注释
代码语言:javascript复制<?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
代码语言:javascript复制<?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
代码语言:javascript复制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
代码语言:javascript复制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日志如下
代码语言:javascript复制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