Skip to content

Commit

Permalink
SpringBoot 单元测试优化
Browse files Browse the repository at this point in the history
  • Loading branch information
ITDragonBlog committed Dec 15, 2017
1 parent 627f4d2 commit c5facba
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
package com.itdragon.common;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.UUID;

import org.springframework.util.DigestUtils;

import com.itdragon.pojo.User;

/**
* 工具类
* @author itdragon
*
*/
public class ITDragonUtils {

private static final String HASH_ALGORITHM = "SHA-1";
private static final int HASH_INTERATIONS = 1024;
private static final int SALT_SIZE = 8;
public class ItdragonUtils {

/**
* 设定安全的密码,生成随机的salt并经过1024次 sha-1 hash
* 加盐加密的策略非常多,根据实际业务来
*/
private void entryptPassword(User user) {
// byte[] salt = Digests.generateSalt(SALT_SIZE);
// user.setSalt(Encodes.encodeHex(salt));
//
// byte[] hashPassword = Digests.sha1(user.getPlainPassword().getBytes(), salt, HASH_INTERATIONS);
// user.setPassword(Encodes.encodeHex(hashPassword));
public static void entryptPassword(User user) {
String salt = UUID.randomUUID().toString();
String temPassword = salt + user.getPlainPassword();
String md5Password = DigestUtils.md5DigestAsHex(temPassword.getBytes());
user.setSalt(salt);
user.setPassword(md5Password);
}

public static String getCurrentDateTime() {
TimeZone zone = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone.setDefault(zone);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(new Date());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package com.itdragon.common;

import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ItdragonResult {

private static final ObjectMapper MAPPER = new ObjectMapper(); // 定义jackson对象

private Integer status; // 响应业务状态

private String msg; // 响应消息

private Object data; // 响应中的数据

public ItdragonResult() {
}

public ItdragonResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}

public ItdragonResult(Object data) {
this.status = 200;
this.msg = "OK";
this.data = data;
}

public static ItdragonResult ok(Object data) {
return new ItdragonResult(data);
}

public static ItdragonResult ok() {
return new ItdragonResult(null);
}

public static ItdragonResult build(Integer status, String msg, Object data) {
return new ItdragonResult(status, msg, data);
}

public static ItdragonResult build(Integer status, String msg) {
return new ItdragonResult(status, msg, null);
}

public Integer getStatus() {
return status;
}

public void setStatus(Integer status) {
this.status = status;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}

/**
* 将json结果集转化为ITDragonResult对象
*
* @param jsonData json数据
* @param clazz ITDragonResult中的object类型
* @return
*/
public static ItdragonResult formatToPojo(String jsonData, Class<?> clazz) {
try {
if (clazz == null) {
return MAPPER.readValue(jsonData, ItdragonResult.class);
}
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (clazz != null) {
if (data.isObject()) {
obj = MAPPER.readValue(data.traverse(), clazz);
} else if (data.isTextual()) {
obj = MAPPER.readValue(data.asText(), clazz);
}
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}

/**
* 没有object对象的转化
*
* @param json
* @return
*/
public static ItdragonResult format(String json) {
try {
return MAPPER.readValue(json, ItdragonResult.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* Object是集合转化
*
* @param jsonData json数据
* @param clazz 集合中的类型
* @return
*/
public static ItdragonResult formatToList(String jsonData, Class<?> clazz) {
try {
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (data.isArray() && data.size() > 0) {
obj = MAPPER.readValue(data.traverse(),
MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
}
return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.itdragon.controller;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.itdragon.repository.UserRepository;
import com.itdragon.common.ItdragonUtils;
import com.itdragon.pojo.User;
import com.itdragon.service.UserService;

@Controller
Expand All @@ -15,9 +20,32 @@ public class UserController {
@Autowired
private UserService userService;

@RequestMapping(method = RequestMethod.POST)
public String register(@Valid User user, HttpServletRequest request) {
// userService.registerUser(user);
try {
// 注册成功直接将当前用户保存到session中
request.getSession().setAttribute("user", user);
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:/login";
}

@RequestMapping("findAll")
@ResponseBody
public String findAll() {
User user = new User();
user.setAccount("itdragon");
user.setUserName("ITDragonBlog");
user.setEmail("[email protected]");
user.setIphone("12345677890");
user.setPlainPassword("987654321");
user.setPlatform("weixin");
user.setCreatedDate(ItdragonUtils.getCurrentDateTime());
user.setUpdatedDate(ItdragonUtils.getCurrentDateTime());
ItdragonUtils.entryptPassword(user);
userService.registerUser(user);
return userService.findAll().toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class User {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private Long id; // 自增长主键
private String account; // 登录的账号
private String userName; // 注册的昵称
@Transient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,37 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.itdragon.pojo.User;

/**
* 核心知识:SpringData Repository 接口
*
* CrudRepository 接口提供了最基本的对实体类的添删改查操作 
* - T save(T entity); //保存单个实体 
* - T findOne(ID id); // 根据id查找实体        
* - void delete(ID/T/Iterable); // 根据Id删除实体,删除实体,批量删除
* PagingAndSortingRepository 提供了分页与排序功能
* - <T, ID extends Serializable> // 第一个参数传实体类,第二个参数传注解数据类型
* - Iterable<T> findAll(Sort sort); // 排序
* - Page<T> findAll(Pageable pageable); // 分页查询(含排序功能)
* JpaSpecificationExecutor 提供了Specification(封装 JPA Criteria查询条件)的查询功能
*
* - List<T> findAll(Specification<T> spec);
* - Page<T> findAll(Specification<T> spec, Pageable pageable);
* - List<T> findAll(Specification<T> spec, Sort sort);
*
* 开发建议
* 1. 这里值只列出了常用方法
* 2. CrudRepository 中的findAll() 方法要慎用。当数据库中数据量大,多线程脚本调用findAll方法,系统可能会宕机。
* 3. CrudRepository 中的deletAll()方法要慎用。这是物理删除,现在企业一般采用逻辑删除。
* 4. PagingAndSortingRepository 和 JpaSpecificationExecutor 能满足大部分业务需求。
*/
@Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long>,
JpaSpecificationExecutor<User>{

Expand All @@ -34,23 +53,23 @@ public interface UserRepository extends PagingAndSortingRepository<User, Long>,
* 注意
* 若有User(用户表) Platform(用户平台表) 存在一对一的关系,且User表中有platformId字段
* SpringData 为了区分:
* findByPlatFormId 表示通过platformId字段查询
* findByPlatForm_Id 表示通过platform表中id字段查询
* findByPlatFormId 表示通过platformId字段查询
* findByPlatForm_Id 表示通过platform实体类中id字段查询
*
* 开发建议
* 表的设计,尽量做单表查询,以确保高并发场景减轻数据库的压力。
*/

// 通过账号查用户信息
User findByAccount(String account);

// 获取指定时间内以xx邮箱结尾的用户信息
List<User> findByEmailEndingWithAndCreatedDateLessThan(String email, String createdDate);

/**
* 重点知识:使用 @Query 注解
*
* 上面的方法虽然简单(不用写sql语句),但它有最为致命的问题-----不支持复杂查询,其次是命名太长
* 使用@Query 注解实现复杂查询,设置 nativeQuery=true 使查询支持原生sql,配合@Modifying 注解实现创建,修改,删除操作
* getActiveUserCount 获取某平台活跃用户数量
*
* 注意
* 若@Query 中有多个参数,SpringData 提供两种方法:
Expand All @@ -61,12 +80,17 @@ public interface UserRepository extends PagingAndSortingRepository<User, Long>,
* 开发建议
* 参数填写的顺序要一致,不要给自己添加麻烦。
*/
// 获取某平台活跃用户数量
@Query(value="SELECT count(u.id) FROM User u WHERE u.platform = :platform AND u.updatedDate = :updatedDate")
long getActiveUserCount(String platform, String updatedDate);

@Query(value="SELECT u FROM User u WHERE u.email = %?1% AND u.iphone = %?2%")
// 通过邮箱或者手机号模糊查询用户信息
@Query(value="SELECT u FROM User u WHERE u.email LIKE %?1% OR u.iphone LIKE %?2%")
List<User> findByEmailAndIhpneLike(String email, String iphone);


// 修改用户邮箱
@Modifying
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
void updateUserEmail(@Param("id") Integer id, @Param("email") String email);

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.itdragon.service;

import java.util.List;
import java.util.Map;

import javax.transaction.Transactional;
Expand All @@ -11,10 +10,9 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.scheduling.config.Task;
import org.springframework.stereotype.Service;

import com.itdragon.common.ItdragonResult;
import com.itdragon.common.SortType;
import com.itdragon.pojo.User;
import com.itdragon.repository.UserRepository;
Expand Down Expand Up @@ -60,8 +58,16 @@ private Specification<User> buildSpecification(Map<String, Object> searchParams)
return spec;
}

public List<User> findAll() {
return (List<User>) userRepository.findAll();

public ItdragonResult registerUser(User user) {
// 检查用户名是否注册,一般在前端验证的时候处理,因为注册不存在高并发的情况,这里再加一层查询是不影响性能的
if (null != userRepository.findByAccount(user.getAccount())) {
// 提示
return ItdragonResult.build(400, "");
}
userRepository.save(user);
// 注册成功后选择发送邮件激活。现在一般都是短信验证码
return ItdragonResult.build(200, "");
}

}
Loading

0 comments on commit c5facba

Please sign in to comment.