Mybatis-7 XML映射器

发布于:2025-09-06 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、XML映射器/SQL映射文件

- 基本介绍

1、MyBatis的真正强大在于它的语句映射(在XxxMapper.xml配置),由于它的异常强大,如果拿它跟具有相同功能的JDBC代码进行对比,你会立即发现省掉了将近95%的代码。MyBatis致力于减少使用成本,让用户能更专注于SQL代码。

    <select id="selectMonsterById" resultType="Monster">
        select * from `monster` where id=#{id}
    </select>
</mappers>

2、SQL映射文件常用的几个顶级元素(按照应被定义的顺序列出): 

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterType – 将会传入这条语句的参数的类全限定名或别名
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。

  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。

- 匹配XxxMapper.xml的4种方式

但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。MyBatis通过以下4种方式查找实现了接口的XXMapper.xml文件。如果没有写,或者写错了路径、文件名,都会报错Type interface *** is not known to the MapperRegistry。
- 1.使用映射器接口实现类的完全限定类名。可以用来配置用@注解实现的映射器。
> \<mapper class="com.stein.mapper.MonsterAnnotation"/>。
- 2.使用相对于类路径的资源引用
> \<mapper resource="com/stein/mapper/monsterMapper.xml"/>

- 3.将包内的映射器接口全部注册为映射器。直接写包名,在包内搜索。
> \<package name="com.stein.mapper"/>

- 4.使用完全限定资源定位符(URL)(不常用)

二、新建演示Module

新建一个干净的环境进行演示,同时再熟悉创建过程。

1.新建Module(不是project)

选择Maven,注意父项是不是上图中顶层的mybatis

2.检查子模组的pom.xml文件,是不是包含了父模组;

检查父模组的pom.xml文件,是不是包含了子模组。

3.复制mybatis_quickstart的框架文件到当前模组中

清空MonsterMapper接口和xml的实现内容

清空mybatis-config.xml中的<mappers>里面的内容,仅保留MonsterMapper的映射路径

Test只保留初始化部分

测试,无报错,正常显示:monsterMapper的运行类型是:class com.sun.proxy.$Proxy7

三、基本使用

