绝大多数情况下,一条完整的信息至少分别来自两张或以上的表,连表查询非常常见,这种情况下可以使用resultMap属性
user表中的userRole是外键,对应role表中的id
需求:通过userName和userRole,查询到符合条件的用户和其RoleName
在这里,用户信息来自user表,roleName来自role表。我们采用封装对象的传参的方式进行查询。首先可以修改pojo的User类,添加一个userRoleName的String属性和相应的setter、getter方法
然后在mapper文件中新建一个select标签:
<select id="getUserList" resultMap="userList" parameterType="User">
select u.*,r.roleName from smbms_user AS u,smbms_role AS r where u.userName
like CONCAT('%',#{userName},'%') AND u.userRole = #{userRole}
AND u.userRole = r.id
</select>
这里没有使用resultType标签了,而是换成了resultMap,它的值由我们自定义。
然后,在刚刚编写完成select标签外,创建一个resultMap标签:
<!--id与使用resultMap属性的标签值一致,type是返回类型-->
<resultMap id="userList" type="User">
<!--column是表的列名 property是查出来的字段值要赋给的实体对象的属性名称-->
<!--意味着我们采用自定义的方式进行映射-->
<result property="id" column="id"></result>
<result property="userCode" column="userCode"></result>
<result property="userName" column="userName"></result>
<!--在这里把role表的roleName列的值赋给了User类的userRoleName属性-->
<result property="userRoleName" column="roleName"></result>
</resultMap>
接口方法没有什么新变化:
List<User> getUserList(User user);
测试类:
public void getUserList() {
SqlSession sqlSession = null;
List<User> list;
User u = new User();
u.setUserName("赵");
u.setUserRole(2);
try {
sqlSession = MyBatisUtils.getSQLSession();
list = sqlSession.getMapper(UserMapper.class).getUserList(u);
for (User user : list)
logger.debug("UserCode:" + user.getUserCode() + "\tUserRoleName:" + user.getUserRoleName() + "\tUserName:"
+ user.getUserName() + "\tUserAddress:"
+ user.getAddress());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
}
可以得到的查询结果是:
UserCode:zhaomin UserRoleName:经理 UserName:赵敏 UserAddress:北京市昌平区天通苑3区12号楼
引申说明:这里是涉及到一个叫做自动映射级别(autoMappingBehavior)的属性,设置的位置是在mybatis-config.xml核心配置文件中标签中的一个,一共三个value,分别是"NONE" “FULL” 和 “PARTIAL”
FULL:自动匹配所有属性
NONE:禁止自动匹配(只能依靠自定义resultMap,没有定义的都会是null)
PARTIAL:默认,自动匹配所有属性,内部嵌套除外
<settings>
<setting name="logImpl" value="LOG4J" />
<setting name="autoMappingBehavior" value="FULL" />
</settings>
resultType和resultMap的区别:
resultType直接表示了返回类型,可以是基本数据类型,也可以是复杂数据类型。
resultMap,是对外部resultMap的引用,用于数据库字段信息与对象属性不一致的情况,可以完成较复杂的联合查询。
二者本质上都是Map数据结构,不要同时使用。
resultMap属性
id:resultMap的唯一标识
type:Java实体类
resultMap子元素:
id:一般对应数据库中该行的主键id,设置此项可提高mybatis性能
result:映射到JavaBean的某个“简单类型”属性
association:映射到JavaBean的某个“复杂类型”属性,比如JavaBean类
collection:映射到JavaBean的某个“复杂类型”属性,比如集合
association:
复杂的类型关联,一对一
内部嵌套:映射一个嵌套JavaBean属性(一个JavaBean作为另一个JavaBean的属性)
属性:
property:映射数据库列的实体对象的属性
javaType:完整Java类名或者别名
resultMap:引用外部resultMap
子元素:
应用情景:多表查询多个字段,如果每一个字段都要"复制"到同一个JavaBean实体类中,非常繁琐。这种情况可以使用JavaBean的嵌套。
例如一个user类,含有一个role类的属性。这样相当于把role类的所有属性都封装并放入了user类。
先添加一个接口方法:通过角色id获得相应的用户列表
List<User> getUserListByRoleId(@Param("userRole") Integer roleId);
mapper文件:
<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>
<resultMap type="Role" id="roleResult">
<id property="id" column="r_id"/>
<result property="roleCode" column="roleCode"/>
<result property="roleName" column="roleName"/>
</resultMap>
<select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
select u.*,r.id as r_id,r.roleCode,r.roleName from smbms_user AS u,smbms_role AS r
where u.userRole = #{userRole} and u.userRole = r.id
</select>
这里相对复杂,解释如下:
首先创建了select语句,因为是连表查询,因此通过resultMap ="userRoleResult"进行第一次映射。
在userRoleResult这个resultMap中,返回的对象其实依旧是User,
然后 < id property=“id” column=“id”/ >中的第一个id就是User类中的id属性名,就叫id。column中的id是对应的列名或者别名,因为在查询语句中没有起别名,所以就是user表的id列的列名:id。
然后是常规的result自定义映射,如果在mybatis-config.xml配置文件中将autoMappingBehavior的value设置为"FULL"的话,所有的result映射都可以不做,因为会进行全局的同名自动映射。
然后需要添加
< association property=“role” javaType=“Role” resultMap=“roleResult”/ >这个标签。因为在User这个类中嵌套了Role,所以需要再次的resultMap,property就是在JavaBean中要映射的JavaBean的属性名,在这里就是User类中的Role role;属性名是role,javaType就是要映射的类的完整限定名,因为在配置文件中已经配置了
,所以这里直接写Role就可以。然后要对Role再做映射配置,所以再次resultMap到roleResult
在id为roleResult的resultMap中,返回的类型type自然是Role,然后这里面的子节点 < id property=“id” column=“r_id”/ >property的id就是Role这个类中id的属性名,就是id,而column则是表中的列名,因为在sql语句中起了别名,因此是r_id
下面依旧是常规的result映射…
全部完成后的测试方法:
public void getUserListByRoleIdTest() {
SqlSession sqlSession = null;
List<User> userList = new ArrayList<>();
Integer roleId = 3;
try {
sqlSession = MyBatisUtils.getSQLSession();
userList = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(roleId);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
logger.debug("getUserListByRoleIdTest userList.size : " + userList.size());
for (User user : userList) {
logger.debug("userList =====> userName: " + user.getUserName()
+ ", userPassword: " + user.getUserPassword()
+ ", Role: " + user.getRole().getId() + " --- "
+ user.getRole().getRoleCode() + " --- " + user.getRole().getRoleName());
}
}
以上就是association在处理一对一情况下的使用,而在处理一对多的时候,就需要使用另一个属性:collection
collection:
复杂类型集合,一对多
内部嵌套:映射一个嵌套结果集到一个列表
属性:
property:映射数据库列的实体对象的属性
ofType:完整Java类名或者别名(集合所包括的类型)
resultMap:引用外部resultMap
子元素:
id
result:
property:映射数据库列的实体对象的属性
column:数据库列名或者别名
模拟需求:获取指定id的用户信息和地址列表(同一用户有多个不同地址)
这里需要引入一张address表,同时创建Address的pojo类
完成后,需要在User类中添加一个List集合,泛型为Address,并生成setter和getter方法
private List<Address> addressList;//用户地址列表
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
然后在接口类中增加方法:
List<User> getAddressListByUserId(@Param("id") Integer userId);
mapper文件:
<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>
<resultMap type="Address" id="addressResult">
<id property="id" column="a_id"/>
<result property="postCode" column="postCode"/>
<result property="tel" column="tel"/>
<result property="contact" column="contact"/>
<result property="addressDesc" column="addressDesc"/>
</resultMap>
<select id="getAddressListByUserId" parameterType="Integer" resultMap="userAddressResult">
select u.*,a.id as a_id,a.contact,a.addressDesc,a.postCode,a.tel,a.userId
from smbms_user u LEFT JOIN smbms_address a ON u.id = a.userId where u.id=#{id}
</select>
这里的用法跟association基本相似,不同的是< collection property=“addressList” ofType=“Address” resultMap=“addressResult”/ >
这里的property指的是在JavaBean中所映射的数据库类的实体对象的属性名,这个属性是 private List< Address> addressList; 属性名就是addressList。
ofType是完整的类名或别名,也就是集合所包含的类型。依旧可以直接使用别名Address。
id、result的配置与association中的一致
测试方法:
public void getAddressListByUserIdTest() {
SqlSession sqlSession = null;
List<User> userList = new ArrayList<>();
Integer userId = 1;
try {
sqlSession = MyBatisUtils.getSQLSession();
userList = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(userId);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
for (User user : userList) {
logger.debug("userCode:" + user.getUserCode() + "\tuserName:" + user.getUserName());
//遍历到address的值的时候,因为它是个集合,所以再嵌套一个循环
for (Address address : user.getAddressList()) {
logger.debug("address---->" + address.getId()
+ "\tcontact:" + address.getContact()
+ "\taddressDesc:" + address.getAddressDesc()
);
}
}
}
Mybatis框架的使用之五传送门:
https://blog.csdn.net/wangduanqs/article/details/85063368