mybatis其他基础用法

include标签

在我们使用查找的时候,经常会使用这样的语句:

select * from mytable;

但是这不好,会对效率产生影响,因为有的时候我们只是需要获取某些字段而已。

select id,age,name from mytable;

但是这样写很繁琐。

可以进行如下的修改:

<sql id="example"> id,age </sql>

<select id="selectBlog" resultMap="mymap">
     select
     <include refid="example"></include>
     from mytable
</select>

sql标签中定义了两个字段,而include标签则自动将这两个字段填充进去。

甚至还能这么改:

<sql id="example"> ${abc},age </sql>

<select id="selectBlog" resultMap="mymap">
    select
    <include refid="example">
        <property name="abc" value="id"/>
    </include>
    from mytable
</select>

property标签将sql中的abc自动修改成了id。

sql标签和include标签的合作不仅仅能够代替字段,更能代替mysql语句的任何一个地方。

<sql id="example"> from ${table} </sql>

<select id="selectBlog" resultMap="mymap">
    select *
    <include refid="example">
        <property name="table" value="mytable"/>
    </include>
</select>

这个例子中,include自动填充了from子句。

#{}和${}

#{}是参数占位符,mybatis会对其进行预编译。

#{}能有效防止sql注入问题。

${}是字符串替换,mybatis对其进行编译。

${}不能防止sql注入问题。

变量替换后,#{}对应的变量会自动加上单引号,而${}对应的变量不会自动加上。

使用时,#{}不一定要加@Param注解,而${}一定需要。

只有在获取用户DAO中的参数数据时才能使用#{}。

表名作参数时,必须用 ${}。如:

select * from ${tableName}

但是!如果输入的表名为"mytable;drop table mytable;",那mybatis直接报异常,你应该庆幸mybatis做了处理,没有让坏人得逞。(mysql注入)

order by 时,必须用 ${}。如:

select * from t_user order by ${columnName}

使用 ${} 时,要注意何时加或不加单引号,即 ${} 和 ’ ${} ’

能用 #{} 的地方就用 #{},不用或少用 ${}

深刻探究ResultMap

借用W3Cschool的一个例子(稍作修改)来探究resultmap能够如何映射复杂的关系。

首先看这个复杂的select查询语句:

<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,
       PA.id as ppost_author_id,
       PA.username as post_author_username,
       PA.password as post_author_password,
       PA.email as post_author_email,
       PA.bio as post_author_bio,
       PA.favourite_section as post_author_favourite_section,
       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 Author PA on P.author_id=PA.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>

可以理解为一个blog,有自己的author,其中包含了很多post,而每个post也包含了自己的author,包含了很多的评论和标签。

我们这样设计映射文件:

<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">
      <id property="id" column="post_author_id"/>
      <result property="username" column="post_author_username"/>
      <result property="password" column="post_author_password"/>
      <result property="email" column="post_author_email"/>
      <result property="bio" column="post_author_bio"/>
      <result property="favouriteSection" column="post_author_favourite_section"/>
    </association>
    <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>

我们一点点来解析这份映射文件。

首先,constructor会调用对象的构造方法。

<constructor>
  <idArg column="id" javaType="int"/>
  <arg column="username" javaType="String"/>
</constructor>

这可以对应某个对象的这个构造函数:

public class User{
  public User(int id,String username){
  ...
  }
}

不过要注意参数的顺序和类型与构造函数相同。构造方法中的参数名称无所谓。

association帮助我们把数据映射到对象中。

  <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>

这个Blog中包含了一个author对象,而author对象中的各字段就被一一映射到位,很方便。

collection帮助我们把数据映射到对象列表中。

    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>

可见这个post对象中包含一个Tag的list。数据就会被映射进去。

collection也可以嵌套使用。

discriminator是一个鉴别器,其作用很类似java中的switch语句。

    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>

如果draft列中是1,那么这个post将被返回成draftpost类型。

类似的用法:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultType="carResult">
      <result property="doorCount" column="door_count" />
    </case>
    <case value="2" resultType="truckResult">
      <result property="boxSize" column="box_size" />
      <result property="extendedCab" column="extended_cab" />
    </case>
    <case value="3" resultType="vanResult">
      <result property="powerSlidingDoor" column="power_sliding_door" />
    </case>
    <case value="4" resultType="suvResult">
      <result property="allWheelDrive" column="all_wheel_drive" />
    </case>
  </discriminator>
</resultMap>

鉴别器会按照vehicle_type列的内容来进行不同的映射和返回类型的区别。如果不满足所有的case,则默认不会执行。

自动映射

当自动映射查询结果时,MyBatis 会获取 sql 返回的列名并在 java 类中查找相同名字的属性(忽略大小写)。 这意味着如果 Mybatis 发现了ID 列和 id 属性,Mybatis会将ID的值赋给 id。

这项特性可以和上面是resultmap混合使用,如果在resultmap中没有特别声明,那么mybatis将进行自动映射。

对于每一个 result map,所有的 ResultSet 提供的列, 如果没有被手工映射,则将被自动映射。自动映射处理完毕后手工映射才会被处理。

通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase设置为 true。

一共三个匹配级别:

NONE:禁止自动匹配

PARTIAL(默认):自动匹配所有属性,有内部嵌套(association、collection)除外

FULL:自动匹配属性(不推荐)

这在conf.xml中配置。加上如下代码:

<settings>
    <setting name="autoMappingBehavior" value="NONE"/>
</settings>

或者对于每一个resultmap都可以设置自动映射的开关:

<resultMap id="mymap" type="others.Blog" autoMapping="false">
    <id property="id" column="id" />
</resultMap>

缓存设置

MyBatis存在缓存支持,提供了一级缓存和二级缓存。

一级缓存是基于PerpetualCache的HashMap本地缓存,作用范围为session内,当session flush或者close以后,该session的缓存会被清空

二级缓存就是全局缓存,超出了session范围,可以被多个sqlSession共享。

一级缓存的是SQL语句,二级缓存的是结果对象。

二级缓存如此配置:

在conf.xml中的setting加入如下代码:

<setting name="cacheEnabled" value="true"/>

然后再具体的映射文件中加入如下代码:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

它创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。

然后再把需要使用cache的查询语句的useCache属性设置为true。

接下来谈一谈readOnly属性。

1.(true)只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。
2.(false)而可读写的缓存会(通过序列化)返回缓存对象的拷贝。速度上会慢一些,但是更安全,因此默认值是 false。

也就是说在readOnly在true的时候,mybatis对于处于不同线程的使用者返回的对象将是同一个。换句话说,如果A修改了这个对象,B再去查询,B将获得这个修改过的对象,这就引起了冲突。但是如果保证仅仅是只读的话,性能提升是很高的。

相对的,readOnly是false的时候,mybatis每次都会返回一个找到对象的复制,这虽然引起了速度上的损耗,但是提升了可靠性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值