当谈到在MySQL中处理Geometry数据时,MyBatis拦截器是一个非常有用的工具。 Geometry类型在空间数据库中广泛使用,它允许存储和操作各种地理和几何数据。 在本博客中,我们将探讨如何使用MyBatis拦截器来处理MySQL中的Geometry数据。
首先,让我们了解一下MyBatis拦截器的基本概念。 拦截器是MyBatis提供的一种机制,用于在执行SQL语句之前或之后进行干预和处理。 通过使用拦截器,我们可以在执行SQL语句之前或之后对其进行修改、增强或拦截。
在处理MySQL中的Geometry数据时,我们可能会遇到一些常见的需求,例如计算两个几何对象之间的距离、获取几何对象的边界框或进行空间查询。 MyBatis拦截器可以帮助我们在执行SQL语句之前或之后对这些操作进行自定义处理。
要使用MyBatis拦截器处理MySQL中的Geometry数据,我们需要以下步骤:
- 创建一个实现了MyBatis的Interceptor接口的拦截器类。 拦截器类需要实现intercept方法,在该方法中可以自定义处理逻辑。 在这个方法中,我们可以获取SQL语句、参数和结果,并对它们进行修改或拦截。
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Geometry {
}
@Data
public class geoInfo Serializable {
private Long id;
private String name;
/**
* 空间位置信息
*/
@Geometry
private String fenceGis;
}
package com.ruoyi.framework.security.interceptor;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ych.common.annotation.Geometry;
import com.ych.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.*;
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Slf4j
public class MybatisGeometryInterceptor implements Interceptor {
private final Integer SRID = 4326;
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
ResultMap map = resultMaps.get(0);
Class<?> className = map.getType();
if (isApplicableResultMap(resultMaps)) {
if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return handleSelect(invocation, statementHandler, metaObject, boundSql, resultMaps, map, className);
} else if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
return handleInsert(invocation, statementHandler, metaObject, boundSql, className);
} else if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
return handleUpdate(invocation, statementHandler, metaObject, boundSql, className);
}
}
return invocation.proceed();
}
private Object handleSelect(Invocation invocation, StatementHandler statementHandler, MetaObject metaObject, BoundSql boundSql, List<ResultMap> resultMaps, ResultMap map, Class<?> className) throws Throwable {
StringBuilder sb = new StringBuilder();
sb.append(SqlCommandType.SELECT.name());
String originalSql = boundSql.getSql();
String reSql = originalSql.replace(SqlCommandType.SELECT.name(), "");
int from = reSql.toUpperCase(Locale.getDefault()).indexOf("FROM");
// 获取字段属性
List<Field> allField = getAllField(className);
for (Field field : allField) {
field.setAccessible(true);
String filedName = StringUtils.toUnderScoreCase(field.getName());
if ("serial_version_uid".equalsIgnoreCase(filedName) || "serialVersionUID".equalsIgnoreCase(filedName)) {
continue;
}
// 判断字段上是否有@Geometry注解
if (field.isAnnotationPresent(Geometry.class)) {
sb.append("ST_ASTEXT(").append(filedName).append(") AS ").append(filedName).append(",");
} else {
// 判断字段上是否有@TableField注解
if (field.isAnnotationPresent(TableField.class)) {
// 获取@TableField注解
TableField tableField = field.getAnnotation(TableField.class);
// exit为true就添加
if (!tableField.exist()) {
continue;
} else {
sb.append(" ").append(filedName).append(",");
}
} else {
sb.append(" ").append(filedName).append(",");
}
}
}
sb.deleteCharAt(sb.length() - 1);
String lastSql = reSql.substring(from);
sb.append(" " + lastSql);
metaObject.setValue("delegate.boundSql.sql", sb.toString());
metaObject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
metaObject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
return invocation.proceed();
}
private Object handleInsert(Invocation invocation, StatementHandler statementHandler, MetaObject metaObject, BoundSql boundSql, Class<?> className) throws Throwable {
Object paramObj = boundSql.getParameterObject();
Field[] fields;
if (paramObj instanceof HashMap) {
fields = ((HashMap) paramObj).get("et").getClass().getDeclaredFields();
} else {
fields = getAllField(paramObj.getClass()).toArray(new Field[0]);
}
String originalSql = boundSql.getSql();
originalSql = originalSql.toUpperCase();
int start = originalSql.indexOf("(");
int end = originalSql.indexOf(")");
String paramNames = originalSql.substring(start + 1, end);
String substring = originalSql.substring(0, originalSql.indexOf("VALUES"));
StringBuilder sb = new StringBuilder();
sb.append(substring).append(" ").append("VALUES ").append("( ");
String[] split = paramNames.split(",");
for (String paramName : split) {
for (Field field : fields) {
if (field.getName().trim().equalsIgnoreCase(StringUtils.underlineToHump(paramName.trim()))) {
// 判断字段上是否有@Geometry注解
if (field.isAnnotationPresent(Geometry.class)) {
sb.append("ST_GEOMFROMTEXT(").append("? ").append(",").append(SRID).append(",").append("'axis-order=long-lat'").append(")").append(",");
} else {
sb.append("?").append(",");
}
}
}
}
sb.deleteCharAt(sb.length() - 1).append(" )");
metaObject.setValue("delegate.boundSql.sql", sb.toString());
return invocation.proceed();
}
private Object handleUpdate(Invocation invocation, StatementHandler statementHandler, MetaObject metaObject, BoundSql boundSql, Class<?> className) throws Throwable {
Object paramObj = boundSql.getParameterObject();
Field[] fields;
if (paramObj instanceof HashMap) {
fields = ((HashMap) paramObj).get("et").getClass().getDeclaredFields();
} else {
fields = getAllField(paramObj.getClass()).toArray(new Field[0]);
}
String originalSql = boundSql.getSql();
originalSql = originalSql.toUpperCase();
int start = originalSql.indexOf("SET");
int end = originalSql.indexOf("WHERE");
StringBuilder sb = new StringBuilder();
sb.append(originalSql, 0, start + 4);
String params = originalSql.substring(start + 4, end);
String after = params.replace("=", "").replace("?", "");
String[] split = after.split(",");
for (String paramName : split) {
for (Field field : fields) {
if (field.getName().trim().equalsIgnoreCase(StringUtils.underlineToHump(paramName.trim()))) {
sb.append(paramName).append("=");
// 判断字段上是否有@Geometry注解
if (field.isAnnotationPresent(Geometry.class)) {
sb.append("ST_GEOMFROMTEXT(").append("? ").append(",").append(SRID).append(",").append("'axis-order=long-lat'").append(")").append(",");
} else {
sb.append("?").append(",");
}
}
}
}
sb.deleteCharAt(sb.length() - 1).append(originalSql.substring(end));
metaObject.setValue("delegate.boundSql.sql", sb.toString());
return invocation.proceed();
}
private boolean isApplicableResultMap(List<ResultMap> resultMaps) {
String resultMapId = resultMaps.get(0).getId();
return resultMapId.contains("TbFence") || resultMapId.contains("TbHarbor") || resultMapId.contains("TbShip");
}
private Object realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return target;
}
/**
* 获取本类及父类所有字段
* @param className
* @return
*/
private ArrayList<Field> getAllField(Class<?> className) {
ArrayList<Field> allField = new ArrayList<>();
while (className != null && className != Object.class) {
// 获取所有字段,包括私有字段
Field[] fields = className.getDeclaredFields();
Collections.addAll(allField, fields);
// 获取父类的 Class 对象
className = className.getSuperclass();
}
return allField;
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
- 在MyBatis配置文件中配置拦截器。 在配置文件中,我们需要将拦截器类添加到MyBatis的拦截器链中。 这样,当执行SQL语句时,拦截器就会被调用,并进行自定义的Geometry处理操作。
<?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>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
<plugins>
<plugin interceptor="你的包名.MybatisGeometryInterceptor"/>
</plugins>
</configuration>
- 在拦截器的intercept方法中,我们可以使用MySQL的空间函数或扩展库来处理Geometry数据。 MySQL提供了一系列的空间函数和操作符,用于处理和查询Geometry数据。 通过在拦截器中使用这些函数,我们可以实现各种Geometry处理操作。
例如,我们可以使用MySQL的ST_Distance函数来计算两个几何对象之间的距离。 在拦截器的intercept方法中,我们可以获取SQL语句和参数,判断是否是Geometry操作,然后使用ST_Distance函数计算距离,并将结果返回给应用程序。
另一个例子是使用MySQL的ST_Envelope函数来获取几何对象的边界框。 在拦截器中,我们可以通过解析SQL语句和参数,找到包含ST_Envelope函数的查询,并将其替换为对应的边界框查询。
通过使用MyBatis拦截器,我们可以灵活地处理MySQL中的Geometry数据,实现各种自定义的操作和查询。 拦截器提供了一个强大的机制,允许我们在SQL语句的执行过程中对Geometry数据进行处理和干预。
总结起来,使用MyBatis拦截器对MySQL中的Geometry进行处理是一种强大而灵活的方法。 通过自定义拦截器类和处理逻辑,我们可以在SQL语句执行前后对Geometry数据进行修改、增强或拦截。 这为我们实现各种Geometry操作和查询提供了便利和灵活性。 希望这篇博客对你在处理MySQL中的Geometry数据时有所帮助!