当前位置: 首页 > wzjs >正文

微网站建设方式南昌seo快速排名

微网站建设方式,南昌seo快速排名,模板网页生成,可以做网站的编程有什么1、目标 本文的主要目标是学习使用MyBatis拦截器,并给出拦截器的实例 2、拦截器的使用 2.1 Intercepts注解和Signature注解 Intercepts注解,指定拦截哪个拦截器的哪个方法,还要指定参数,因为可能发生方法重载 按照顺序可以拦…

1、目标

本文的主要目标是学习使用MyBatis拦截器,并给出拦截器的实例

2、拦截器的使用

2.1 @Intercepts注解和@Signature注解

@Intercepts注解,指定拦截哪个拦截器的哪个方法,还要指定参数,因为可能发生方法重载

按照顺序可以拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler这4个接口的方法

@Signature注解,指定type是这4个接口中的某个接口,指定method是接口中的一个方法,指定args是方法的入参

2.2 四个接口

public interface Executor {ResultHandler NO_RESULT_HANDLER = null;int update(MappedStatement ms, Object parameter) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;List<BatchResult> flushStatements() throws SQLException;void commit(boolean required) throws SQLException;void rollback(boolean required) throws SQLException;CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);boolean isCached(MappedStatement ms, CacheKey key);void clearLocalCache();void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);Transaction getTransaction();void close(boolean forceRollback);boolean isClosed();void setExecutorWrapper(Executor executor);}

Executor的拦截器在SQL语句执行之前和之后执行,可以在这里添加逻辑来处理SQL执行过程中的需求,如日志记录或性能监控

public interface StatementHandler {Statement prepare(Connection connection, Integer transactionTimeout)throws SQLException;void parameterize(Statement statement)throws SQLException;void batch(Statement statement)throws SQLException;int update(Statement statement)throws SQLException;<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;<E> Cursor<E> queryCursor(Statement statement)throws SQLException;BoundSql getBoundSql();ParameterHandler getParameterHandler();}

StatementHandler的拦截器在生成SQL语句和设置参数阶段执行,可以在这里修改SQL语句或参数,也可以打印日志

public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps) throws SQLException;}

ParameterHandler的拦截器在StatementHandler设置参数之后执行

public interface ResultSetHandler {<E> List<E> handleResultSets(Statement stmt) throws SQLException;<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;void handleOutputParameters(CallableStatement cs) throws SQLException;}

ResultSetHandler的拦截器在SQL执行之后、将结果集映射到Java对象之前执行,可以在这里对查询结果进行修改或处理

2.3 Interceptor接口

类需要实现Interceptor接口,重写intercept方法

public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;default Object plugin(Object target) {return Plugin.wrap(target, this);}default void setProperties(Properties properties) {// NOP}}

Interceptor接口的plugin方法和setProperties方法都是default默认方法,可以不重写

Interceptor接口的intercept方法的入参是Invocation对象

public class Invocation {private final Object target;private final Method method;private final Object[] args;public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);}
}

Invocation对象的target属性是被代理对象,即4个接口中的一个,method是接口的方法,args是方法的入参

Invocation对象的proceed方法是执行被代理对象的方法,因此拦截器可以在执行被代理对象的方法之前或者之后进行操作,它还有返回值就是被代理对象的方法的返回值

2.4 @Component

实现Interceptor接口的类需要加上@Component注解,将它注入到IOC容器中

3、拦截器的实例

3.1 设计表

create table `news` (`id` varchar(100) primary key not null,`title` varchar(100) not null,`create_user_id` int,`create_date_time` date
)

3.2 整体类结构

在这里插入图片描述

3.3 pom.xml文件

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.2</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><!--MD5依赖--><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.16.0</version></dependency>
</dependencies>

3.4 application.yml文件

server:port: 9021spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://ip:port/数据库名字?useUnicode=trueusername: xxxpassword: xxxlogging:level:com:lwc:debugmybatis:mapper-locations: classpath:/*.xml

如果没有配置mapper-locations是xml文件的位置,就会抛出异常:org.apache.ibatis.binding.BindingException

在这里插入图片描述

3.5 创建主启动类MybatisIntercetorApplication

@SpringBootApplication
public class MybatisIntercetorApplication {public static void main(String[] args) {SpringApplication.run(MybatisIntercetorApplication.class, args);}
}

3.6 controller接口

@RestController
@RequiredArgsConstructor
@Log4j2
@RequestMapping("/news")
public class NewsController {private final NewsService newsService;@PostMapping("/save")public String saveNews(@RequestBody Map<String, Object> map) {newsService.saveNews(map);return "saveNews success";}}

3.7 service接口

@Service
@RequiredArgsConstructor
@Log4j2
public class NewsService {private final NewsMapper newsMapper;public void saveNews(Map<String, Object> map) {List<String> fieldNameList = new ArrayList<>();List<Object> fieldValueList = new ArrayList<>();map.forEach((k, v) -> {fieldNameList.add(k);fieldValueList.add(v);});newsMapper.insertNews(fieldNameList, fieldValueList);}}

3.8 mapper接口

