Mybatis的一些知识点

本文详细的介绍一下mapper.xml文件当中,遇到各种情况下的写法。文章内容基本来自http://www.cnblogs.com/rollenholt/p/3365866.html。

读过之后,自己总结一下并写下来(鉴别器直接抄过来的)

1.Sql注入

Mybatis是可以防止sql注入的。Mybatis在配置sql语句的过程中,如

select * from tableA a where a.id=#{id} and a.name=${name}

 

 可以看到,在我们传入的变量的占位表示方法有两种,一种是‘#’一种是‘$’   那么这两种符号有什么区别呢?区别就是‘#’是将传入的参数加上单引号作为字符串传入,另一种‘$’则是直接传入。那么明显利用#是可以防止sql注入的,因为注入的语句会被变成字符串来解释,而不是命令。所以采用#会更好

基础知识

数据库操作无外乎增删改查 CRUD,反映在Mapper里面的写法如下

<!-- 最基本的查询 -->
<select id="selectUserFunc" paramType="int" resultType="com.entity.User">
       select * from s_user u where  u.id=#{0}
</select>

<!-- 利用类的别名 -->
<typeAlias type="com.entity.User" alias="User"/>

<select id="selectUserFunc" paramType="int" resultType="User">
       select * from s_user u where  u.id=#{0}
</select>

<!-- 上面两种查询要求查询的列名和User类中的属性名是一致的,如果不一致则可以在sql语句里面用别名动动手脚-->

<select id="selectUserFunc" paramType="int" resultType="com.entity.User">
       select
       c_id  as userID,
       c_name as userName
        from s_user u where  u.id=#{0}
</select>

 

<!-- 上面所描述的查询方法直接将返回与类对应了起来,实际在后台中,创建了一个resultMap来对应类和列,我们可以直接创建一个resultMap来解决列和类中属性名不匹配的问题-->

<resultMap id="userResultMap"  type="com.entity.User">
      <id property="id" column="user_id"/>
      <result property="username" column="user_passowrd">
</resultMap>

<select id="selectUserFunc" paramType="int" resultMap="userResultMap"/>
      select * from user where user_id=#{0}
</select>

<!--解释一下  resultMap是对返回的一种全面的描述id指的是这个resultmap的名称,在使用resultMap时用到type指的是这个resultmap对应的类reusltMap 里面的id元素一般是主键,可以增强性能property 是该元素对应的类的属性的名称column 对应的是表里面的列名  

-->

上面描述中所有的参数都是int,显然我们在实际使用中不可能这么简单,一般来说都会用到多个参数,在使用多个参数时,可以将一个类传进去,也可以传map进去,相对来说map的使用会更加灵活一些。下面介绍一下传递多个参数的方法。

<!-- 外部调用函数为   fun(param1,param2) -->
<select id="fun"  resultMap="ResultMapUser">  <!--省略了paramType-->
    select * from user where id=#{0} and username=#{1}


<!-- 外部调用函数为  
map.add("id",1);
map.add("name",2);
 fun(map) -->
<select id="fun1" paramType="Map" resultMap="ResultMapUser">  <!--使用map传递-->
    select * from user where id=#{id} and username=#{name}
</select>

<!--外部函数为 User user=new User();
           user.setID(1);
           user.setName("2");
          fun(user); -->
<select id="fun" paramType="com.entity.User" resultMap="ResultMapUser"> <!--使用类(javabean)传递-->
select * from user where id=#{id} and username=#{name}
</select>  <!-- id 和 name 是类中的属性名-->


<!--使用注解的例子 在mapper接口定义时-->
Public User fun(@Param('userid') int id,@Param('username') String name)
<!-- 这样在使用的时候就可以直接用注解中的名称就可以了-->
<select id="fun"resultMap="ResultMapUser"> 
select * from user where id=#{userid} and username=#{username}
</select>  <!-- id 和 name 是类中的属性名-->

当类的一个属性也是一个类(另一张表中的内容)时,分两种情况,分别是包含一个类和一个类的List

一对一的情况

Class User{
 
public User(){}
public User(int id){
this.id=id;
}
private int id; private String name; private Company company; } Class Company{ private int id; private String name }

表的结构如下

TABLE User
(
     user_id
     user_name
     company_id
)

TABLE Company
(
     company_id
     company_name
)

可以看到,User类里面包含了一个company类。我们可以采用如下的方法进行查询

<resultMap id="selectUserDetail" type="User">
  <constructor>
    <idArg column="user_id" javaType="int"/>
  </constructor>
  <result property="name" column="user_name"/>
  <association property="company" javaType="Company">
    <id property="id" column="company_id"/>
    <result property="name" column="company_name"/>
  </association>
</resultMap>

resultMap中

constructor - 类在实例化时,用来注入结果到构造方法中

idArg - ID 参数;标记结果作为 ID 可以帮助提高整体效能

            arg - 注入到构造方法的一个普通结果

      id – 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能

      result – 注入到字段或 JavaBean 属性的普通结果

association – 一个复杂的类型关联;许多结果将包成这种类型嵌入结果映射 – 

      collection – 结果映射自身的关联,或者参考一个复杂类型的集

      discriminator – 使用结果值来决定使用哪个结果映射

             case – 基于某些值的结果映射

 

 
