实战SSM_O2O商铺_10【商铺注册】Service层的实现

简介: 实战SSM_O2O商铺_10【商铺注册】Service层的实现

20191208103724395.png

概述


截至目前,我们已经完成了持久层insertShop的开发,编写了操作图片和路径的工具类,使用ENUM定义了操作店铺使用的常量,定义了操作店铺返回的DTO类ShopExecution。

铺垫了这么多,终于可以编写Service层的代码了,service层操作店铺返回DTO类ShopExecution(不仅包含Shop的信息,同时也要包含操作店铺的状态信息)。


ShopService开发步骤


20180522060708647.png


Service层接口类ShopService

src/main/java建立 com.artisan.o2o.service包,新增接口类ShopService

package com.artisan.o2o.service;
import java.io.File;
import com.artisan.o2o.dto.ShopExecution;
import com.artisan.o2o.entity.Shop;
public interface ShopService {
  ShopExecution addShop(Shop shop, File shopFile);
}


Service层接口实现类ShopServiceImpl

src/main/java先建立com.artisan.o2o.service.impl, 编写ShopService的接口实现类ShopServiceImpl

具体逻辑详见注释


package com.artisan.o2o.service.impl;
import java.io.File;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.artisan.o2o.dao.ShopDao;
import com.artisan.o2o.dto.ShopExecution;
import com.artisan.o2o.entity.Shop;
import com.artisan.o2o.enums.ShopStateEnum;
import com.artisan.o2o.exception.ShopOperationException;
import com.artisan.o2o.service.ShopService;
import com.artisan.o2o.util.FileUtil;
import com.artisan.o2o.util.ImageUtil;
@Service
public class ShopServiceImpl implements ShopService {
  private static final Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);
  @Autowired
  private ShopDao shopDao;
  /**
   * 3个步骤需要在一个事务中,添加@Transactional注解
   * 
   * 1. 将shop基本信息添加到数据库,返回shopId
   * 
   * 2. 根据shopId创建目录,得到图片存储的相对路径
   * 
   * 3. 将相对路径更新到数据库
   * 
   * 
   * Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚
   * 
   * (1)注解@Transactional 只能应用到 public 方法才有效
   * 
   * (2)在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring成的代理对象来管理,这会造成自调用问题。
   * 若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,
   * 有@Transactional注解的方法的事务被忽略,不会发生回滚。
   * 
   * 
   * 上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP
   * 代理造成的。为解决这两个问题,可以使用 AspectJ 取代 Spring AOP 代理
   * 
   * 在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP
   * 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional
   * 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor
   * 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑,
   * 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源
   * DataSource 提交或回滚事务
   * 
   */
  @Override
  @Transactional
  public ShopExecution addShop(Shop shop, File shopImg) {
    // 非空判断 (这里先判断shop是否为空,严格意义上讲shop中的are的属性也需要判断)
    if (shop == null) {
      return new ShopExecution(ShopStateEnum.NULL_SHOP_INFO);
    }
    // 关键步骤1. 设置基本信息,插入shop
    // 初始状态: 审核中
    shop.setEnableStatus(0);
    shop.setCreateTime(new Date());
    shop.setLastEditTime(new Date());
    int effectedNum = shopDao.insertShop(shop);
    if (effectedNum <= 0) {
      throw new ShopOperationException("店铺创建失败");
    } else {
      // 关键步骤2. 添加成功,则继续处理文件,获取shopid,用于创建图片存放的目录
      if (shopImg != null) {
        try {
          // 需要根据shopId来创建目录,所以也需要shop这个入参
          addShopImg(shop, shopImg);
        } catch (Exception e) {
          logger.error("addShopImg error {} ", e.toString());
          throw new ShopOperationException("addShopImg error:" + e.getMessage());
        }
        // 关键步骤3. 更新tb_shop中 shop_img字段
        effectedNum = shopDao.updateShop(shop);
        if (effectedNum <= 0) {
          logger.error("updateShop error {} ", "更新店铺失败");
          throw new ShopOperationException("updateShop error");
        }
      }
    }
    // 返回店铺的状态:审核中,以及店铺信息
    return new ShopExecution(ShopStateEnum.CHECK, shop);
  }
  /**
   * 
   * 
   * @Title: addShopImg
   * 
   * @Description: 根据shopId创建目录,并生成水印图片
   * 
   * @param shop
   * @param shopImg
   * 
   * @return: void
   */
  private void addShopImg(Shop shop, File shopImg) {
    String imgPath = FileUtil.getShopImagePath(shop.getShopId());
    // 生成图片的水印图
    String relativeAddr = ImageUtil.generateThumbnails(shopImg, imgPath);
    // 将相对路径设置个shop,用于更新数据库
    shop.setShopImg(relativeAddr);
  }
}