@Mapper
public interface NewsMapper {public int insertNews(@Param("fieldNameList") List<String> fieldNameList,@Param("fieldValueList") List<Object> fieldValueList);
}

3.9 ModifySqlInterceptor拦截器

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Component
@Log4j2
public class ModifySqlInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 目标:在执行StatementHandler对象的prepare方法之前修改sql,因此修改sql之后再调用invocation.proceed()方法StatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();log.info("modify before, sql: {}", sql);// parameterObject参数必须是Map,并且map包含fieldNameList和fieldNameList,才会修改sqlObject parameterObject = boundSql.getParameterObject();if(!(parameterObject instanceof Map)){return invocation.proceed();}Map<String, Object> map = (Map<String, Object>) parameterObject;if(!map.containsKey("fieldNameList") || !map.containsKey("fieldValueList")) {return invocation.proceed();}// 修改sql,添加多个字段List<String> fieldNameList = (List<String>) map.get("fieldNameList");fieldNameList.add("id");fieldNameList.add("create_user_id");fieldNameList.add("create_date_time");String fieldName = fieldNameList.stream().collect(Collectors.joining(", "));String fieldValue = fieldNameList.stream().map(s -> "?").collect(Collectors.joining(", "));StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(sql.substring(0, sql.indexOf("(") + 1));stringBuilder.append(" ");stringBuilder.append(fieldName);stringBuilder.append(" ");stringBuilder.append(sql.substring(sql.indexOf(")"), sql.lastIndexOf("(") + 1));stringBuilder.append(" ");stringBuilder.append(fieldValue);stringBuilder.append(" )");String newSql = stringBuilder.toString();// 采用SystemMetaObject.forObject方法实例化BoundSql对象MetaObject metaObject = SystemMetaObject.forObject(boundSql);metaObject.setValue("sql", newSql);log.info("modify after, sql: {}", boundSql.getSql());return invocation.proceed();}}

ModifySqlInterceptor拦截器用来在执行StatementHandler对象的prepare方法之前修改sql,因此修改sql之后再调用invocation.proceed()方法

修改sql需要先得到原来的sql,还要得到参数名字和参数值,它们都可以通过StatementHandler对象的BoundSql对象获取到sql和parameterObject,将参数值全部用?替换可以防止sql注入,最后使用SystemMetaObject.forObject方法实例化BoundSql对象并调用metaObject.setValue(“sql”, newSql)方法修改sql

其中,SystemMetaObject.forObject方法是MyBatis用来反射对象的工具类,它可以获取对象属性,也可以设置对象属性

3.10 AddParamsInterceptor拦截器

@Intercepts({@Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class})
})
@Component
@Log4j2
public class AddParamsInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 目标:在StatementHandler对象的parameterize方法完成参数赋值之后再添加新的参数值,因此先执行invocation.proceed()方法然后添加参数值// StatementHandler对象的parameterize方法返回值是voidinvocation.proceed();// 获取StatementHandler对象中的sql得到表名RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler) invocation.getTarget();MetaObject metaObject = SystemMetaObject.forObject(routingStatementHandler);String sql = (String) metaObject.getValue("delegate.boundSql.sql");String tableName = sql.substring(sql.indexOf("into") + 5, sql.indexOf("(")).trim();// 获取StatementHandler对象中的参数名和参数值,得到MapObject parameterObject = metaObject.getValue("delegate.boundSql.parameterObject");if(!(parameterObject instanceof Map)){return invocation.proceed();}Map<String, Object> map = (Map<String, Object>) parameterObject;if(!map.containsKey("fieldNameList") || !map.containsKey("fieldValueList")) {return invocation.proceed();}List<String> fieldNameList = (List<String>) map.get("fieldNameList");List<Object> fieldValueList = (List<Object>) map.get("fieldValueList");Map<String, Object> res = new HashMap<>();for(int i = 0; i < fieldValueList.size(); i++) {res.put(fieldNameList.get(i).toLowerCase(), fieldValueList.get(i));}// 获取StatementHandler对象中的parameterMappings,得到参数下标,因为parameterize方法的参数值赋值是根据parameterMappings的大小来的List<ParameterMapping> parameterMappings = (List<ParameterMapping>) metaObject.getValue("delegate.boundSql.parameterMappings");int size = parameterMappings.size();// 添加参数值,需要按照顺序,根据修改的sql的字段顺序Object[] args = invocation.getArgs();PreparedStatement preparedStatement = (PreparedStatement) args[0];preparedStatement.setString(++size, getId(tableName, res));preparedStatement.setInt(++size, UserIdThreadLocal.getUserId());preparedStatement.setDate(++size, new Date(System.currentTimeMillis()));return null;}private String getId(String tableName, Map<String, Object> map) {if("news".equals(tableName.toLowerCase())) {String title = (String) map.get("title");String id = MD5Util.getMd5(title);return id;}return null;}}

AddParamsInterceptor拦截器用来在StatementHandler对象的parameterize方法完成参数赋值之后再添加新的参数值,因此先执行invocation.proceed()方法然后添加参数值

