0
点赞
收藏
分享

微信扫一扫

SpringMVC集成Mybatis高并发秒杀系统(1)-DAO


1.文章为转载翻译​​高并发秒杀系统实例​​​,有侵权请及时通知.
一.根据业务需求选取jar包.
DAO层采用Mybatis,交由SpringIOC容器管理,数据源c3p0管理,mysql数据库所以初步选择的包种有:
Spring4.1.7,
Mybatis
mybatis-spring整合包
c3p0及其依赖
mysql驱动包

主要掌握:

1.如何基于只写接口和配置完成DAO层,即不用写DAO层的实例类?

2.如何处理mybatis和spring的整合配置

3.常见的问题解决方案

一.结果

SpringMVC集成Mybatis高并发秒杀系统(1)-DAO_xml


SeckillDao

package org.seckill.dao;

import java.util.Date;
import java.util.List;

import org.apache.ibatis.annotations.Param;
import org.seckill.entity.Seckill;

public interface SeckillDao {
/**
* 减少库存
* @param seckillId
* @param killTime
* @return >1则返回更新的行数
*/
int reduceNumber(@Param("seckillId")long seckillId,@Param("killTime")Date killTime);
Seckill queryById(long seckillId);
/**
* 分页获取全部
* @param offset 起始位置
* @param limit 单页条数
* @return
*/
List<Seckill> queryAll(@Param("offset")int offset,@Param("limit")int limit);
}

SuccessKilledDao.java

package org.seckill.dao;

import org.apache.ibatis.annotations.Param;
import org.seckill.entity.SuccessKilled;

public interface SuccessKilledDao {
int insertSucessKilled(@Param("seckillId")long seckillId,@Param("userPhone")long userPhone);

/**
* 通过id获取成功秒杀单信息对象,和取得秒杀对象
* @param seckillId
* @return
*/
SuccessKilled queryByIdwithSeckill(long seckillId);
}

SeckillDao.xml

<?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="org.seckill.dao.SeckillDao">
<update id="reduceNumber">
<![CDATA[
update
seckill
set
number = number-1
where seckill_id = #{seckillId}
and start_time <= #{killTime}
and end_time >= #{killTime}
and number > 0
]]>
</update>


<select id="queryById" resultType="Seckill" parameterType="long">
<![CDATA[
select seckill_id,name,number,start_time,end_time,create_time
from seckill
where seckill_id = #{seckillId}

]]>
</select>

<select id="queryAll" resultType="Seckill">
<![CDATA[
select seckill_id,name,number,start_time,end_time,create_time
from seckill
order by create_time desc
limit #{offset},#{limit}
]]>
</select>
</mapper>

SuccessKilledDao.xml

<?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="org.seckill.dao.SuccessKilledDao">
<insert id="insertSucessKilled">
<![CDATA[
insert ignore into success_killed(seckill_id,user_phone)
values(#[seckillId],#{userPhone})
]]>
</insert>

<select id="queryByIdwithSeckill" resultType="SuccessKilled">
<![CDATA[
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.number",
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}
]]>
</select>
</mapper>

Seckill.java

package org.seckill.entity;

import java.util.Date;

public class Seckill {
private long seckillId;

private String name;

private int number;

private Date startTime;

private Date endTime;

private Date creatTime;
//省略get,set方法和toString
}

SuccessKilled.java

package org.seckill.entity;

import java.util.Date;

public class SuccessKilled {
private long seckillId;

private long userPhone;

private short state;

private Date createTime;

//拓展多对一
private Seckill seckill;
//getter,setter,toString...
}

SeckillDaoTest.java

package org.seckill.test;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.seckill.dao.SeckillDao;
import org.seckill.entity.Seckill;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-dao.xml")
public class SeckillDaoTest {
// @Resource 测试注入成功
// private SqlSessionFactoryBean ssfb;

// @Resource 注入成功
// private ComboPooledDataSource cpd;

// @Resource 注入成功
// private MapperScannerConfigurer ddd;

// @Resource 注入成功
// private MapperScannerConfigurer ddd;

@Resource
private SeckillDao seckillDao;
@Test
public void testReduceNumber() {
try{
int i = seckillDao.reduceNumber(1000l, new Date());
System.out.println(i);
}catch (Exception e) {
System.out.println("出问题了");
e.printStackTrace();
}
}

@Test
public void testQueryById() {
try{
Seckill seckill = seckillDao.queryById(1000);
System.out.println(seckill);
}catch (Exception e) {
System.out.println("出问题了");
e.printStackTrace();
}
}

@Test
public void testQueryAll() {
try{
List<Seckill> seckillList = seckillDao.queryAll(1, 10);
System.out.println(seckillList.size());
}catch (Exception e) {
System.out.println("出问题了");
e.printStackTrace();
}
}
}

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=utf8
uname=root
password=123

log4j.properties

# Configure logging for testing: optionally with log file
log4j.rootLogger=WARN, stdout
# log4j.rootLogger=WARN, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

mybatis-config.xml

<?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>
<!-- 使用generatedKeys去获取自增主键值 -->
<setting name="useGeneratedKeys" value="true"></setting>
<!-- 使用列别名替代列名 -->
<setting name="useColumnLabel" value="true"></setting>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
</configuration>

spring-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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 ">

<!-- 1.配置数据库信息 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 2.配置数据池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}" />
<property name="jdbcUrl" value="${url}" />
<property name="user" value="${uname}" />
<property name="password" value="${password}" />

<!-- C3P0私有属性 -->
<property name="maxPoolSize" value="30" />
<property name="minPoolSize" value="10" />
<property name="autoCommitOnClose" value="false" />
<property name="checkoutTimeout" value="5000" />
<!-- 获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="9" />
</bean>

<!-- 3.配置工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="typeAliasesPackage" value="org.seckill.entity" />
<property name="mapperLocations" value="classpath:org/seckill/dao/mapper/*Dao.xml" />
</bean>

<!-- 4:配置扫描Dao接口的包,动态实现Dao接口,注入到Spring容器中 -->
<bean id="ddd" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--这里是今天要说的重点-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!-- 给出需要扫描的Dao接口包 -->
<property name="basePackage" value="org.seckill.dao"></property>
</bean>
</beans>

数据库mysql

/*1.创建数据库*/
CREATE DATABASE seckill;
use seckill;
CREATE TABLE seckill(
seckill_id bigint NOT NULL auto_increment COMMENT'商品库存id',
name varchar(120) NOT NULL COMMENT'商品名称',
number int NOT NULL COMMENT '库存数量',
start_time timestamp not null comment'秒杀开启时间',
end_time timestamp not null comment'秒杀结束时间',
create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'创建时间',
primary key (seckill_id),
key idx_start_time(start_time),
key idx_end_time(end_time),
index idx_create_time(create_time)
)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表';
/*初始化数据*/
insert into seckill(name,number,start_time,end_time)
values('1000元秒杀iphone6',100,'2017-11-01 00:00:00','2017-11-02 00:00:00');

insert into seckill(name,number,start_time,end_time)
values('500元秒杀ipad2',200,'2017-11-01 00:00:00','2017-11-02 00:00:00');

insert into seckill(name,number,start_time,end_time)
values('300元秒杀小米4',300,'2017-11-01 00:00:00','2017-11-02 00:00:00');

insert into seckill(name,number,start_time,end_time)
values('200元秒杀红米note',400,'2017-11-01 00:00:00','2017-11-02 00:00:00');


/* */
create table success_killed(
seckill_id bigint NOT NULL COMMENT'秒杀商品id',
user_phone bigint NOT NULL COMMENT'用户手机号',
state tinyint NOT NULL DEFAULT -1 COMMENT'标识:-1:无效,0:成功,1:已付款,2:已发货',
create_time timestamp NOT NULL COMMENT'创建时间',
primary key(seckill_id,user_phone)/*联合主键*/,
key idx_create_time(create_time)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表';

二.解析
1.@Param(“”) 形参标识怎么使用?
当在mapper里的输入参数有两个时,就必须在接口的形参里增加Param注解,不然无法在执行sql语句时辨识,虽然接口形参写的十分明确,但是java对形参的保存基本是没有的,offset和limit相当于arg1,arg2,在select语句#取值的时候会分辨不清,必须用Param注解标识.

2.offset和limit有什么使用意义呢?
准备一张有十条数据以上的的表,执行语句select * from table limit 8,10
表达的含义是,从第八条数据开始显示数据,一直显示十条,即分页功能offset=8,limit=10

3.

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.number",
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}

可以发现,对sickill对象的赋值也是把sickill对象的内属性一个一个的赋上去的,先是经过驼峰转换,把下划线替换成后字母大写,再根据属性映射装配给sickill对象,之所以加隐号是为了避免解析器误解,驼峰转换被注解说明在mybatis-config.xml

7.执行过程中遇到过的问题与解决方案

1.各种ClassNotFound,从提示里选中找不到的类,百度一哈属于什么jar包,补一哈子就行了,比如放入了c3p0包之后,报mchange什么什么找不到,就知道了c3p0还依赖一个mchange-commons-java-xxx.jar,
2.No appenders could be found for logger,没配置log4.properties时就报出这样的错.
3.context:property-placeholder标签未声明异常,xml文件总有红叉
头上面增加:
​​​http://www.springframework.org/schema/context​​​ ​​http://www.springframework.org/schema/context/spring-context.xsd​​​
4.最重要的最重要的找了很久的错误的
jdbc.properties里的用户名属性千万不要写username,这样在取${username}的时候怕是取的是windows环境变量下的username了,还有数据源ComboPooledDataSource的属性是user而不是username,这一点一定要跟其他数据源分清。

5.bug修改

SpringMVC集成Mybatis高并发秒杀系统(1)-DAO_spring_02


内嵌驼峰转换好像总是会报错,当SuccessKilled里的seckill对象也需要驼峰内置转换的时候会报错,修改一下:

<select id="queryByIdwithSeckill" resultMap="getSuccessKilled">
<![CDATA[
select
sk.seckill_id,
sk.user_phone,
sk.create_time,
sk.state,
s.seckill_id,
s.name,
s.number,
s.start_time,
s.end_time,
s.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>
<resultMap type="org.seckill.entity.SuccessKilled" id="getSuccessKilled">
<id property="seckillId" column="sk.seckill_id"/>
<result property="userPhone" column="sk.user_phone"/>
<result property="state" column="sk.state"/>
<result property="createTime" column="sk.create_time"/>
<association property="seckill" javaType="org.seckill.entity.Seckill">
<id property="seckillId" column="s.seckill_id"/>
<result property="name" column="s.name"/>
<result property="number" column="s.number"/>
<result property="startTime" column="s.start_timee"/>
<result property="endTime" column="s.end_time"/>
<result property="creatTime" column="s.create_time"/>
</association>
</resultMap>

搞定


举报

相关推荐

0 条评论