ShopOperationException

package com.artisan.o2o.exception;
/**
 * 
 * 
 * @ClassName: ShopOperationException
 * 
 * @Description: 继承自RuntimeException ,这样在标注了@Transactional事务的方法中,出现了异常,才回回滚数据。
 * 
 *               默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring
 *               将回滚事务;除此之外,Spring 不会回滚事务。
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年5月21日 下午5:37:53
 */
public class ShopOperationException extends RuntimeException {
  private static final long serialVersionUID = 6860566652051914211L;
  public ShopOperationException(String message) {
    super(message);
  }
}

单元测试

package com.artisan.o2o.service;
import java.io.File;
import java.util.Date;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.artisan.o2o.BaseTest;
import com.artisan.o2o.dto.ShopExecution;
import com.artisan.o2o.entity.Area;
import com.artisan.o2o.entity.PersonInfo;
import com.artisan.o2o.entity.Shop;
import com.artisan.o2o.entity.ShopCategory;
import com.artisan.o2o.enums.ShopStateEnum;
public class ShopServiceTest extends BaseTest {
  @Autowired
  ShopService shopService;
  @Test
  public void testAddShop() {
    Shop shop = new Shop();
    PersonInfo personInfo = new PersonInfo();
    Area area = new Area();
    ShopCategory shopCategory = new ShopCategory();
    personInfo.setUserId(1L);
    area.setAreaId(1);
    shopCategory.setShopCategoryId(1L);
    shop.setOwner(personInfo);
    shop.setArea(area);
    shop.setShopCategory(shopCategory);
    shop.setShopName("咖啡点");
    shop.setShopDesc("小工匠的咖啡店");
    shop.setShopAddr("NanJing");
    shop.setPhone("9876553");
    shop.setPriority(99);
    shop.setCreateTime(new Date());
    shop.setLastEditTime(new Date());
    shop.setEnableStatus(ShopStateEnum.CHECK.getState());
    shop.setAdvice("审核中");
    File shopFile = new File("D:/o2o/artisan.jpg");
    ShopExecution se = shopService.addShop(shop, shopFile);
    Assert.assertEquals(ShopStateEnum.CHECK.getState(), se.getState());
  }
}


可以在addShop方法中,加入断点,通过debug的方式,一步步的调试分析


20180522060858462.png

