2012-11-02 58 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;" 

現在,您可以序列化它以發送到其他地方運行,或者您可以將它打印到日誌中,以便將它們縫合在一起並手動運行查詢。

*代碼沒有經過測試,可能是次要問題或類似的問題

相關問題