主要內(nèi)容:
- 開發(fā)中數(shù)據(jù)模型的分析流程
- 一對一查詢
- 一對多查詢
一、開發(fā)中數(shù)據(jù)模型的分析流程
一般在開發(fā)中我們會直接拿到相關(guān)的數(shù)據(jù)庫,此時我們需要對數(shù)據(jù)模型進行分析之后,才能進行相關(guān)的開發(fā)。
1.1 分析思路
- (1)每張表記錄的數(shù)據(jù)內(nèi)容
在企業(yè)中分模塊對每張表記錄的內(nèi)容進行熟悉,這相當(dāng)于學(xué)習(xí)系統(tǒng)需求(功能)的過程。 - (2)每張表數(shù)據(jù)庫重要字段的設(shè)置
重點查看那每張表中的非空字段和外鍵字段,這屬于比較重要的字段。 - (3)數(shù)據(jù)庫級別表之間的關(guān)系
數(shù)據(jù)庫級別表之間的關(guān)系指的就是表之間的外鍵關(guān)系。 - (4)表與表的業(yè)務(wù)關(guān)系
在分析表和表之間的業(yè)務(wù)關(guān)系一定要建立在某個業(yè)務(wù)意義基礎(chǔ)之上,不能單純的說是一對多或一對一關(guān)系。
1.2 模型分析
1.2.1 數(shù)據(jù)模型
這里我們先給出本例子中所要用到的相關(guān)表:
- 用戶表,在之前已經(jīng)給出了
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 '創(chuàng)建訂單時間',
`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 '商品購買數(shù)量',
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 '生產(chǎn)日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1.2.2 模型分析
1.2.2.1 單張表分析
用戶表
user:記錄了購買商品的用戶信息
比較重要的字段就是其自增主鍵id訂單表
orders:記錄了用戶所創(chuàng)建的訂單(購買商品的訂單)
比較重要的字段就是其中的訂單號number和外鍵user_id訂單明細表
orderdetail:記錄了訂單的詳細信息即訂單中所包含的一些商品信息。
比較重要的字段就是自增主鍵id,外鍵orders_id和外鍵items_id商品信息表
items:記錄了用戶所購買的商品信息
比較重要的字段就是自增主鍵id
1.2.2.2 表與表之間的業(yè)務(wù)關(guān)系
(1)先分析數(shù)據(jù)庫級別有關(guān)系之間的業(yè)務(wù)關(guān)系
用戶表(
user)和訂單表(orders)
user-->orders:一個用戶可以創(chuàng)建多個訂單,一對多
orders-->user:一個訂單只由一個用戶來創(chuàng)建,一對一訂單表(
orders)和訂單明細表(ordredetail)
orders-->orderdetail:一個訂單可以包括多個訂單明細,因為一個訂單可以購買多個商品,每個商品的購買信息在ordredetail記錄,一對多關(guān)系
ordredetail-->orders:一個訂單明細只能包括在一個訂單中,一對一訂單明細表(
orderdetail)和商品表(items)
orderdetail-->items:一個訂單明細只對應(yīng)一個商品信息,一對一關(guān)系
items-->orderdetail:一個商品對應(yīng)多個明細,一對多
(2)再分析數(shù)據(jù)庫級別沒有關(guān)系的表之間是否有業(yè)務(wù)關(guān)系
orders和items:可以通過orderdetail建立關(guān)系
orders-->items:一對多
items-->orders:一對多
也就是多對多關(guān)系。user和items
user-->items:一對多
items-->user:一對多
也就是多對多關(guān)系
注意:這里我們的多有關(guān)系在分析的時候都必須建立在相關(guān)實際業(yè)務(wù)基礎(chǔ)之上。
二、一對一關(guān)系映射
2.1 需求
查詢訂單信息,關(guān)聯(lián)查詢創(chuàng)建訂單的用戶信息
2.2 resultType實現(xiàn)(工程mybatis08)
- sql語句
- 確定查詢的主表:
orders - 確定查詢的關(guān)聯(lián)表:
user - 關(guān)聯(lián)查詢使用內(nèi)連接還是外連接?
由于在orders中有一個外鍵(user_id),通過外鍵來關(guān)聯(lián)查詢用戶表只能查詢出一條記錄,可以使用內(nèi)連接。
- 確定查詢的主表:
SELECT orders.*, user.username, user.sex, user.address
FROM orders , USER
WHERE orders.user_id = user.id
- 創(chuàng)建
pojo
這里我們使用resultType的方式實現(xiàn),我們需要對訂單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查詢到的結(jié)果集映射到pojo中,pojo中必須要包括所有的查詢列名。原始的orders.java不能映射全部字段,需要創(chuàng)建一個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">
<!-- 查詢訂單關(guān)聯(lián)查詢用戶 -->
<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 {
// 創(chuàng)建sqlSessionFactory
// mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream is = Resources.getResourceAsStream(resource);
// 創(chuàng)建會話工廠,要想build方法中傳入配置信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void testFindOrdersCustoms() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//創(chuàng)建代理對象
OrdersCustomMapper ordersCustom = sqlSession.getMapper(OrdersCustomMapper.class);
//調(diào)用mapper的方法
List<OrdersCustom> list = ordersCustom.findOrdersUser();
System.out.println(list.size());
sqlSession.close();
}
}
2.3 resultMap實現(xiàn)(工程mybatis09)
sql語句
其中sql語句和之前的一樣。映射思路
使用resultMap將查詢結(jié)果中訂單信息映射到orders對象中,在orders類中添加一個User屬性,將關(guān)聯(lián)查詢出來的用戶信息映射到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">
<!-- 訂單查詢關(guān)聯(lián)查詢用戶的resultMap,即將整個查詢的結(jié)果映射到orders中 -->
<resultMap type="Orders" id="OrdersUserResultMap">
<!-- 配置要映射的訂單信息,如果有多個列組成唯一標(biāo)識,那需要配置多個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"/>
<!-- 配置要映射關(guān)聯(lián)的用戶信息,用于映射關(guān)聯(lián)查詢單個對象的信息,
property表示要將關(guān)聯(lián)查詢的用戶信息映射到orders中的哪個屬性 -->
<association property="user" javaType="User">
<!-- id:關(guān)聯(lián)查詢用戶的唯一表示
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>
<!-- 查詢訂單關(guān)聯(lián)查詢用戶,使用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();
//創(chuàng)建代理對象
OrdersMapper orders = sqlSession.getMapper(OrdersMapper.class);
//調(diào)用mapper的方法
List<Orders> list = orders.findOrdersUser();
System.out.println(list.size());
sqlSession.close();
}
2.4 resultMap 和 resultType 小結(jié)
實現(xiàn)一對一查詢時
resultType此種方式較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應(yīng)的屬性,即可完成映射。如果沒有查詢結(jié)果的特殊要求建議使用此種方式。resultMap如果對查詢結(jié)果有特殊的要求則推薦使用此種方式??梢詫崿F(xiàn)延遲加載。而resultType則不能實現(xiàn)延遲加載。
三、一對多關(guān)系映射(工程mybatis10)
3.1需求
查詢訂單及訂單明細的信息,保留用戶信息
3.2 sql語句
- 確定主查詢表:訂單表
- 確定關(guān)聯(lián)查詢表:訂單明細表
- 在一對一查詢基礎(chǔ)上,添加訂單明細表關(guān)聯(lián)即可,這種方式使用內(nèi)連接顯示的是多的一方的數(shù)據(jù),有重復(fù)。
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重復(fù)。
3.3 使用resultType
此時這種方式將上邊的查詢結(jié)果映射到pojo中,訂單信息就會有重復(fù)

當(dāng)然我們還是可以使用此種方式的,如果要實現(xiàn)不重復(fù),則在將訂單明細映射到
Orders.java中的orderDetails中,需要自己處理,使用遍歷,去掉重復(fù)記錄,將訂單明細存儲在List中,但顯然有點麻煩。這里我們使用resultMap。
3.4 使用 resultMap
要求是對orders映射不能出現(xiàn)重復(fù)記錄。于是我們在Orders.java中添加一個屬性List<orderDetail> orderDetails屬性,最終會將訂單信息映射到orders中,訂單所對應(yīng)的訂單明細就會映射到Orders.java中的orderDetails屬性中。映射成的orders的記錄數(shù)為兩條,不重復(fù),而每個Orders.java中的orderDetail屬性存儲了該訂單所對應(yīng)的訂單明細。
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">
<!-- 訂單查詢關(guān)聯(lián)查詢用戶的resultMap,即將整個查詢的結(jié)果映射到orders中 -->
<resultMap type="Orders" id="OrdersUserResultMap">
<!-- 配置要映射的訂單信息,如果有多個列組成唯一標(biāo)識,那需要配置多個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"/>
<!-- 配置要映射關(guān)聯(lián)的用戶信息,用于映射關(guān)聯(lián)查詢單個對象的信息,
property表示要將關(guān)聯(lián)查詢的用戶信息映射到orders中的哪個屬性 -->
<association property="user" javaType="User">
<!-- id:關(guān)聯(lián)查詢用戶的唯一表示
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">
<!-- 很明顯這里的訂單信息和用戶信息和上面的重復(fù),我們可以使用繼承 -->
<!-- 訂單信息 -->
<!-- 用戶信息 -->
<!-- 訂單明細信息,一個訂單查詢出了多個訂單明細,要是用collection進行映射
collection:對關(guān)聯(lián)查詢到多條記錄映射到集合中
property:映射到orders類中的哪個屬性
ofType:要映射到集合中pojo的屬性 -->
<collection property="orderdetails" ofType="Orderdetail">
<!-- id:訂單明細的唯一標(biāo)識,property:要將訂單明細的唯一標(biāo)識映射到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>
<!-- 查詢訂單關(guān)聯(lián)查詢用戶以及訂單明細,使用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>
<!-- 查詢訂單關(guān)聯(lián)查詢用戶,使用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>
說明:我們在映射訂單明細的唯一標(biāo)識的時候需要使用一個字段,如果直接使用訂單明細的id,則會和訂單表的id重復(fù),于是我們給此字段起了一個別名。
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;
//查詢訂單和訂單明細,保留了關(guān)聯(lián)用戶
public List<Orders> findOrdersAndOrderdetail() throws Exception;
}
3.4.3 測試
OrdersMapperTest.java
@Test
public void testFindOrdersAndOrderdetail() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//創(chuàng)建代理對象
OrdersMapper orders = sqlSession.getMapper(OrdersMapper.class);
//調(diào)用mapper的方法
List<Orders> list = orders.findOrdersAndOrderdetail();
System.out.println(list.size());
sqlSession.close();
}
說明:使用斷點調(diào)試方式可以查看結(jié)果,我們發(fā)現(xiàn)確實值有兩條訂單記錄,而每條訂單記錄中有兩條訂單明細記錄,這樣就很好的去除了重復(fù)。