1. Mybatis 的参数

1.1 parameterType (输入类型)

1.1.1 传递简单参数

基本类型和 String 我们可以直接写类型名称,可以使用包名.类名的方式,例如:java.lang.String。

1.1.2 传递 POJO 对象

对于实体类类型,目前我们只能使用全限定名称。

Mybatis 使用 ognl 表达式解析对象字段的值,#{} 或者 ¥{} 括号中为的值为 pojo 属性名称

OGNL表达式:

是由 Apache 开发的,全称是 Object Graphic Navigation Language(对象图导航语言)。它是通过对象的取值方法来获取数据,在写法上把get省略了。

比如我们获取用户的名称:

类中的写法:user.getUsername();

OGNL表达式写法:user.username

Mybaits 中为什么能直接写 username, 而不用user. 呢?

因为在 parameterType 中已经提供了这个属性所属的类,所以此时不需要写对象名,而是直接写属性名。

1.1.3 传递 POJO 包装对象

开发中通过 pojo 查询条件,查询条件是综合的查询条件,不仅包括用户查询条件还包括其他的查询条件 (比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传入输入参数,pojo 类中包含 pojo。

这个方法也经常用于开发中,由多个对象组成查询条件,然后进行查询

那么这个传递 POJO 包装对象到底应该怎么使用呢?

以下是一个例子:

1.4.1 例子 - 该例子的前置条件在 完成CRUD一章有

  1. 首先我们要在 dao 类中写入一个方法:
/**
     * 根据 queryVo 中的条件查询用户
     * @param vo
     * @return
     */
    List<User> findByVo(QueryVo vo);
  1. 接着创建一个 POJO 类,这个 POJO 和其他的 POJO 没有什么不同,只不过,它其中封装或者说包装了一个 POJO 对象:
package com.itheima.domain;

public class QueryVo {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
  1. 然后配置映射文件
<!--    根据QueryVo 的条件查询用户-->
    <select id="findByVo" parameterType="com.itheima.domain.QueryVo" resultType="com.itheima.domain.User">
        select * from user where username like #{user.username};
    </select>
  1. 在测试文件写入以下测试方法:
@Test
    public void testFindByVo() {
        QueryVo vo = new QueryVo();
        User user = new User();
        user.setUsername("%王%");
        vo.setUser(user);
        // 执行查询一个方法
        List<User> users = userDao.findByVo(vo);
        for(User u : users) {
            System.out.println(user);
        }
        sqlSession.commit();
    }
  1. 以下是执行结果:
2019-08-22 19:27:55,480 0      [           main] DEBUG ache.ibatis.logging.LogFactory  - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
2019-08-22 19:27:55,509 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
2019-08-22 19:27:55,509 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
2019-08-22 19:27:55,509 29     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
2019-08-22 19:27:55,510 30     [           main] DEBUG source.pooled.PooledDataSource  - PooledDataSource forcefully closed/removed all connections.
2019-08-22 19:27:55,593 113    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
2019-08-22 19:27:56,699 1219   [           main] DEBUG source.pooled.PooledDataSource  - Created connection 391630194.
2019-08-22 19:27:56,700 1220   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1757cd72]
2019-08-22 19:27:56,704 1224   [           main] DEBUG .itheima.dao.IUserDao.findByVo  - ==>  Preparing: select * from user where username like ?; 
2019-08-22 19:27:56,734 1254   [           main] DEBUG .itheima.dao.IUserDao.findByVo  - ==> Parameters: %王%(String)
2019-08-22 19:27:56,763 1283   [           main] DEBUG .itheima.dao.IUserDao.findByVo  - <==      Total: 4
User{id=41, username='老王', address='北京', sex='男', birthday=Tue Feb 27 17:47:08 CST 2018}
User{id=42, username='小二王', address='北京金燕龙', sex='女', birthday=Fri Mar 02 15:09:37 CST 2018}
User{id=43, username='小二王', address='北京金燕龙', sex='女', birthday=Sun Mar 04 11:34:34 CST 2018}
User{id=46, username='老王', address='北京', sex='男', birthday=Wed Mar 07 17:37:26 CST 2018}
2019-08-22 19:27:56,765 1285   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1757cd72]
2019-08-22 19:27:56,765 1285   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1757cd72]
2019-08-22 19:27:56,765 1285   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 391630194 to pool.

