9

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

 3 years ago
source link: http://developer.51cto.com/art/202012/634367.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

6vIVb2E.jpg!mobile

本文转载自微信公众号「Java后端技术全栈」,作者田维常。转载本文请联系Java后端技术全栈公众号。

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

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

来自官网。

Mybatis映射九个顶级元素:

IjEj6f2.png!mobile

  • mapper:映射文件的根节点,只有一个属性namespace(命名空间),作用如下:
    • 用于区分不同的mapper,全局唯一。
    • 绑定DAO接口,即面向接口编程,当绑定一个接口,就不用写此接口的实现类,会通过接口的完全限定名找到对应的mapper配置来执行SQL语句,所以,namespace的命名必须要写接口的完全限定名。
  • cache:配置给定命名空间的缓存。
  • cache-ref:从其他命名空间引用缓存配置。
  • resultMap:用来描述数据库结果集和对象的对应关系。
  • sql:可以重用的SQL块,也可以被其他语句引用。通常时存放一些公用性的SQL。
  • insert:映射插入语句。
  • update:更新映射语句。
  • delete:删除映射语句。
  • select:映射查询语句。

nqYRNvV.png!mobile

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

i2EBVne.png!mobile

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

FFNBb2.png!mobile

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中,改参数被标识为‘?’。

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

3UzM3iB.png!mobile

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

如果是我们的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> 

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

数据库里有两条数据:

yqeqMvN.png!mobile

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> 

MRvEjq.png!mobile

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

QJR3MnV.png!mobile

  • 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

quiea2a.png!mobile

从这里可以知道,关于增加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类似,这里就没有必要逐一演示了。

注解方式

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

ueQ3uyA.png!mobile

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

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的区别。

【责任编辑:武晓燕 TEL:(010)68476606】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK