我正在学习如何使用MyBatis。老实说,我非常喜欢这个框架。它很容易使用,我很高兴,因为我可以使用我的sql命令:)我使用MyBatis 3.4.2和PostgreSQL数据库。
例如,我喜欢在插入@SelectKey
注释之前执行查询的难易程度。如果我在界面方法之前添加一些注释,数据映射就像魅力一样,如下所示:@Results({ @Result(property = "javaField", column = "database_field", javaType = TypeHandler.class)
。
我不喜欢(我希望你能把我放在正确的方向)如下:
(问题1)我有查询允许我使用null和normal值而不使用任何额外的“if”java语句来检查变量是否包含null或not null值。它们看起来像这样:
SELECT * FROM table
WHERE key_name = ? AND ((? IS NULL AND user_id IS NULL) OR User_id = ?)
使用JDBC我需要执行以下操作:
stmt = connection.prepareStatement(query);
stmt.setString(1, "key");
stmt.setString(2, userId);
stmt.setString(3, userId);
正如您所看到的,我需要传递两次 userId ,因为这是JDBC的工作方式。老实说,我的期望是下面的代码将与MyBatis一起使用,但不幸的是它不起作用。第三个参数仍然需要定义。
我想知道是否可以将此功能添加到MyBatis。如果MyBatis可以自动绑定 userId 两次,那应该没问题,如下所示:
@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId})
SomeClass findByKeyAndUserId(String key, Long userId);
我实际做了哪些解决方法如下。我讨厌它,因为它很棘手,需要额外的java“if”语句:
@Select("SELECT * FROM table WHERE key_name = #{key} AND COALESCE(user_id, -1) = #{userId}")
SomeClass findByKeyAndUserId(String key, Long userId);
userId = (userId == null) ? -1 : userId;
SomeClass abc = mapper.findByKeyAndUserId(key, userId);
我不知道使用MyBatis管理这种情况的最佳做法是什么。请指导我。
(问题2) @Select
时的映射。在映射具有相同结果类型的查询结果时,有没有办法避免重复代码?
第一次查询:
@Select("SELECT * FROM table WHERE ...")
@Results({
@Result(property = "key", column = "key_name", javaType = String.class),
@Result(property = "value", column = "key_value", javaType = String.class),
@Result(property = "userId", column = "user_id", javaType = Long.class),
@Result(property = "interval", column = "interval", javaType = Long.class),
@Result(property = "description", column = "description", javaType = String.class),
@Result(property = "status", column = "status", typeHandler = StatusTypeHandler.class)
})
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
第二次查询:
@Select("SELECT * FROM table WHERE <different conditions then before>")
@Results({
<I need to add here the exact same code then before in query 1>
})
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
我可以以某种方式重用与映射相关的代码吗?我需要添加映射,因为我对status字段使用特殊类型处理程序。我使用基于注释的配置。
(问题3) @Param
注释
关于@Param
注释的文档中我看不到任何内容。这很难弄清楚为什么我的java参数没有正确限制。最后我意识到我的代码中缺少@Param
注释。为什么在官方文档中没有提到这个?我做错了什么,@Param
没必要使用?
该代码工作正常:
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
这不起作用:
SomeClass findByKeyAndUserId(String key, Long userId);
更新(问题1)
我的第一个想法与提到的 @blackwizard 类似:“Mybatis按名称绑定参数,然后一次,两次,N次,因为它可以引用它,它可以工作。 “
但这实际上不能正常工作。如果userId不为null,则它可以工作。如果它为null,我会从数据库中返回一个很好的异常。我想MyBatis以错误的方式绑定null值。也许这是一个错误。我不知道:(
例外:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2
### The error may exist in com/.../dao/TableDao.java (best guess)
### The error may involve ....dao.Table.findByKeyAndUserId-Inline
### The error occurred while setting parameters
### SQL: SELECT * FROM table WHERE key_name = ? AND (? IS NULL AND user_id IS NULL) OR user_id = ? AND status = 1
### Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2
最后,我找到了三种不同的解决方案。
(问题1:解决方案1)
我按照 @blackwizard 提到的,我能够将条件userId = (userId == null) ? -1 : userId
从java移到MyBatis级别。而且还不错!正确的语法是:<if test='userId==null'><bind name='userId' value='-1'/></if>
。
(问题1:解决方案2)
我从postgres返回could not determine data type of parameter $2
错误的原因是因为在null值的情况下,JDBC驱动程序无法确定参数的类型。所以让我们手动定义它。
@Select("SELECT * FROM table "
+ "WHERE key_name = #{key} AND ((#{userId}::BIGINT IS NULL AND user_id IS NULL) OR user_id = #{userId})")
})
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
(问题1:解决方案3) 第二个解决方案依赖于PorstgreSQL。以下解决方案完全独立于数据库。感谢@blackwizard的好评。
@Select("SELECT * FROM table "
+ "WHERE key_name = #{key} AND ((#{userId, jdbcType=BIGINT} IS NULL AND user_id IS NULL) OR user_id = #{userId})")
})
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
我个人更喜欢解决方案3.它包含的附加代码更少。
答案 0 :(得分:4)
问题1:
命名参数:
@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId}")
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
Mybatis确实按名称绑定参数,然后一次,两次,N次绑定参数,因为它可以被引用,它可以工作。
你可以在Mybatis中使用 if :使用XML标签,虽然它并不是真的更好......不管怎样,你可以将某一天用于其他目的。要在注释值中使用XML标记,该值必须完全嵌入到字符串开头和结尾的<script>
标记中。
@Select({"<script>",
"<if 'userId==null'><bind name='userId' value='1'/></if>",
"SELECT * FROM table WHERE key_name = #{key} ",
"AND COALESCE(user_id, -1) = #{userId}",
"</script>"})
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId);
您也可以使用typeHandler设置默认值,只需将其与参数一起使用:#{userId, typeHandler=CustomDefaultValueTypeHandler}
编辑:回复其他问题:如果您想允许传递空值而不是默认值处理替换,那么您必须给Mybatis一些关于所谓绑定参数类型的提示,因为它无法解析实际的null类型,因为它不知道/看到Mapper接口中的变量声明。所以:#{userId, javaType=int,jdbcType=NUMERIC}
。只有两个属性中的一个就足够了。
与MyBatis的其他部分一样,几乎总能确定 javaType 来自参数对象,除非该对象是 HashMap 。那么 应指定 javaType 以确保正确的 TypeHandler 使用
注意对于所有可为空的列,JDBC都需要JDBC类型,如果 null作为值传递。你可以自己调查一下 读取 PreparedStatement.setNull()方法的JavaDocs。
第2期:您绝对无法重复/共享您在注释中定义的内容。这不是因为Mybatis,而是注释。您必须在XML中定义结果映射,并使用 @ResultMap 引用它们。 Documentation州:
ResultMap Method N/A This annotation is used to provide the id of a <resultMap> element in an XML mapper to a @Select or @SelectProvider annotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Results or @ConstructorArgs annotation if both are specified on an annotated select.
第3期:因为我已经answered in your other question,@ Param注释可以将方便的参数列表转换为地图,例如:
Map<String, Object> params = new HashMap<String, Object>();
params.put("key", key);
params.put("userId", userId);
我同意Mybatis文档可能会更好,您会找到更多资源,例如here。
但是,Mybatis documentation说明了以下 @Param 注释
@Param Parameter N/A If your mapper method takes multiple parameters, this annotation can be applied to a mapper method parameter to give each of them a name. Otherwise, multiple parameters will be named by their position prefixed with "param" (not including any RowBounds parameters). For example #{param1}, #{param2} etc. is the default. With @Param("person"), the parameter would be named
#{person}.
答案 1 :(得分:0)
问题3:
请尝试从JDK 8开始提供的-parameters编译选项。 您可以省略@Param注释。
请参阅https://github.com/mybatis/mybatis-3/issues/549
感谢。