2. Mybatis 输出结果封装

2.1 resultType 配置结果类型

reusltType 属性可以指定结果集的类型,他支持基本类型和实体类类型。

我们在前面的 CRUD 案例中已经对此属性进行过应用了。

需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名

同时,当然是实体类名称时,还有一个要求,实体类中的属性名称必须和查询语句中的类名保持一致,否则无法实现封装。

2.1.1 基本类型实例

  1. 在 dao 接口中 :
int CountUsers(); // 查询所有结果
  1. 映射文件配置:
<select id="CountUsers" resultType="int">
	select count(id) from user;
</select>

2.1.2 实体类类型实例

  1. 在 dao 接口中:
List<User> findAll(); // 查询所有
  1. 映射文件中:
<select id="findAll" resultType="com.domain.User">
	select * from user;
</select>

2.1.3 特殊情况实例

  1. 修改实体类
    我们将实体类修改成这个样子:
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private String userAddress;
    private String userSex;
    private Date userBirthday;

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + userName + '\'' +
                ", userAddress='" + userAddress + '\'' +
                ", userSex='" + userSex + '\'' +
                ", userBirthday=" + userBirthday +
                '}';
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }
}

此时,实体类已经和我们之前的使用的实体类不一样了。

  1. 然后,我们需要将映射文件中的部分内容进行修改:
  1. 首先: 获取 插入数据的id 这一个操作中的
keyProperty = "id"

由于我们已经修改了实体类,实体类中已经没有 id 这一个属性类了,就会报错

将其修改为:

keyProperty = "userId"
  1. 其次,当 sql 语句中的
(#{username}, #{address}, #{sex}, #{birthday});

values 开始寻找 OGNL 表达式,当 OGNL 表达式找不到对应的属性名称,也会报错。

将其修改为:

(#{userName}, #{userAddress}, #{userSex}, #{userBirthday});
  1. 因为 Mybatis 需要我们实体类中的 属性名称和数据库中的属性名相同,当我们修改了实体类之后,它们之间不能一一对应,所以出现了以下的情况:
User{userId=null, username='老王', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='小二王', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='小二王', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='传智播客', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='老王', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='小马宝莉', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='mybatis update user1', userAddress='null', userSex='null', userBirthday=null}
User{userId=null, username='mybatis save user and get id', userAddress='null', userSex='null', userBirthday=null}

至于为什么会有 username 的结果,这是因为,在 windows 中的 mysql 的列名不分大小写。在这里需要注意的是,在 Linux 操作系统下,mysql 数据库是严格区分大小的。这个时候连 username 都封装不进去。

当然解决这些问题最直接的方法,当时直接给属性起别名:

select id as userId, username as userName, address as userAddress, birthday as userBirthday, sex as userSex from user;

当我们起好别名以后,这样,我们的属性名和列名就一致了。

起别名这种方式,一定是可以的,同时这种方式也是效率最高的,因为这是在 SQL 层面上解决问题。

当然,Mybatis 框架也提供了一个 resultMap 结果类型,采用配置的方式,解决这个问题。

2.2 resultMap 结果类型

resultMap 标签可以建立查询的列名和实体类的属性名不一致时建立对应关系。从而实现封装。

在 select 标签中使用 resultMap 属性指引引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

2.2.1 定义 resultMap
<!-- 建立 User 实体和数据库表的对应关系
	type 属性:指定实体类的全限定类名
	id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->
<resultMap id="userMap" type="com.itheima.domain.User">
<!--        主字段的对应 -->
        <id property="userId" column="id"></id>
<!--        非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
</resultMap>

同时我们必须修改查询所有这段映射文件配置的内容,以使用 resultMap:

<!--    查询所有操作-->
    <select id="findAll" resultMap="userMap">
        <!--select id as userId, username as userName, address as userAddress, sex as userSex, birthday as userBirthday from user; -->
        select * from user;
    </select>

由于在程序的运行时,必须多解析一段 xml 语句,所以效率不及直接使用sql语句,但是,带来的好处是开发的效率大大地提高。