MyBaits 中的 Mapper.xml 文件详解(中)

简介: MyBatis 的真真强大在于他的映射语句,这是它的魔力所在。由于它的异常强大,映射器的XML文件就显得相对简单。如果拿它跟具有相同功能的JDBC

参数描述


  • 你之前见到的所有语句中,使用的都是简单参数。实际上参数是 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 元素,因此可以具有相同的结构和元素,或者从别处引用一个


相关文章
|
5天前
|
XML Java 数据格式
java创建xml文件内容
java创建xml文件内容
10 0
|
5天前
|
XML Java 数据格式
java解析xml文件内容
java解析xml文件内容
14 0
|
3天前
|
XML 数据格式
XML配置Servlet文件,不使用注解配置路径的方法
XML配置Servlet文件,不使用注解配置路径的方法
|
6天前
|
SQL XML 数据库
后端数据库开发高级之通过在xml文件中映射实现动态SQL
后端数据库开发高级之通过在xml文件中映射实现动态SQL
14 3
|
6天前
|
SQL XML Java
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
24 3
|
15天前
|
SQL JSON 数据处理
实时计算 Flink版产品使用问题之把hdfs集群里的core-site.xml hdfs.xml两个文件放到flink/conf/目录下,启动集群说找不到hdfs,该如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1天前
|
XML Java 数据库
配置applicationContext.xml文件
配置applicationContext.xml文件
|
1天前
|
XML JavaScript Java
解析XML文件的几种方法
解析XML文件的几种方法
|
5天前
|
XML Java 数据格式
java删除xml文件内容
java删除xml文件内容
6 0
|
6天前
|
XML 存储 JavaScript
python读取xml文件详细讲解
python读取xml文件详细讲解
17 0