Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MyBatis: compare String value using dynamic query

I'm using MyBatis to map some queries where I need to compare a String argument (myString).

My Mapper interface is:

public Map<Integer, String> findSomething(@Param("myString") String myString);

My XML is as follow:

<select id="findSomething" parameterType="String" resultType="Map">
    SELECT column1 as key,
           column2 as value
    FROM my_table
    <where>
         <choose>
            <when test="myString == 'xxx'">
                column3 = 1
            </when>
            <when test="myString == 'yyy'">
                myColumn  = 2
            </when>
            <when test="myString == 'zzz'">
                myColumn  = 3
            </when>
        </choose>
    </where>
    ORDER BY value;
</select>

When I execute this statement the following error is throwed:

ERROR [stderr] Caused by: org.apache.ibatis.exceptions.PersistenceException: 
ERROR [stderr] ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'myString' in 'class java.lang.String'

A String comparison made this way, smells bad. Unfortunately it was the way database was modeled.

MyBatis version: 3.2.2

like image 576
axcdnt Avatar asked Oct 18 '13 12:10

axcdnt


People also ask

What is TypeHandler in MyBatis?

typeHandlers. Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type.

How do I use my like in MyBatis?

select * from patient where last_name like '%'|| #{lastName} ||'%'.

Does MyBatis use JDBC?

Background information. MyBatis is a persistence framework for Java that supports custom SQL statements, stored procedures, and advanced mappings. MyBatis eliminates the need to use JDBC code, manually configure parameters, and retrieve result sets.

How do I avoid SQL injection in MyBatis?

In general, you should consider using #{} for string substitution instead of ${} . This is because #{} causes Mybatis to create a preparedStatement which prevents SQLInjection compared to ${} which would inject unmodified string into SQL.


2 Answers

Tested with MyBatis 3.2.8

<select id="findSomething" parameterType="String" resultType="Map">
    SELECT column1 as key,
           column2 as value
    FROM my_table
    <where>
         <choose>
            <when test='"xxx".equals(myString)'>
                column3 = 1
            </when>
            <when test='"yyy".equals(myString)'>
                myColumn  = 2
            </when>
            <when test='"zzz".equals(myString)'>
                myColumn  = 3
            </when>
        </choose>
    </where>
    ORDER BY value;
</select>

Note the single quotes around test and the double quotes around de constant values. The trick is to use equals as you can see.

like image 164
Manuel Vera Silvestre Avatar answered Sep 21 '22 23:09

Manuel Vera Silvestre


Definitely I had to go through a complicated approach.

Changed mapper method signature:

public Map<Integer, String> findSomething(@Param("myPojo") MyPojo myPojo);

The XML must be:

<select id="findSomething" resultType="Map">
SELECT column1 as key,
       column2 as value
FROM my_table
<where>
     <choose>
        <when test="myPojo == 'xxx'">
            column3 = 1
        </when>
        <when test="myPojo == 'yyy'">
            myColumn  = 2
        </when>
        <when test="myPojo == 'zzz'">
            myColumn  = 3
        </when>
    </choose>
</where>
ORDER BY value;

Just don't forget, being a POJO, the parameter class must have it's respective getters and setters.

like image 37
axcdnt Avatar answered Sep 19 '22 23:09

axcdnt