实战SSM_O2O商铺_10【商铺注册】Service层的实现
文章目錄
- 概述
- ShopService開發步驟
- Service層接口類ShopService
- Service層接口實現類ShopServiceImpl
- ShopOperationException
- 單元測試
- 注意事項
- Github地址
概述
截至目前,我們已經完成了持久層insertShop的開發,編寫了操作圖片和路徑的工具類,使用ENUM定義了操作店鋪使用的常量,定義了操作店鋪返回的DTO類ShopExecution。
鋪墊了這么多,終于可以編寫Service層的代碼了,service層操作店鋪返回DTO類ShopExecution(不僅包含Shop的信息,同時也要包含操作店鋪的狀態信息)。
ShopService開發步驟
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);@Autowiredprivate 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@Transactionalpublic 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 {@AutowiredShopService shopService;@Testpublic 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的方式,一步步的調試分析
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數據庫信息:
同樣的需要檢查下操作水印圖片的操作是否正常,加上了水印,OK
注意事項
1. addShop的多個操作步驟(設置基本信息,插入tb_shop || 添加成功,則繼續處理文件,獲取shopid,用于創建圖片存放的目錄 || 更新tb_shop中 shop_img字段 ),需要找同一個事務中,所以addShop方法添加了@Transactional注解
2. Spring默認情況下會對運行期例外(RunTimeException)進行事務回滾,所以ShopOperationException必須繼承RuntimeException才可以實現回滾,如果繼承Exception則不會回滾數據庫操作。
Github地址
代碼地址: https://github.com/yangshangwei/o2o
總結
以上是生活随笔為你收集整理的实战SSM_O2O商铺_10【商铺注册】Service层的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实战SSM_O2O商铺_09【商铺注册】
- 下一篇: 实战SSM_O2O商铺_11【商铺注册】