您的位置:首页 > 其它

Mybatis 中xml和注解映射,原来那么简单

2020-12-10 12:18 204 查看

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

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

    <mapper namespace="com.tian.mybatis.mapper.UserMapper">
        <resultMap id="" type=""></resultMap>
        <sql id=""></sql>
        <cache blocking="" ></cache>
        <cache-ref namespace=""></cache-ref>
        <select id="selectUserById"></select>
        <insert id="insert" ></insert>
        <update id=""></update>
        <delete id=""></delete>
    </mapper>

    select详解

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

    select使用案例

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.tian.mybatis.mapper.UserMapper">
        <select id="selectUserById"  resultType="com.tian.mybatis.entity.User" parameterType="int" >
            select * from m_user where id = #{id}
        </select>
    </mapper>
    • id必须在这个Mapper中是唯一的,可以被用来引用这条语句 ,这个id必须与只对应的是
      XxxMapper.java
      中的方法,必须是一一对应。
    • 返回类型:User类型,
      resultType
      :查询语句返回结果类型的完全限定名或别名。别名使用方式和
      parameterType
      是一样的。
    • 参数:整形,表示查询语句传入参数的类型和完全限定名或别名。支持基础数据类型和复杂数据类型。

    #{参数名}:告诉

    MyBatis
    生成的
    PreparedStatement
    参数,相对于
    JDBC中
    ,改参数被标识为‘?’。

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

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

    如果是我们的entity类,那么

    resultType
    是无法使用别名的,只能使用
    resultMap
    才可以使用别名。

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

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

    数据库里有两条数据:

    UserMapper.xml

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

    UserMapper.java

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

    测试类:

    public class MybatisApplication {
        public static final String URL = "jdbc:mysql://localhost.com:3306/mblog?useUnicode=true";
        public static final String USER = "root";
        public static final String PASSWORD = "123456";
        public static void main(String[] args) {
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            SqlSession sqlSession = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
                //工厂模式
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                //获取sql操作会话
                sqlSession = sqlSessionFactory.openSession();
                //构造对象(这里比较特殊,这里构造对象的方式后面会专门分享)
                UserMapper userMapper =  sqlSession.getMapper(UserMapper.class);
                //查询统计
                System.out.println(userMapper.countUser());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                sqlSession.close();
            }
        }
    }

    输出:2

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

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

    实体类User

    public class User {
        private Integer id;
        private String userName;
        private Integer age; 
        //set get toString方法这里就不贴了
    }

    UserMapper.xml
    文件内容:

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

    • type:对应的是我们的实体类,全路径名。
    • id:可以理解为别名。

    • id:唯一标识,此id值用于select元素
      resultMap
      属性的引用。
    • column:对应我们数据库表中的字段名称。
    • property:对应我们的实体类的属性,比如:User中的属性userName,要和数据库表m_user中的name对应。
    • result:标识一些简单属性,其中column属性代表数据库的字段名,property代表查询出来的字段名映射到实体类的某个属性。

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

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

    输出:

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

    注意:实体类的

    get set
     和
    toString()
    方法这里给省略, 希望大家在使用的使用,使用快捷键很简单的就搞定了。

    上面提到过

    resultType
    resultMap
    ,那么他们两到底有什么区别呢?

    resultType和resultMap 有什么区别?

    • resultType
      :直接表示返回类型, 包括基本数据类型和复杂数据类型。
    • resultMap
      :外部
      resultMap
      定义的引用,通过对应的外部
      resultMap
      的id,表示结果映射到哪个
      resultMap
      上,一般用于字段名和属性名不一致的情况,或者需要做复杂的联合查询以便自由控制映射结果。
    两者的关联

    当进行查询时,查询出来的每个字段都会放在一个Map里,当查询元素返回属性是

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

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

    注意:

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

    增删改案例

    insert

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

    <insert id="insert" parameterType="com.tian.mybatis.entity.User">
            INSERT INTO m_user(`name`,age) VALUES ( #{userName},#{age})
    </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添加注解

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

    再次测试

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

    输出:

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

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

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

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

    输出:

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

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

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

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

    注解和xml是否可以共存?

        <update id="updateAuthorIfNecessary">
            update m_user
            <trim prefix="SET" suffixOverrides=",">
                <if test="userName != null and userName != ''">
                    `name` = #{userName},
                </if>
                <if test="gender != null and gender != 0">
                    gender = #{gender},
                </if>
                <if test="age != null and age != 0">
                    age = #{age},
                </if>
            </trim>
            where id=#{id}
        </update>

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

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

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

    nested exception is java.lang.IllegalArgumentException:
    Mapped Statements collection already contains value for com.tian.mybatis.mapper.UserMapper.updateAuthorIfNecessary. 
    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
    的某个复杂的”数据类型”属性,仅处理一对一的关联关系。

    <resultMap type="com.tian.mybatis.entity.User" id="userMapRole">
            <id column="id" property="id" />
            <result column="name" property="userName" />
            <result column="age" property="age" />
            <association property="role" javaType="UserRole">
                <id column="id" property="id" />
                <result column="roleName" property="roleName" />
            </association>
    </resultMap>

    association的属性节点:

    • property:映射数据库列的实体对象属性名。
    • javaType
      :完整的
      java
      类名和限定名。propert所映射的属性的类型。

    子元素

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

    • result:

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

    collection

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

    <resultMap type="com.tian.mybatis.entity.User" id="userMapAddress">
            <id column="id" property="id"/>
            <result column="name" property="userName"/>
            <collection property="lists" ofType="UserAddress">       
                <id column = "id" property = "id">
                <result column="addressDesc" property="addressDesc"/>
            </collection>
        </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的区别。


    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: