我有一些复杂的查询来构建一些可选的过滤器,MyBatis似乎是生成动态SQL的理想选择。我可以使用MyBatis生成动态SQL而不执行它吗?
但是,我仍然希望我的查询在与应用程序的其余部分(不使用MyBatis)相同的框架中执行。
所以我希望做的是严格使用MyBatis来生成SQL,但从那里使用我的应用程序的其余部分来实际执行它。这可能吗?如果是这样,怎么样?
我有一些复杂的查询来构建一些可选的过滤器,MyBatis似乎是生成动态SQL的理想选择。我可以使用MyBatis生成动态SQL而不执行它吗?
但是,我仍然希望我的查询在与应用程序的其余部分(不使用MyBatis)相同的框架中执行。
所以我希望做的是严格使用MyBatis来生成SQL,但从那里使用我的应用程序的其余部分来实际执行它。这可能吗?如果是这样,怎么样?
尽管MyBatis被设计为在构建它之后执行查询,但您可以利用它的配置和一点点“内部知识”来获取所需内容。
MyBatis是一个非常好的框架,不幸的是它缺乏文档方面的知识,所以源代码是你的朋友。如果你在周围挖掘,你应该碰到这些类:org.apache.ibatis.mapping.MappedStatement
和org.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'
只需添加到波格丹的正确答案:您需要传递一个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();
[...]
每个人都知道如何使用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;"
现在,您可以序列化它以发送到其他地方运行,或者您可以将它打印到日志中,以便将它们缝合在一起并手动运行查询。
*代码没有经过测试,可能是次要问题或类似的问题
对我来说,它显示'?'而不是实际值。例如'在哪里登录=?'。任何解决这个问题?谢谢 (我没有使用mapper类) – agpt