参数描述
- 你之前见到的所有语句中,使用的都是简单参数。实际上参数是 MyBatis 非常强大的元素。对于简单的使用场景,大约 90% 的情况下你都不需要使用复杂的参数,比如:
<select id="findById" resultType="blog"> select * from blog where id = #{id} </select>
- 上面的实例说明了一个非常简单的参数映射,参数类型被设置为 long , 这样这个参数就可以被设置为任何类型。原始类型或简单数据类型(比如: Long 和 String)因为没有相关属性,它会完全用参数值来代替,然而如果传入一个复杂的独享,行为就有点不同了
<insert id="insert" parameterType="blog" useGeneratedKeys="true" keyProperty="id"> insert into blog (`id`, `name`, `title`, `content`) values (#{id}, #{name}, #{title}, #{content}) </insert>
- 如果 Blog 类型的参数中传递到语句中, id, name, title, content 属性将被查找,然后将他们传入预处理的参数中
- 对于向语句中传递的参数来说,这个真是既简单又有效,不过参数映射的功能远不止于此。
- 首先, MyBatis 的其他部分一样,参数也可以指定一个特殊的数据类型,如下:
#{property, javaType=int, jdbcType=NUMERIC}
- 向MyBatis 的其他部分一样, javaType 几乎总是可以更具参数对象的类型确定下来, 除非该对象是一个 HashMap. 这个时候,你需要显式的指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。
- JDBC 要求, 如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 jdbcType 阅读 PreparedStatement.setNull()的 JavaDoc 文档来获取更多信息。
- 要更进一步地自定义类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
- 字符串替换,默认情况下 #{} 格式的语法会导致 MyBatis 创建 PreparedStatement 参数占位符并安全地设置参数(就像使用 ? 一样)。这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在SQL 语句中插入一个不转义的字符串比如:
像 ORDER BY , 你可以这样来使用
ORDER BY ${columName}
- 这里 MyBatis 不会修改或转义字符串
- 当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。 举个例子,如果你想通过任何一列从表中 select 数据时,不需要像下面这样写:
@Select("select * from blog where id = #{id}") List<Blog> findById(@Param("id") long id); @Select("select * from blog where name = #{name}") List<Blog> findByName(@Param("name") String name); @Select("select * from blog where title = #{title}") List<Blog> findByTitle(@Param("title") String title); // and more "findByXxx" method
- 可以只写成这样的一个方法
@Select("select * from blog where ${column} = #{value}") List<Blog> findByColumn(@Param("column") String column, @Param("value") String value);
- 其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理。因此你可以像下面这样来达到上述功能。
List<Blog> blogOfId = blogMapper.findByColumn("id", 1L); List<Blog> blogOfName = blogMapper.findByColumn("name", "name"); List<Blog> blogOfEmail = blogMapper.findByColumn("title", "title");
- 这种方法也同样适合于用来替换表
- 注意: 这种方式接受用户的输入,并将其用于语句中的参数是不安全的,会导致潜在的SQL注入工具,因此要么不允许用户输入这些字段,要么自行转义并检验
结果集映射
- resultMap 元素是 MyBatis 中最强大的的元素,他可以让你从 90% 的JDBC ResultSets 数据库提取代码中解放出来,并在一些情况下允许你进行一些 JDBC 不支持的操作。实际上,在位一些比如链接的复杂语句编写映射代码的时候,一份 resultMap 能够替代实际通能功能的长待数千行的代码。ResultMap 的设计事项是,对于简单的语句根本不需要结果映射, 而对于复杂一点的语句只需要描述他们之间的关系就可以了。
- 显示指定 resultType 示例 :(使用别名)
<!-- mybatis-config.xml 中 --> <typeAlias type=" cn.edu.cqvie.dto.Blog" alias="blog"/> <!-- SQL 映射文件中 --> <select id="findAll" resultType="blog"> select id, name, content from blog </select>
- 这样的一个 JavaBean 可以被映射到 ResultSet。这种情况是基于数据库字段和 JavaBean字段能够精准匹配的情况,如果无法匹配的话
就会导致结果集映射失败,也可以通过修改查询SQL 字段类的别名来实现。
- ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下面这些繁琐的配置。 但出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。
<resultMap type="blog" id="resultMap"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="title" property="title"/> <result column="content" property="content"/> </resultMap>
- 而在引用它的语句中使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
<select id="select" resultMap="resultMap"> select id, name, content from blog </select>
高级结果映射
- MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们不总都是这样。 如果能有一种完美的数据库映射模式,所有应用程序都可以使用它,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
- 一个非常复杂的SQL
<!-- 非常复杂的语句 --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>
- 你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 不用紧张,我们会一步一步来说明。虽然它看起来令人望而生畏,但其实非常简单。
<!-- 非常复杂的结果映射 --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>
- resultMap 元素有很多子元素和一个值得深入探讨的结构。 下面是resultMap 元素的概念视图。
结果集映射(resultMap)
- constructor - 用于在实例化类时,注入结果到构造方法中
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
- 嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个
- collection – 一个复杂类型的集合
- 嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个
- dcriminator – 使用结果值来决定使用哪个 resultMap
- case – 基于某些值的结果映射
- 嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个