一、本课目标
- 掌握resultMap的基本配置项(重点)
- 掌握使用resultmap实现复杂类型关联(重点)
- 了解resultMap自动映射级别
- 了解MyBatis缓存
二、resultMap
image.pngtype表示这个resultmap这个映射结果的类型,通常情况下是java实体类。
其子元素中的id,一般对应数据库中该行的主键id;result也是映射到javaBean的某个“简单类型”的属性。id和result的相同点都是映射一个单列的值到一个属性或者一个字段,但是为什么还需要id呢?因为设置id可以提高mybatis的性能,因为id是作为唯一标识的,所以当和其它实例进行对比的时候,这个id就非常的有用,特别是应用到缓存或者内建的结果映射的时候,它可以非常有效的提高整体性能。当然不设置id也是可以的,但是出于性能的考虑,一般都设置一下。
2.1association
image.png需求:根据用户角色的id去获取该角色下的用户列表信息,也包括用户的角色的信息。
配置文件:
<resultMap type="user" id="userRoleResult">
<id property="id" column="id"/>
<result property="userCode" column="userCode"/>
<result property="userName" column="userName"/>
<result property="userRole" column="userRole"/>
<association property="role" javaType="Role">
<id property="id" column="r_id"/>
<result property="roleCode" column="roleCode"/>
<result property="roleName" column="roleName"/>
</association>
</resultMap>
<select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
select u.*,r.id as r_id,r.roleCode, r.roleName
from smbms_user u,smbms_role r where u.userRole=#{userRole}
and u.userRole=r.id
</select>
java代码:
/**
* 根据角色id查询出用户列表信息
* @param roleId
* @return
*/
public List<User> getUserListByRoleId(@Param("userRole")Integer roleId);
测试代码:
@Test
public void testGetUserListByRoleId() {
SqlSession sqlSession = null;
List<User> userList= new ArrayList<User>();
Integer roleId = 3;
try {
sqlSession = MyBatisUtil.createSqlSession();
userList = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(roleId);
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user:userList) {
logger.debug("mmmmmmmmmmmmmmp" + user.getUserCode());
}
}
测试正常。
association里面的peoperty属性对应的是user里面的某个字段;javatype指的是user里面这个字段的数据类型。这个地方一定要注意所出现的列名都是无歧义的。比如在这个地方role里面有一个id,user里面也有一个id,所以在写sql语句的时候就需要把这两个id给区分开。
注:在测试的时候出现一个bug:java.lang.NoClassDefFoundError: Could not initialize class cn.smbms.utils.MyBatisUtil,原因是我把resultMap写成resultType了。
思考;在上一个例子中,“userRoleResult”联合一个association结果映射来加载User实例,那么association的role结果映射是否可复用?
可以的,配置文件可以修改如下:
<resultMap type="role" id="roleResult">
<id property="id" column="r_id"/>
<result property="roleCode" column="roleCode"/>
<result property="roleName" column="roleName"/>
</resultMap>
<resultMap type="user" id="userRoleResult">
<id property="id" column="id"/>
<result property="userCode" column="userCode"/>
<result property="userName" column="userName"/>
<result property="userRole" column="userRole"/>
<association property="role" javaType="Role" resultMap="roleResult"/>
</resultMap>
<select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
select u.*,r.id as r_id,r.roleCode, r.roleName
from smbms_user u,smbms_role r where u.userRole=#{userRole}
and u.userRole=r.id
</select>
2.2collection
image.png需求:要求获取指定用户的相关信息和地址列表,因为一个用户可以有多个地址,所以需要在用户实体类中添加一个地址实体类的集合。
首先创建Address实体类:
package cn.smbms.pojo;
import java.util.Date;
public class Address {
private Integer id; //主键ID
private String postCode; //邮编
private String contact; //联系人
private String addressDesc; //地址
private String tel; //联系电话
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate; //更新时间
private Integer userId; //用户ID
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String getAddressDesc() {
return addressDesc;
}
public void setAddressDesc(String addressDesc) {
this.addressDesc = addressDesc;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
}
在User实体类中添加以下代码:
private List<Address> addressList; // 用户地址列表
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
修改配置文件:
<resultMap type="user" id="userAddressResult">
<id property="id" column="id"/>
<result property="userCode" column="userCode"/>
<result property="userName" column="userName"/>
<collection property="addressList" ofType="address">
<id property="id" column="a_id"/>
<result property="contact" column="contact"/>
<result property="addressDesc" column="addressDesc"/>
<result property="tel" column="tel"/>
<result property="postCode" column="postCode"/>
</collection>
</resultMap>
<select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer">
select
u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode
from
smbms_user u, smbms_address a
where
u.id=a.userId
and
u.id=#{id}
</select>
在接口中增加方法:
/**
* 根据用户id获取用户信息以及地址列表
* @param userId
* @return
*/
public List<User> getAddressListByUserId(@Param("id")Integer userId);
测试代码:
@Test
public void testGetAddressListByUserId() {
SqlSession sqlSession = null;
List<User> userList= new ArrayList<User>();
Integer userId = 1;
try {
sqlSession = MyBatisUtil.createSqlSession();
userList = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(userId);
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user:userList) {
logger.debug(user.getUserName() + user.getUserCode());
for (Address address:user.getAddressList()) {
logger.debug(address.getId() + address.getContact() + address.getAddressDesc());
}
}
}
测试结果正常。
如何提高映射结果的可重用性?
可以通过collection的resultMap属性。
将xml配置文件修改如下即可:
<resultMap type="address" id="addressResult">
<id property="id" column="a_id"/>
<result property="contact" column="contact"/>
<result property="addressDesc" column="addressDesc"/>
<result property="tel" column="tel"/>
<result property="postCode" column="postCode"/>
</resultMap>
<resultMap type="user" id="userAddressResult">
<id property="id" column="id"/>
<result property="userCode" column="userCode"/>
<result property="userName" column="userName"/>
<collection property="addressList" ofType="address"
resultMap="addressResult"/>
</resultMap>
<select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer">
select
u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode
from
smbms_user u, smbms_address a
where
u.id=a.userId
and
u.id=#{id}
</select>
三、自动映射级别
问题:在以上collection示例中,输出在userAddressResult中没有进行匹配的属性,比如,userPassword,观察输出结果。
根据之前的经验,当在mybatis-config.xml的setting标签中没有把自动映射行为即autoMappingBehavior的value设置为NONE的话,则会把查出来的字段跟实体类中的属性自动匹配。
现在做一个测试:去mybatis-config.xml里面把设置自动映射的标签注释掉:
<settings>
<setting name="logImpl" value="LOG4J"></setting>
<!-- <setting name="autoMappingBehavior" value="NONE"></setting> -->
</settings>
然后在测试代码中输出在resultMap中没有进行映射的字段:
@Test
public void testGetAddressListByUserId() {
SqlSession sqlSession = null;
List<User> userList= new ArrayList<User>();
Integer userId = 1;
try {
sqlSession = MyBatisUtil.createSqlSession();
userList = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(userId);
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user:userList) {
logger.debug(user.getUserName() + user.getUserCode() + user.getUserPassword());
for (Address address:user.getAddressList()) {
logger.debug(address.getId() + address.getContact()
+ address.getAddressDesc() + address.getUserId());
}
}
}
其他的跟上面的collection示例相同,运行结果如下:
搜狗截图20180826104246.png
结果发现未做映射的字段全都为空,但是根据之前的经验,这里应该是自动映射的啊,为什么?
这是因为如果是普通的数据类型属性,则会自动匹配所有的;现在没有自动匹配所有,是因为有内部嵌套,也就是说是因为你使用了collection或者association。如果在使用这两个标签的情况下也想进行自动匹配,则需要对映射级别进行如下设置:
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
注:这里“FULL”必须全部大写,否则会报错。
运行结果如下:
image.png
可以看到用户密码已经有了,但是地址对象下面的userId还是空的。
这是因为在查询的sql语句中没有查出来address表中的userId。修改sql语句:
<select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer">
select
u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode, a.userId
from
smbms_user u, smbms_address a
where
u.id=a.userId
and
u.id=#{id}
</select>
测试之后结果如下:
image.png
小结:
image.png四、MyBatis缓存
image.png和大多数的这种持久层框架一样,Mybatis也提供缓存,包括一级缓存和二级缓存。所谓一级缓存,是基于Mybatis自带的一个HashMap的本地缓存,它的作用范围只在session的作用域里,所以当sessionflush之后,这个session里面的所有cache都会被清空。
二级缓存是一个全局缓存,它的作用域超出session范围之外,可以被其他的所有的sqlsession所共享。二级缓存的配置方式有以下两种:
1、可以在核心配置文件中通过setting标签设置相应的缓存
image.png
2、在Mapper XML文件中设置缓存
image.png
需要注意的是二级缓存的作用域只针对Mapper里面的namespace(每一个mapper文件都有的那个namespace),也就是说只有指定的namespace才能共享查询的cache。
3、单独设置cache
image.png
网友评论