MyBatis,Select 提供程序和 SQLBuilder
MyBatis, Select Provider and SQLBuilder
这不是一个简单的问题,我的英语不是我想要的那么好...我会尽力的。
我使用 java 8,在 Postgres 9.6 上使用 Mybatis 3.4.6,我需要做一个自定义动态查询。
在我的 mapper.java class 中,我创建了一个与 myBatis SQL Builder class
一起使用的方法
@SelectProvider(type = PreIngestManager.class, method = "selectPreIngestsSQLBuilder")
@Results({ @Result(property = "id", column = "id"), @Result(property = "inputPath", column = "input_path"),
@Result(property = "idCategoriaDocumentale", column = "id_categoria_documentale"), @Result(property = "idCliente", column = "id_cliente"),
@Result(property = "outputSipPath", column = "output_sip_path"), @Result(property = "esito", column = "esito"),
@Result(property = "stato", column = "stato"), @Result(property = "pathRdp", column = "path_rdp"),
@Result(property = "dataInizio", column = "data_inizio"), @Result(property = "dataFine", column = "data_fine") })
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto,
@Param("dataInizioInferiore") Date dataInizioInferiore, @Param("dataInizioSuperiore") Date dataInizioSuperiore,
@Param("statiPreIngest") String statiPreIngest);
我已经指定了 @SelectProvider 注释,class 和要指向的方法,在示例中是 PreIngestManager.class 和 selectPreIngestsSQLBuilder 方法。
这是方法
public String selectPreIngestsSQLBuilder(Map<String, Object> params) {
return new SQL() {
{
SELECT("*");
FROM("pre_ingest");
WHERE("id_categoria_documentale = #{idCatDoc}");
if (params.get("nomePacchetto") != null)
WHERE("input_path like '%' || #{nomePacchetto}");
if (params.get("dataInizioInferiore") != null) {
if (params.get("dataInizioSuperiore") != null) {
WHERE("data_inizio between #{dataInizioInferiore} and #{dataInizioSuperiore}");
} else {
WHERE("data_inizio >= #{dataInizioInferiore}");
}
} else {
if (params.get("dataInizioSuperiore") != null) {
WHERE("data_inizio <= #{dataInizioSuperiore}");
}
}
if (params.get("statiPreIngest") != null)
WHERE("stato in (#{statiPreIngest})");
ORDER_BY("id ASC");
}
}.toString();
}
这些是我的问题:
我必须指定 @Results 注释和每个 @Result 吗,或者我可以使用 java 模型 class 吗?我试过 @ResultMap(value = { "mycompany.model.PreIngest" }) 但它没有用。
最重要的是,如文档所述,使用 SQL 构建器,您可以访问将它们作为最终对象的方法参数
// With conditionals (note the final parameters, required for the anonymous inner class to access them)
public String selectPersonLike(final String id, final String firstName,
final String lastName) {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
FROM("PERSON P");
if (id != null) {
WHERE("P.ID like #{id}");
}
if (firstName != null) {
WHERE("P.FIRST_NAME like #{firstName}");
}
if (lastName != null) {
WHERE("P.LAST_NAME like #{lastName}");
}
ORDER_BY("P.LAST_NAME");
}}.toString();
}
但是如果我将那些 final 放在我的方法中,我将无法访问它们。我需要从方法声明中删除@Param 吗? SQLBuilder 需要在没有 @SelectProvider 的情况下调用吗?我在混合溶液吗?
据我研究,目前我看到了 3 种方法来执行动态查询或自定义 where 条件。
- 使用 MyBatisGenerator 库并结合 where 条件作为搜索条件与 SelectByExample 方法一起使用。 (我在查询简单的时候就用这个)
- 直接编写 SQL 查询,使用 if、choose、语句等修改 XML 映射器文件 as descripted here
- 使用带@SelectProvider 注解的SQL Builder class。
你知道什么时候更喜欢 2° 方法而不是 3° 方法吗?为什么在 3° 方法文档中我找不到如何使用它?写了如何创建自定义查询,但没有写如何启动它们。
非常感谢您的宝贵时间和建议。
我不知道如何使用 sql 生成器执行此操作,但我确实知道如何使用 xml 映射器文件执行此操作:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="path.to.class.PreIngestMapper">
<resultMap id="preIngestManager" type="path.to.class.PreIngestManager">
<id property="id" column="id" />
<result property="id" column="id" />
<result property="inputPath" column="input_path" />
<result property="idCategoriaDocumentale" column="id_categoria_documentale" />
...
</resultMap>
<select id="selectPreIngests" parameterType="Map" resultMap="preIngestManager">
SELECT *
FROM pre_ingest
WHERE id_categoria_documentale = #{idCatDoc}
<if test = "nomePacchetto != null">
and input_path like '%' || #{nomePacchetto}
</if>
...
;
</select>
</mapper>
不知道你是否已经找到答案,我只是想分享一下我的经验。顺便说一句,如果我的英语不好,请原谅我。
注意:我使用的是MyBatis 3.4.6和Spring框架。
have I to specify @Results annotation and every @Result , or can I use a java model class ?
实际上你可以做任何一个。
如果你想使用@Results和@ResultMap,你只需要在一个mapper文件中指定一次@Results注解。诀窍是您需要为要在其他函数中使用的结果指定 id。
使用您的 classes 的截断版本,例如:
@Results(id="myResult", value= {
@Result(property = "id", column = "id"),
@Result(property = "inputPath", column = "input_path"),
@Result(property = "idCategoriaDocumentale", ... })
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto, ...);
然后在另一个函数中,您可以使用@ResultMap,其值引用前面提到的@Results 中的id。
@ResultMap("myResult")
List<PreIngest> selectPreIngestsBySomethingElse(....);
..., or can I use a java model class ?
您可以使用 java 模型 class 作为结果而不使用 @Results 和 @ResultMap,但是您必须确保您的 java 模型 class 具有相同的结果properties/fields 作为您查询的结果。数据库表通常具有 snake_case 的字段。由于 java 使用的是驼峰式命名法,因此您必须将设置添加到您的 mybatis-config.xml 文件中。
这是我平时在mybatis中添加的-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- changes from the defaults -->
<setting name="lazyLoadingEnabled" value="false" />
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
重要的是 mapUnderscoreToCamelCase,将它设置为 true 之后你就可以使用你的 java 模型 class 而无需 @Results 和 @ResultMap 的麻烦.您可以在 MyBatis 3 Configuration.
中找到所有设置说明
这是使用您的 classes 的示例,
class:
public class PreIngest {
private Long idCategoriaDocumentale;
private Long idCliente;
........ other fields
........ setter, getter, etc
}
映射器文件:
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto, ...);
现在开始使用 SqlBuilder。
But if I put those final in my method I can't access them. Do I need to delete the @Param from the method declaration? Do SQLBuilder need to be called without @SelectProvider ?
我无法回答您方法中的那些 final,因为我从未使用 final 参数创建 SqlBuilder class。
对于 SqlBuilder,您必须使用 @SelectProvider、@InsertProvider、@UpdateProvider 或 @DeleteProvider,这取决于您使用的查询。
根据我使用 SQLBuilder 的经验,如果您需要多个参数并使用 Map 参数从 SqlBuilder 访问它,@Param 是必需的 class。如果你不想在映射文件中使用@Param,那么你需要确保所述映射函数中只有一个参数。如果只指定一个参数,则可以使用 java 模型 class 作为参数。
如果使用你的 class 例如,你可以有一个 class
public class PersonFilter {
private Long id;
private String firstName;
private String lastName;
...... setter, getter, etc
}
映射函数
@SelectProvider(type=PersonSqlBuilder.class, method="selectPersonLike")
List<Person> selectPersonLike(PersonFilter filter);
SqlBuilder class
public class PersonSqlBuilder {
public String selectPersonLike(PersonFilter filter) {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
FROM("PERSON P");
if (filter.getId() != null) {
WHERE("P.ID like #{id}");
}
if (filter.getFirstName() != null) {
WHERE("P.FIRST_NAME like #{firstName}");
}
if (filter.getLastName() != null) {
WHERE("P.LAST_NAME like #{lastName}");
}
ORDER_BY("P.LAST_NAME");
}}.toString();
}
}
就是这样。希望我的经验能有所帮助。
这不是一个简单的问题,我的英语不是我想要的那么好...我会尽力的。
我使用 java 8,在 Postgres 9.6 上使用 Mybatis 3.4.6,我需要做一个自定义动态查询。
在我的 mapper.java class 中,我创建了一个与 myBatis SQL Builder class
一起使用的方法@SelectProvider(type = PreIngestManager.class, method = "selectPreIngestsSQLBuilder")
@Results({ @Result(property = "id", column = "id"), @Result(property = "inputPath", column = "input_path"),
@Result(property = "idCategoriaDocumentale", column = "id_categoria_documentale"), @Result(property = "idCliente", column = "id_cliente"),
@Result(property = "outputSipPath", column = "output_sip_path"), @Result(property = "esito", column = "esito"),
@Result(property = "stato", column = "stato"), @Result(property = "pathRdp", column = "path_rdp"),
@Result(property = "dataInizio", column = "data_inizio"), @Result(property = "dataFine", column = "data_fine") })
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto,
@Param("dataInizioInferiore") Date dataInizioInferiore, @Param("dataInizioSuperiore") Date dataInizioSuperiore,
@Param("statiPreIngest") String statiPreIngest);
我已经指定了 @SelectProvider 注释,class 和要指向的方法,在示例中是 PreIngestManager.class 和 selectPreIngestsSQLBuilder 方法。
这是方法
public String selectPreIngestsSQLBuilder(Map<String, Object> params) {
return new SQL() {
{
SELECT("*");
FROM("pre_ingest");
WHERE("id_categoria_documentale = #{idCatDoc}");
if (params.get("nomePacchetto") != null)
WHERE("input_path like '%' || #{nomePacchetto}");
if (params.get("dataInizioInferiore") != null) {
if (params.get("dataInizioSuperiore") != null) {
WHERE("data_inizio between #{dataInizioInferiore} and #{dataInizioSuperiore}");
} else {
WHERE("data_inizio >= #{dataInizioInferiore}");
}
} else {
if (params.get("dataInizioSuperiore") != null) {
WHERE("data_inizio <= #{dataInizioSuperiore}");
}
}
if (params.get("statiPreIngest") != null)
WHERE("stato in (#{statiPreIngest})");
ORDER_BY("id ASC");
}
}.toString();
}
这些是我的问题:
我必须指定 @Results 注释和每个 @Result 吗,或者我可以使用 java 模型 class 吗?我试过 @ResultMap(value = { "mycompany.model.PreIngest" }) 但它没有用。
最重要的是,如文档所述,使用 SQL 构建器,您可以访问将它们作为最终对象的方法参数
// With conditionals (note the final parameters, required for the anonymous inner class to access them)
public String selectPersonLike(final String id, final String firstName,
final String lastName) {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
FROM("PERSON P");
if (id != null) {
WHERE("P.ID like #{id}");
}
if (firstName != null) {
WHERE("P.FIRST_NAME like #{firstName}");
}
if (lastName != null) {
WHERE("P.LAST_NAME like #{lastName}");
}
ORDER_BY("P.LAST_NAME");
}}.toString();
}
但是如果我将那些 final 放在我的方法中,我将无法访问它们。我需要从方法声明中删除@Param 吗? SQLBuilder 需要在没有 @SelectProvider 的情况下调用吗?我在混合溶液吗?
据我研究,目前我看到了 3 种方法来执行动态查询或自定义 where 条件。
- 使用 MyBatisGenerator 库并结合 where 条件作为搜索条件与 SelectByExample 方法一起使用。 (我在查询简单的时候就用这个)
- 直接编写 SQL 查询,使用 if、choose、语句等修改 XML 映射器文件 as descripted here
- 使用带@SelectProvider 注解的SQL Builder class。
你知道什么时候更喜欢 2° 方法而不是 3° 方法吗?为什么在 3° 方法文档中我找不到如何使用它?写了如何创建自定义查询,但没有写如何启动它们。
非常感谢您的宝贵时间和建议。
我不知道如何使用 sql 生成器执行此操作,但我确实知道如何使用 xml 映射器文件执行此操作:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="path.to.class.PreIngestMapper">
<resultMap id="preIngestManager" type="path.to.class.PreIngestManager">
<id property="id" column="id" />
<result property="id" column="id" />
<result property="inputPath" column="input_path" />
<result property="idCategoriaDocumentale" column="id_categoria_documentale" />
...
</resultMap>
<select id="selectPreIngests" parameterType="Map" resultMap="preIngestManager">
SELECT *
FROM pre_ingest
WHERE id_categoria_documentale = #{idCatDoc}
<if test = "nomePacchetto != null">
and input_path like '%' || #{nomePacchetto}
</if>
...
;
</select>
</mapper>
不知道你是否已经找到答案,我只是想分享一下我的经验。顺便说一句,如果我的英语不好,请原谅我。
注意:我使用的是MyBatis 3.4.6和Spring框架。
have I to specify @Results annotation and every @Result , or can I use a java model class ?
实际上你可以做任何一个。
如果你想使用@Results和@ResultMap,你只需要在一个mapper文件中指定一次@Results注解。诀窍是您需要为要在其他函数中使用的结果指定 id。
使用您的 classes 的截断版本,例如:
@Results(id="myResult", value= {
@Result(property = "id", column = "id"),
@Result(property = "inputPath", column = "input_path"),
@Result(property = "idCategoriaDocumentale", ... })
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto, ...);
然后在另一个函数中,您可以使用@ResultMap,其值引用前面提到的@Results 中的id。
@ResultMap("myResult")
List<PreIngest> selectPreIngestsBySomethingElse(....);
..., or can I use a java model class ?
您可以使用 java 模型 class 作为结果而不使用 @Results 和 @ResultMap,但是您必须确保您的 java 模型 class 具有相同的结果properties/fields 作为您查询的结果。数据库表通常具有 snake_case 的字段。由于 java 使用的是驼峰式命名法,因此您必须将设置添加到您的 mybatis-config.xml 文件中。
这是我平时在mybatis中添加的-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- changes from the defaults -->
<setting name="lazyLoadingEnabled" value="false" />
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
重要的是 mapUnderscoreToCamelCase,将它设置为 true 之后你就可以使用你的 java 模型 class 而无需 @Results 和 @ResultMap 的麻烦.您可以在 MyBatis 3 Configuration.
中找到所有设置说明这是使用您的 classes 的示例,
class:
public class PreIngest {
private Long idCategoriaDocumentale;
private Long idCliente;
........ other fields
........ setter, getter, etc
}
映射器文件:
List<PreIngest> selectPreIngestsByFilters(@Param("idCatDoc") Long idCatDoc, @Param("nomePacchetto") String nomePacchetto, ...);
现在开始使用 SqlBuilder。
But if I put those final in my method I can't access them. Do I need to delete the @Param from the method declaration? Do SQLBuilder need to be called without @SelectProvider ?
我无法回答您方法中的那些 final,因为我从未使用 final 参数创建 SqlBuilder class。
对于 SqlBuilder,您必须使用 @SelectProvider、@InsertProvider、@UpdateProvider 或 @DeleteProvider,这取决于您使用的查询。
根据我使用 SQLBuilder 的经验,如果您需要多个参数并使用 Map 参数从 SqlBuilder 访问它,@Param 是必需的 class。如果你不想在映射文件中使用@Param,那么你需要确保所述映射函数中只有一个参数。如果只指定一个参数,则可以使用 java 模型 class 作为参数。
如果使用你的 class 例如,你可以有一个 class
public class PersonFilter {
private Long id;
private String firstName;
private String lastName;
...... setter, getter, etc
}
映射函数
@SelectProvider(type=PersonSqlBuilder.class, method="selectPersonLike")
List<Person> selectPersonLike(PersonFilter filter);
SqlBuilder class
public class PersonSqlBuilder {
public String selectPersonLike(PersonFilter filter) {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
FROM("PERSON P");
if (filter.getId() != null) {
WHERE("P.ID like #{id}");
}
if (filter.getFirstName() != null) {
WHERE("P.FIRST_NAME like #{firstName}");
}
if (filter.getLastName() != null) {
WHERE("P.LAST_NAME like #{lastName}");
}
ORDER_BY("P.LAST_NAME");
}}.toString();
}
}
就是这样。希望我的经验能有所帮助。