The original text comes from: http://blog.csdn.net/soonfly/article/details/63688288
This article mainly talks about association relationships: one-to-one relationship and one-to-many relationship.
Create 5 tables first:DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`catename` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`orderno` varchar(20) NOT NULL COMMENT '系统订单号',
`totalprice` decimal(10,2) DEFAULT NULL COMMENT '订单总价',
`create_time` int(11) NOT NULL COMMENT '创建时间',
`create_userid` int(10) unsigned NOT NULL COMMENT '创建用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
DROP TABLE IF EXISTS `order_detail`;
CREATE TABLE `order_detail` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`order_id` int(10) unsigned NOT NULL,
`product_id` int(10) unsigned NOT NULL,
`productname` varchar(50) NOT NULL COMMENT '商品快照:名称',
`price` decimal(10,2) NOT NULL COMMENT '商品快照:单价',
`num` int(10) unsigned NOT NULL COMMENT '购买数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`productname` varchar(50) NOT NULL,
`price` decimal(10,2) DEFAULT NULL,
`cateid` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`salt` varchar(50) DEFAULT NULL,
`sex` smallint(1) DEFAULT NULL COMMENT '0-未知 1-男 2-女',
`address` varchar(50) DEFAULT NULL,
`cellphone` varchar(30) DEFAULT NULL,
`email` varchar(30) DEFAULT NULL,
`islock` smallint(1) unsigned NOT NULL DEFAULT '0',
`isvalidate` smallint(1) unsigned NOT NULL DEFAULT '1',
`isdel` smallint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8;
The five table relationships are as follows:
4 relationship lines on the carding diagram:
1. 1 user can have 0 or more orders: [user table]–>[order table] is a 1-to-many (0.n) relationship
2 , 1 valid order will purchase 1 or more commodity entries: [order table]–>[order_detail table] is a 1-to-many (1.n) relationship
3. 1 commodity entry must correspond to a product detail: [ order_detail table]->[product table] is a 1-to-1 relationship
4. There may be 0 or more products under a product category: [category table]->[product table] is 1-to-many (0.n) Relationship
5. All 1-to-many relationships, in turn, must be 1-to-1 relationships. For example, [user table]->[order table] is a 1-to-many (0.n) relationship, then in turn [order table]->[user table] is a 1-to-1 relationship. Each order will have a unique user associated with it.
Note that all 1-to-1 relationships, in turn, are not necessarily 1-to-1 relationships. For example, [order_detail table]->[product table] is a 1-to-1 relationship, and conversely [product table]->[order_detail table] is not a 1-to-1 relationship. Because the same product may appear in multiple different order entries (everyone is willing to buy popular products)
这篇讲一对一关系
[order表]–>[user表]是1对1关系,那么就拿这个来练手。1对1关联关系用于对一个表外键的扩展。
现在要在后台系统中按以下字段显示订单列表。
[order表]中没有用户名称,用户地址,联系电话这三个字段,只能靠creat_userid去关联查询对应的那个用户信息。
查询语句如下:
SELECT `order`.*,`user`.username,`user`.address,`user`.cellphone
FROM `order`,`user`
WHERE `order`.create_userid=`user`.id
有三种方式可以实现:
一、扩展新建POJO对象,不使用association标签
step1.我们已经有一个与表对应的Order类,但是没有用户名称,用户地址,联系电话这三个字段。现在新建一个类OrderExtend,扩充字段:
public class OrderExtend extends Order{
public OrderExtend() {
super();
}
/*添加用于展示的用户名称,用户地址,联系电话这三个字段*/
String username;
String address;
String cellphone;
/*下面get和set方法*/
getter and setter....
}
step2.创建接口及Xml
public interface OrderExtendMapper {
//查询单个订单详情,关联查询用户信息
public OrderExtend getByOrderno(String orderno) throws Exception;
//查询订单列表,关联查询用户信息
public List<OrderExtend> getList() throws Exception;
}
<mapper namespace="twm.mybatisdemo.mapper.OrderExtendMapper">
<select id="getByOrderno" parameterType="String"
resultType="twm.mybatisdemo.pojo.OrderExtend">
SELECT
`order`.*,`user`.username,`user`.address,`user`.cellphone
FROM `order` ,`user`
WHERE `order`.create_userid=`user`.id AND `order`.create_userid=#{id}
</select>
<select id="getList" resultType="twm.mybatisdemo.pojo.OrderExtend">
SELECT
`order`.*,`user`.username,`user`.address,`user`.cellphone
FROM `order`
,`user`
WHERE `order`.create_userid=`user`.id
</select>
</mapper>
step3.调用
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionAssist.getSession();
OrderExtendMapper ordermapper = session
.getMapper(OrderExtendMapper.class);
OrderExtend order = ordermapper.getByOrderno("M201209012578917");
System.out.println(order.getOrderno() + "," + order.getUsername() + ","
+ order.getAddress() + "," + order.getCellphone());
}
二、(推荐)用sql联合查询,使用association标签
这个不需要新建扩展类了。在Order类中,新增一个User类型的属性,将查询出来的用户相关数据通过association标签映射到user。
association专门用来建立1对1关联关系。其中
property:指定对象的属性名
javaType:指定要映射的对象的类型。
step1.在Order类中,新增一个User类型的属性
public class OrderExtend extends Order{
/*添加用于展示的用户名称,用户地址,联系电话这三个字段*/
String username;
String address;
String cellphone;
User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
/*下面get和set方法*/
getter and setter....
}
step2.创建映射器接口及配置Xml
在twm.mybatisdemo.mapper
包下创建
OrderMapper.java:
public interface OrderMapper {
//查询单个订单详情,关联查询用户信息
public Order getByOrderno(String orderno) throws Exception;
//查询订单列表,关联查询用户信息
public List<Order> getList() throws Exception;
}
OrderMapper.xml:
<mapper namespace="twm.mybatisdemo.mapper.OrderMapper">
<!-- 定义类型映射 -->
<resultMap type="Order" id="OrderMap">
<!-- 订单表属性 -->
<id column="id" property="id" />
<result column="orderno" property="orderno" />
<result column="create_time" property="create_time" />
<result column="create_userid" property="create_userid" />
<!-- 关联的用户信息 -->
<!-- association用于关联查询:
property指属性,javaType是要映射的对象的类型。 -->
<association property="user" javaType="User">
<result column="username" property="username" />
<result column="address" property="address" />
<result column="cellphone" property="cellphone" />
</association>
</resultMap>
<select id="getByOrderno" parameterType="String"
resultMap="OrderMap">
SELECT
`order`.*,`user`.username,`user`.address,`user`.cellphone
FROM `order`
,`user`
WHERE `order`.create_userid=`user`.id AND
`order`.orderno=#{orderno}
</select>
<select id="getList" resultMap="OrderMap">
SELECT
`order`.*,`user`.username,`user`.address,`user`.cellphone
FROM `order`
,`user`
WHERE `order`.create_userid=`user`.id
</select>
</mapper>
step3.调用
public static void main(String[] args) throws Exception {
SqlSession session = SqlSessionAssist.getSession();
OrderMapper ordermapper = session.getMapper(OrderMapper.class);
Order order = ordermapper.getByOrderno("M201209012578917");
System.out.println(order.getOrderno() + ","
+ order.getUser().getUsername() + ","
+ order.getUser().getAddress() + ","
+ order.getUser().getCellphone());
}
三、不用sql联合查询,通过association的延迟加载来实现
什么是延迟加载?如果先查询订单信息即可满足业务要求就不会去查询用户,只有当用到用户信息时再查询用户信息。
对用户信息按需去查询就是延迟加载。
比如上面,只有当调用Order中的getUser方法获取关联的user数据时,才会触发数据库查询user表。
mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。
lazyLoadingEnabled:全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。允许值有:true | false。默认值:false
aggressiveLazyLoading:当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。允许值有:true | false。默认值:true
和第二种方式比,其它都不变。只是DAOImplement层有一些变化,XML文件要调整三处:
第一处:新增一个用户查询语句:
<!-- 新增一个用户查询语句:getUser -->
<select id="getUser" parameterType="int" resultType="User">
SELECT
`username`,`address`,`cellphone`
FROM `user`
WHERE `id` =#{_parameter}
</select>
第二处:把原来resultMap的association标签改为
<association property="user" javaType="User" column="create_userid" select="getUser" />
第三处:把getByOrderno和getList查询语句改为普通的select单表查询。
<?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="twm.mybatisdemo.mapper.OrderMapper">
<!-- 定义类型映射 -->
<resultMap type="Order" id="OrderMap">
<!-- 订单表属性 -->
<id column="id" property="id" />
<result column="orderno" property="orderno" />
<result column="create_time" property="create_time" />
<result column="create_userid" property="create_userid" />
<!-- 关联的用户信息 -->
<!-- association用于关联查询: property指属性,javaType是要映射的对象的类型。 -->
<association property="user" javaType="User" column="create_userid"
select="getUser" />
</resultMap>
<!-- 新增一个用户查询:getUser。getUser这一段可以删掉,用user对象的查询方法 -->
<select id="getUser" parameterType="int" resultType="User">
SELECT
`username`,`address`,`cellphone`
FROM `user`
WHERE `id` =#{_parameter}
</select>
<select id="getByOrderno" parameterType="String" resultMap="OrderMap">
SELECT * FROM `order` WHERE `order`.orderno=#{orderno}
</select>
<select id="getList" resultMap="OrderMap">
SELECT * FROM `order`
</select>
</mapper>
一切OK了。
association的几个属性:
property:指定内部对象属性名
javaType:内部映射的对象的类型。
column:要传给select语句的参数,相当于指定外键字段。
select:指定用户查询语句的ID
The statement of getUser user query can also be omitted, because twm.mybatisdemo.mapper.UserMapper(UserMapper.xml)
a selectById
query was created in . So here you can delete the query of getUser and change the association:
<association property="user" javaType="User" column="create_userid" select="twm.mybatisdemo.mapper.UserMapper.selectById" />
- 1
In fact, most tables displayed in business scenarios use multiple table fields.
If lazy loading is used, there will be an N+1 problem.
What is the N+1 problem?
Each time the User object inside the Order is obtained, a select query will be performed.
When the getList method of the Order is executed during the running process, the SQL will first perform a query. If there are N order records in the query result, it will actually be displayed in each order. In the process, the query to select the user needs to be run once, for a total of n times.
SQL is executed n+1 times in total. Compared with the second method of performing a joint query only once, this method is undoubtedly inefficient.
If the table of the business scenario displays fields and there is no cross-table, then the lazy loading method can be used