2012-11-02 53 views
11

我有一些复杂的查询来构建一些可选的过滤器,MyBatis似乎是生成动态SQL的理想选择。我可以使用MyBatis生成动态SQL而不执行它吗?

但是,我仍然希望我的查询在与应用程序的其余部分(不使用MyBatis)相同的框架中执行。

所以我希望做的是严格使用MyBatis来生成SQL,但从那里使用我的应用程序的其余部分来实际执行它。这可能吗?如果是这样,怎么样?

回答

11

尽管MyBatis被设计为在构建它之后执行查询,但您可以利用它的配置和一点点“内部知识”来获取所需内容。

MyBatis是一个非常好的框架,不幸的是它缺乏文档方面的知识,所以源代码是你的朋友。如果你在周围挖掘,你应该碰到这些类:org.apache.ibatis.mapping.MappedStatementorg.apache.ibatis.mapping.BoundSql,它们是构建动态SQL的关键角色。这是一个基本的使用示例:

MySQL表user与此数据是:

name login 
----- ----- 
Andy a 
Barry b 
Cris c 

User类:

package pack.test; 
public class User { 
    private String name; 
    private String login; 
    // getters and setters ommited 
} 

UserService接口:

package pack.test; 
public interface UserService { 
    // using a different sort of parameter to show some dynamic SQL 
    public User getUser(int loginNumber); 
} 

UserService.xml映射文件:

<mapper namespace="pack.test.UserService"> 
    <select id="getUser" resultType="pack.test.User" parameterType="int"> 
     <!-- dynamic change of parameter from int index to login string --> 
     select * from user where login = <choose> 
              <when test="_parameter == 1">'a'</when> 
              <when test="_parameter == 2">'b'</when> 
              <otherwise>'c'</otherwise> 
             </choose> 
    </select> 
</mapper> 

sqlmap-config.file

<configuration> 
    <settings> 
     <setting name="lazyLoadingEnabled" value="false" /> 
    </settings> 
    <environments default="development"> 
     <environment id="development"> 
      <transactionManager type="JDBC"/> 
      <dataSource type="POOLED"> 
       <property name="driver" value="com.mysql.jdbc.Driver"/> 
       <property name="url" value="jdbc:mysql://localhost/test"/> 
       <property name="username" value="..."/> 
       <property name="password" value="..."/> 
      </dataSource> 
     </environment> 
     </environments> 
    <mappers> 
     <mapper resource="pack/test/UserService.xml"/> 
    </mappers> 
</configuration> 

AppTester显示结果:

package pack.test; 

import java.io.Reader; 
import org.apache.ibatis.io.Resources; 
import org.apache.ibatis.mapping.BoundSql; 
import org.apache.ibatis.mapping.MappedStatement; 
import org.apache.ibatis.session.SqlSession; 
import org.apache.ibatis.session.SqlSessionFactoryBuilder; 

public class AppTester { 
    private static String CONFIGURATION_FILE = "sqlmap-config.xml"; 

    public static void main(String[] args) throws Exception { 
     Reader reader = null; 
     SqlSession session = null; 
     try { 

      reader = Resources.getResourceAsReader(CONFIGURATION_FILE); 
      session = new SqlSessionFactoryBuilder().build(reader).openSession(); 
      UserService userService = session.getMapper(UserService.class); 

      // three users retreived from index 
      for (int i = 1; i <= 3; i++) { 
       User user = userService.getUser(i); 
       System.out.println("Retreived user: " + user.getName() + " " + user.getLogin()); 

       // must mimic the internal statement key for the mapper and method you are calling 
       MappedStatement ms = session.getConfiguration().getMappedStatement(UserService.class.getName() + ".getUser"); 
       BoundSql boundSql = ms.getBoundSql(i); // parameter for the SQL statement 
       System.out.println("SQL used: " + boundSql.getSql()); 
       System.out.println(); 
      } 

     } finally { 
      if (reader != null) { 
       reader.close(); 
      } 
      if (session != null) { 
       session.close(); 
      } 
     } 
    } 
} 

而结果:

Retreived user: Andy a 
SQL used: select * from user where login = 'a' 

Retreived user: Barry b 
SQL used: select * from user where login = 'b' 

Retreived user: Cris c 
SQL used: select * from user where login = 'c' 
+0

对我来说,它显示'?'而不是实际值。例如'在哪里登录=?'。任何解决这个问题?谢谢 (我没有使用mapper类) – agpt

0

只需添加到波格丹的正确答案:您需要传递一个JavaBean到getBoundSql()与getter的你的接口参数,如果你的接口有一个更复杂的签名。

假设您想根据登录号码和/或用户名查询用户。您的界面可能是这样的:

package pack.test; 
public interface UserService { 
    // using a different sort of parameter to show some dynamic SQL 
    public User getUser(@Param("number") int loginNumber, @Param("name") String name); 
} 

我要离开了映射代码,因为它是不相关的讨论,但你在AppTester代码应该变成:

[...] 
final String name = "Andy"; 
User user = userService.getUser(i, name); 
System.out.println("Retreived user: " + user.getName() + " " + user.getLogin()); 

// must mimic the internal statement key for the mapper and method you are calling 
MappedStatement ms = session.getConfiguration().getMappedStatement(UserService.class.getName() + ".getUser"); 
BoundSql boundSql = ms.getBoundSql(new Object() { 
    // provide getters matching the @Param's in the interface declaration 
    public Object getNumber() { 
    return i; 
    } 
    public Object getName() { 
    return name; 
    } 

}); 
System.out.println("SQL used: " + boundSql.getSql()); 
System.out.println(); 
[...] 
1

每个人都知道如何使用BoundSql 。getSql()来获取从MyBatis的一个paramaterized查询字符串,像这样:

// get parameterized query 
MappedStatement ms = configuration.getMappedStatement("MyMappedStatementId"); 
BoundSql boundSql = ms.getBoundSql(parameters); 
System.out.println("SQL" + boundSql.getSql()); 
// SELECT species FROM animal WHERE name IN (?, ?) or id = ? 

但是现在你需要等式的另一半,对应于问号值列表:

// get parameters 
List<ParameterMapping> boundParams = boundSql.getParameterMappings(); 
String paramString = ""; 
for(ParameterMapping param : boundParams) { 
    paramString += boundSql.getAdditionalParameter(param.getProperty()) + ";"; 
} 
System.out.println("params:" + paramString); 
// "Spot;Fluffy;42;" 

现在,您可以序列化它以发送到其他地方运行,或者您可以将它打印到日志中,以便将它们缝合在一起并手动运行查询。

*代码没有经过测试,可能是次要问题或类似的问题

相关问题