1.insert、delete、update、select这个我们在前面讲解过,分别对应增删改查的方法和SQL语句的映射。
2.如何获取到刚刚添加的Monster对象的id主键【useGeneratedKeys="true"】

    <insert id="insertMonster" parameterType="com.stein.entity.Monster" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO `monster`(`age`,`birthday`,`email`,`gender`,`name`,`salary`)--
        values(#{age},#{birthday},#{email},#{gender},#{name},#{salary})
    </insert>  

四、parameterType(输入参数类型)

parameterType(输入参数类型)

1.传入简单类型,比如按照id查Monster(前讲过)

比如:

Monster selectMonsterById(Integer monsterId);

2.传入POJO类型,查询时需要有多个筛选条件

3.当有多个条件时,传入的参数就是Pojo类型的Java对象,比如这里的Monster对象

4.当传入的参数类是String时,也可以使用${}方式来收参数

案例1:请查询id=1或者name='白骨精'的妖怪

        添加接口方法:

public interface MonsterMapper {
    //通过id或者名字查询
    public List<Monster> findMonsterByNameORId(Monster monster);
}

        实现/配置方法MonsterMapper.xml

<mapper namespace="com.stein.mapper.MonsterMapper">
    <!--`id`表示数据库中的字段名,#{id}表示monster的属性monster.id-->
    <select id="findMonsterByNameORId" parameterType="Monster" resultType="Monster">
        select * from `monster` where `id`=#{id} or `name`=#{name}
    </select>
</mapper>

        测试

    @Test
    public void findMonsterByNameORId(){
        Monster monster = new Monster();
        monster.setId(9);
        monster.setName("狐狸精");
        List<Monster> monsters=monsterMapper.findMonsterByNameORId(monster);
        for (Monster m : monsters) {
            System.out.println("monster="+m);
        }

        if(sqlSession != null){
            sqlSession.close();
            System.out.println("查询成功");
        }
    }

案例2:请查询name中包含"精"的妖怪

这儿“包含”用的是模糊查询

1.先在DB里面测试sql语句是否正确

    -- %百分号是SQL中的通配符,代表零个、一个或多个字符。

select * from `monster` where `name` like "%精%"

2.添加接口方法

public interface MonsterMapper {
    //查询名字中含义'精'妖怪
    public List<Monster> findMonsterByName(String name);
}

3.使用映射器实现接口方法

select * from `monster` where `name` like "%${name}%"

注意区别前面的【%精%】  是写死的,要换成参数,等待传入。这儿有了两种写法:

        1."% #{name} %" ,可以看见是#{}的写法,后面运行代码看日志里面的语句是:

        select * from `monster` where `name` like "%?%"

        这是一个预编译指令,先用?来替代,然后再把变量替换?问号,结果出错了:Could not set parameters for mapping。

        2."% ${name} %" ,可以看见是${}的写法,后面运行代码看日志里面的语句是:

         select * from `monster` where `name` like "%精%"

        可以发现直接使用变量的值进行替换了,可以正常运行。

        这儿同时也给出了#{},#{}的区别。前者是预编译指令,可以防止注入;后者是直接替换,不能防止注入。

        顺带补充说明,这儿单引号'% ${name} %',或者双引号"% ${name} %"都能使用。但本人推崇使用双引号。因为对应Linux中的单引号‘${var}’不能被替换字符。

<mapper namespace="com.stein.mapper.MonsterMapper">

    <select id="findMonsterByName" parameterType="String" resultType="Monster">

        select * from `monster` where `name` like "%精%"

    </select>
</mapper>

4.测试

    @Test
    public void findMonsterByName(){
        String key="精";
        List<Monster> monsters=monsterMapper.findMonsterByName(key);
        for (Monster m : monsters) {
            System.out.println("monster="+m);
        }
        if(sqlSession != null){
            sqlSession.close();
        }
        System.out.println("查询完毕");
    }

五、传入类型:HashMap(重点)

1.HashMap传入参数更加灵活,比如可以灵活的增加查询的属性,而不受限于Monster这个Pojo属性本身
2.演示如何遍历一个List<Map<String,Object>>的数据类型

应用实例1·传入HashMap

要求:声明一个方法,按传入参数是HashMap的方式,查询id>10并且salary大于40的所有妖怪

不在使用POJO传递参数,POJO有属性的限制,使用更自由的HashMap

1.测试SQL语句

select * from `monster` where `id`>10 and `salary` > 10;

2.添加接口方法

List<Monster> findMonsterByIdAndSalaryUseParameterHashMap(Map<String,Object> map);

3.实现方法

    <select id="findMonsterByIdAndSalaryUseParameterHashMap" parameterType="map" resultType="Monster">
        select * from `monster` where `id`>#{id} and `salary` > #{salary};
    </select>

4.测试

    @Test
    public void findMonsterByIdAndSalaryUseParameterHashMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",10);
        map.put("salary",40);
        List<Monster> monsters=monsterMapper.findMonsterByIdAndSalaryUseParameterHashMap(map);
        for (Monster m : monsters) {
            System.out.println("monster="+m);
        }
        if(sqlSession != null){
            sqlSession.close();
        }
        System.out.println("查询成功");
    }

应用实例2-传入和返回都是HashMap

要求:将上面的方法的改成返回参数也以HashMap的类型

1.测试的SQL语句不变

2.添加接口方法

//传入和返回值都是HashMap的方式
    List<Map<String,Object>> findMonsterByIdAndSalary_ParameterHashMap_ResultHashMap(Map<String,Object> map);

3.实现方法

        注意这里的parameterType和resultType

    <select id="findMonsterByIdAndSalary_ParameterHashMap_ResultHashMap" parameterType="map" resultType="map">
        select * from `monster` where `id`>#{id} and `salary` > #{salary};
    </select>

4.测试

    @Test
    public void findMonsterByIdAndSalary_ParameterAndResultHashMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",10);
        map.put("salary",40);
        List<Map<String,Object>> monsterList=monsterMapper.findMonsterByIdAndSalary_ParameterHashMap_ResultHashMap(map);
        for (Map<String, Object> monsterMap : monsterList) {
            //System.out.println("monster="+monsterMap);

            Set<Map.Entry<String, Object>> entries = monsterMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                System.out.println(entry.getKey()+" : "+entry.getValue());
            }
            System.out.println("==========分割线=============");
        }
        if(sqlSession != null){
            sqlSession.close();
        }
        System.out.println("查询成功");
    }

六、传入类型:resultMap(结果集映射)

基本介绍
        当实体类的属性和表的字段名字不一致时,我们可以通过resultMap进行映射,从而屏蔽实体类属性名和表的字段名的不同。

演示案例1-参数的映射

        本质上是通过SQL的语句,配合POJO属性的替换#{}来完成映射的。

1.DB里面创建表user

