在项目中,某些实体类之间肯定有关联关系,比如一对一,一对多等。mybatis 中使用 association 和 collection 。
association:一对一关联(has one)
collection :一对多关联(has many)
注意:
只有做查询时才会涉及到联表,使用其完成联表查询 。两个标签都可以总结为三种方式。
先来看看如下代码(set,get方法没有列出)
public class User {
private Integer userId;
private String userName;
private Integer age;
private Card card;//一个人一张身份证,1对1
}
public class Card {
private Integer cardId;
private String cardNum;//身份证号
private String address;//地址
}
public interface UserMapper {
/**
* 通过userId查询user信息
* @param userId
* @return
*/
User queryById(int userId);
}
<select id="queryById" parameterType="int" resultMap="userMap">
SELECT u.user_name,u.age,c.card_id,c.card_num,c.address
FROM tb_user u,tb_card c
WHERE u.card_id=c.card_id
AND
u.user_id=#{userId}
</select>
以上是实体类,dao层的设计以及在UserMapper.xml中 queryById 方法的 sql 语句的编写,因为不论用 association的哪种方式,sql语句都是一样的写,sql 语句都是一样的写,不同的只是 userMap 的写法,所以这里先给出这段代码。User 和 Card 是一对一的关系,在数据库中,t_user 表通过外键 card_id 关联 t_card 表。下面分别用 association 的三种用法来实现 queryById 方法。
这种方法需要再定义 CardMapper.java如下:
public interface CardMapper {
Card queryCardById(int cardId);
}
在 CardMapper.xml 中实现该方法:
<select id="queryCardById" parameterType="int" resultType="com.ck.smm.entity.Card">
SELECT *
FROM tb_card
WHERE card_id=#{cardId}
</select>
然后再看 UserMapper.xml 是如何引用这个方法的:
<resultMap type="User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<association property="card" column="card_id"
select="com.ck.ssm.dao.CardMapper.queryCardById">
</association>
</resultMap>
在这里直接通过 select 引用 CardMapper 的 queryById 方法。个人感觉这种方法比较麻烦,因为还要在 CardMapper 里定义 queryCardById 方法并且实现再引用才有用,不过这种方法思路清晰,易于理解。
<resultMap type="com.ck.smm.entity.Card" id="cardMap">
<id property="cardId" column="card_id"/>
<result property="cardNum" column="card_num"/>
<result property="address" column="address"/>
</resultMap>
<resultMap type="User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<association property="card" resultMap="cardMap">
</association>
</resultMap>
第二种方法就是在 UserMapper.xml 中先定义一个 Card 的 resultMap,然后在 User 的r esultMap 的 association 标签中通过 resultMap=“cardMap” 引用。这种方法相比于第一种方法较为简单。
<resultMap type="com.ck.smm.entity.User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<association property="card" column="card_id" javaType="com.ck.smm.entity.Card">
<id property="cardId" column="card_id"/>
<result property="cardNum" column="card_num"/>
<result property="address" column="address"/>
</association>
</resultMap>
这种方法就把 Card 的 resultMap 定义在了 association 标签里面,通过 javaType 来指定是哪个类的 resultMap,个人认为这种方法最简单,缺点就是 cardMap 不能复用。具体用哪种方法,视情况而定。
一个土豪有多个手机,看如下代码:
User实体类
public class User{
private Integer userId;
private String userName;
private Integer age;
private List<MobilePhone> mobilePhone;//土豪,多个手机,1对多
}
手机类
public class MobilePhone {
private Integer mobilePhoneId;
private String brand;//品牌
private double price;//价格
private User user;//主人
}
dao层
public interface UserMapper {
/**
* 通过userId查询user信息
* @param userId
* @return
*/
User queryById(int userId);
}
UserMapper.xml 中的 select 查询语句
<select id="queryById" parameterType="int" resultMap="userMap">
SELECT u.user_name,u.age,m.brand,m.price
FROM tb_user u,tb_mobile_phone m
WHERE m.user_id=u.user_id
AND
u.user_id=#{userId}
</select>
数据库中,tb_mobile_phone 中 user_id 作为外键。那么下面来看 resultMap 如何定义:
先定义 MobilePhoneMapper.java
public interface MobilePhoneMapper{
List<MobilePhone> queryMbByUserId(int userId);
}
然后实现该方法 MobilePhoneMapper.xml
<resultMap type="com.ck.smm.entity.MobilePhone" id="mobilePhoneMap">
<id property="mobilePhoneId" column="user_id"/>
<result property="brand" column="brand"/>
<result property="price" column="price"/>
<association property="user" column="user_id"
select="com.ck.ssm.dao.UserMapper.queryById">
</association>
</resultMap>
<select id="queryMbByUserId" parameterType="int" resultMap="mobilePhoneMap">
SELECT brand,price
FROM tb_mobile_phone
WHERE user_id=#{userId}
</select>
做好以上准备工作,那就可以在 UserMapper.xml 中引用了
<resultMap type="com.ck.smm.entity.User" id="userMap">
<id property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<collection property="mobilePhone" column="user_id"
select="com.ck.ssm.dao.MobilePhoneMapper.queryMbByUserId">
</collection>
</resultMap>
这种方法和 association 的 第一种用法几乎是一样的不同之处就是 mobilePhMap 中用到了association ,queryMbByUserId 中要使用mobilePhoneMap,而不能直接使用 resultType。
<resultMap type="com.ck.smm.entity.MobilePhone" id="mobilephoneMap">
<id column="mobile_phone_id" property="mobilePhoneId"/>
<result column="brand" property="brand" />
<result column="price" property="price" />
</resultMap>
<resultMap type="com.ck.smm.entity.User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<collection property="mobilePhone" resultMap="mobilephoneMap" >
</collection>
</resultMap>
定义好这两个 resultMap ,再引用 UserMap 就行了。
<resultMap type="com.ck.smm.entity.User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<collection property="mobilePhone"column="user_id" ofType="com.ck.smm.entity.MobilePhone">
<id column="mobile_phone_id" property="mobilePhoneId" />
<result column="brand" property="brand" />
<result column="price" property="price" />
</collection>
</resultMap>
这种方法需要注意,一定要有 ofType,collection 装的元素类型是啥 ofType 的值就是啥,这个一定不能少。
id:命名空间中的唯一标识符,可以被用来引用这条语句
parameterType:表示查询语句传入参数的类型的完全限定名或别名,支持基础数 据类型和复杂数据类型。
resultType:查询语句返回结果类型的完全限定名或别名。
property : 对象属性的名称
javaType :对象属性的类型
column :所对应的外键字段名称
select :使用另一个查询封装的结果
ofType :collection中的属性, 用于指定集合中元素的对象类型。
表中主键字段要有所区分,不能都写成id,要写成 user_id、card_id,反正要有所区分,不然查询的时候会查不到完整的数据。
参考:https://www.jianshu.com/p/018c0f083501