diff --git a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java index 3a643ccf8cc..6e39aa6a61c 100644 --- a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java @@ -34,7 +34,7 @@ */ public class SqlSourceBuilder extends BaseBuilder { - private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName"; + private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName,like"; public SqlSourceBuilder(Configuration configuration) { super(configuration); @@ -131,6 +131,8 @@ private ParameterMapping buildParameterMapping(String content) { typeHandlerAlias = value; } else if ("jdbcTypeName".equals(name)) { builder.jdbcTypeName(value); + } else if ("like".equals(name)) { + builder.like(value); } else if ("property".equals(name)) { // Do Nothing } else if ("expression".equals(name)) { diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index 040e182a8cd..04771be0a96 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -16,6 +16,7 @@ package org.apache.ibatis.mapping; import java.sql.ResultSet; +import java.util.Optional; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; @@ -37,11 +38,42 @@ public class ParameterMapping { private TypeHandler typeHandler; private String resultMapId; private String jdbcTypeName; + private LikeEnum like; private String expression; private ParameterMapping() { } + public enum LikeEnum { + + NONE, + + LEFT { + @Override + public String format(String source) { + return "%" + source; + } + }, + + RIGHT { + @Override + public String format(String source) { + return source + "%"; + } + }, + + ALL { + @Override + public String format(String source) { + return RIGHT.format(LEFT.format(source)); + } + }; + + public String format(String source) { + return source; + } + } + public static class Builder { private final ParameterMapping parameterMapping = new ParameterMapping(); @@ -94,6 +126,18 @@ public Builder jdbcTypeName(String jdbcTypeName) { return this; } + public Builder like(String likeMode) { + LikeEnum likeEnum = null; + for (LikeEnum value : LikeEnum.values()) { + if (value.name().equalsIgnoreCase(likeMode)) { + likeEnum = value; + break; + } + } + parameterMapping.like = Optional.ofNullable(likeEnum).orElse(LikeEnum.NONE); + return this; + } + public Builder expression(String expression) { parameterMapping.expression = expression; return this; @@ -196,6 +240,15 @@ public String getJdbcTypeName() { return jdbcTypeName; } + /** + * Used for handling concat parameter with %. + * + * @return like pattern + */ + public LikeEnum getLike() { + return like; + } + /** * Expression 'Not used'. * @@ -217,6 +270,7 @@ public String toString() { // sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString() sb.append(", resultMapId='").append(resultMapId).append('\''); sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\''); + sb.append(", like='").append(like).append('\''); sb.append(", expression='").append(expression).append('\''); sb.append('}'); return sb.toString(); diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java index a9f5a91a0b8..60c1693c0be 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2022 the original author or authors. + * Copyright 2009-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,6 +83,9 @@ public void setParameters(PreparedStatement ps) { if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } + if (parameterMapping.getLike() != null && value instanceof String) { + value = parameterMapping.getLike().format((String) value); + } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { diff --git a/src/test/java/org/apache/ibatis/submitted/select_with_like/Mapper.java b/src/test/java/org/apache/ibatis/submitted/select_with_like/Mapper.java new file mode 100644 index 00000000000..c997a793738 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/select_with_like/Mapper.java @@ -0,0 +1,16 @@ +package org.apache.ibatis.submitted.select_with_like; + +import org.apache.ibatis.annotations.Insert; + +interface Mapper { + + @Insert({ "" }) + void selectWithLikeRight(String name); + + @Insert({ "" }) + void selectWithNoLike(String name); + + @Insert({ "" }) + void selectWithOodLike(String name); + + } diff --git a/src/test/java/org/apache/ibatis/submitted/select_with_like/SelectWithLikeTest.java b/src/test/java/org/apache/ibatis/submitted/select_with_like/SelectWithLikeTest.java new file mode 100644 index 00000000000..50b8aa43a45 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/select_with_like/SelectWithLikeTest.java @@ -0,0 +1,77 @@ +package org.apache.ibatis.submitted.select_with_like; + + +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; +import org.apache.ibatis.session.Configuration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Proxy; +import java.sql.PreparedStatement; + +public class SelectWithLikeTest { + + + private static Configuration configuration; + + @BeforeAll + static void setUp() { + configuration = new Configuration(); + configuration.addMapper(Mapper.class); + } + + + @Test + void testSelectWithLikeLeft() { + Object parameterObject = "jack"; + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithLikeRight"); + PreparedStatement preparedStatement = (PreparedStatement) Proxy.newProxyInstance( + SelectWithLikeTest.class.getClassLoader(), new Class[] { PreparedStatement.class }, (proxy, method, args) -> { + if (method.getName().equals("setString")) { + Assertions.assertEquals(args[1], parameterObject + "%"); + } + return null; + }); + + BoundSql boundSql = mappedStatement.getBoundSql(parameterObject); + DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); + parameterHandler.setParameters(preparedStatement); + } + + @Test + void testSelectWithNoLike() { + Object parameterObject = "jack"; + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithNoLike"); + PreparedStatement preparedStatement = (PreparedStatement) Proxy.newProxyInstance( + SelectWithLikeTest.class.getClassLoader(), new Class[] { PreparedStatement.class }, (proxy, method, args) -> { + if (method.getName().equals("setString")) { + Assertions.assertEquals(args[1], parameterObject); + } + return null; + }); + + BoundSql boundSql = mappedStatement.getBoundSql(parameterObject); + DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); + parameterHandler.setParameters(preparedStatement); + } + + @Test + void testSelectWithOodLike() { + Object parameterObject = "jack"; + MappedStatement mappedStatement = configuration.getMappedStatement("selectWithOodLike"); + PreparedStatement preparedStatement = (PreparedStatement) Proxy.newProxyInstance( + SelectWithLikeTest.class.getClassLoader(), new Class[] { PreparedStatement.class }, (proxy, method, args) -> { + if (method.getName().equals("setString")) { + Assertions.assertEquals(args[1], parameterObject); + } + return null; + }); + + BoundSql boundSql = mappedStatement.getBoundSql(parameterObject); + DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); + parameterHandler.setParameters(preparedStatement); + } +}