1.powerdesigner设置表公共字段
powerdesigner:这是一个还没有进行添加公共字段的数据库模型(pdm)
接下来我们需要在每一个表中添加公共属性,需要用到vbs脚本 统一添加字段脚本下载 下载完成后在powerdesigner中点击 工具->Execute Commands->Edit/Run Script…(或者可以使用快捷键 Ctrl+Shift+X)在弹出的框中选择文件打开下载好的vbs脚本,run之后会动态生成。
2.pdm生成数据库脚本
统一增加公共属性后,点击顶部DataBase->Generate DataBase,弹出框选择要创建sql脚本的路径和文件名称
点击确定后生成脚本。
在这之前可以通过Preview查看将要生成的sql
生成sql后,可以直接通过脚本创建库表。
通过Mybatis拦截器做公共属性的自动赋值
mybatis拦截器是在sql执行之前后将sql进行拦截,修改sql
具体内容👇
package com.yunyu.base.plugin;
import com.yunyu.base.constant.SessionKey;
import com.yunyu.base.dto.ILoginDto;
import com.yunyu.base.entity.AbstractEntity;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
/**
* @create 2018-03-24 0:11
*
* @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
* @Signature(type = Executor.class, method = "createCacheKey", args = {MappedStatement.class, Object.class, RowBounds.class, BoundSql.class}),
* @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
* @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class })
*/
@Component
@Intercepts({
@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "createCacheKey", args = {MappedStatement.class, Object.class, RowBounds.class, BoundSql.class})
})
public class MyBatisInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisInterceptor.class);
/** Mybatis全局公共参数变量名 */
private static final String COMMON_FIELDS_KEY = "commonFields";
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
Executor executor = (Executor) invocation.getTarget();
if (args.length == 2) {
// 插入、更新、删除时拦截处理
invokeUpdate(ms, parameter);
return executor.update(ms, parameter);
} else {
// 查询时拦截处理
RowBounds rowBounds = (RowBounds) args[2];
BoundSql boundSql = (BoundSql) args[3];
invokeQuery(parameter, boundSql);
return executor.createCacheKey(ms, parameter, rowBounds, boundSql);
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 执行INSERT、UPDAT、DELETEE时填充公共字段
* @param ms Mapper映射
* @param parameter 入参对象
*/
private void invokeUpdate(MappedStatement ms, Object parameter) throws InvocationTargetException, IllegalAccessException {
if (parameter != null && parameter instanceof AbstractEntity) { // Mapper操作
AbstractEntity entity = (AbstractEntity) parameter;
// 提取公共参数并填充
Map<String, Object> commonFieldsMap = buildCommonFields();
SqlCommandType sqlCommandType = ms.getSqlCommandType();
if (sqlCommandType == sqlCommandType.INSERT) {
entity.setAddAction((String) commonFieldsMap.get("action"));
entity.setAddTermIp((String) commonFieldsMap.get("termIp"));
entity.setAddUserId((Long) commonFieldsMap.get("userId"));
entity.setAddDt((Long) commonFieldsMap.get("dt"));
} else if (sqlCommandType == sqlCommandType.UPDATE) {
entity.setVersion((entity.getVersion() != null ? entity.getVersion() : 1) + 1);
entity.setUpdAction((String) commonFieldsMap.get("action"));
entity.setUpdTermIp((String) commonFieldsMap.get("termIp"));
entity.setUpdUserId((Long) commonFieldsMap.get("userId"));
entity.setUpdDt((Long) commonFieldsMap.get("dt"));
}
} else { // Dao操作
BoundSql boundSql = ms.getBoundSql(parameter);
invokeQuery(parameter, boundSql);
}
}
/**
* 执行SELECT时填充公共字段
* @param parameter 入参对象
* @param boundSql 参数绑定关系
*/
private void invokeQuery(Object parameter, BoundSql boundSql) throws InvocationTargetException, IllegalAccessException {
// 若参数映射没有包含的key直接返回
List<String> paramNames = new ArrayList<>();
boolean hasKey = hasParamKey(paramNames, boundSql.getParameterMappings());
if (!hasKey) {
return;
}
// 改写参数
processParams(parameter, paramNames);
}
/**
* 通过Dao设置参数时
* @param invocation 目标方法
*/
private void invokeSetParameter(Invocation invocation) throws NoSuchFieldException, IllegalAccessException, InvocationTargetException, SQLException {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
// 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射
Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");
boundSqlField.setAccessible(true);
BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);
// 若参数映射没有包含的key直接返回
List<String> paramNames = new ArrayList<>();
boolean hasKey = hasParamKey(paramNames, boundSql.getParameterMappings());
if (!hasKey) {
return;
}
// 反射获取 参数对像
Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
parameterField.setAccessible(true);
Object parameterObject = parameterField.get(parameterHandler);
// 改写参数
parameterObject = processParams(parameterObject, paramNames);
// 改写的参数设置到原parameterHandler对象
parameterField.set(parameterHandler, parameterObject);
parameterHandler.setParameters(ps);
}
/**
* 判断已生成SQL参数映射中是否包含commonFields
* @param paramNames 参数名列表
* @param parameterMappings 参数映射表
* @return true:已包含,false:未包含
*/
private boolean hasParamKey(List<String> paramNames, List<ParameterMapping> parameterMappings) {
boolean hasKey = false;
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getProperty().startsWith(COMMON_FIELDS_KEY)) {
hasKey = true;
} else {
paramNames.add(parameterMapping.getProperty());
}
}
return hasKey;
}
/**
* 对原参数对象做处理,植入公共参数信息
* @param paramObj 原参数对象
* @param paramNames 参数名列表
* @return 新参数对象
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private Object processParams(Object paramObj, List<String> paramNames) throws InvocationTargetException, IllegalAccessException {
// 提取公共字段信息
Map<String, Object> commonFields = buildCommonFields();
if (paramObj == null) {
// 无参数时,添加Map参数对象
Map<String, Object> paramMap = new MapperMethod.ParamMap<>();
paramMap.put(COMMON_FIELDS_KEY, commonFields);
paramObj = paramMap;
} else if ((ClassUtils.isPrimitiveOrWrapper(paramObj.getClass())
|| String.class.isAssignableFrom(paramObj.getClass())
|| Number.class.isAssignableFrom(paramObj.getClass()))
&& paramNames.size() == 1) {
// 单参数时,将参数转为Map
Map<String, Object> paramMap = new MapperMethod.ParamMap<>();
paramMap.put(paramNames.iterator().next(), paramObj);
paramMap.put(COMMON_FIELDS_KEY, commonFields);
paramObj = paramMap;
} else if (paramObj instanceof Map) {
// 原参数为Map,且Map的key中没有commonFields,添加到参数Map中
((Map) paramObj).putIfAbsent(COMMON_FIELDS_KEY, commonFields);
} else {
// 参数是Bean,反射设置值
PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(paramObj.getClass(), COMMON_FIELDS_KEY);
if (ps != null && ps.getReadMethod() != null && ps.getWriteMethod() != null) {
Object value = ps.getReadMethod().invoke(paramObj);
if (value == null) {
ps.getWriteMethod().invoke(paramObj, commonFields);
}
}
}
return paramObj;
}
/**
* 提取公共字段信息
* @return 公共字段信息
*/
private Map<String, Object> buildCommonFields() {
Map<String, Object> commonFieldsMap = new HashMap<>();
String action = null;
Long userId = null;
String termIp = "127.0.0.1";
Long mchId = null;
// web环境下
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
// 提取请求来源、终端IP
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
action = request.getRequestURI().toString();
termIp = request.getRemoteHost();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("客户端IP地址:%s, 客户端请求URI:%s", termIp, action));
}
// 提取来访用户信息
HttpSession session = request.getSession();
ILoginDto loginInfo = (ILoginDto) session.getAttribute(SessionKey.LOGIN_INFO);
if (loginInfo != null) { // 已登录
userId = loginInfo.getUserId();
mchId = loginInfo.getMchId();
}
}
commonFieldsMap.put("action", action); // 来访URL
commonFieldsMap.put("userId", userId); // 会话用户ID
commonFieldsMap.put("termIp", termIp); // 来访终端IP
commonFieldsMap.put("dt", System.currentTimeMillis()); // 当前时间
commonFieldsMap.put("mchId", mchId); // 会话商户ID
return commonFieldsMap;
}
}
PageHelper分页就是通过mybatis的拓展点(拦截器)做的分页,通过拦截器还可以做数据权限的控制……