@Mapper
public interface DefaultMapper{
List<Map<String, Object>> querySql(String sql);
}
@Component
public class JdbcUtils{
private static final Logger log = LoggerFactory.getLogger(JdbcUtils.class);
@Autowired
private DefaultMapper defaultMapper;
public List<Map<String, Object>> queryData(Integer page, Integer pageSize){
Integer offset = (page -1) * pageSize;
String sql = "select user_id,USER_NAME from user" + "LIMIT ? OFFSET ?";
String realSql = sql.repalceFirst("\\?", pageSize.toString()).repalceFirst("\\?", offset.toString());
List<Map<String, Object>> = defaultMapper.querySql(realSql);
}
}
有一个功能需求,希望代码中硬编码写死的sql语句在Mapper方法中执行的时候,sql需要转成小写。
因为用 List<Map<String, Object>>
接收 mybatis 返回时,sql语句中的字段名是大写,那 Map中的key也是大写;sql语句中的字段名是大写,Map中的key也是小写。
现在需要查到的List 中字段名都是小写,那硬编码sql中的字段名就必须也是小写。
所以,需要写一个切面,保证传给mapper中执行的sql 全部是小写。
@StringToLowercase
注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface StringToLowercase {
String value() default "sql";
}
切面方法 StringToLowercaseAspect
import com.mhd.taskmodel.aspect.annotation.StringToLowercase;
import org.apache.ibatis.annotations.Param;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* @Description:
* @title: StringToLowercaseAspect
* @Author mhd
* @Date: 2025/5/30 15:48
* @Version 1.0
*/
@Aspect
@Component
public class StringToLowercaseAspect {
/**
* 使用注解 StringToLowercase 将方法入参中指定参数转成小写
* @param joinPoint
* @param stringToLowercase
* @return
* @throws Throwable
*/
@Around("@annotation(stringToLowercase)")
public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint, StringToLowercase stringToLowercase) throws Throwable {
// 获取目标参数名
String targetParamName = stringToLowercase.value();
// 获取方法参数列表
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Parameter[] parameters = method.getParameters();
Object[] args = joinPoint.getArgs();
int targetIndex = -1;
// 双重查找策略:
// 1. 通过参数名查找
// 2. 通过@Param注解查找
// 第一优先级:参数名匹配
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].getName().equals(targetParamName)) {
targetIndex = i;
break;
}
}
if (targetIndex == -1) {
// 第二优先级:@Param注解匹配 需要在 maven 中添加 <arg>-parameters</arg>配置
for (int i = 0; i < parameters.length; i++) {
Param paramAnnotation = parameters[i].getAnnotation(Param.class);
if (paramAnnotation != null && paramAnnotation.value().equals(targetParamName)) {
targetIndex = i;
break;
}
}
}
// 转换目标参数
if (targetIndex != -1 && args[targetIndex] instanceof String) {
args[targetIndex] = ((String) args[targetIndex]).toLowerCase();
return joinPoint.proceed(args);
}
// 兜底,默认取方法中的第一个参数
args[0] = ((String) args[0]).toLowerCase();
return joinPoint.proceed(args);
}
}
修改后的使用:
@Mapper
public interface DefaultMapper{
@StringToLowercase("sql")
List<Map<String, Object>> querySql(String sql);
}
在 Maven 的 pom.xml
中添加以下配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
在启动类中加上注解 @EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class SqlParamConverterApplication {
public static void main(String[] args) {
SpringApplication.run(SqlParamConverterApplication.class, args);
}
}
@Mapper
public interface UserInfoMapper {
// 其他方法...
@ConvertSqlParam("sqlStr") // 指定要转换的参数名
List<Map<String, Object>> querySql(@Param("sql") String sqlStr,
@Param("tableName") String table);
}
@Autowired
private UserInfoMapper userInfoMapper;
public void executeQuery() {
// 原始SQL包含大写
String sql = "SELECT * FROM Users WHERE Status = 'ACTIVE'";
String table = "user_table";
// 执行查询
List<Map<String, Object>> result = userInfoMapper.querySql(sql, table);
// 实际执行的SQL将是小写:
// "select * from users where status = 'active'"
}
@Param
注解匹配-parameters
@Param
注解这个解决方案通过自定义注解和AOP切面,实现了对指定参数的精确转换,保持了代码的清晰性和可维护性。
// 修改注解定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertSqlParam {
String[] value(); // 支持多个参数名
}
// 修改切面处理
@Around("@annotation(convertSqlParam)")
public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint,
ConvertSqlParam convertSqlParam) throws Throwable {
String[] targetParamNames = convertSqlParam.value();
// 获取方法参数...
for (String targetName : targetParamNames) {
int index = findParameterIndex(targetName, parameters);
if (index != -1 && args[index] instanceof String) {
args[index] = ((String) args[index]).toLowerCase();
}
}
return joinPoint.proceed(args);
}
// 扩展注解功能
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertSqlParam {
String value();
CaseType caseType() default CaseType.LOWERCASE;
}
enum CaseType {
LOWERCASE, UPPERCASE
}
// 修改切面处理
@Around("@annotation(convertSqlParam)")
public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint,
ConvertSqlParam convertSqlParam) throws Throwable {
// ...
if (index != -1 && args[index] instanceof String) {
String value = (String) args[index];
switch (convertSqlParam.caseType()) {
case LOWERCASE:
args[index] = value.toLowerCase();
break;
case UPPERCASE:
args[index] = value.toUpperCase();
break;
}
}
// ...
}