mybatis逆向工程,同意返回封住哪个类、springmvc全局异常处理、校验工具-validator,Json转换工具,获取spring上下文,http请求监听等。
1. mybatis generator
pom中增加插件:
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
生成文件GeneratorConfig.xml
注意要对自己的数据库的jar包位置、用户名、密码等进行修改
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!--classPathEntry:数据库的JDBC驱动 -->
<classPathEntry
location="D:\apache-maven-3.5.0\mysql-connector-java-5.1.34.jar" />
<context id="MysqlTables" targetRuntime="MyBatis3">
<!-- 注意这里面的顺序确定的,不能随变更改 -->
<!-- 自定义的分页插件 <plugin type="com.deppon.foss.module.helloworld.shared.PaginationPlugin"/> -->
<!-- 可选的(0 or 1) -->
<!-- 注释生成器 -->
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 必须的(1 required) -->
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/jimin"
userId="root" password="123456">
</jdbcConnection>
<!-- 可选的(0 or 1) -->
<!-- 类型转换器或者加类型解析器 -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 必须的(1 required) -->
<!-- java模型生成器 -->
<!-- targetProject:自动生成代码的位置 -->
<javaModelGenerator targetPackage="com.njupt.model"
targetProject="F:\gitee\permission\src\main\java"
>
<!-- TODO enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 必须的(1 required) -->
<!-- map xml 生成器 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="F:\gitee\permission\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 可选的(0 or 1) -->
<!-- mapper 或者就是dao接口生成器 -->
<javaClientGenerator targetPackage="com.njupt.dao"
targetProject="F:\gitee\permission\src\main\java"
type="XMLMAPPER">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 必须的(1...N) -->
<!-- pojo 实体生成器 -->
<!-- tableName:用于自动生成代码的数据库表;domainObjectName:对应于数据库表的javaBean类名 -->
<!-- schema即为数据库名 可不写 -->
<table tableName="sys_user" domainObjectName="SysUser"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
<!-- 忽略字段 可选的(0 or 1) -->
<!-- <ignoreColumn column="is_use" /> -->
<!--//无论字段是什么类型,生成的类属性都是varchar。 可选的(0 or 1) 测试无效 -->
<!-- <columnOverride column="city_code" jdbcType="VARCHAR" /> -->
</table>
<table tableName="sys_role_user" domainObjectName="SysRoleUser"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
<table tableName="sys_role_acl" domainObjectName="SysRoleAcl"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
<table tableName="sys_role" domainObjectName="SysRole"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
<table tableName="sys_log" domainObjectName="SysLog"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
<columnOverride column="old_value" jdbcType="VARCHAR" />
<columnOverride column="new_value" jdbcType="VARCHAR" />
</table>
<table tableName="sys_dept" domainObjectName="SysDept"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
<table tableName="sys_acl_module" domainObjectName="SysAclModule"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
<table tableName="sys_acl" domainObjectName="SysAcl"
enableInsert="true" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
最后mybatis-generator:generate -e进行运行。
2. 项目的json返回和jsp返回
先写一个统一的返回封装类:
@Data
public class JsonData {
private boolean ret;
private String msg;
private Object data;
public JsonData(boolean ret){
this.ret = ret;
}
public static JsonData success(Object object,String msg){
JsonData jsonData = new JsonData(true);
jsonData.setMsg(msg);
jsonData.setData(object);
return jsonData;
}
public static JsonData success(Object object){
JsonData jsonData = new JsonData(true);
jsonData.setData(object);
return jsonData;
}
public static JsonData success(){
return new JsonData(true);
}
public static JsonData fail(String msg){
JsonData jsonData = new JsonData(false);
jsonData.setMsg(msg);
return jsonData;
}
public Map<String,Object> toMap(){
HashMap<String,Object> result = new HashMap<>();
result.put("ret",ret);
result.put("msg",msg);
result.put("data",data);
return result;
}
}
然后规定以.json结尾的就是json数据,以.page结尾的就是普通返回jsp的数据。
3. 全局异常处理
那么普通情况下,不会出现问题,但是在出现异常的时候呢,我们不希望在页面出现一坨异常信息,怎么办?这个时候就需要有一个全局异常类:
全局异常类也要分.json和.page两种:
@Slf4j
@Component
public class SpringExceptionResolver implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response,
Object handler, Exception e) {
String url = request.getRequestURI().toString();
ModelAndView mv;
String defaultMsg = "system error";
//.json
if(url.endsWith(".json")){
if(e instanceof PermissionException || e instanceof ParamException){
//是自定义的异常
JsonData result = JsonData.fail(e.getMessage());
mv = new ModelAndView("jsonView",result.toMap());//jsonView与spring-servlet中呼应
}else {
log.error("unkonen exception,url:"+url,e);
JsonData result = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView",result.toMap());
}
}else if(url.endsWith(".page")){//.page
log.error("unkonen exception,url:"+url,e);
JsonData result = JsonData.fail(defaultMsg);
mv = new ModelAndView("exception",result.toMap());//去jsp目录下找一个叫exception的页面
}else {
log.error("unkonen exception,url:"+url,e);
JsonData result = JsonData.fail(defaultMsg);
mv = new ModelAndView("jsonView",result.toMap());
}
return mv;
}
}
注意不要忘记被springmvc管理:
<bean id="springExceptionResolver" class="com.njupt.common.SpringExceptionResolver"/>
对于PermissionException:
public class PermissionException extends RuntimeException{
public PermissionException() {
super();
}
public PermissionException(String message) {
super(message);
}
public PermissionException(String message, Throwable cause) {
super(message, cause);
}
public PermissionException(Throwable cause) {
super(cause);
}
protected PermissionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
4. BeanValidator
要对数据进行校验,我们用hibernate-validator来实现注解校验,先引入pom:
<dependency>
<groupId>Maven.javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
然后写一个接受验证错误的信息类BeanValidator:
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.njupt.exception.ParamException;
import org.apache.commons.collections.MapUtils;
import javax.validation.*;
import java.util.*;
public class BeanValidator {
private static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
public static <T>Map<String,String> validate(T t, Class... groups){
Validator validator = validatorFactory.getValidator();
Set validatorResult = validator.validate(t,groups);
if(validatorResult.isEmpty()){
return Collections.EMPTY_MAP;
}else {
LinkedHashMap errors = Maps.newLinkedHashMap();
Iterator iterator = validatorResult.iterator();
while(iterator.hasNext()){
ConstraintViolation violation = (ConstraintViolation)iterator.next();
errors.put(violation.getPropertyPath().toString(),violation.getMessage());
}
return errors;
}
}
public static Map<String,String> validateList(Collection<?> collection){
Preconditions.checkNotNull(collection);
Iterator iterator = collection.iterator();
Map errors;
do{
if(!iterator.hasNext()){
return Collections.EMPTY_MAP;
}
Object object = iterator.next();
errors = validate(object,new Class[0]);
}while (errors.isEmpty());
return errors;
}
public static Map<String,String> validateObject(Object first,Object... objects){
if(objects != null && objects.length > 0){
return validateList(Lists.asList(first,objects));
}else {
return validate(first,new Class[0]);
}
}
//这里不返回具体的参数校验错误信息,而是直接抛出异常
public static void check(Object param) throws ParamException{
Map<String,String> map = BeanValidator.validateObject(param);
// if(map != null && map.entrySet().size()>0){
// throw new ParamException(map.toString());
// }
if(MapUtils.isEmpty(map)){
throw new ParamException(map.toString());
}
}
}
对于直接抛出异常,我们需要些一个ParamException,也是直接继承RuntimeExceptionin即可。
我们对其进行测试一下:
@Data
public class TestVo {
@NotBlank
private String msg;
@NotNull
private Integer id;
}
在TestController中,测试一下,不携带参数,看是否会自动报出空错误:
@RequestMapping("/validate.json")
@ResponseBody
public JsonData validate(TestVo testVo){
try {
Map<String,String> map = BeanValidator.validateObject(testVo);
if(map != null && map.entrySet().size() > 0){
for(Map.Entry<String,String> entry : map.entrySet()){
log.info("{}->{}",entry.getKey(),entry.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return JsonData.success("hello validate");
}
访问:http://localhost:8080/test/validate.json
看控制台是否出现:msg->不能为空;id->不能为null这些信息,出现了则说明我们的校验已经生效了。
5. JsonMapper
json与对象之间的互相转换。
<!--jackson-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
</dependency>
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import java.io.IOException;
import java.text.SimpleDateFormat;
@Slf4j
public class JsonMapper {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
//所有字段都列入进行转换
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
//取消默认转换timestamp形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
//忽略空bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);
//统一时间的格式
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
//忽略json存在属性,但是java对象不存在属性的错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
}
/**
* 序列化方法,将对象转为字符串
* @param obj
* @param <T>
* @return
*/
public static <T> String obj2String(T obj){
if(obj == null){
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
return null;
}
}
/**
* 序列化方法,同上,只是输出的格式是美化的,便于测试
* @param obj
* @param <T>
* @return
*/
public static <T> String obj2StringPretty(T obj){
if(obj == null){
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
return null;
}
}
/**
* 比较简单的反序列化的方法,将字符串转为单个对象
* @param str
* @param clazz
* @param <T>
* @return
*/
public static <T> T String2Obj(String str,Class<T> clazz){
if(StringUtils.isEmpty(str) || clazz == null){
return null;
}
try {
return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz);
} catch (IOException e) {
log.warn("parse string to obj error",e);
return null;
}
}
/**
* 复杂对象的反序列化(通用)
* @param str
* @param typeReference
* @param <T>
* @return
*/
public static <T> T Str2Obj(String str, TypeReference typeReference){
if(StringUtils.isEmpty(str) || typeReference == null){
return null;
}
try {
return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference));
} catch (IOException e) {
log.warn("parse string to obj error",e);
return null;
}
}
/**
* 第二种方式实现复杂对象的反序列化
* @param str
* @param collectionClass
* @param elementClasses
* @param <T>
* @return
*/
public static <T> T Str2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
try {
return objectMapper.readValue(str,javaType);
} catch (IOException e) {
log.warn("parse string to obj error",e);
return null;
}
}
}
这里我直接还有复杂对象的转换,我顺便也将时间处理的工具类也放进去了:
public class DateTimeUtil {
//joda-time
//str->Date
//Date->str
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date strToDate(String dateTimeStr, String formatStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date,String formatStr){
if(date == null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
//固定好格式
public static Date strToDate(String dateTimeStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date){
if(date == null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
public static void main(String[] args) {
System.out.println(DateTimeUtil.dateToStr(new Date(),"yyyy-MM-dd HH:mm:ss"));
System.out.println(DateTimeUtil.strToDate("2010-01-01 11:11:11","yyyy-MM-dd HH:mm:ss"));
}
}
6. spring上下文获取
如何获取spring上下文?
@Component("applicationContextHelper")
public class ApplicationContextHelper implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static <T> T popBean(Class<T> clazz){
if(applicationContext == null){
return null;
}
return applicationContext.getBean(clazz);
}
public static <T> T popBean(String name,Class<T> clazz){
if(applicationContext == null){
return null;
}
return applicationContext.getBean(name,clazz);
}
}
注意要在spring初始化的时候就加载这个类,所以在spring-servlet中添加:
<bean class="com.njupt.common.ApplicationContextHelper" lazy-init="false"/>
可以进行测试一下:
@RequestMapping("/validate1.json")
@ResponseBody
public JsonData validate1(TestVo testVo) throws ParamException{
SysAclModuleMapper moduleMapper = ApplicationContextHelper.popBean(SysAclModuleMapper.class);
SysAclModule module = moduleMapper.selectByPrimaryKey(1);
log.info(JsonMapper.obj2String(module));
BeanValidator.check(testVo);
return JsonData.success("hello validate");
}
7. Http请求监听
就是mvc拦截器的应用了,我们在这里可以记录他访问的url是什么,还有就是一个接口的相应时间。都记录与日志中。
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter{
private static final String STRAT_TIME = "requestStartTime";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getRequestURI().toString();
Map parameterMap = request.getParameterMap();
log.info("preHandle,url:{},params:{}",url, JsonMapper.obj2String(parameterMap));
long start = System.currentTimeMillis();
request.setAttribute(STRAT_TIME,start);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String url = request.getRequestURI().toString();
long start = (long)request.getAttribute(STRAT_TIME);
long end = System.currentTimeMillis();
log.info("afterCompletion,preHandle,url:{},time cost:{}",url, end-start);
}
}
在spring-servlet中也要配置拦截器。
<mvc:interceptors>
<bean class="com.njupt.common.HttpInterceptor"/>
</mvc:interceptors>