提交 40d1956b 编写于 作者: Y yingjun

项目架构

上级
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
/target/
/catalina.base_IS_UNDEFINED/
/.settings/
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>beauty_ssm</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yingjun.ssm</groupId>
<artifactId>beauty_ssm</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
<spring.version>4.2.0.RELEASE</spring.version>
</properties>
<dependencies>
<!--common start -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.8</version>
</dependency>
<!--common end -->
<!--spring start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring end -->
<!--springmvc 控制层 start -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.4</version>
</dependency>
<!--springmvc end -->
<!--mybatis 数据访问层 start -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.8</version>
</dependency>
<!--mybatis 数据访问层 end -->
</dependencies>
<!-- Build Settings -->
<!--http://maven.apache.org/pom.html#Build_Settings -->
<build>
<finalName>${project.artifactId}_${project.version}_${maven.build.timestamp}</finalName>
<resources>
<resource>
<!--directory: 资源所在的位置 -->
<directory>src/main/resources</directory>
<!--filtering: 是否替换资源中的属性placehold -->
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
</plugin>
</plugins>
</build>
</project>
package com.yingjun.ssm.cache;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.yingjun.ssm.util.ProtoStuffSerializerUtil;
/**
* redis缓存
*
* @author yingjun10627
*
*/
@Component
public class RedisCache {
public final static String CAHCENAME="cache";//缓存名
public final static int CAHCETIME=60;//默认缓存时间
@Autowired
private RedisTemplate<String, String> redisTemplate;
public <T> boolean putCache(String key, T obj) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(bkey, bvalue);
}
});
return result;
}
public <T> void putCacheWithExpireTime(String key, T obj, final long expireTime) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(bkey, expireTime, bvalue);
return true;
}
});
}
public <T> boolean putListCache(String key, List<T> objList) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(bkey, bvalue);
}
});
return result;
}
public <T> boolean putListCacheWithExpireTime(String key, List<T> objList, final long expireTime) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(bkey, expireTime, bvalue);
return true;
}
});
return result;
}
public <T> T getCache(final String key, Class<T> targetClass) {
byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
@Override
public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
return connection.get(key.getBytes());
}
});
if (result == null) {
return null;
}
return ProtoStuffSerializerUtil.deserialize(result, targetClass);
}
public <T> List<T> getListCache(final String key, Class<T> targetClass) {
byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
@Override
public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
return connection.get(key.getBytes());
}
});
if (result == null) {
return null;
}
return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
}
/**
* 精确删除key
*
* @param key
*/
public void deleteCache(String key) {
redisTemplate.delete(key);
}
/**
* 模糊删除key
*
* @param pattern
*/
public void deleteCacheWithPattern(String pattern) {
Set<String> keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
/**
* 清空所有缓存
*
* @param key
*/
public void clearCache() {
deleteCacheWithPattern(RedisCache.CAHCENAME+"|*");
}
}
package com.yingjun.ssm.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import org.springframework.cache.annotation.Cacheable;
import com.yingjun.ssm.entity.Goods;
public interface GoodsDao {
/**
* 根据偏移量查询可用商品列表
*
* @param offset
* @param limit
* @return
*/
List<Goods> queryAll(@Param("offset") int offset, @Param("limit") int limit);
/**
* 商品减库存
*
* @param goodsId
* @return 如果更新行数大于1,表示更新的行数
*/
int reduceNumber(long goodsId);
/**
* 使用存储过程执行抢购
*
* 能提升并发性的原因:
* 1、减少多个sql语句执行来回的网络延时。
* 2、通过mysql自身的事物提升效率。
*
* @param paramMap
*/
void bugWithProcedure(Map<String, Object> paramMap);
}
package com.yingjun.ssm.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.yingjun.ssm.entity.Order;
public interface OrderDao {
/**
* 插入订单明细
*
* @param orderId
* @param userId
* @return
*/
int insertOrder(@Param("userId") long userId,@Param("goodsId") long goodsId, @Param("title")String title);
/**
* 根据用户手机号查询订单
*
* @param userPhone
* @return
*/
List<Order> queryByUserPhone(@Param("userPhone") long userPhone);
/**
* 根据偏移量查询订单列表
* @param offset
* @param limit
* @return
*/
List<Order> queryAll(@Param("offset") int offset, @Param("limit") int limit);
}
package com.yingjun.ssm.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.yingjun.ssm.entity.User;
public interface UserDao {
/**
* 根据手机号查询用户对象
*
* @param userPhone
* @return
*/
User queryByPhone(long userPhone);
/**
* 根据偏移量查询用户列表
*
* @param offset
* @param limit
* @return
*/
List<User> queryAll(@Param("offset") int offset, @Param("limit") int limit);
/**
* 增加积分
*/
void addScore(@Param("add")int add);
}
package com.yingjun.ssm.dto;
import java.io.Serializable;
/**
*
* @author yingjun
*
* ajax 请求的返回类型封装JSON结果
*/
public class BaseResult<T> implements Serializable {
private static final long serialVersionUID = -4185151304730685014L;
private boolean success;
private T data;
private String error;
public BaseResult(boolean success, String error) {
this.success = success;
this.error = error;
}
public BaseResult(boolean success, T data) {
this.success = success;
this.data = data;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
@Override
public String toString() {
return "BaseResult [success=" + success + ", data=" + data + ", error=" + error + "]";
}
}
package com.yingjun.ssm.dto;
import java.io.Serializable;
public class OrderResultDto implements Serializable {
private static final long serialVersionUID = 1L;
private long orderId;
private long goodsId;
private String title;
public long getOrderId() {
return orderId;
}
public void setOrderId(long orderId) {
this.orderId = orderId;
}
public long getGoodsId() {
return goodsId;
}
public void setGoodsId(long goodsId) {
this.goodsId = goodsId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "OrderResultDto [orderId=" + orderId + ", goodsId=" + goodsId + ", title=" + title + "]";
}
}
package com.yingjun.ssm.entity;
import java.util.Date;
public class Goods {
private long goodsId;
private String title;
private float price;
private short state;//0表示下架 1表示正常
private int number;
private Date createTime;
private Date updateTime;
public long getGoodsId() {
return goodsId;
}
public void setGoodsId(long goodsId) {
this.goodsId = goodsId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public short getState() {
return state;
}
public void setState(short state) {
this.state = state;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public String toString() {
return "Goods [goodsId=" + goodsId + ", title=" + title + ", price=" + price + ", state=" + state + ", number=" + number + ", createTime="
+ createTime + ", updateTime=" + updateTime + "]";
}
}
package com.yingjun.ssm.entity;
import java.util.Date;
/**
* 订单
* @author yingjun
*
*/
public class Order {
private long orderId;
private User user;
private long goodsId;
private String title;
private Date createTime;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public long getOrderId() {
return orderId;
}
public void setOrderId(long orderId) {
this.orderId = orderId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public long getGoodsId() {
return goodsId;
}
public void setGoodsId(long goodsId) {
this.goodsId = goodsId;
}
@Override
public String toString() {
return "Order [user=" + user + ", orderId=" + orderId + ", goodsId=" + goodsId + ", title=" + title + ", createTime=" + createTime + "]";
}
}
package com.yingjun.ssm.entity;
import java.util.Date;
/**
* 用户
* @author yingjun
*
*/
public class User {
private long userId;
private String userName;
private long userPhone;
private Date createTime;
private int score;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public long getUserPhone() {
return userPhone;
}
public void setUserPhone(long userPhone) {
this.userPhone = userPhone;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", userPhone=" + userPhone + ", createTime=" + createTime + ", score=" + score
+ "]";
}
}
package com.yingjun.ssm.enums;
public enum ResultEnum {
SUCCESS(1, "成功"),
INVALID_USER(-1, "无效用户"),
PARAM_USER(-2, "参数错误"),
INNER_ERROR(-3, "系统异常");
private int state;
private String msg;
ResultEnum(int state, String msg) {
this.state = state;
this.msg = msg;
}
public int getState() {
return state;
}
public String getMsg() {
return msg;
}
public static ResultEnum stateOf(int index) {
for (ResultEnum state : values()) {
if (state.getState() == index) {
return state;
}
}
return null;
}
}
package com.yingjun.ssm.exception;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;
import com.alibaba.fastjson.JSON;
import com.yingjun.ssm.dto.BaseResult;
/**
* 错误信息统一处理
* 对未处理的错误信息做一个统一处理
* @author yingjun
*
*/
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@ResponseBody
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
LOG.error("用户 " + WebUtils.getCookie(request, "userPhone").getValue() + " 访问" + request.getRequestURI() + " 发生错误, 错误信息:" + ex.getMessage());
//这里有2种选择
//跳转到定制化的错误页面
/*ModelAndView error = new ModelAndView("error");
error.addObject("exMsg", ex.getMessage());
error.addObject("exType", ex.getClass().getSimpleName().replace("\"", "'"));*/
//返回json格式的错误信息
try {
PrintWriter writer = response.getWriter();
BaseResult<String> result=new BaseResult(false, ex.getMessage());
writer.write(JSON.toJSONString(result));
writer.flush();
} catch (Exception e) {
}
return null;
}
}
package com.yingjun.ssm.exception;
/**
*
* @author yingjun
*
*/
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyException(String message) {
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
}
package com.yingjun.ssm.quartz;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.yingjun.ssm.cache.RedisCache;
import com.yingjun.ssm.dao.UserDao;
/**
* 业务相关的作业调度
*
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
* 字符代表所有可能的值
/ 字符用来指定数值的增量
L 字符仅被用于天(月)和天(星期)两个子表达式,表示一个月的最后一天或者一个星期的最后一天
6L 可以表示倒数第6天
* @author yingjun
*
*/
@Component
public class BizQuartz {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserDao userDao;
@Autowired
private RedisCache cache;
/**
* 用户自动加积分
* 每天9点到17点每过1分钟所有用户加一次积分
*/
@Scheduled(cron = "0 0/1 9-17 * * ? ")
public void addUserScore() {
LOG.info("@Scheduled--------addUserScore()");
userDao.addScore(10);
}
/**
* 每隔5分钟定时清理缓存
*/
@Scheduled(cron = "0 0/5 * * * ? ")
public void cacheClear() {
LOG.info("@Scheduled-------cacheClear()");
cache.clearCache();
}
}
package com.yingjun.ssm.service;
import java.util.List;
import org.springframework.cache.annotation.Cacheable;
import com.yingjun.ssm.entity.Goods;
public interface GoodsService {
/**
* 根据偏移量查询可用商品列表
*
* @param offset
* @param limit
* @return
*/
List<Goods> getGoodsList(int offset, int limit);
/**
* 商品购买
*
* @param userId
* @param goodsId
* @param useProcedure
* 是否用存储过程提高并发能力
*/
void buyGoods(long userPhone, long goodsId, boolean useProcedure);
}
package com.yingjun.ssm.service;
import java.util.List;
import com.yingjun.ssm.entity.User;
public interface UserService {
List<User> getUserList(int offset, int limit);
}
package com.yingjun.ssm.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
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.yingjun.ssm.cache.RedisCache;
import com.yingjun.ssm.dao.GoodsDao;
import com.yingjun.ssm.dao.OrderDao;
import com.yingjun.ssm.dao.UserDao;
import com.yingjun.ssm.entity.Goods;
import com.yingjun.ssm.entity.User;
import com.yingjun.ssm.enums.ResultEnum;
import com.yingjun.ssm.exception.MyException;
import com.yingjun.ssm.service.GoodsService;
@Service
public class GoodsServiceImpl implements GoodsService {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private GoodsDao goodsDao;
@Autowired
private OrderDao orderDao;
@Autowired
private UserDao userDao;
@Autowired
private RedisCache cache;
@Override
public List<Goods> getGoodsList(int offset, int limit) {
String cache_key=RedisCache.CAHCENAME+"|getGoodsList|"+offset+"|"+limit;
List<Goods> result_cache=cache.getListCache(cache_key, Goods.class);
if(result_cache==null){
//缓存中没有再去数据库取,并插入缓存(缓存时间为60秒)
result_cache=goodsDao.queryAll(offset, limit);
cache.putListCacheWithExpireTime(cache_key, result_cache, RedisCache.CAHCETIME);
LOG.info("put cache with key:"+cache_key);
return result_cache;
}else{
LOG.info("get cache with key:"+cache_key);
}
return result_cache;
}
@Transactional
@Override
public void buyGoods(long userPhone, long goodsId, boolean useProcedure){
// 用户校验
User user = userDao.queryByPhone(userPhone);
if (user == null) {
throw new MyException(ResultEnum.INVALID_USER.getMsg());
}
if (useProcedure) {
//通过存储方式的方法进行操作
Map<String, Object> map = new HashMap<String, Object>();
map.put("userId", user.getUserId());
map.put("goodsId", goodsId);
map.put("title", "抢购");
map.put("result", null);
goodsDao.bugWithProcedure(map);
int result = MapUtils.getInteger(map, "result", ResultEnum.INNER_ERROR.getState());
if (result <= 0) {
// 买卖失败
throw new MyException(ResultEnum.INNER_ERROR.getMsg());
} else {
// 买卖成功
// 此时缓存中的数据不是最新的,需要对缓存进行清理(具体的缓存策略还是要根据具体需求制定)
cache.deleteCacheWithPattern("getGoodsList*");
LOG.info("delete cache with key: getGoodsList*");
return;
}
} else {
int inserCount = orderDao.insertOrder(user.getUserId(), goodsId, "普通买卖");
if (inserCount <= 0) {
// 买卖失败
throw new MyException(ResultEnum.INNER_ERROR.getMsg());
} else {
// 减库存
int updateCount = goodsDao.reduceNumber(goodsId);
if (updateCount <= 0) {
// 减库存失败
throw new MyException(ResultEnum.INNER_ERROR.getMsg());
} else {
// 买卖成功
// 此时缓存中的数据不再是最新的,需要对缓存进行清理(具体的缓存策略还是要根据具体需求制定)
cache.deleteCacheWithPattern("getGoodsList*");
LOG.info("delete cache with key: getGoodsList*");
return;
}
}
}
}
}
package com.yingjun.ssm.service.impl;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.yingjun.ssm.cache.RedisCache;
import com.yingjun.ssm.dao.UserDao;
import com.yingjun.ssm.entity.Goods;
import com.yingjun.ssm.entity.User;
import com.yingjun.ssm.service.UserService;
@Service
public class UserServiceImpl implements UserService {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserDao userDao;
@Autowired
private RedisCache cache;
@Override
public List<User> getUserList(int offset, int limit) {
String cache_key=RedisCache.CAHCENAME+"|getUserList|"+offset+"|"+limit;
//先去缓存中取
List<User> result_cache=cache.getListCache(cache_key, User.class);
if(result_cache==null){
//缓存中没有再去数据库取,并插入缓存(缓存时间为60秒)
result_cache=userDao.queryAll(offset, limit);
cache.putListCacheWithExpireTime(cache_key, result_cache, RedisCache.CAHCETIME);
LOG.info("put cache with key:"+cache_key);
}else{
LOG.info("get cache with key:"+cache_key);
}
return result_cache;
}
}
package com.yingjun.ssm.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.yingjun.ssm.entity.User;
/**
* 序列话工具
*/
public class ProtoStuffSerializerUtil {
/**
* 序列化对象
* @param obj
* @return
*/
public static <T> byte[] serialize(T obj) {
if (obj == null) {
throw new RuntimeException("序列化对象(" + obj + ")!");
}
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(obj.getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
try {
protostuff = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new RuntimeException("序列化(" + obj.getClass() + ")对象(" + obj + ")发生异常!", e);
} finally {
buffer.clear();
}
return protostuff;
}
/**
* 反序列化对象
* @param paramArrayOfByte
* @param targetClass
* @return
*/
public static <T> T deserialize(byte[] paramArrayOfByte, Class<T> targetClass) {
if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
}
T instance = null;
try {
instance = targetClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("反序列化过程中依据类型创建对象失败!", e);
}
Schema<T> schema = RuntimeSchema.getSchema(targetClass);
ProtostuffIOUtil.mergeFrom(paramArrayOfByte, instance, schema);
return instance;
}
/**
* 序列化列表
* @param objList
* @return
*/
public static <T> byte[] serializeList(List<T> objList) {
if (objList == null || objList.isEmpty()) {
throw new RuntimeException("序列化对象列表(" + objList + ")参数异常!");
}
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
protostuff = bos.toByteArray();
} catch (Exception e) {
throw new RuntimeException("序列化对象列表(" + objList + ")发生异常!", e);
} finally {
buffer.clear();
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return protostuff;
}
/**
* 反序列化列表
* @param paramArrayOfByte
* @param targetClass
* @return
*/
public static <T> List<T> deserializeList(byte[] paramArrayOfByte, Class<T> targetClass) {
if (paramArrayOfByte == null || paramArrayOfByte.length == 0) {
throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
}
Schema<T> schema = RuntimeSchema.getSchema(targetClass);
List<T> result = null;
try {
result = ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(paramArrayOfByte), schema);
} catch (IOException e) {
throw new RuntimeException("反序列化对象列表发生异常!", e);
}
return result;
}
}
\ No newline at end of file
package com.yingjun.ssm.util;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* TimeUtils
*
*/
public class TimeUtils {
private final static long minute = 60 * 1000;// 1分钟
private final static long hour = 60 * minute;// 1小时
private final static long day = 24 * hour;// 1天
private final static long month = 31 * day;// 月
private final static long year = 12 * month;// 年
public static final SimpleDateFormat DATE_FORMAT_DATE_D = new SimpleDateFormat("yyyy-MM-dd");
public static final SimpleDateFormat DATE_FORMAT_DATE_M = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public static final SimpleDateFormat DATE_FORMAT_DATE_S = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private TimeUtils() {
throw new AssertionError();
}
/**
* long time to string
*
* @param timeInMillis
* @param dateFormat
* @return
*/
public static String getTime(long timeInMillis, SimpleDateFormat dateFormat) {
return dateFormat.format(new Date(timeInMillis));
}
/**
* long time to string, format is {@link #DEFAULT_DATE_FORMAT}
*
* @param timeInMillis
* @return
*/
public static String getTime(long timeInMillis) {
return getTime(timeInMillis, DATE_FORMAT_DATE_S);
}
/**
* get current time in milliseconds
*
* @return
*/
public static long getCurrentTimeInLong() {
return System.currentTimeMillis();
}
/**
* get current time in milliseconds, format is {@link #DEFAULT_DATE_FORMAT}
*
* @return
*/
public static String getCurrentTimeInString() {
return getTime(getCurrentTimeInLong());
}
/**
* get current time in milliseconds
*
* @return
*/
public static String getCurrentTimeInString(SimpleDateFormat dateFormat) {
return getTime(getCurrentTimeInLong(), dateFormat);
}
public static String getTimeFormatText(Date date) {
if (date == null) {
return null;
}
long diff = new Date().getTime() - date.getTime();
long r = 0;
if (diff > year) {
r = (diff / year);
return r + "年前";
}
if (diff > month) {
r = (diff / month);
return r + "个月前";
}
if (diff > day) {
r = (diff / day);
return r + "天前";
}
if (diff > hour) {
r = (diff / hour);
return r + "小时前";
}
if (diff > minute) {
r = (diff / minute);
return r + "分钟前";
}
return "刚刚";
}
}
package com.yingjun.ssm.web;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.yingjun.ssm.dto.BaseResult;
import com.yingjun.ssm.dto.OrderResultDto;
import com.yingjun.ssm.entity.Goods;
import com.yingjun.ssm.entity.Order;
import com.yingjun.ssm.enums.ResultEnum;
import com.yingjun.ssm.exception.MyException;
import com.yingjun.ssm.service.GoodsService;
@Controller
@RequestMapping("/goods")
public class GoodsController {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private GoodsService goodsService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model, Integer offset, Integer limit) {
LOG.info("invoke----------/goods/list");
offset = offset == null ? 0 : offset;//默认便宜0
limit = limit == null ? 50 : limit;//默认展示50条
List<Goods> list = goodsService.getGoodsList(offset, limit);
model.addAttribute("goodslist", list);
return "goodslist";
}
@RequestMapping(value = "/{goodsId}/buy", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8" })
@ResponseBody
public BaseResult<Object> buy(@CookieValue(value = "userPhone", required = false) Long userPhone,
@PathVariable("goodsId") Long goodsId){
LOG.info("invoke----------/"+goodsId+"/buy userPhone:"+userPhone);
if (userPhone == null||goodsId==null) {
return new BaseResult<Object>(false, ResultEnum.PARAM_USER.getMsg());
}
try {
goodsService.buyGoods(userPhone, goodsId, false);
}catch (MyException e1) {
return new BaseResult<Object>(false, e1.getMessage());
}catch (Exception e) {
return new BaseResult<Object>(false, ResultEnum.INNER_ERROR.getMsg());
}
return new BaseResult<Object>(true, "success");
}
}
package com.yingjun.ssm.web;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.yingjun.ssm.entity.User;
import com.yingjun.ssm.service.UserService;
@Controller
@RequestMapping("/user")
public class UserController {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserService userService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model, Integer offset, Integer limit) {
LOG.info("invoke----------/user/list");
offset = offset == null ? 0 : offset;//默认便宜0
limit = limit == null ? 50 : limit;//默认展示50条
List<User> list = userService.getUserList(offset, limit);
model.addAttribute("userlist", list);
return "userlist";
}
}
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/beauty_ssm?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=yingjun
druid.pool.size.max=20
druid.pool.size.min=3
druid.pool.size.init=3
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<Encoding>UTF-8</Encoding>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} %-5level {%thread} [%logger{20}] : %msg%n</pattern>
</layout>
</appender>
<appender name="LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Encoding>UTF-8</Encoding>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${catalina.base}/logs/app.log.%d{yyyy-MM-dd}</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Encoding>UTF-8</Encoding>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${catalina.base}/logs/error.log.%d{yyyy-MM-dd}</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="org.apache.ibatis" level="info" />
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="LOG" />
<appender-ref ref="ERROR" />
</root>
</configuration>
\ No newline at end of file
<?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="com.yingjun.ssm.dao.GoodsDao">
<select id="queryAll" resultType="Goods">
SELECT *
FROM _goods
ORDER BY create_time DESC
limit #{offset},#{limit}
</select>
<update id="reduceNumber">
UPDATE _goods
SET
number = number -1
WHERE
goods_id = #{goodsId}
AND state = 1
AND number >0;
</update>
<!--mybatis调用存储过程-->
<select id="bugWithProcedure" statementType="CALLABLE">
call execute_buy(
#{userId,jdbcType=BIGINT,mode=IN},
#{goodsId,jdbcType=BIGINT,mode=IN},
#{title,jdbcType=VARCHAR,mode=IN},
#{result,jdbcType=INTEGER,mode=OUT}
)
</select>
</mapper>
\ No newline at end of file
<?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="com.yingjun.ssm.dao.OrderDao">
<insert id="insertOrder">
INSERT INTO
_order(user_id,goods_id,title)
VALUES
(#{userId},#{goodsId},#{title})
</insert>
<select id="queryByUserPhone" resultType="Order">
SELECT *
FROM
_order, _user
WHERE
_order.user_id=_user.user_id
AND
_user.user_phone=#{userPhone}
</select>
<select id="queryAll" resultType="Order">
SELECT *
FROM _order
ORDER BY
create_time DESC
limit #{offset},#{limit}
</select>
</mapper>
\ No newline at end of file
<?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="com.yingjun.ssm.dao.UserDao">
<select id="queryByPhone" resultType="User">
SELECT *
FROM _user
WHERE
user_phone= #{userPhone} limit 1
</select>
<select id="queryAll" resultType="User">
SELECT *
FROM _user
ORDER BY create_time DESC
limit #{offset},#{limit}
</select>
<!--mybatis调用存储过程-->
<select id="bugWithProcedure" statementType="CALLABLE">
call execute_buy(
#{userId,jdbcType=BIGINT,mode=IN},
#{goodsId,jdbcType=BIGINT,mode=IN},
#{title,jdbcType=TIMESTAMP,mode=IN},
#{result,jdbcType=INTEGER,mode=OUT}
)
</select>
<update id="addScore">
UPDATE _user
SET
score = score + #{add}
</update>
</mapper>
\ No newline at end of file
<?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-->
<setting name="useColumnLabel" value="true"/>
<!--开启驼峰命名转换Table:create_time到 Entity(createTime)-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
#redis config
redis.pass=hundsun@1
redis.pool.maxTotal=105
redis.pool.maxIdle=10
redis.pool.maxWaitMillis=5000
redis.pool.testOnBorrow=true
#redis\u5355\u8282\u70b9\u914d\u7f6e
redis.ip=120.27.141.45
redis.port=6379
#redis\u4e3b\u4ece\u9ad8\u53ef\u7528\u914d\u7f6e
#sentinel1.ip=192.168.43.225
#sentinel1.port=63791
#sentinel2.ip=192.168.43.225
#sentinel2.port=63792
#sentinel3.ip=192.168.43.225
#sentinel3.port=63792
\ No newline at end of file
<?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"
>
<!--配置整合mybatis过程-->
<!--1、配置数据库相关参数-->
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
<!--2.数据源druid -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.pool.size.init}" />
<property name="minIdle" value="${druid.pool.size.min}" />
<property name="maxActive" value="${druid.pool.size.max}" />
<!-- 配置监控统计拦截的filters,wall用于防止sql注入,stat用于统计分析 -->
<property name="filters" value="wall,stat" />
</bean>
<!--3、配置SqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"/>
<!--配置mybatis全局配置文件:mybatis-config.xml-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--扫描entity包,使用别名,多个用;隔开-->
<property name="typeAliasesPackage" value="com.yingjun.ssm.entity"/>
<!--扫描sql配置文件:mapper需要的xml文件-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!--4、配置扫描Dao接口包,动态实现DAO接口,注入到spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注入SqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 给出需要扫描的Dao接口-->
<property name="basePackage" value="com.yingjun.ssm.dao"/>
</bean>
</beans>
<?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"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd ">
<task:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.yingjun.ssm.quartz"/>
</beans>
<?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.yingjun.ssm.cache" />
<!-- 引入redis配置 -->
<context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>
<!-- Redis 配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.pool.maxTotal}" />
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
<!--redis Sentinel主从高可用方案配置 -->
<!-- <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="master-1"></property>
</bean>
</property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${sentinel1.ip}"></constructor-arg>
<constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${sentinel2.ip}"></constructor-arg>
<constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${sentinel3.ip}"></constructor-arg>
<constructor-arg name="port" value="${sentinel3.port}"></constructor-arg>
</bean>
</set>
</property>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true">
<property name="password" value="${redis.pass}" />
<property name="poolConfig">
<ref bean="jedisPoolConfig" />
</property>
<constructor-arg name="sentinelConfig" ref="sentinelConfiguration" />
</bean> -->
<!-- redis单节点数据库连接配置 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.ip}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.pass}" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
<!-- redisTemplate配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,封装使操作更便捷 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
</beans>
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!--扫描service包(包含子包)下所有使用注解的类型-->
<context:component-scan base-package="com.yingjun.ssm.service"/>
<!--配置事务管理器(mybatis采用的是JDBC的事务管理器)-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置基于注解的声明式事务,默认使用注解来管理事务行为-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置springMVC-->
<!--1:开始springMVC注解模式-->
<!--简化配置:
1、自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
2、提供一系列:数据绑定,数字和日期的format,@NumberFormat,@DataTimeFormat,xml,json默认读写支持
-->
<mvc:annotation-driven/>
<!--2:静态资源默认servlet配置
1、加入对静态资源的处理:js,css,gif,png
2、允许使用"/"做整体映射
-->
<mvc:default-servlet-handler/>
<!--3:配置JSP 显示ViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--4:扫描web相关的bean-->
<context:component-scan base-package="com.yingjun.ssm.web"/>
<!-- 全局异常捕捉 -->
<bean class="com.yingjun.ssm.exception.GlobalExceptionResolver" />
</beans>
\ No newline at end of file
-- 买逻辑的存储过程
DELIMITER $$ -- 结束标志;转换为 $$
-- 定义存储过程
-- 参数:in 输入参数; out 输出参数
-- row_count():返回上一条修改类型sql(delete,insert,upodate)的影响行数
-- row_count: 0:未修改数据; >0:表示修改的行数; <0:sql错误/未执行修改sql
CREATE PROCEDURE `beauty_ssm`.`execute_buy`
(IN v_user_id BIGINT,
IN v_goods_id BIGINT,
IN v_title VARCHAR(120),
OUT r_result INT)
BEGIN
DECLARE insert_count INT DEFAULT 0;
START TRANSACTION;
INSERT INTO _order (user_id, goods_id, title)
VALUES(v_user_id, v_goods_id, v_title);
SELECT ROW_COUNT() INTO insert_count;
IF (insert_count = 0) THEN
ROLLBACK;
SET r_result = -1;
ELSEIF (insert_count < 0) THEN
ROLLBACK ;
SET r_result = -2;
ELSE
UPDATE _goods SET number = number - 1
WHERE goods_id = v_goods_id
AND number > 0;
SELECT ROW_COUNT() INTO insert_count;
IF (insert_count = 0) THEN
ROLLBACK;
SET r_result = 0;
ELSEIF (insert_count < 0) THEN
ROLLBACK;
SET r_result = -2;
ELSE
COMMIT;
SET r_result = 1;
END IF;
END IF;
END;
$$
-- 代表存储过程定义结束
DELIMITER ; -- 结束标志换回默认的;
SET @r_result = -3;
-- 执行存储过程
call execute_buy(1000,1000,'抢购iphone', @r_result);
-- 获取结果
SELECT @r_result;
-- 存储过程
-- 1.存储过程优化:事务行级锁持有的时间
-- 2.不要过度依赖存储过程
-- 3.简单的逻辑可以应用存储过程
-- 4.QPS:一个秒杀单6000/qps
--需要 MySQL 5.6.5以上的版本
CREATE DATABASE beauty_ssm;
USE beauty_ssm;
-- 用户表
CREATE TABLE _user(
`user_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` VARCHAR(50) NOT NULL COMMENT '用户名',
`user_phone` BIGINT NOT NULL COMMENT '手机号',
`score` INT NOT NULL COMMENT '积分',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`user_id`),
KEY `idx_user_phone`(`user_phone`)
)ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='用户表';
-- 商品表
CREATE TABLE _goods(
`goods_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品ID',
`title` VARCHAR(120) NOT NULL COMMENT '商品名称',
`state` INT NOT NULL COMMENT '商品状态',
`price` FLOAT NOT NULL COMMENT '商品价格',
`number` INT NOT NULL COMMENT '商品数量',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`goods_id`)
)ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='商品表';
-- 订单表
CREATE TABLE _order(
`order_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`goods_id` BIGINT NOT NULL COMMENT '商品ID',
`title` VARCHAR(120) NOT NULL COMMENT '订单名称',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`order_id`),
KEY `idx_user_id`(`user_id`),
KEY `idx_goods_id`(`goods_id`)
)ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='订单表';
--插入初始数据
INSERT INTO
_user(user_name, user_phone, score)
VALUES
('阿坚', 18768128888, 0),
('小明', 18968129999, 0);
INSERT INTO
_goods(title, state, price,number)
VALUES
('iphone7', 1, 3999, 100),
('ipad3', 1, 1999, 2000);
\ No newline at end of file
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.css" rel="stylesheet">
<script src="http://apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
<script src="http://apps.bdimg.com/libs/respond.js/1.4.2/respond.js"></script>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
\ No newline at end of file
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
request.setCharacterEncoding("utf-8");
String path = request.getContextPath();
%>
\ No newline at end of file
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<%@include file="common/tag.jsp"%>
<!DOCTYPE html>
<html>
<head>
<title>商品列表</title>
<%@include file="common/head.jsp"%>
</head>
<body>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h2>商品列表</h2>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<td>商品ID</td>
<td>商品名称</td>
<td>商品价格</td>
<td>商品状态</td>
<td>商品数量</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<c:forEach var="goods" items="${goodslist}">
<tr>
<td>${goods.goodsId}</td>
<td>${goods.title}</td>
<td>${goods.price}</td>
<td><c:if test="${goods.state==0}">已下架</c:if> <c:if test="${goods.state==1}">销售中</c:if></td>
<td>${goods.number}</td>
<td><button class="btn btn-info" id="goodsBuy" onclick="handler.goodsBuy(${goods.goodsId});">购买</button></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
<%--登录弹出层 输入电话--%>
<div id="loginModal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title text-center">
<span class="glyphicon glyphicon-phone"> </span>用户电话
</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<input type="text" name="userPhone" id="userPhone" placeholder="填写手机号" class="form-control">
</div>
</div>
</div>
<div class="modal-footer">
<%--验证信息--%>
<span id="userPhoneMessage" class="glyphicon"> </span>
<button type="button" id="loginBtn" class="btn btn-success">
<span class="glyphicon glyphicon-phone"></span>提交
</button>
</div>
</div>
</div>
</div>
</body>
<script src="http://apps.bdimg.com/libs/jquery.cookie/1.4.1/jquery.cookie.js"></script>
<script src="<%=path%>/resource/script/handler.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
//初始化业务逻辑script
handler.goods.init({});
})
</script>
</html>
\ No newline at end of file
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<%@include file="common/tag.jsp"%>
<!DOCTYPE html>
<html>
<head>
<title>用户列表</title>
<%@include file="common/head.jsp"%>
</head>
<body>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading text-center">
<h2>用户列表</h2>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<td>用户ID</td>
<td>用户名</td>
<td>手机号</td>
<td>积分</td>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${userlist}">
<tr>
<td>${user.userId}</td>
<td>${user.userName}</td>
<td>${user.userPhone}</td>
<td>${user.score}</td>
<td><fmt:formatDate value="${user.createTime}" pattern="yyyy-MM-dd HH:mm:ss" /></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--配置DispatcherServlet -->
<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置SpringMVC需要加载的配置文件 spring-xxx.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<!--默认匹配所有的请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- druid -->
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
\ No newline at end of file
//存放主要交互逻辑的js代码
var handler = {
//封装相关ajax的url
URL: {
goodsBuy: function (goodsId) {
return '/beauty_ssm/goods/' + goodsId + '/buy';
}
},
//验证手机号
validatePhone: function (phone) {
if (phone && phone.length == 11 && !isNaN(phone)) {
return true;//直接判断对象会看对象是否为空,空就是undefine就是false; isNaN 非数字返回true
} else {
return false;
}
},
//详情页秒杀逻辑
goods: {
//详情页初始化
init: function (params) {
//在cookie中查找手机号
var userPhone = $.cookie('userPhone');
//验证手机号
if (!handler.validatePhone(userPhone)) {
//绑定手机 控制输出
var loginModal = $('#loginModal');
loginModal.modal({
show: true,//显示弹出层
backdrop: 'static',//禁止位置关闭
keyboard: false//关闭键盘事件
});
$('#loginBtn').click(function () {
var inputPhone = $('#userPhone').val();
if (handler.validatePhone(inputPhone)) {
//电话写入cookie(7天过期)
$.cookie('userPhone', inputPhone, {expires: 7});
//验证通过  刷新页面
window.location.reload();
} else {
$('#userPhoneMessage').hide().html('<label class="label label-danger">手机号错误!</label>').show(300);
}
});
}
}
},
goodsBuy: function (goodsId) {
//执行购买请求
$.post(handler.URL.goodsBuy(goodsId), {}, function (result) {
if (result && result['success']) {
alert("购买成功!");
window.location.reload();
}else{
alert(result['error']);
}
});
},
}
\ No newline at end of file
package com.yingjun.ssm.dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.yingjun.ssm.entity.Goods;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class GoodsDaoTest {
@Autowired
private GoodsDao goodsDao;
@Test
public void testQueryAll() {
List<Goods> list=goodsDao.queryAll(0, 100);
for (Goods goods : list) {
System.out.println(goods);
}
System.out.println("--------------------------");
}
@Test
public void testReduceNumber() {
int result=goodsDao.reduceNumber(1000);
System.out.println("testReduceNumber result:"+result);
System.out.println("--------------------------");
}
@Test
public void testBugWithProcedure() {
Map<String,Object> map=new HashMap<String,Object>();
map.put("userId", 1000L);
map.put("goodsId", 1000L);
map.put("title", "抢购iPhone7");
map.put("result", null);
goodsDao.bugWithProcedure(map);
//获取result
System.out.println("testBugWithProcedure result:"+map.get("result"));
}
}
package com.yingjun.ssm.dao;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.yingjun.ssm.entity.Goods;
import com.yingjun.ssm.entity.Order;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class OrderDaoTest {
@Autowired
private OrderDao orderDao;
@Autowired
private GoodsDao goodsDao;
@Test
public void testInsertOrder() {
Goods goods=goodsDao.queryAll(0, 1).get(0);
System.out.println(goods);
int result=orderDao.insertOrder(1000,goods.getGoodsId(),goods.getTitle());
System.out.println("testInsertOrder result:"+result);
System.out.println("--------------------------");
}
@Test
public void testQueryByUserPhone() {
List<Order> list=orderDao.queryByUserPhone(18768128888L);
for (Order order : list) {
System.out.println(order);
}
System.out.println("--------------------------");
}
@Test
public void testQueryAll() {
List<Order> list=new ArrayList<Order>();
for (Order order : list) {
System.out.println(order);
}
System.out.println("--------------------------");
}
}
package com.yingjun.ssm.dao;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.yingjun.ssm.entity.User;
/**
*
* @author yingjun
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-dao.xml")
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
public void testQueryById() {
User user=userDao.queryByPhone(18768128888L);
System.out.println(user);
System.out.println("--------------------------");
}
@Test
public void testQueryAll() {
List<User> list=userDao.queryAll(0, 100);
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testAddScore() {
userDao.addScore(10);
List<User> list=userDao.queryAll(0, 100);
for (User user : list) {
System.out.println(user);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册