mybatis操作数据库的信息:

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
五月 21, 2018 6:09:55 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 2, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 10000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1br1ebw9v10u0o3ghf6lww|130d63be, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1br1ebw9v10u0o3ghf6lww|130d63be, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/o2o?useUnicode=true&characterEncoding=utf8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 30, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 10, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@3adcc812] will be managed by Spring
==>  Preparing: INSERT INTO tb_shop ( owner_id, area_id, shop_category_id, shop_name, shop_desc, shop_addr, phone, shop_img, priority, create_time, last_edit_time, enable_status, advice) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ); 
==> Parameters: 1(Long), 1(Integer), 1(Long), 咖啡点(String), 小工匠的咖啡店(String), NanJing(String), 9876553(String), null, 99(Integer), 2018-05-21 18:09:57.067(Timestamp), 2018-05-21 18:09:57.067(Timestamp), 0(Integer), 审核中(String)
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6] from current transaction
==>  Preparing: update tb_shop SET shop_name=?, shop_desc=?, shop_addr=?, phone=?, shop_img=?, priority=?, last_edit_time=?, enable_status=?, advice=?, area_id=?, shop_category_id=? where shop_id = ? 
==> Parameters: 咖啡点(String), 小工匠的咖啡店(String), NanJing(String), 9876553(String), \upload\item\shopImage\24\2018052118095757182.jpg(String), 99(Integer), 2018-05-21 18:09:57.067(Timestamp), 0(Integer), 审核中(String), 1(Integer), 1(Long), 24(Long)
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@512baff6]
五月 21, 2018 6:09:58 下午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@ae45eb6: startup date [Mon May 21 18:09:50 BOT 2018]; root of context hierarchy

数据库信息:


20180522061129589.png

同样的需要检查下操作水印图片的操作是否正常,加上了水印,OK


20180522061421982.png


注意事项


1. addShop的多个操作步骤(设置基本信息,插入tb_shop || 添加成功,则继续处理文件,获取shopid,用于创建图片存放的目录 || 更新tb_shop中 shop_img字段 ),需要找同一个事务中,所以addShop方法添加了@Transactional注解


2. Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚,所以ShopOperationException必须继承RuntimeException才可以实现回滚,如果继承Exception则不会回滚数据库操作。


Github地址


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

相关文章
|
4月前
|
搜索推荐 JavaScript Java
计算机Java项目|基于SSM的个性化商铺系统
计算机Java项目|基于SSM的个性化商铺系统
|
4月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的商铺租赁管理系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的商铺租赁管理系统附带文章和源代码部署视频讲解等
54 7
|
5月前
|
SQL 测试技术
实战SSM_O2O商铺_32【商品】商品编辑之Dao层的实现
实战SSM_O2O商铺_32【商品】商品编辑之Dao层的实现
47 0
|
5月前
|
前端开发 数据库
实战SSM_O2O商铺_31【商品】商品添加之View层的实现
实战SSM_O2O商铺_31【商品】商品添加之View层的实现
38 0
|
5月前
|
前端开发 fastjson 测试技术
实战SSM_O2O商铺_30【商品】商品添加之Controller层的实现
实战SSM_O2O商铺_30【商品】商品添加之Controller层的实现
41 0
|
5月前
|
前端开发 Java 数据库连接
实战SSM_O2O商铺_29【商品】商品添加之Service层的实现及重构
实战SSM_O2O商铺_29【商品】商品添加之Service层的实现及重构
32 0
|
5月前
|
测试技术
实战SSM_O2O商铺_28【商品】商品添加之Dao层的实现
实战SSM_O2O商铺_28【商品】商品添加之Dao层的实现
54 0
|
5月前
|
SQL 前端开发 测试技术
实战SSM_O2O商铺_27【商品类别】删除商品类别从Dao到View层的开发
实战SSM_O2O商铺_27【商品类别】删除商品类别从Dao到View层的开发
44 0
|
2月前
|
Java 数据库连接 Maven
手把手教你如何搭建SSM框架、图书商城系统案例
这篇文章是关于如何搭建SSM框架以及实现一个图书商城系统的详细教程,包括了项目的配置文件整合、依赖管理、项目结构和运行效果展示,并提供了GitHub源码链接。
手把手教你如何搭建SSM框架、图书商城系统案例
|
7天前
|
Java 应用服务中间件 数据库连接
ssm项目整合,简单的用户管理系统
文章介绍了一个使用SSM框架(Spring、SpringMVC、MyBatis)构建的简单用户管理系统的整合过程,包括项目搭建、数据库配置、各层代码实现以及视图展示。
ssm项目整合,简单的用户管理系统