小言_互联网的博客

Mybatis 中xml和注解映射,so easy啦

306人阅读  评论(0)

关注“Java后端技术全栈”

回复“面试”获取全套面试资料

MyBatis 提供了XML配置和注解配置两种方式。今天就来搞搞这两种方式是如何实现的。

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

来自官网。

Mybatis映射九个顶级元素:

  • mapper:映射文件的根节点,只有一个属性namespace(命名空间),作用如下:

    • 用于区分不同的mapper,全局唯一。

    • 绑定DAO接口,即面向接口编程,当绑定一个接口,就不用写此接口的实现类,会通过接口的完全限定名找到对应的mapper配置来执行SQL语句,所以,namespace的命名必须要写接口的完全限定名。

  • cache:配置给定命名空间的缓存。

  • cache-ref:从其他命名空间引用缓存配置。

  • resultMap:用来描述数据库结果集和对象的对应关系。

  • sql:可以重用的SQL块,也可以被其他语句引用。通常时存放一些公用性的SQL

  • insert:映射插入语句。

  • update:更新映射语句。

  • delete:删除映射语句。

  • select:映射查询语句。

xml方式

九个顶级映射元素对应标签:


   
  1. <mapper namespace= "com.tian.mybatis.mapper.UserMapper">
  2.     <resultMap id= ""  type= ""></resultMap>
  3.     <sql id= ""></sql>
  4.     <cache blocking= "" ></cache>
  5.     <cache-ref namespace= ""></cache-ref>
  6.     < select id= "selectUserById"></ select>
  7.     <insert id= "insert" ></insert>
  8.     <update id= ""></update>
  9.     < delete id= ""></ delete>
  10. </mapper>

select详解

可以看得出,后面可选项还是蛮多的。下面是官网对每项的解释。

select使用案例


   
  1. <?xml version= "1.0" encoding= "UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC  "-//mybatis.org//DTD Mapper 3.0//EN"
  4.          "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace= "com.tian.mybatis.mapper.UserMapper">
  6.     < select id= "selectUserById"  resultType= "com.tian.mybatis.entity.User" parameterType= "int" >
  7.          select * from m_user where id = #{id}
  8.     </ select>
  9. </mapper>
  • id必须在这个Mapper中是唯一的,可以被用来引用这条语句 ,这个id必须与只对应的是XxxMapper.java中的方法,必须是一一对应。

  • 返回类型:User类型,resultType:查询语句返回结果类型的完全限定名或别名。别名使用方式和parameterType是一样的。

  • 参数:整形,表示查询语句传入参数的类型和完全限定名或别名。支持基础数据类型和复杂数据类型。

#{参数名}:告诉MyBatis生成的PreparedStatement参数,相对于JDBC中,改参数被标识为‘?’。

别名与参数映射类型如下:

返回类型中别名的使用,注意:

如果是我们的entity类,那么resultType是无法使用别名的,只能使用resultMap才可以使用别名。


   
  1. <?xml version= "1.0" encoding= "UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC  "-//mybatis.org//DTD Mapper 3.0//EN"
  4.          "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace= "com.tian.mybatis.mapper.UserMapper">
  6.     <resultMap id= "User"  type= "com.tian.mybatis.entity.User"/>
  7.     < select id= "selectUserById"  resultMap= "User" parameterType= "int" >
  8.          select * from m_user where id = #{id}
  9.     </ select>
  10. </mapper>

但是如果使用的上面映射表里,也可以直接使用别名。

数据库里有两条数据:

UserMapper.xml


   
  1. <?xml version= "1.0" encoding= "UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC  "-//mybatis.org//DTD Mapper 3.0//EN"
  4.          "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace= "com.tian.mybatis.mapper.UserMapper">
  6.     < select id= "countUser" resultType= "int">
  7.          select count( 1) from m_user
  8.     </ select>
  9. </mapper>

UserMapper.java


   
  1. import com.tian.mybatis.entity.User;
  2. public  interface UserMapper {
  3.      int countUser();
  4. }

测试类:


   
  1. public class MybatisApplication {
  2.     public static final String URL =  "jdbc:mysql://localhost.com:3306/mblog?useUnicode=true";
  3.     public static final String USER =  "root";
  4.     public static final String PASSWORD =  "123456";
  5.     public static void main(String[] args) {
  6.         String resource =  "mybatis-config.xml";
  7.         InputStream inputStream = null;
  8.         SqlSession sqlSession = null;
  9.         try {
  10.             inputStream = Resources.getResourceAsStream(resource);
  11.              //工厂模式
  12.             SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(inputStream);
  13.              //获取sql操作会话
  14.             sqlSession = sqlSessionFactory.openSession();
  15.              //构造对象(这里比较特殊,这里构造对象的方式后面会专门分享)
  16.             UserMapper userMapper =  sqlSession.getMapper(UserMapper.class);
  17.              //查询统计
  18.             System.out. println(userMapper.countUser());
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         } finally {
  22.             try {
  23.                 inputStream. close();
  24.             } catch (IOException e) {
  25.                 e.printStackTrace();
  26.             }
  27.             sqlSession. close();
  28.         }
  29.     }
  30. }

输出:2

当数据库表中的字段名和我们entity中的字段名不一致,怎么处理?

在实际开发中,这种常见是在所难免。我们可以使用下面的这种方式解决。

实体类User


   
  1. public class User {
  2.     private Integer id;
  3.     private String userName;
  4.     private Integer age; 
  5.      //set get toString方法这里就不贴了
  6. }

UserMapper.xml文件内容:


   
  1. <?xml version= "1.0" encoding= "UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC  "-//mybatis.org//DTD Mapper 3.0//EN"
  4.          "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace= "com.tian.mybatis.mapper.UserMapper">
  6.     <resultMap id= "User"  type= "com.tian.mybatis.entity.User">
  7.         <id column= "id" property= "id"/>
  8.         <result column= "name" property= "userName"/>
  9.     </resultMap>
  10.     < select id= "selectUserById"  resultMap= "User" parameterType= "int" >
  11.          select * from m_user where id = #{id}
  12.     </ select>
  13. </mapper>

  • type:对应的是我们的实体类,全路径名。

  • id:可以理解为别名。

  • id:唯一标识,此id值用于select元素resultMap属性的引用。

  • column:对应我们数据库表中的字段名称。

  • property:对应我们的实体类的属性,比如:User中的属性userName,要和数据库表m_user中的name对应。

  • result:标识一些简单属性,其中column属性代表数据库的字段名,property代表查询出来的字段名映射到实体类的某个属性。

继续使用我们前面的测试类进行测试:


   
  1. UserMapper userMapper =  sqlSession.getMapper(UserMapper.class);
  2. System.out. println(userMapper.selectUserById( 1));

输出:User{id=1, userName='tian', age=22}

注意:实体类的get settoString()方法这里给省略, 希望大家在使用的使用,使用快捷键很简单的就搞定了。

上面提到过resultTyperesultMap,那么他们两到底有什么区别呢?

resultType和resultMap 有什么区别?

  • resultType:直接表示返回类型, 包括基本数据类型和复杂数据类型。

  • resultMap:外部resultMap定义的引用,通过对应的外部resultMap的id,表示结果映射到哪个resultMap上,一般用于字段名和属性名不一致的情况,或者需要做复杂的联合查询以便自由控制映射结果。

两者的关联

当进行查询时,查询出来的每个字段都会放在一个Map里,当查询元素返回属性是resultType的时候,会将键值对取出赋所指定的属性。其实MyBatis的每个查询映射的返回类型都是resultMap,只是当我们使用resultType的时候,会自动把对应的值赋给所指定的对象属性,当使用resultMap时候,因为map不是很好的表示领域,我们就进一步的转化为对应的实体对象。resultMap主要作用于复杂的联合查询上。

resultMap的自动映射级别:默认级别为PARTIAL,也可以在settings更改值。

注意resultTyperesultMap本质是一样的,都是Map数据结构,但是二者不能同时存在。

增删改案例

insert

从这里可以知道,关于增加insert是没有返回值类型可以让我们指定的。默认返回int类型。


   
  1. <insert id= "insert" parameterType= "com.tian.mybatis.entity.User">
  2.         INSERT INTO m_user( `name`,age) VALUES ( #{userName},#{age})
  3. </insert>

对应Mapper中的方法

int insert(User user);

另外的update和delete类似,这里就没有必要逐一演示了。

注解方式

九个顶级映射元素对应注解:

其他部分注解是配合九个注解进行使用的。

select注解

把本地的UserMapper.xml删掉,然后改一下mybatis-config.xml,把其中的UserMapper.xml给注释掉。添加

<mapper class="com.tian.mybatis.mapper.UserMapper"/>

UserMapper.java添加注解


   
  1. public  interface UserMapper {
  2.     @Select( "select * from m_user where id = #{id}")
  3.     User selectUserById(Integer id);
  4. }

再次测试


   
  1. User user = sqlSession.selectOne( "com.tian.mybatis.mapper.UserMapper.selectUserById"1);
  2. System.out. println(user);

输出:

User{id=1, userName='null', age=22}

从输出内容看到,userName为null,这也是因为和数据库表汇中的字段name不一致导致的,那么如何处理呢?

这么搞,再添加一个注解:


   
  1. public  interface UserMapper {
  2.    @Select( "select * from m_user where id = #{id}")
  3.    @Results( @Result(column =  "name",property =  "userName"))
  4.    User selectUserById(Integer id);
  5. }

输出:

User{id=1, userName='tian', age=22}

这样也就是在使用注解的时候,处理实体属性名和数据库表字段名不一样的问题的办法。

insert、update、delete同样也可以使用注解来搞定了。

@Insert、@Update、@Delete配上相应的SQL语句。

注解和xml是否可以共存?


   
  1.     <update id= "updateAuthorIfNecessary">
  2.         update m_user
  3.         <trim prefix= "SET" suffixOverrides= ",">
  4.             < if test= "userName != null and userName != ''">
  5.                  `name` = #{userName},
  6.             </ if>
  7.             < if test= "gender != null and gender != 0">
  8.                 gender = #{gender},
  9.             </ if>
  10.             < if test= "age != null and age != 0">
  11.                 age = #{age},
  12.             </ if>
  13.         </trim>
  14.         where id=#{id}
  15.     </update>

同时在UserMapper.java中的方法上添加注解


   
  1. @Update( "update m_user set  `name` = #{userName},gender = #{gender},age = #{age} where id=#{id}")
  2. int updateAuthorIfNecessary(User user);

再次中子星的时候回报异常的:


   
  1. nested exception is java.lang.IllegalArgumentException:
  2. Mapped Statements collection already contains value  for com.tian.mybatis.mapper.UserMapper.updateAuthorIfNecessary. 
  3. please check file [D:\workspace\my_code\mybatis\target\classes\mapper\UserMapper.xml] and com/tian/mybatis/mapper/UserMapper.java (best guess)

大致意思就是说,已经存在了,即就是不能同时使用xml和注解。二者选其一。

xml可以喝注解结合使用,但是得保证同一个方法不能同时存在xml和注解。

建议

简单的sql处理可以使用注解,复杂的sql使用xml。但是实际工作还得看你待的项目中有没有对这个进行规范化。

在项目中无非就三种:

1.全部必须使用xml方式。

2.全部必须使用注解方式。

3.可以同时使用xml和注解。

高级映射

association

映射到JavaBean的某个复杂的”数据类型”属性,仅处理一对一的关联关系。


   
  1. <resultMap  type= "com.tian.mybatis.entity.User" id= "userMapRole">
  2.         <id column= "id" property= "id" />
  3.         <result column= "name" property= "userName" />
  4.         <result column= "age" property= "age" />
  5.         <association property= "role" javaType= "UserRole">
  6.             <id column= "id" property= "id" />
  7.             <result column= "roleName" property= "roleName" />
  8.         </association>
  9. </resultMap>

association的属性节点:

  • property:映射数据库列的实体对象属性名。

  • javaType:完整的java类名和限定名。propert所映射的属性的类型。

子元素

  • id:一般为映射主键,可以提高性能。

  • result:

    • column:映射的数据库的字段名。

    • property:映射的数据列对应的实体对象属性。

collection

映射到JavaBean的某个复杂的”数据类型”属性,这个属性是一个集合列表,处理一对多的关联关系。


   
  1. <resultMap  type= "com.tian.mybatis.entity.User" id= "userMapAddress">
  2.         <id column= "id" property= "id"/>
  3.         <result column= "name" property= "userName"/>
  4.         <collection property= "lists" ofType= "UserAddress">       
  5.             <id column =  "id" property =  "id">
  6.             <result column= "addressDesc" property= "addressDesc"/>
  7.         </collection>
  8.     </resultMap>

ofType:完整的Java类名和限定名。propert所映射的属性的类型。

其余和association基本一致。

association和collection都具备延迟加载功能。

延迟加载:先从单表查询,需要时再查关联表,大大的提高了数据库性能,因为相对来说单表查询比多表查询要快。

xml和注解的关系

上面我们已经讲了两种方式的实现,下面来对比一下,两种方式的关系:

xml方式

必须有个一个XxxMapper.xml与之对应,方法名对应xml中的id,方法入参和方法出参都必须对应起来,很容易出问题。我们在开发的时候有的是可以使用代码生成器生成,但是有的是必须自己手写,有的公司也是要求必须手写,所以这里需要注意。

注解方式

不需要XxxMapper.xml文件,只需要在对应XxxMapper.java中的方法上加上注解就搞定了,但是这里是有坑的。毕竟把sql放到了我们的Java代码里了。

优缺点

xml方式: 增加了xml文件,修改麻烦,条件不确定(ifelse判断),容易出错,特殊转义字符比如大于小于 。

注解方式:复杂sql不好用,搜集sql不方便,管理不方便,修改需重新编译

总结

本文讲述了Mybatis的两种映射方式,以及一些注意点,一些关系和区别。

实体属性名和数据库表字段名不一样的情况下,xml和注解分别是如何处理的。resultType和resultMap的区别。

推荐阅读

6000多字 | 秒杀系统设计注意点【理论】

面试官:说说你对Java异常的理解

下载《亿级流量网站架构核心技术》pdf


转载:https://blog.csdn.net/o9109003234/article/details/111027036
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场