高级映射注意点
如果实体类属性为另一个对象,或者是另一个对象的集合时,在查询的时候我们就需要使用高级映射,标签:
association -- 属性为对象时使用
collection -- 属性为集合时使用
这里就以员工和部门为例。
部门对象包含员工集合类,这里使用了 lombok 插件。代码如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department implements Serializable {
/**
* 部门 ID
*/
private int pid;
/**
* 部门名称
*/
private String name;
/**
* 部门员工
*/
private List<Employee> employeeList;
}
员工对象中所属部门使用的是部门对象,代码如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee implements Serializable {
/**
* 员工 ID
*/
private String eid;
/**
* 员工姓名
*/
private String name;
/**
* 所属部门
*/
private Department department;
/**
* 手机号
*/
private String telPhone;
/**
* 年龄
*/
private int age;
}
两个实体类对应的 Mapper 接口我只实现了一个 Add 和 Get 方法,如下:
public interface EmployeeMapper {
int addEmployee(Employee employee);
Employee getEmployeeByEid(String eId);
}
public interface DepartmentMapper {
int addDepartment(Department dep);
Department getDepById(int pid);
}
Mapper 接口对应的 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.hukanmasheng.mapper.EmployeeMapper">
<resultMap id="EmployeeDep" type="Employee">
<result property="eid" column="eid" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="telPhone" column="telphone" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<association property="department" javaType="Department">
<result property="pid" column="pid" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
</association>
</resultMap>
<select id="getEmployeeByEid" parameterType="string" resultMap="EmployeeDep">
select a.*,b.* from mybatis_employee a,mybatis_department b
where a.pid=b.pid and a.eid=#{eid}
</select>
<insert id="addEmployee" parameterType="Employee">
insert into mybatis_employee (eid,name,pid,telphone,age)
values (#{eid},#{name},#{department.pid},#{telPhone},#{age})
</insert>
</mapper>
<?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.hukanmasheng.mapper.DepartmentMapper">
<resultMap id="DepEmployee" type="Department">
<result property="pid" column="pid" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<collection property="employeeList" ofType="Employee">
<result property="eid" column="eid" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="telPhone" column="telphone" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
</collection>
</resultMap>
<select id="getDepById" resultMap="DepEmployee">
select a.*,b.* from mybatis_department a,mybatis_employee b
where a.pid=b.pid and a.pid=#{pid}
</select>
<insert id="addDepartment" parameterType="Department">
insert into mybatis_department (pid,name) values (#{pid},#{name})
</insert>
</mapper>
从配置文件可以看到, Employee 中的 department 属性使用的映射为:
<association property="department" javaType="Department">
<result property="pid" column="pid" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
</association>
属性为一个对象时,使用 association 标签。
这里的一个注意点就是 association 中对象的类型是填写在 javaType 属性中的。我这里 Mybatis 核心文件配置了别名,所以不需要写类的全限定名。
DepartmentMapper 配置文件中员工集合使用映射为:
<collection property="employeeList" ofType="Employee">
<result property="eid" column="eid" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="telPhone" column="telphone" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
</collection>
属性为集合是,使用 coolection 标签。
这里需要和 association 做一个对比,因为是集合,所以不用配置 javaType 属性,而是将集合存储的对象类型配置在 ofType 属性上。
二级缓存注意点
这里是我自己遇到的一个小问题,在Mapper.xml 文件配置 二级缓存信息的时候,为了省事,我直接配置
<cache />
结果发现,从二级缓存中取出的对账地址和第一次从数据库取出的对象地址不一致。根据日志可以发现 SQL 只执行了一次。
Opening JDBC Connection
Created connection 1741979653.
==> Preparing: select * from mybatis_user where id=?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 胡侃, 123
<== Total: 1
com.hukanmasheng.pojo.User@55141def
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@67d48005]
Returned connection 1741979653 to pool.
As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
Cache Hit Ratio [com.hukanmasheng.mapper.UserMapper]: 0.5
com.hukanmasheng.pojo.User@30c15d8b
false
后来去 MyBatis 中文网查到了原因。官方给出了一个缓存的配置
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
在这个配置下面有一段关于 readOnly 属性的描述:
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
原来只有将 readOnly 设置为 true 时,才会返回同一个对象实例。