create table USER(
user_id INT not null auto_increment,
user_email varchar(255) default "",
user_name varchar(255) default "",
PRIMARY KEY (user_id)
)CHARSET=utf8

2.创建java的POJO/Entity

故意设计的属性名字和DB不一致

完善getter,setter,NoargsConstructor,ToString等

public class User {
    //一样的,因为自增长,也看不到这个属性
    private int user_id;

    //_换成了大写
    private String userEmail;

    //_换成了小写
    private String username;
}

3.创建UserMapper接口和方法

public interface UserMapper {
    //插入user方法
    void insertUser(User user);
}

4.测试SQL语句

insert into `user`(`user_email`,`user_name`) values("94595892@qq.com","stein")

5.创建UserMapper.xml,使用映射器实现该方法

<mapper namespace="com.stein.mapper.UserMapper">
    <insert id="insertUser" parameterType="User">
        insert into `user`(`user_email`,`user_name`) values(#{userEmail},#{username})
    </insert>

注意:

 ①user()表中的字段对应DB,values()的字段对应Java的Entity,从而完成不同字段的映射。

 ②需要将UserMapper.xml这个文件在mybatis-config.xml进行注册。可以单文件注册,也可以通过文件夹进行注册。

    <mappers>
        <!--原生xml方式引入(注册)MonsterMapper.xml文件-->
        <!--<mapper resource="com/stein/mapper/MonsterMapper.xml"/>-->
        <!--<mapper resource="com/stein/mapper/UserMapper.xml"/>-->

        <!--通过添加文件夹的方式注册xml文件-->
        <package name="com.stein.mapper"/>
    </mappers>

③ParameterType写的是简称,所以要在Alias里面进行配置,否则要用全名

    <typeAliases>
        <!--<typeAlias type="com.stein.entity.Monster" alias="Monster" />-->
        <!--
            如果一个包下有很多的类,我们可以直接引入包,这样
            该包下面的所有类名,可以直接使用
        -->
        <package name="com.stein.entity"/>
    </typeAliases>

6.创建测试文件,进行测试

public class UserTest {
    private SqlSession sqlSession;
    private UserMapper userMapper;

    @Before
    public void init(){
        sqlSession= MybatisUtils.getSqlSession();
        //获取到MonsterMapper对象?实际是代理对象 class com.sun.proxy.$Proxy9
        //底层是使用了动态代理机制
        userMapper = sqlSession.getMapper(UserMapper.class);
        System.out.println("userMapper的运行类型是:"+userMapper.getClass());
    }

    @Test
    public void insertUser(){
        User user = new User();
        user.setUsername("PapaPig");
        user.setUserEmail("papapig@gmail.com");
        userMapper.insertUser(user);
        if(sqlSession!=null){
            sqlSession.commit();
            sqlSession.close();
        }
        System.out.println("添加用户成功");
    }

}

演示案例2-返回结果的映射

要求:返回所以的user查询结果

1.添加接口方法UserMapper

List<User> findAllUser();

2.使用映射器实现该方法

常规方法

    <select id="findAllUser" resultType="User">
        select * from `user`
    </select>

通过后期的测试可以发现,结果是查到了,但是封装到User的时候,因为属性名不匹配,导致属性值最终为null。

解决方法:使用resultMap来对不一致的地方进行映射

    <resultMap id="userResultMapper" type="User">
        <result property="userEmail" column="user_email"/>
        <result property="username" column="user_name"/>
    </resultMap>

    <select id="findAllUser" resultMap="userResultMapper">
        select * from `user`
    </select>

  <select id="findAllUser" resultType="User" resultMap="userResultMapper">

原来的resultType就替换成了resultMap,=等号指向它的id名字

type是结果要返回的最终类型

property是POJO/Entity的属性,column是DB表的字段名。从这里,也可以看出property和column的区别了。

3.测试

    @Test
    public void findUser(){
        List<User> allUser = userMapper.findAllUser();
        for(User user:allUser){
            System.out.println(user);
        }
        if(sqlSession!=null){
            sqlSession.close();
        }
        System.out.println("查询完毕");
    }

注意事项和细节

1、解决表字段和对象属性名不一致,也支持使用字段别名。

    <select id="findAllUser" resultType="User">
        select user_id,user_email as userEmail,user_name as username  from `user`
    </select>

2、如果是MyBatis-PIus处理就比较简单(后期会用到),可以使用注解@TableField来解决实体字段名和表字段名不一致的问题,还可以使用@TableName来解决实体类名和表名不一致的问题


网站公告

今日签到

点亮在社区的每一天
去签到