Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
晶之木
miaosha
提交
d1a009ca
M
miaosha
项目概览
晶之木
/
miaosha
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
miaosha
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
d1a009ca
编写于
1月 20, 2019
作者:
Q
qiurunze123
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
提交mybatis
上级
d1a7509c
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
93 addition
and
117 deletion
+93
-117
README.md
README.md
+1
-1
docs/mybatis-code.md
docs/mybatis-code.md
+6
-114
src/main/java/com/geekq/miaosha/mybatis/Mapper/UserMapper.java
...ain/java/com/geekq/miaosha/mybatis/Mapper/UserMapper.java
+21
-0
src/main/java/com/geekq/miaosha/mybatis/TeacherVo.java
src/main/java/com/geekq/miaosha/mybatis/TeacherVo.java
+18
-0
src/main/java/com/geekq/miaosha/mybatis/controller/UbatisController.java
...om/geekq/miaosha/mybatis/controller/UbatisController.java
+23
-1
src/main/resources/mybatis/conf.xml
src/main/resources/mybatis/conf.xml
+5
-0
src/main/resources/mybatis/mapper/userMapper.xml
src/main/resources/mybatis/mapper/userMapper.xml
+19
-1
未找到文件。
README.md
浏览文件 @
d1a009ca
...
...
@@ -80,7 +80,7 @@
#### [分布式系统发展历程(已更新)](/docs/fenbushi.md)
#### [生产环境内存调优](/docs/jvm-goods.md)
#### [mybatis源码解析--未更新](/docs/mybatis-code.md)
#### [mybatis源码解析
与使用
--未更新](/docs/mybatis-code.md)
#### [redis 使用与进阶以及如何进行集群--已更新](/docs/redis-good.md)
#### [spring源码--未更新](/docs/redis-code.md)
#### [分布式治理框架-dubbo - zk - 解析--未更新](/docs/redis-code.md)
...
...
docs/mybatis-code.md
浏览文件 @
d1a009ca
###
秒杀常见问题
###
mybatis使用与总结
有问题或者宝贵意见联系我的QQ,非常希望你的加入!
> 秒杀注意事项以及整体简略设计
#### [1.如何解决卖超问题]()
--在sql加上判断防止数据边为负数
--数据库加唯一索引防止用户重复购买
--redis预减库存减少数据库访问 内存标记减少redis访问 请求先入队列缓冲,异步下单,增强用户体验
> mybatis 使用
#### resultType 和 resultMap
#### [注册功能 -- 如果有前端的牛人加入修改几个页面那是再好不过了哈哈哈]()
#### [全局异常处理拦截]()
1.
定义全局的异常拦截器
2.
定义了全局异常类型
3.
只返回和业务有关的
4.
详情请看GlobleException
#### [页面级缓存thymeleafViewResolver]()
1.
详细请看basecontroller 缓存渲染页面
#### [对象级缓存redis🙋🐓]()
redis永久缓存对象减少压力
redis预减库存减少数据库访
内存标记方法减少redis访问
#### [订单处理队列rabbitmq]()
请求先入队缓冲,异步下单,增强用户体验
请求出队,生成订单,减少库存
客户端定时轮询检查是否秒杀成功
#### [解决分布式session]()
--生成随机的uuid作为cookie返回并redis内存写入
--拦截器每次拦截方法,来重新获根据cookie获取对象
--下一个页面拿到key重新获取对象
--HandlerMethodArgumentResolver 方法 supportsParameter 如果为true 执行 resolveArgument 方法获取miaoshauser对象
--如果有缓存的话 这个功能实现起来就和简单,在一个用户访问接口的时候我们把访问次数写到缓存中,在加上一个有效期。
通过拦截器. 做一个注解 @AccessLimit 然后封装这个注解,可以有效的设置每次访问多少次,有效时间是否需要登录!
#### [秒杀安全 -- 安全性设计]()
秒杀接口隐藏
数字公式验证码
接口防刷限流(通用 注解,拦截器方式)
#### [通用缓存key的封装采用什么设计模式]()
模板模式的优点
-具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构
-代码复用的基本技术,在数据库设计中尤为重要
-存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”
-缺点: 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大
#### [redis的库存如何与数据库的库存保持一致]()
redis的数量不是库存,他的作用仅仅只是为了阻挡多余的请求透穿到DB,起到一个保护的作用
因为秒杀的商品有限,比如10个,让1万个请求区访问DB是没有意义的,因为最多也就只能10个
请求下单成功,所有这个是一个伪命题,我们是不需要保持一致的
#### [redis 预减成功,DB扣减库存失败怎么办]()
-其实我们可以不用太在意,对用户而言,秒杀不中是正常现象,秒杀中才是意外,单个用户秒杀中
-1.本来就是小概率事件,出现这种情况对于用户而言没有任何影响
-2.对于商户而言,本来就是为了活动拉流量人气的,卖不完还可以省一部分费用,但是活动还参与了,也就没有了任何影响
-3.对网站而言,最重要的是体验,只要网站不崩溃,对用户而言没有任何影响
#### [为什么redis数量会减少为负数]()
//预见库存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock,""+goodsId) ;
if(stock <0){
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
假如redis的数量为1,这个时候同时过来100个请求,大家一起执行decr数量就会减少成-99这个是正常的
进行优化后改变了sql写法和内存写法则不会出现上述问题
#### [为什么要单独维护一个秒杀结束标志]()
-1.前提所有的秒杀相关的接口都要加上活动是否结束的标志,如果结束就直接返回,包括轮寻的接口防止一直轮寻
-2.管理后台也可以手动的更改这个标志,防止出现活动开始以后就没办法结束这种意外的事件
#### [rabbitmq如何做到消息不重复不丢失即使服务器重启]()
-1.exchange持久化
-2.queue持久化
-3.发送消息设置MessageDeliveryMode.persisent这个也是默认的行为
-4.手动确认
#### [为什么threadlocal存储user对象,原理]()
1.
并发编程中重要的问题就是数据共享,当你在一个线程中改变任意属性时,所有的线程都会因此受到影响,同时会看到第一个线程修改后的值
<br>
有时我们希望如此,比如:多个线程增大或减小同一个计数器变量
<br>
但是,有时我们希望确保每个线程,只能工作在它自己的线程实例的拷贝上,同时不会影响其他线程的数据
<br>
举例: 举个例子,想象你在开发一个电子商务应用,你需要为每一个控制器处理的顾客请求,生成一个唯一的事务ID,同时将其传到管理器或DAO的业务方法中,
以便记录日志。一种方案是将事务ID作为一个参数,传到所有的业务方法中。但这并不是一个好的方案,它会使代码变得冗余。
你可以使用ThreadLocal类型的变量解决这个问题。首先在控制器或者任意一个预处理器拦截器中生成一个事务ID
然后在ThreadLocal中 设置事务ID,最后,不论这个控制器调用什么方法,都能从threadlocal中获取事务ID
而且这个应用的控制器可以同时处理多个请求,
同时在框架 层面,因为每一个请求都是在一个单独的线程中处理的,所以事务ID对于每一个线程都是唯一的,而且可以从所有线程的执行路径获取
运行结果可以看出每个线程都在维护自己的变量:
Starting Thread: 0 : Fri Sep 21 23:05:34 CST 2018
<br>
Starting Thread: 2 : Fri Sep 21 23:05:34 CST 2018
<br>
Starting Thread: 1 : Fri Jan 02 05:36:17 CST 1970
<br>
Thread Finished: 1 : Fri Jan 02 05:36:17 CST 1970
<br>
Thread Finished: 0 : Fri Sep 21 23:05:34 CST 2018
<br>
Thread Finished: 2 : Fri Sep 21 23:05:34 CST 2018
<br>
局部线程通常使用在这样的情况下,当你有一些对象并不满足线程安全,但是你想避免在使用synchronized关键字
<br>
块时产生的同步访问,那么,让每个线程拥有它自己的对象实例
<br>
注意:局部变量是同步或局部线程的一个好的替代,它总是能够保证线程安全。唯一可能限制你这样做的是你的应用设计约束
<br>
所以设计threadlocal存储user不会对对象产生影响,每次进来一个请求都会产生自身的线程变量来存储
#### [maven 隔离]()
maven隔离就是在开发中,把各个环境的隔离开来,一般分为
本地(local)
开发(dev)
测试(test)
线上(prod)
在环境部署中为了防止人工修改的弊端! spring.profiles.active=@activatedProperties@
#### [redis 分布式锁实现方法]()
我用了四种方法 , 分别指出了不同版本的缺陷以及演进的过程 orderclosetask
V1---->>版本没有操作,在分布式系统中会造成同一时间,资源浪费而且很容易出现并发问题
V2--->>版本加了分布式redis锁,在访问核心方法前,加入redis锁可以阻塞其他线程访问,可以
很好的处理并发问题,但是缺陷就是如果机器突然宕机,或者线路波动等,就会造成死锁,一直
不释放等问题
V3版本-->>很好的解决了这个问题v2的问题,就是加入时间对比如果当前时间已经大与释放锁的时间
说明已经可以释放这个锁重新在获取锁,setget方法可以把之前的锁去掉在重新获取,旧值在于之前的
值比较,如果无变化说明这个期间没有人获取或者操作这个redis锁,则可以重新获取
V4---->>采用成熟的框架redisson,封装好的方法则可以直接处理,但是waittime记住要这只为0
#### [服务降级--服务熔断(过载保护))]()
自动降级: 超时.失败次数,故障,限流
<br>
人工降级:秒杀,双11
<br>
9.
所有秒杀相关的接口比如:秒杀,获取秒杀地址,获取秒杀结果,获取秒杀验证码都需要加上
<br>
秒杀是否开始结束的判断
\ No newline at end of file
MyBatis的每一个查询映射的返回类型都是ResultMap,
只是当我们提供的返回类型属性是resultType的时候,MyBatis对自动的给我们把对应的值赋给resultType所指定对象的属性,
而当我们提供的返回类型是resultMap的时候,将数据库中列数据复制到对象的相应属性上,可以用于复制查询,两者不能同时用。
\ No newline at end of file
src/main/java/com/geekq/miaosha/mybatis/Mapper/UserMapper.java
浏览文件 @
d1a009ca
...
...
@@ -4,6 +4,7 @@ package com.geekq.miaosha.mybatis.Mapper;
import
com.geekq.miaosha.mybatis.entity.User
;
import
org.apache.ibatis.annotations.Mapper
;
import
org.apache.ibatis.annotations.Param
;
import
org.omg.CORBA.INTERNAL
;
import
java.util.List
;
...
...
@@ -14,5 +15,25 @@ public interface UserMapper {
public
int
insert
(
User
user
);
public
int
update
(
User
user
);
public
int
delete
(
Integer
id
);
/**
* 返回值时resulttype 与数据库字段一一对应 没有的话无法对应
* @param id
* @param name
* @return
*/
public
List
<
User
>
getUserList
(
@Param
(
"id"
)
Integer
id
,
@Param
(
"name"
)
String
name
);
/**
* 返回值是resultmap 可以对应实体类的字段 与 数据库 字段对应起来
*
* @param id
* @param name
* @return
*/
public
List
<
User
>
getUserListMap
(
@Param
(
"id"
)
Integer
id
,
@Param
(
"name"
)
String
name
);
}
src/main/java/com/geekq/miaosha/mybatis/TeacherVo.java
0 → 100644
浏览文件 @
d1a009ca
package
com.geekq.miaosha.mybatis
;
import
lombok.Getter
;
import
lombok.Setter
;
import
java.io.Serializable
;
@Setter
@Getter
public
class
TeacherVo
implements
Serializable
{
private
Integer
cid
;
private
String
cName
;
private
String
teacherId
;
private
String
tName
;
}
src/main/java/com/geekq/miaosha/mybatis/controller/UbatisController.java
浏览文件 @
d1a009ca
...
...
@@ -38,7 +38,7 @@ public class UbatisController {
@RequestMapping
(
value
=
"/testSelectLIst"
,
produces
=
"text/html"
)
@ResponseBody
public
void
testSelectUser
(){
List
<
User
>
result
=
userMapper
.
getUserList
(
1
,
"xiaoming
"
);
List
<
User
>
result
=
userMapper
.
getUserList
(
null
,
"
"
);
System
.
out
.
println
(
result
.
size
());
}
@RequestMapping
(
value
=
"/testInsert"
,
produces
=
"text/html"
)
...
...
@@ -51,4 +51,26 @@ public class UbatisController {
int
result
=
userMapper
.
insert
(
user
);
System
.
out
.
println
(
result
);
}
@RequestMapping
(
value
=
"/testUpdate"
,
produces
=
"text/html"
)
@ResponseBody
public
void
testUpdate
(){
User
user
=
new
User
();
user
.
setName
(
"xiaoming"
);
user
.
setAge
(
16
);
user
.
setId
(
1
);
user
.
setAddress
(
"xingong"
);
int
result
=
userMapper
.
update
(
user
);
System
.
out
.
println
(
result
);
}
@RequestMapping
(
value
=
"/testDelete"
,
produces
=
"text/html"
)
@ResponseBody
public
void
testDelete
(){
int
result
=
userMapper
.
delete
(
2
);
System
.
out
.
println
(
result
);
}
}
src/main/resources/mybatis/conf.xml
浏览文件 @
d1a009ca
...
...
@@ -2,6 +2,11 @@
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 为实体类me.gacl.domain.User配置一个别名_User -->
<!-- <typeAlias type="me.gacl.domain.User" alias="_User"/> -->
-->
<!--<package name="com.geekq.miaosha.mybatis.entity.User" alias = />-->
<mappers>
<mapper
resource=
"com.geekq.miaosha.mybatis.Mapper.UserMapper"
/>
</mappers>
...
...
src/main/resources/mybatis/mapper/userMapper.xml
浏览文件 @
d1a009ca
...
...
@@ -13,7 +13,6 @@ userMapper(userMapper.xml文件去除后缀)保证唯一性
<!--
根据id查询得到一个user对象
-->
<resultMap
type=
"com.geekq.miaosha.mybatis.entity.User"
id=
"userResultMap"
>
<!-- id表示查询结果集中唯一标识 column:查询出的列名
property:type所指定的POJO中的属性名
...
...
@@ -46,6 +45,16 @@ userMapper(userMapper.xml文件去除后缀)保证唯一性
</if>
</select>
<select
id=
"getUserListMap"
resultMap=
"userResultMap"
>
select
<include
refid=
"base_column"
/>
from users where 1=1
<if
test=
"id != null and id !=''"
>
and id=#{id}
</if>
<if
test=
"name != null and name !=''"
>
and name=#{name}
</if>
</select>
<!-- 插入自动递增-->
...
...
@@ -55,6 +64,15 @@ userMapper(userMapper.xml文件去除后缀)保证唯一性
</insert>
<!-- update-->
<update
id=
"update"
parameterType=
"com.geekq.miaosha.mybatis.entity.User"
keyProperty=
"id"
>
update users set name=#{name},age=#{age} ,_address=#{address} where id=#{id}
</update>
<!-- update-->
<update
id=
"delete"
parameterType=
"int"
>
delete from users where id=#{id}
</update>
</mapper>
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录