主要内容:
- 开发中数据模型的分析流程
- 一对一查询
- 一对多查询
一、开发中数据模型的分析流程
一般在开发中我们会直接拿到相关的数据库,此时我们需要对数据模型进行分析之后,才能进行相关的开发。
1.1 分析思路
- (1)每张表记录的数据内容
在企业中分模块对每张表记录的内容进行熟悉,这相当于学习系统需求(功能)的过程。 - (2)每张表数据库重要字段的设置
重点查看那每张表中的非空字段和外键字段,这属于比较重要的字段。 - (3)数据库级别表之间的关系
数据库级别表之间的关系指的就是表之间的外键关系。 - (4)表与表的业务关系
在分析表和表之间的业务关系一定要建立在某个业务意义基础之上,不能单纯的说是一对多或一对一关系。
1.2 模型分析
1.2.1 数据模型
这里我们先给出本例子中所要用到的相关表:
- 用户表,在之前已经给出了
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8
- 订单表
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
- 订单明细表
CREATE TABLE `orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orders_id` int(11) NOT NULL COMMENT '订单id',
`items_id` int(11) NOT NULL COMMENT '商品id',
`items_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`orders_id`),
KEY `FK_orderdetail_2` (`items_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
- 商品表
CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '商品名称',
`price` float(10,1) NOT NULL COMMENT '商品定价',
`detail` text COMMENT '商品描述',
`pic` varchar(64) DEFAULT NULL COMMENT '商品图片',
`createtime` datetime NOT NULL COMMENT '生产日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1.2.2 模型分析
1.2.2.1 单张表分析
用户表
user
:记录了购买商品的用户信息
比较重要的字段就是其自增主键id
订单表
orders
:记录了用户所创建的订单(购买商品的订单)
比较重要的字段就是其中的订单号number
和外键user_id
订单明细表
orderdetail
:记录了订单的详细信息即订单中所包含的一些商品信息。
比较重要的字段就是自增主键id
,外键orders_id
和外键items_id
商品信息表
items
:记录了用户所购买的商品信息
比较重要的字段就是自增主键id
1.2.2.2 表与表之间的业务关系
(1)先分析数据库级别有关系之间的业务关系
用户表(
user
)和订单表(orders
)
user-->orders
:一个用户可以创建多个订单,一对多
orders-->user
:一个订单只由一个用户来创建,一对一订单表(
orders
)和订单明细表(ordredetail
)
orders-->orderdetail
:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在ordredetail
记录,一对多关系
ordredetail-->orders
:一个订单明细只能包括在一个订单中,一对一订单明细表(
orderdetail
)和商品表(items
)
orderdetail-->items
:一个订单明细只对应一个商品信息,一对一关系
items-->orderdetail
:一个商品对应多个明细,一对多
(2)再分析数据库级别没有关系的表之间是否有业务关系
orders
和items
:可以通过orderdetail
建立关系
orders-->items
:一对多
items-->orders
:一对多
也就是多对多关系。user
和items
user-->items
:一对多
items-->user
:一对多
也就是多对多关系
注意:这里我们的多有关系在分析的时候都必须建立在相关实际业务基础之上。
二、一对一关系映射
2.1 需求
查询订单信息,关联查询创建订单的用户信息
2.2 resultType实现(工程mybatis08
)
- sql语句
- 确定查询的主表:
orders
- 确定查询的关联表:
user
- 关联查询使用内连接还是外连接?
由于在orders
中有一个外键(user_id
),通过外键来关联查询用户表只能查询出一条记录,可以使用内连接。
- 确定查询的主表:
SELECT orders.*, user.username, user.sex, user.address
FROM orders , USER
WHERE orders.user_id = user.id
- 创建
pojo
这里我们使用resultType
的方式实现,我们需要对订单pojo
进行增强。
orders.java
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
.......
}
OrdersCustom.java
public class OrdersCustom extends Orders {
private String username;// 用户名称
private String address;// 用户地址
private String sex ;//性别
.......
}
说明:将上面sql
查询到的结果集映射到pojo
中,pojo
中必须要包括所有的查询列名。原始的orders.java
不能映射全部字段,需要创建一个pojo
,让其继承包括查询字段较多的那个pojo
类。
- 编写
mapper.xml
OrdersCustomMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mapper.OrdersCustomMapper">
<!-- 查询订单关联查询用户 -->
<select id="findOrdersUser" resultType="OrdersCustom">
SELECT orders.*, user.username, user.sex, user.address
FROM orders , USER
WHERE orders.user_id = user.id
</select>
</mapper>
- 接口
mapper.java
OrdersCustomMapper.java
package cn.itcast.mapper;
import java.util.List;
import cn.itcast.pojo.OrdersCustom;
//订单mapper
public interface OrdersCustomMapper {
public List<OrdersCustom> findOrdersUser() throws Exception;
}
- 测试
OrdersCustomMapperTest.java
package cn.itcast.mapper;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import cn.itcast.pojo.OrdersCustom;
public class OrdersCustomMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// 创建sqlSessionFactory
// mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream is = Resources.getResourceAsStream(resource);
// 创建会话工厂,要想build方法中传入配置信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void testFindOrdersCustoms() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersCustomMapper ordersCustom = sqlSession.getMapper(OrdersCustomMapper.class);
//调用mapper的方法
List<OrdersCustom> list = ordersCustom.findOrdersUser();
System.out.println(list.size());
sqlSession.close();
}
}
2.3 resultMap实现(工程mybatis09
)
sql语句
其中sql
语句和之前的一样。映射思路
使用resultMap
将查询结果中订单信息映射到orders
对象中,在orders
类中添加一个User
属性,将关联查询出来的用户信息映射到orders
对象中的user
属性中。需要在orders
类中添加user
属性。
Orders.java
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//用户信息
private User user ;
......
}
- 定义
mapper.xml
OrdersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mapper.OrdersMapper">
<!-- 订单查询关联查询用户的resultMap,即将整个查询的结果映射到orders中 -->
<resultMap type="Orders" id="OrdersUserResultMap">
<!-- 配置要映射的订单信息,如果有多个列组成唯一标识,那需要配置多个id,
property指定将唯一表示映射到pojo中的哪个属性上 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置要映射关联的用户信息,用于映射关联查询单个对象的信息,
property表示要将关联查询的用户信息映射到orders中的哪个属性 -->
<association property="user" javaType="User">
<!-- id:关联查询用户的唯一表示
column:指定用于唯一表示用户信息的列
javaType:映射到User的哪个属性 -->
<id column="user_id" property="id"/>
<result column="" property="note"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 查询订单关联查询用户,使用resultMap -->
<select id="findOrdersUser" resultMap="OrdersUserResultMap">
SELECT orders.*, user.username, user.sex, user.address
FROM orders , USER
WHERE orders.user_id = user.id
</select>
</mapper>
- 接口
OrdersMapper.java
package cn.itcast.mapper;
import java.util.List;
import cn.itcast.pojo.Orders;
//订单mapper
public interface OrdersMapper {
public List<Orders> findOrdersUser() throws Exception;
}
- 测试
OrdersMapperTest.java
@Test
public void testFindOrdersUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapper orders = sqlSession.getMapper(OrdersMapper.class);
//调用mapper的方法
List<Orders> list = orders.findOrdersUser();
System.out.println(list.size());
sqlSession.close();
}
2.4 resultMap 和 resultType 小结
实现一对一查询时
resultType
此种方式较为简单,如果pojo
中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用此种方式。resultMap
如果对查询结果有特殊的要求则推荐使用此种方式。可以实现延迟加载。而resultType
则不能实现延迟加载。
三、一对多关系映射(工程mybatis10
)
3.1需求
查询订单及订单明细的信息,保留用户信息
3.2 sql语句
- 确定主查询表:订单表
- 确定关联查询表:订单明细表
- 在一对一查询基础上,添加订单明细表关联即可,这种方式使用内连接显示的是多的一方的数据,有重复。
SELECT
orders.*,
user.username,
user.sex,
user.address,
orderdetail.items_id,
orderdetail.id orderdetail_id,
orderdetail.items_num,
orderdetail.orders_id
FROM
orders , USER , orderdetail
WHERE
orders.user_id = user.id AND orderdetail.orders_id = orders.id
说明:这里之所以要将订单明细的id
起别名,是因为在映射文件中映射的时候会和订单id
重复。
3.3 使用resultType
此时这种方式将上边的查询结果映射到pojo
中,订单信息就会有重复
当然我们还是可以使用此种方式的,如果要实现不重复,则在将订单明细映射到
Orders.java
中的orderDetails
中,需要自己处理,使用遍历,去掉重复记录,将订单明细存储在List
中,但显然有点麻烦。这里我们使用resultMap
。
3.4 使用 resultMap
要求是对orders
映射不能出现重复记录。于是我们在Orders.java
中添加一个属性List<orderDetail> orderDetails
属性,最终会将订单信息映射到orders
中,订单所对应的订单明细就会映射到Orders.java
中的orderDetails
属性中。映射成的orders
的记录数为两条,不重复,而每个Orders.java
中的orderDetail
属性存储了该订单所对应的订单明细。
3.4.1 mapper.xml
OrdersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.mapper.OrdersMapper">
<!-- 订单查询关联查询用户的resultMap,即将整个查询的结果映射到orders中 -->
<resultMap type="Orders" id="OrdersUserResultMap">
<!-- 配置要映射的订单信息,如果有多个列组成唯一标识,那需要配置多个id,
property指定将唯一表示映射到pojo中的哪个属性上 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置要映射关联的用户信息,用于映射关联查询单个对象的信息,
property表示要将关联查询的用户信息映射到orders中的哪个属性 -->
<association property="user" javaType="User">
<!-- id:关联查询用户的唯一表示
column:指定用于唯一表示用户信息的列
javaType:映射到User的哪个属性 -->
<id column="user_id" property="id"/>
<result column="" property="note"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<!-- 查询订单和订单明细的resultMap -->
<resultMap type="Orders" id="OrdersAndOrderdetailResultMap" extends="OrdersUserResultMap">
<!-- 很明显这里的订单信息和用户信息和上面的重复,我们可以使用继承 -->
<!-- 订单信息 -->
<!-- 用户信息 -->
<!-- 订单明细信息,一个订单查询出了多个订单明细,要是用collection进行映射
collection:对关联查询到多条记录映射到集合中
property:映射到orders类中的哪个属性
ofType:要映射到集合中pojo的属性 -->
<collection property="orderdetails" ofType="Orderdetail">
<!-- id:订单明细的唯一标识,property:要将订单明细的唯一标识映射到Orderdetail的哪个属性 -->
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="ordersId"/>
<result column="items_id" property="itemsId"/>
</collection>
</resultMap>
<!-- 查询订单关联查询用户以及订单明细,使用resultMap -->
<select id="findOrdersAndOrderdetail" resultMap="OrdersAndOrderdetailResultMap">
SELECT orders.*, user.username, user.sex,user.address, orderdetail.id orderdetail_id,
orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id
FROM orders , USER , orderdetail
WHERE orders.user_id = user.id AND orderdetail.orders_id = orders.id
</select>
<!-- 查询订单关联查询用户,使用resultMap -->
<select id="findOrdersUser" resultMap="OrdersUserResultMap">
SELECT orders.*, user.username, user.sex, user.address
FROM orders , USER
WHERE orders.user_id = user.id
</select>
</mapper>
说明:我们在映射订单明细的唯一标识的时候需要使用一个字段,如果直接使用订单明细的id
,则会和订单表的id
重复,于是我们给此字段起了一个别名。
3.4.2 mapper.java
OrdersMapper.java
package cn.itcast.mapper;
import java.util.List;
import cn.itcast.pojo.Orders;
//订单mapper
public interface OrdersMapper {
public List<Orders> findOrdersUser() throws Exception;
//查询订单和订单明细,保留了关联用户
public List<Orders> findOrdersAndOrderdetail() throws Exception;
}
3.4.3 测试
OrdersMapperTest.java
@Test
public void testFindOrdersAndOrderdetail() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建代理对象
OrdersMapper orders = sqlSession.getMapper(OrdersMapper.class);
//调用mapper的方法
List<Orders> list = orders.findOrdersAndOrderdetail();
System.out.println(list.size());
sqlSession.close();
}
说明:使用断点调试方式可以查看结果,我们发现确实值有两条订单记录,而每条订单记录中有两条订单明细记录,这样就很好的去除了重复。