mybatis 的基本使用 —— XML 配置

Mr.Hddmybatis大约 15 分钟...

mybatis 的基本使用

mybatis

【官网】mybatis – MyBatis 3 | 官网open in new window

【github】 mybatis/mybatis-3: MyBatis SQL mapper framework for Java (github.com)open in new window

ORM(Object Relationship Mapping)对象关系映射。

  • 对象:Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系

XML 映射

​ mybatis 中使用 接口与XML形成映射关系,接口中定义方法,便于应用层调用,XML 文件中书写 sql 语句,便于操作数据库,由于直接书写 sql 语句,所以 mybatis 更加易于精细的优化。

简单实例: 查询所有用户的信息:

image-20221102170446881

public interface UserMapper {
   /**
   * 查询所有的用户信息
   * @return 所有的用户信息
   */
   List<User> getAllUser();
}

映射关系

  • 接口 getAllUser<select id="getAllUser" resultType="com.pojo.User"></select> 相映射
  • 实体类 Usersmbms_user 表相映射。

XML 映射文件中内容

以下标签都是可选的,需要使用对应的功能时引入。

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。

参数相关

在 mapper 接口中传递参数可以在对应的 xml 文件中获取。xml 文件中主要有两种获取参数的方式:

  • #{argument}:本质就是占位符赋值
  • ${argument}:本质就是字符串拼接

注意

  • ${} 使用字符串拼接的方式拼接 sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
  • #{} 使用占位符赋值的方式拼接 sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号

单个参数

如果在 mapper 接口中只有一个参数,那么在 xml 可用【任意名称变量】接受这个参数来赋值。

public interface UserMapper {
  /**
   * 根据用户 Id 查询用户信息
   * @param userId 用户 Id
   * @return 返回用户信息
   */
  List<User> getAllUserById(int userId);
}

多个参数

​ 若 mapper 接口中的方法参数为多个时,此时 MyBatis 会自动将这些参数放在一个map集合中:

会有两种形式

  • arg0,arg1, ...为键,以参数为值;
  • param1,param2,...为键,以参数为值;

例如通过 #{arg0} 或者 #{param1} 来获取第一个参数的值。

注意

例如:若有两个参数,不要以这样的组合获取:#{arg0}, #{param2}

使用 @Param() 注解标识参数

在 mapper 接口中为每个参数标注 @Param("参数名") 注解,然后在 xml 文件中就可以通过 #{参数名} 来获取参数值。例如:

public interface UserMapper {
  /**
   * 更新用户信息 (根据用户名和旧密码更新密码)
   * @param userName     用户名
   * @param oldUserPwd   旧密码
   * @param newUserPwd   新密码
   * @return 影响的行数
   */
  int updateUserPasswordInt(@Param("userName") String userName,
                            @Param("oldUserPwd") String oldUserPwd,
                            @Param("newUserPwd") String newUserPwd);
}

其他参数

实体类类型的参数

可以通过传入的实体类对象利用点操作符获取对应的属性值来赋值。例如:

public interface UserMapper {
  /**
   * 增加用户
   * @return 影响的行数
   */
  int insertUser(@Param("UserMsg") User UserMsg);
}

Map 类型的参数

传多个参数的另一种解决方案,将这些参数封装到 Map 集合中,然后在通过对应的键获取参数值,例如:

public interface UserMapper {
    /**
   * 查询用户
   * @param infoMap 传入的 Map
   * @return 用户信息
   */
  List<User> getUserByMap(Map<String,Object> infoMap);
}

注意

使用 Map 作为参数时,不要使用 @Param() 注解,这会使 Map 中的键不能暴露在外面,就会导致 #{key} 失效。

返回值相关

返回实体类对象

以实体类对象作为返回值

需要在 xml 文件中指定返回值的类型;即指定对应语句的 resultType 属性或者 resultMap 属性:

  1. resultType 属性对应某个实体类,且该实体类中属性名与对应的表的字段名一致才可用;
  2. 若实体类中属性名与对应的表的字段名不一致时,可使用 resultMap 属性,具体用法在下面。
public interface UserMapper {
  /**
   * 返回对应用户
   * @param userName 用户名
   * @param userRole 用户角色
   * @return 用户信息
   */
  User getUserByNameRole(@Param("userName") String userName ,@Param("userRole") Integer userRole);
}
将实体类对象的属性以属性名,属性值的形式装入 Map
public interface UserMapper {
  /**
   * 通过用户 Id 查询用户信息
   * @param id 用户Id
   * @return 用户信息
   */
  Map<String, Object> getAllUserInMap(@Param("id") int id);
}

注意

注意 xml 文件中 resultTypemap 类型。

也可以将以主键作为 key ,其他字段作为 value
public interface UserMapper {
  /**
   * 通过用户 Id 查询用户信息
   * @param id 用户Id
   * @return 用户信息
   */
  @MapKey("id")
  Map<Long, User> getAllUserInMap(@Param("id") int id);
}

注意

  • xml 文件中 resultTypemap 类型。
  • 需要使用 @MapKey 指定返回 Mapkey

返回 List

返回 List<实体类>
public interface UserMapper {
  /**
   * 根据用户 Id 查询用户信息
   * @param userId 用户 Id
   * @return 返回用户信息
   */
  List<User> getAllUserById(int userId);
}

