前言:
在项目中,某些实体类之间肯定有关键关系,比如一对一,一对多等。在hibernate 中用one to one
和one to many
,而mybatis 中就用association
和collection
。
association: 一对一关联(has one)
collection:一对多关联(has many)
注意,只有在做select查询时才会用到这两个标签,都有三种用法,且用法类似。
欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。
一、association的三种用法:
先看如下代码(省略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 UserDao {
/**
* 通过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层的设计以及在UserDao.xml中queryById方法的sql语句的编写,因为不论用association的哪种方式,sql语句都是一样的写,不同的只是userMap的写法,所以这里先给出这段代码。User询Card是一对一关系,在数据库中,tb_user表通过外键card_id关联tb_card表。下面分别用association的三种用法来实现queryById方法。
1、第一种用法:association中使用select
这种方法需要再定义CardDao.java,如下:
public interface CardDao {
Card queryCardById(int cardId);
}
在CardDao.xml中实现该方法:
<select id="queryCardById" parameterType="int" resultType="Card">
SELECT *
FROM tb_card
WHERE card_id=#{cardId}
</select>
然后再看UserDao.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.zhu.ssm.dao.
CardDao.queryCardById">
</association>
</resultMap>
在这里直接通过select引用CardDao的queryById方法。个人感觉这种方法比较麻烦,因为还要在CardDao里定义queryCardById方法并且实现再引用才有用,不过这种方法思路清晰,易于理解。
2、第二种方法,嵌套resultMap
<resultMap type="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>
第二种方法就是在UserDao.xml中先定义一个Card的resultMap,然后在User的resultMap的association标签中通过resultMap="cardMap"引用。这种方法相比于第一种方法较为简单。
3、第三种方法:嵌套resultMap简化版
<resultMap type="User" id="userMap">
<result property="userName"
column="user_name"/>
<result property="age"
column="age"/>
<association
property="card"
column="card_id"
javaType="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不能复用。具体用哪种方法,视情况而定。
二、collection的三种用法:
一个土豪有多个手机,看如下代码:
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 UserDao {
/**
* 通过userId查询user信息
* @param userId
* @return
*/
User queryById(int userId);
}
UserDao.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如何定义:
1、第一种方法:用select,跟association 中使用select类似:
先定义 MobilePhoneDao.java
public interface MobilePhoneDao {
List<MobilePhone> queryMbByUserId(int userId);
}
然后实现该方法 MobilePhoneDao.xml
<resultMap type="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.zhu.ssm.dao.UserDao.queryById">
</association>
</resultMap>
<select id="queryMbByUserId" parameterType="int" resultMap="mobilePhoneMap">
SELECT brand,price
FROM tb_mobile_phone
WHERE user_id=#{userId}
</select>
做好以上准备工作,那就可以在UserDao.xml中引用了
<resultMap type="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.zhu.ssm.dao
.MobilePhoneDao.queryMbByUserId">
</collection>
</resultMap>
这种方法和association的第一种用法几乎是一样的不同之处就是mobilePhMap中用到了association ,queryMbByUserId中要使用mobilePhoneMap,而不能直接使用resultType。
2、第二种方法:嵌套resultMap
<resultMap type="MobilePhone" id="mobilephoneMap">
<id column="mobile_phone_id" property="mobilePhoneId"/>
<result column="brand" property="brand" />
<result column="price" property="price" /></resultMap>
<resultMap type="User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<collection property="mobilePhone" resultMap="mobilephoneMap" >
</collection>
</resultMap>
定义好这两个resultMap,再引用UserMap就行了。
3、第三种方法:嵌套resultMap简化版
<resultMap type="User" id="userMap">
<result property="userName" column="user_name"/>
<result property="age" column="age"/>
<collection property="mobilePhone"
column="user_id"
ofType="MobilePhone">
<id column="mobile_phone_id" property="mobilePhoneId" />
<result column="brand"
property="brand" />
<result column="price"
property="price" />
</collection>
</resultMap>
这种方法需要注意,一定要有ofType,collection 装的元素类型是啥ofType的值就是啥,这个一定不能少。
注意:
所有resultMap中的type、select 标签中的resultType以及association中的javaType,collection中的ofType,这里只写了类名,是因为在mybatis-config.xml中配置了typeAliases,否则就要写该类的全类名。配置如下:
<typeAliases>
<packagename="com.zhu.smm.entity"/>
</typeAliases>
总结:
1、association表示的是has one的关系,一对一时使用。user has one card,所以在user的resultMap中接收card时应该用association;
2、collection表示的是has many的关系,一对多时使用。user has many mobilePhone,所以在user的resultMap中接收mobilePhone时应该用collection 。
3、都有三种用法,且非常类似,resultMap要复用建议第二种方法,不需要复用建议第三种方法。
4、特别注意表中主键字段要有所区分,不能都写成id,要写成user_id、card_id,反正要有所区分,不然查询的时候会查不到完整的数据。