关联关系概述
这三种关系的具体说明如下:
- 一对一:在任意一方引入对方主键作为外键
- 一对多:在"多"的一方,添加"一"的一方的主键作为外键
- 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键
在Java中,通过对象也可以进行关系描述
- 一对一的关系:如A类中定义B类类型的属性b,B类中定义A类类型的属性a
- 一对多的关系:就是一个A类类型对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a
- 多对多的关系:在A类中定义B类类型的集合,在B类中定义A类类型的集合
1. 一对一
在<resultMap>元素中,包含了一个<association>子元素来处理一对一关联关系.
属性 | 描述 |
property | 指定映射到的实体类对象属性,与表字段一一对应 |
column | 指定表中对应的字段 |
JavaType | 指定映射到实体对象属性的类型 |
select | 指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询 |
fetchType | 指定在关联查询时是否启用延迟加载.fetchType属性有lazy和eager两个属性值,默认值为lazy |
<!--方式一:嵌套查询-->
<association property="card" column="card_id"
JavaType="com.itcast.po.IdCard"
select="com.itcast.mapper.IdCardMapper.findCodeById"/>
<!--方式二:嵌套结果-->
<association property="card" JavaType="com.itcast.po.IdCard">
<id property="id" column="Card_id"/>
<result property="code" column="code"/>
</association>
demo:
查询个人及其关联的身份证信息是先通过查询个人表中的主键来获个人信息,然后通过表中的外键,来获取证件表中的身份证号信息
(1)创建数据表
USE mybatis;
#创建一个名称为tb_idcard的表
CREATE TABLE tb_idcard(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
#插入两条数据
INSERT INTO tb_idcard(CODE) VALUES('152221198711020624');
INSERT INTO tb_idcard(CODE) VALUES('152221198711020317');
#创建一个名称为tb_person的表
CREATE TABLE tb_person(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32),
age INT,
sex VARCHAR(8),
card_id INT UNIQUE,
FOREIGN KEY(card_id) REFERENCES tb_idcard(id)
);
#插入两条数据
INSERT INTO tb_person(name,age,sex,card_id) VALUES('Rose',29,'女',1);
INSERT INTO tb_person(name,age,sex,card_id) VALUES('tom',27,'男',2);
(2)在com.itcast.mapper下,创建证件映射文件IdCardMapper.xml和个人映射文件PersonMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息
IdCardMapper.xml
<mapper namespace="com.itcast.mapper.IdCardMapper">
<!--根据ID查询证件信息-->
<select id="findCodeById" parameterType="Integer" resultType="IdCard">
select * from tb_idcard where id=#{id}
</select>
</mapper>
PersonMapper.xml
<mapper namespace="com.itcast.mapper.PersonMapper">
<!--嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型-->
<select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
select * from tb_person where id=#{id}
</select>
<resultMap type="Person" id="IdCardWithPersonResult">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<!--一对一:association使用select属性引入另外一条SQL语句-->
<association property="card" column="card_id" JavaType="IdCard"
select="com.itcast.mapper.IdCardMapper.findCodeById"/>
</resultMap >
</mapper>
(3)核心配置文件mybatis-config.xml,引入Mapper映射文件并定义别名
<configuration>
<!--引入数据库连接配置文件-->
<properties resource="db.properties"/>
<!--使用扫描包的形式定义别名-->
<typeAliases>
<package name="com.itcast.po"/>
</typeAliases>
<!--配置环境,默认的环境id为mysql-->
<environments default="mysql">
<!--配置id为mysql的数据库环境-->
<environment id="mysql">
<!--使用JDBC的事务管理-->
<transactionManager type="JDBC">
<!--数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<!--配置Mapper的位置-->
<mappers>
<mapper resource="com/itcast/mapper/IdCardMapper.xml"/>
<mapper resource="com/itcast/mapper/PersonMapper.xml"/>
</mappers>
</configuration>
(4)MyBatisAssociatedTest
/*
MyBatis关联查询映射测试类
*/
public class MyBatisAssociatedTest(){
//嵌套查询
@Test
public void findPersonByIdTest(){
//通过工具类生成sqlsession对象
SqlSession sqlSession =MyBatisUtils.getSession();
//使用MyBatis嵌套查询的方式查询id为1的人的信息
Person person=sqlSession.selectOne("com.itcast.mapper"+
"PersonMapper.findPersonById",1)
//输出查询结果信息
System.out.println(person);
//关闭sqlSession
sqlSession.close();
}
}
在PersonMapper.xml中,使用嵌套结果的方式进行个人及其关联的证件信息查询
<!--嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集-->
<select id="findPersonById2" parameterType="Integer"
resultMap="IdCardWithPersonResult2">
select p.*,idcard.code
from tb_person p,tb_idcard idcard
where p.card_id=idcard.id
and p.id=#{id}
</select>
<resultMap type="Person" id="IdCardWithPersonResult2">
<id property="id" column="id"/>
<result property="name" column="name">
<result property="age" column="age">
<result property="sex" column="sex">
<association property="card" javaType="IdCard">
<id property="id" column="card_id"/>
<result property="code" column="code"/>
</association>
</resultMap>
测试findPersonByIdTest2
/*
嵌套结果
*/
@Test
public void findPersonByIdTest2(){
//通过工具类生成sqlsession对象
SqlSession sqlSession =MyBatisUtils.getSession();
//使用MyBatis嵌套结果的方式查询id为1的人的信息
Person person=sqlSession.selectOne("com.itcast.mapper"+
"PersonMapper.findPersonById2",1)
//输出查询结果信息
System.out.println(person);
//关闭sqlSession
sqlSession.close();
}
}
2.一对多
<resultMap>元素中,包含了一个<collection>子元素,它有一个属性ofType,此属性和JavaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型
可以参考下面两种示例进行配置:
<!--方式一:嵌套查询-->
<collection property="orderList" column="id"
ofType="com.itcast.po.Orders"
select="com.itcast.mapper.OrdersMapper.selectOrders"/>
<!--方式二:嵌套结果-->
<collection property="orderList" ofType="com.itcast.po.Orders">
<id property="id" column="orders_id"/>
<result property="number" column="number"/>
</collection>
demo:一个用户多个订单
(1)创建2个数据库表
#创建一个名称为tb_user的表
CREATE TABLE tb_user(
id int(32) PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
address VARCHAR(256)
);
#插入3条数据
INSERT INTO tb_user VALUES('1','詹姆斯','克利夫兰');
INSERT INTO tb_user VALUES('2','科比','洛杉矶');
INSERT INTO tb_user VALUES('3','保罗','洛杉矶');
#创建一个名称为tb_orders的表
CREATE TABLE tb_orders(
id int(32) PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(32) NOT NULL,
user_id int(32) NOT NULL,
FOREIGN KEY(user_id) REFERENCES tb_user(id)
);
#插入3条数据
INSERT INTO tb_orders VALUES('1','1000011','1');
INSERT INTO tb_orders VALUES('2','1000012','2');
INSERT INTO tb_orders VALUES('3','1000013','3');
(2)创建类Orders和User,定义相关属性和方法
(3)在com.itcast.mapper包下创建UserMapper.xml
<mapper namespace="com.itcast.mapper.UserMapper">
<!--一对多:查看某一用户及其关联的订单信息
注意:当关联查询出的列名相同,则需要使用别名区分-->
<select id="findUserWithOrders" parameterType="Integer"
resultMap="UserWithOrdersResult">
select u.*,o.id as orders_id,o.number
from tb_user u, tb_orders o
where u.id=o.user_id
and u.id=#{id}
</select>
<resultMap type="User" id="UserWithOrdersResult">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<!--一对多:collection
ofType表示属性集合中元素的类型,List<Orders>属性即Orders类-->
<collection property="orderList" ofType="Orders" >
<id property="id" column="orders_id"/>
<result property="number" column="number">
</collection>
</resultMap>
</mapper>
(4)将映射文件UserMapper.xml的路径配置到核心配置文件mybatis-config.xml中,其中代码如下
<mapper resource="com/itcast/mapper/UserMapper.xml"/>
(5)MybatisAssociatedTest测试类
/*
一对多
*/
@Test
public void findUserTest(){
//通过工具类生成sqlsession对象
SqlSession sqlSession =MyBatisUtils.getSession();
//查询id为1的人的信息
User user=sqlSession.selectOne("com.itcast.mapper."+
"UserMapper.findUserWithOrders",1)
//输出查询结果信息
System.out.println(user);
//关闭sqlSession
sqlSession.close();
}
}
3.多对多
(1)创建数据库表tb_product和tb_ordersitem
#创建一个名称为tb_product的表
CREATE TABLE tb_product(
id int(32) PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32),
price DOUBLE
);
#插入3条数据
INSERT INTO tb_product VALUES('1','Java基础入门','44.5');
INSERT INTO tb_product VALUES('2','Java Web程序开发入门','38.5');
INSERT INTO tb_product VALUES('3','SSM框架整合入门','50.0');
#创建一个名称为tb_ordersitem的表
CREATE TABLE tb_ordersitem(
id int(32) PRIMARY KEY AUTO_INCREMENT,
orders_id INT(32),
product_id int(32),
FOREIGN KEY(orders_id) REFERENCES tb_orders(id),
FOREIGN KEY(product_id) REFERENCES tb_product(id)
);
#插入3条数据
INSERT INTO tb_ordersitem VALUES('1','1','1');
INSERT INTO tb_ordersitem VALUES('2','1','3');
INSERT INTO tb_ordersitem VALUES('3','3','3');
(2)创建持久化类Product,定义相关属性和方法
(3)创建订单实体映射文件OrdersMapper.xml
<mapper namespace="com.itcast.mapper.OrdersMapper">
<!--多对多:通过执行另外一条SQL映射语句来返回预期的特殊类型-->
<select id="findOrdersWithPorduct" parameterType="Integer"
resultMap="OrdersWithPorductResult">
select * from tb_orders where id=#{id}
</select>
<resultMap type="Orders" id="OrdersWithProductResult">
<id property="id" column="id"/>
<result property="number" column="number"/>
<collection property="productList" column="id" ofType="product">
select="com.itcast.mapper.productMapper.findProductById">
</collection>
</resultMap>
</mapper>
和商品实体映射文件ProductMapper.xml
<mapper namespace="com.itcast.mapper.ProductMapper">
<!--多对多:通过执行另外一条SQL映射语句来返回预期的特殊类型-->
<select id="findPorductById" parameterType="Integer"
resultType="Product">
select * from tb_product where id IN(
select product_id from tb_ordersitem where orders_id=#{id}
)
</select>
</mapper>
(4)测试方法findOrdersTest()
/*
多对多
*/
@Test
public void findOrdersTest(){
//通过工具类生成sqlsession对象
SqlSession sqlSession =MyBatisUtils.getSession();
//查询id为1的人的信息
Orders orders=sqlSession.selectOne("com.itcast.mapper."+
"OrdersMapper.findOrdersWithPorduct",1)
//输出查询结果信息
System.out.println(orders);
//关闭sqlSession
sqlSession.close();
}
}
使用嵌套结果的方式查询
<mapper namespace="com.itcast.mapper.OrdersMapper">
<!--多对多嵌套结果查询:查询某订单及其关联的商品详情-->
<select id="findOrdersWithPorduct2" parameterType="Integer"
resultMap="OrdersWithPorductResult2">
select o.*,p.id as pid,p.name,p.price
from tb_orders o, tb_product p,tb_ordersitem oi
where oi.orders_id=o.id
and oi.product_id=p.id
and o.id=#{id}
</select>
<!--自定义手动映射类型-->
<resultMap type="Orders" id="OrdersWithProductResult2">
<id property="id" column="id"/>
<result property="number" column="number"/>
<!--多对多关联映射:collection-->
<collection property="productList" ofType="product">
<id property="id" column="pid"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
</collection>
</resultMap>