项目简介
在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番。
记录在该项目中学到了什么玩意..
该项目源码对应的gitHub地址(由观看其视频的人编写,并非视频源代码):https://github.com/codingXiaxw/seckill
我结合其资料和观看视频的时候整理出从该项目学到了什么…
项目Dao层
日志记录工具:
<!--1.日志 java日志有:slf4j,log4j,logback,common-logging slf4j:是规范/接口 日志实现:log4j,logback,common-logging 使用:slf4j+logback -->
Mybatis之前没注意到的配置属性:
使用jdbc的getGeneratekeys获取自增主键值,这个属性还是挺有用的。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--配置全局属性--> <settings> <!--使用jdbc的getGeneratekeys获取自增主键值--> <setting name="useGeneratedKeys" value="true"/> <!--使用列别名替换列名 默认值为true select name as title(实体中的属性名是title) form table; 开启后mybatis会自动帮我们把表中name的值赋到对应实体的title属性中 --> <setting name="useColumnLabel" value="true"/> <!--开启驼峰命名转换Table:create_time到 Entity(createTime)--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
Mybatis返回的对象如果有关联字段,除了使用resultMap还有下面这种方式(虽然我还是觉得resultMap会方便一点)
<select id="queryByIdWithSeckill" resultType="SuccessKilled"> <!--根据seckillId查询SuccessKilled对象,并携带Seckill对象--> <!--如何告诉mybatis把结果映射到SuccessKill属性同时映射到Seckill属性--> <!--可以自由控制SQL语句--> SELECT sk.seckill_id, sk.user_phone, sk.create_time, sk.state, s.seckill_id "seckill.seckill_id", s.name "seckill.name", s.number "seckill", s.start_time "seckill.start_time", s.end_time "seckill.end_time", s.create_time "seckill.create_time" FROM success_killed sk INNER JOIN seckill s ON sk.seckill_id=s.seckill_id WHERE sk.seckill_id=#{seckillId} AND sk.user_phone=#{userPhone} </select>
数据库连接池可能用到的属性:
<!--c3p0私有属性--> <property name="maxPoolSize" value="30"/> <property name="minPoolSize" value="10"/> <!--关闭连接后不自动commit--> <property name="autoCommitOnClose" value="false"/> <!--获取连接超时时间--> <property name="checkoutTimeout" value="1000"/> <!--当获取连接失败重试次数--> <property name="acquireRetryAttempts" value="2"/>
spring与Junit整合:
/** * Created by codingBoy on 16/11/27. * 配置spring和junit整合,这样junit在启动时就会加载spring容器 */ @RunWith(SpringJUnit4ClassRunner.class) //告诉junit spring的配置文件 @ContextConfiguration({"classpath:spring/spring-dao.xml"}) public class SeckillDaoTest { //注入Dao实现类依赖 @Resource private SeckillDao seckillDao; @Test public void queryById() throws Exception { long seckillId=1000; Seckill seckill=seckillDao.queryById(seckillId); System.out.println(seckill.getName()); System.out.println(seckill); } }
Mybatis参数为一个以上时
之前在学习MyBatis的时候,如果参数超过了一个,那么是使用Map集合来进行装载的!
在这次教程中发现,可以不用Map集合(如果都是基本数据类型)!
例子:使用@Param就行了!
int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);
在XML文件中可以直接忽略parameterType了!
避免重复插入数据时抛出异常
如果主键重复插入数据的时候,Mybatis正常是会抛出异常的,我们又不希望它抛出异常,那么我们可以这样做:
写ignore..
Service层
tdo
一个dto包作为传输层,dto和entity的区别在于:entity用于业务数据的封装,而dto用于完成web和service层的数据传递。
对于dto这个概念,在之前我是接触过一次的,但是没有好好地实践起来。这次看到了它的用法了。
我的理解是:Service与Web层数据传递数据的再包装了一个对象而已。因为很多时候Service层返回的数据如果使用的是POJO,POJO很多的属性是多余的,还有一些想要的数据又包含不了。此时,dto又可以再一次对要传输的数据进行抽象,封装想获取的数据。
定义多个异常对象
之前的异常对象都是针对整个业务的,其实还是可以细分多个异常类的出来的。比如“重复秒杀”,”秒杀关闭“这些都是属于秒杀的业务。
这样做的好处就是看到抛出的异常就能够知道是具体哪部分错了。
对于视频中在Service层就catch住了很多异常,我觉得可以在Service层直接抛出,在Controller也能抛出,直接使用统一异常处理器类来管理会更加方便!
提倡使用注解方式使用事务
我觉得就是代码更加清晰吧,使用注解的话。
在视频下面还有同学说如果在Service中调用事务方法会有些坑,我暂时还没遇到过。先存起来吧:
并发性上不去是因为当多个线程同时访问一行数据时,产生了事务,因此产生写锁,每当一个获取了事务的线程把锁释放,另一个排队线程才能拿到写锁,QPS和事务执行的时间有密切关系,事务执行时间越短,并发性越高,这也是要将费时的I/O操作移出事务的原因。
关于同类中调用事务方法的时候有个坑,同学们需要注意下AOP切不到调用事务方法。事务不会生效,解决办法有几种,可以搜一下,找一下适合自己的方案。本质问题时类内部调用时AOP不会用代理调用内部方法。
“关于同类中调用事务方法的时候有个坑” 解决方案
1、如果是基于接口动态代理 是没有问题的,直接使用接口调用
2、如果是基于class的动态代理 可以用 AopContext.currentProxy() 解决,注意剥离方法一定是public 修饰 !!