添加参数值是通过parameterize方法的入参PreparedStatement对象的setXxx方法实现的,同时可以防止sql注入因为参数值是字符串的话会加单引号,同时添加参数值的顺序要和ModifySqlInterceptor拦截器添加参数名字的顺序一致

setXxx方法的key:

获取StatementHandler对象中的parameterMappings,得到参数下标,因为parameterize方法的参数值赋值是根据parameterMappings的大小来的

setXxx方法的value:

① 参数id的值是根据表名tableName和title这个字段的值使用MD5哈希算法得到id值,id值是一个字符串因此用setString方法

② 参数create_user_id的值是根据ThreadLocal中的userId得到的,这个userId是前端发送请求会携带userId请求头然后在过滤器解析请求头中的userId并存放到ThreadLocal中的,create_user_id是一个整数因此用setInt方法

③ 参数create_date_time的值是根据当前时间得到的,create_date_time是时间类型因此用setDate方法

3.11 UserIdThreadLocal

public class UserIdThreadLocal {private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void setUserId(Integer userId) {threadLocal.set(userId);}public static Integer getUserId() {return threadLocal.get();}public static void removeUserId() {threadLocal.remove();}}

UserIdThreadLocal存放了userId,它包含static静态的ThreadLocal

3.12 UserIdFilter过滤器

@WebFilter("/*")
@Component
public class UserIdFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;String userIdString = request.getHeader("userId");if(userIdString == null) {filterChain.doFilter(servletRequest, servletResponse);}Integer userId = Integer.parseInt(userIdString);try {UserIdThreadLocal.setUserId(userId);filterChain.doFilter(servletRequest, servletResponse);} finally {UserIdThreadLocal.removeUserId();}}}

@WebFilter(“/*”)表示过滤所有请求,必须加@Component将过滤器对象注入到IOC容器中

3.13 SqlPrintInterceptor拦截器

@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class})
})
@Component
@Log4j2
public class SqlPrintInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 目标:打印替换?的参数的sql语句int res = (int) invocation.proceed();Object[] args = invocation.getArgs();PreparedStatement preparedStatement = (PreparedStatement) args[0];MetaObject metaObject = SystemMetaObject.forObject(preparedStatement);preparedStatement = (PreparedStatement) metaObject.getValue("h.statement.delegate");String sql = preparedStatement.toString().substring(preparedStatement.toString().indexOf(":") + 1).replace("\n", " ");List<String> list = new ArrayList<>();for (String s : sql.split(" ")) {if("".equals(s)) {continue;}list.add(s.trim());}log.info(list.stream().collect(Collectors.joining(" ")));return res;}}

SqlPrintInterceptor拦截器的目标是打印替换?的参数的sql语句,即实际sql执行语句,通过SystemMetaObject.forObject方法得到PreparedStatement对象

3.14 输出结果

在这里插入图片描述

可以看到拦截器修改sql成功

在这里插入图片描述

这是MyBatis输出的sql日志,可以看到拦截器添加参数值成功,sql中的?和参数值一一对应,其中参数类型已经解析完成

在这里插入图片描述

可以看到拦截器打印日志成功,实际执行的sql是将?替换成参数值,为了防止sql注入,字符串类型和日期类型都用单引号防止sql注入,这也是#{}可以防止sql注入的原因,整数类型没有加单引号

http://www.dtcms.com/wzjs/75705.html

相关文章:

  • wordpress炫酷博客seo诊断分析
  • 先做亚马逊网站怎么操作全国人大常委会副委员长
  • 建设银行企业网站失败微指数查询入口
  • 嘉善县科正建设网站郑州厉害的seo优化顾问
  • 西宁微网站建设多少钱免费使用seo软件
  • wordpress微信群大全吉林关键词排名优化软件
  • 国内知名的网站建设公司有哪些搜狗推广登陆
  • 网站开发涉及技术网络平台推广广告费用
  • 网站制作需要学多久合肥网站优化平台
  • 深圳沙头网站建设一键生成app制作器
  • 网站怎么创建自己的网站百度seo优化哪家好
  • 网站建设开发方式包括一l丫企业全网推广公司
  • 做技术类网站赚钱吗优化 英语
  • 做yield网站多少钱深圳抖音推广公司
  • 广东企业网站建设百度推广的五大优势
  • 广州网站建设88关键词优化seo优化排名
  • 个人做视频网站seo网站推广多少钱
  • 如何建立自己的免费网站福州模板建站哪家好
  • 义乌义亭招工做网站养猪工作aso应用商店优化原因
  • 建设微网站平台引流推广平台
  • 武汉企业网站推广报价百度店铺怎么开通
  • dw制作班级网站培训机构最新消息
  • 亳州网站制作公司广州网站建设公司
  • 国外视频模板网站e合肥网络推广营销
  • 自己怎么做彩票网站吗海外推广营销平台
  • dw怎么做购物网站成都私人做网站建设
  • 建设工程业绩查询网站上海网络推广服务
  • 中国手工活加工网官网seo搜索推广
  • 纺织行业网站怎么做交换友链要注意什么
  • 网页编辑的软件关键词自动优化