<select id="selectFunc" resultMap="selectUserDetail" >
     select a.user_id,
               a.user_name,
              a.company_id,
b.company_id, b.company_name from user a , company b where a.company_id=b.company_id
</select>

在上面的代码中,constructor元素利用构造函数填入一定的信息,可以提高效率。不过必须有构造函数的支持。

对于单个类的引用,我们使用association来表示关联。下面我们再来看另一种写法

<resultMap id="userMap" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
  <association property="company" column="company_id" javaType="Company" select="selectCompany"/>
</resultMap>
 
<select id="selectUser" parameterType="int" resultMap="userMap">
  SELECT * FROM BLOG WHERE USER_ID = #{id}
</select>
 
<select id="selectCompany" parameterType="int" resultType="Company">
  SELECT * FROM AUTHOR WHERE Company_ID = #{id}
</select>

这种写法将类直接作为关联的对象,查询的时候分别查询了User 和Company,感觉更符合面向对象的思路。但是这样的设计会造成n+1次查询的问题。

所以我们采取如下的方法会更好

<select id="selectUser" parameterType="int" resultMap="UserMap">
  select
    B.company_id            as cid,
    B.company_name       as cname,
    A.user_id            as uid,
    A.user_name     as uname,
    A.company_id   as ucid,
  from User A left outer join Company B on A.company_id=B.company_id
  where B.id = #{id}
</select>
<resultMap id="userMap" type="User">
  <id property="id" column="uid" />
  <result property="name" column="uanme"/>
  <association property="company" column="ucid" javaType="Company" resultMap="comMap"/>
</resultMap>
 
<resultMap id="comMap" type="Company">
  <id property="id" column="cid"/>
  <result property="username" column="cname"/>
</resultMap>

这样写相当于将所有需要查询的一起查询了,避免n+1   resultMap分开写可以进行重用,比如单独查company时。如果不需要重用,也可以直接写在一起,如下所示

<resultMap id="userMap" type="User">
  <id property="id" column="uid" />
  <result property="name" column="uanme"/>
<association property="company" javaType="Company">
<id property="id" column="cid"/>
  <result property="username" column="cname"/>

  </association>
</resultMap>



 以上就是针对一对一的关联写法,使用association来进行关联。那么当我们遇到如下的java对象

一对多的情况

Class User{
   private List<Company> companys;
   private int id;
   private String name;
   //getter and setter
}

 

并不是一个类,而是一对多的关系。这时候我们就不能使用association来进行关联了针对上面代码中的List,我们使用collection来描述

<collection property="company" ofType="com.entity.Company">
  <id property="id" column="company_id"/>
  <result property="name" column="company_name"/>
</collection>
<!-- ofType 属性值得就是list中元素的类型-->

 

collection和association的使用基本一致,只是把javaType 换成了ofType。所以不再赘述

鉴别器

 

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

鉴别器会根据指定的列的值,确定返回的resultMap 

如下面的例子

<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" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

在这个示例中, MyBatis 会从结果集中得到每条记录, 然后比较它的 vehicle_type的值。 如果它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。换句话说,这样 做完全是剩余的结果映射被忽略(除非它被扩展,这在第二个示例中讨论) 。如果没有任何 一个实例相匹配,那么 MyBatis 仅仅使用鉴别器块外定义的结果映射。所以,如果 carResult 按如下声明:

<resultMap id="carResult" type="Car">
  <result property="doorCount" column="door_count" />
</resultMap>

那么只有 doorCount 属性会被加载。这步完成后完整地允许鉴别器实例的独立组,尽管 和父结果映射可能没有什么关系。这种情况下,我们当然知道 cars 和 vehicles 之间有关系, 如 Car 是一个 Vehicle 实例。因此,我们想要剩余的属性也被加载。我们设置的结果映射的 简单改变如下。

<resultMap id="carResult" type="Car" extends="vehicleResult">
  <result property="doorCount" column="door_count" />
</resultMap>

在 vehicleResult 和 carResult 的属性都会被加载了。

尽管曾经有些人会发现这个外部映射定义会多少有一些令人厌烦之处。 因此还有另外一 种语法来做简洁的映射风格。比如:

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

要记得 这些都是结果映射, 如果你不指定任何结果, 那么 MyBatis 将会为你自动匹配列 和属性。所以这些例子中的大部分是很冗长的,而其实是不需要的。也就是说,很多数据库 是很复杂的,我们不太可能对所有示例都能依靠它。

获得数据库生成的主键

利用insert插入数据的时候,有时我们会用到数据库生成的主键

如下类

Class User{

     private int id;

     private String name;

     private String hobby;
}

id为数据库自动生成的。

在Mybatis Mapper文件中添加属性“useGeneratedKeys”和“keyProperty”,其中keyProperty是Java对象的属性名!

<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="id" parameterType="com.entity.User">  
    insert into user(userName,userhobby)  
    values(#{name},#{hobby})  
</insert>  

上所示,我们在insert中指定了keyProperty="id",其中id代表插入的User对象的主键属性。

System.out.println("插入前主键为:"+user.getUserId());  
userDao.insertAndGetId(user);//插入操作  
System.out.println("插入后主键为:"+user.getUserId());  

 

目前接触到的就这些,如果有新的再加

 

posted @ 2016-02-27 11:22  麻木鲁克  Views(183)  Comments(0)    收藏  举报