略
DAO编码后的思考
DAO层演变为接口设计和SQL编写
好处:源代码与SQL分离,方便Review,DAO拼接等逻辑在Service层去完成.
事务的优点和注意事项
- 开发团队达成一致约定,明确标注事务方法的编程风格
- 保证事务方法的执行时间尽可能短,不要穿插远程调用或网络请求的操作,需要把这些剥离到事务方法外
- 不是所有方法都要放到事务中,如只有一条的修改,只读操作不需要事务控制.
编码过程
修改之前代码
加入spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.yuda"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <import resource="spring-dao.xml"/> <import resource="spring-service.xml"/> </beans>
|
spring-service.xml
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
|
DTO
数据传输层:用于对Entity进行封装,然后传输给其他层.
Exposer.java
暴露秒杀地址DTO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Data public class Exposer { private boolean exposed; private String md5; private long seckillId; private long now; private long start; private long end; public Exposer(boolean exposed, String md5, long seckillId) { this.exposed = exposed; this.md5 = md5; this.seckillId = seckillId; } public Exposer(boolean exposed, long now, long start, long end) { this.exposed = exposed; this.now = now; this.start = start; this.end = end; } public Exposer(boolean exposed, long seckillId) { this.exposed = exposed; this.seckillId = seckillId; } }
|
SeckillExecution.java
秒杀执行后的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Data public class SeckillExecution { private long seckillId; private int state; private String stateInfo; private SuccessKilled successKilled; public SeckillExecution(long seckillId, SeckillStatEnum statEnum, SuccessKilled successKilled) { this.seckillId = seckillId; this.state = statEnum.getState(); this.stateInfo = statEnum.getStateinfo(); this.successKilled = successKilled; } public SeckillExecution(long seckillId, int state, String stateInfo) { this.seckillId = seckillId; this.state = state; this.stateInfo = stateInfo; } }
|
使用枚举来封装数据字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public enum SeckillStatEnum { SUCCESS(1, "成功"), END(0, "秒杀结束"), REPEAT_KILL(-1, "重复秒杀"), INNER_ERROR(-2, "系统异常"), DATA_REWRITE(-3, "数据篡改");
private int state; private String stateinfo;
SeckillStatEnum(int state, String stateinfo) { this.state = state; this.stateinfo = stateinfo; }
public int getState() { return state; } public String getStateinfo() { return stateinfo; } public static SeckillStatEnum stateOf(int index) { for (SeckillStatEnum statEnum : values()) { if (statEnum.getState() == index) { return statEnum; } } return null; } }
|
异常类编写
- SeckillCloseException 秒杀关闭异常
- SeckillException 秒杀异常
- RepeatKillException 重复秒杀异常
只举一个例子,其他类似
1 2 3 4 5
| public class RepeatKillException extends RuntimeException { public RepeatKillException(String s) { super(s); } }
|
Service层正式编写
接口
1 2 3 4 5 6 7 8 9 10
| public interface SeckillService { List<Seckill> getSeckillList(); Seckill getById(long seckillId); Exposer exportSeckillUrl(long seckillId); SeckillExecution executeSeckill(long seckillId, long userPhone, String md5); }
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| package com.yuda.serivce.impl;
import com.yuda.dao.SeckillDao; import com.yuda.dao.SuccessKilledDao; import com.yuda.dto.Exposer; import com.yuda.dto.SeckillExecution; import com.yuda.entity.Seckill; import com.yuda.entity.SuccessKilled; import com.yuda.enums.SeckillStatEnum; import com.yuda.exception.RepeatKillException; import com.yuda.exception.SeckillCloseException; import com.yuda.exception.SeckillException; import com.yuda.serivce.SeckillService; 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 org.springframework.util.DigestUtils;
import java.util.Date; import java.util.List;
@Service @Transactional public class SeckillServiceImpl implements SeckillService {
private final Logger loggger = LoggerFactory.getLogger(this.getClass());
@Autowired private SeckillDao seckillDao;
@Autowired private SuccessKilledDao successKilledDao;
private final String slat = "abcafafafa;jo;213;ofj;";
@Override public List<Seckill> getSeckillList() { return seckillDao.queryAll(0, 10); }
@Override public Seckill getById(long seckillId) { return seckillDao.queryById(seckillId); }
@Override public Exposer exportSeckillUrl(long seckillId) { Seckill seckill = seckillDao.queryById(seckillId); if (seckill == null) { return new Exposer(false, seckillId); } Date startTime = seckill.getStartTime(); Date endTime = seckill.getEndTime(); Date nowTime = new Date(); if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) { return new Exposer(false, seckillId, nowTime.getTime(), endTime.getTime()); } String md5 = getMD5(seckillId); return new Exposer(true, md5, seckillId); }
@Override public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) { if (md5 == null || !md5.equals(getMD5(seckillId))) { throw new SeckillException("seckill data rewrite"); } Date nowTime = new Date(); try { int updateCount = seckillDao.reduceNumber(seckillId, nowTime); if (updateCount <= 0) { throw new SeckillCloseException("seckill is closed"); } else { int insertCount = successKilledDao.insertSucceccKilled(seckillId, userPhone); if (insertCount <= 0) { throw new RepeatKillException("seckill repeated"); } else { SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, successKilled); } } } catch (SeckillCloseException ee) { throw ee; } catch (RepeatKillException eee) { throw eee; } catch (Exception e) { loggger.error(e.getMessage(), e); throw new SeckillException("seckill inner error" + e.getMessage()); } } private String getMD5(long seckillId) { String base = seckillId + "/" + slat; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } }
|