注意

虽然返回值类型是 List<User> ,但是只需要 resultMap 或者 resultType 指定 List 中元素的映射或类型即可。

返回 List<Map<String, Object>>
public interface UserMapper {
  /**
   * 查询所有用户, 使用 List<Map<String, Object>>
   * @return 所有用户信息
   */
  @MapKey("id")
  List<Map<String, Object>> getAllUserInList();
}

注意

  • 注意 xml 文件中 resultTypemap 类型而非 List
  • @MapKey() 注解可用在返回值为 List<[Object,]Map> 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有:value,填入的是对象的某个属性名,作为 Mapkey 值。

HashMap 中的键是唯一的,所有选用的属性应该是表中的主键或唯一字段,TreeMap 中的键不唯一,可根据需要自定义键值。

返回某个字段

根据该字段在实体类中对应的属性的返回值来决定返回值类型

public interface UserMapper {
  /**
   * 查询 User 的数量
   * @return User 数量
   */
  int UserCount();
}

返回结果映射 (resultMap)

结果映射配置open in new window

【普通使用】

​ 当数据库中字段名称与对应的实体类属性名称不一致时,可以使用其;下面表中使用下划线连接,而实体类中使用驼峰式命名,对其进行映射。

<!-- XML 映射文件 -->
<resultMap id="mapUser" type="com.pojo.User">
  <id column="id" property="id" />
  <result column="user_code" property="userCode" />
  <result column="user_name" property="userName" />
  <result column="user_password" property="userPassword" />
  <result column="modify_date" property="modifyDate" />
</resultMap>
  • resultMap-id 表示该映射的名称,在后续使用该映射时使用,eg: resultMap="mapUser"

  • resultMap-type 表示要映射的实体类

  • id 可以对主键或唯一字段设置,可提高整体性能,但只能设置一个

  • result 用来配置字段与属性的映射

  • result-column 字段名

  • result-property 属性名

其他解决方案

​ 如果表中的所有字段都是蛇形命名,而是实体类属性都是驼峰式命名,那么可以在 XML 配置文件中对齐进行配置来解决这一问题。

​ 具体为配置 <settings> 中的 mapUnderscoreToCamelCase 属性。其默认值为 false (不开启),配置为 true 时开启。

<settings>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

多对一映射

因为一个表中的数据可能与其他表的数据相关,那么在数据库层面就有多对一的映射。

例如:有一个公司职员信息表和一个公司部门信息表。

职工ID(eid)职工所属部门ID(departId)职工薪资(esalary)职工联系方式(email)
----

问题

​ 若要根据员工ID查询一个员工个人信息及其所在部门信息,那么员工这一个人就会对应它所在部门的所有信息,这就会导致 部分信息对应一个员工,即多对一。那么就需要将数据层面的多对一映射关系转换到实体类之间。

解决方案:

  1. 使用 resultMap 中的 result 的级联属性
  2. 使用 resultMapassociation
public class Employee implements Serializable{
  private String eid;
  private String departId;
  private Integer esalary;
  private String email;
  // 因为要查询其部门信息,所以要再加一个字段
  private Edepartment Edepart;
}






 

相关信息

<association property="Edepart" javatype="Edepartment">
  • property 为实体类属性。
  • javatype 表示该属性对应的实体类。

另一种解决方案: 分部查询 + association

将根据员工ID查询一个员工个人信息及其所在部门信息分解为先根据员工ID查询个人信息,然后再使用员工的所在部门 ID 去查询对应的部门信息。

public interface EmployeeMapper {
  /**
   * 第一步:根据员工 ID 查询一个员工个人信息
   */
  Employee getEmployeesInfoById(@Param("eid") Integer eid);
}

分部查询更加灵活,因为是对不同接口中功能的组合,并且各个分部语句也能实现其他所需的功能(但这个需要开启延迟加载)。

<settings>
 <!-- 延迟加载全局开启,开启时,所有关联对象都会延迟加载-->
 <setting name="lazyLoadingEnabled" value="true"/>

  <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。否则就会按需加载,其在 3.4.1 之后默认为 false 
	所有高于 3.4.1 可以不写。
	-->
 <setting name="aggressiveLazyLoading" value="false"/>
</settings>

一对多映射

需要查询部门信息及其所有员工的信息

解决方案:

  1. 使用 collection
  2. 分部查询 + collection

第一种:使用 collection

public class Edepartment implements Serializable{
  private String departId;
  private String departHead;
  private String departResearchDirection;
  // 该部门的员工集合
  private List<Employee> employees;
}





 

注意

<collection> 中并没有设置其属性 Edepart ,即部门信息,这个并不需要在这里设置,不然就成套娃了。

第二种:分部查询:

  1. 先根据部门ID 查询部门信息
  2. 再根据部门ID 查询所有的员工信息
public interface EdepartmentMapper {
  /**
   * 第一步:先根据部门ID 查询部门信息
   */
  Edepartment getEdeptById(@Param("did") Integer did);
}

相关信息

延迟加载也同样适用于该分部查询。可在开启全局延迟加载的情况下设置 collection-fetch 属性决定这一分部查询是否开启延迟加载。

缓存相关

一级缓存

二级缓存

第三方二级缓存

你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.9