目录
3.1 创建订单类的sql映射文件:OrderMapper.xml
项目结构图
环境:
开发工具:Intellij idea 2018.3版本
JDK:1.8
数据库:Mysql 8.0.11
Mybatis:3.4.1
在讲案例前先明确什么是一对一,一对多。
1. 一对一关系和一对多关系
(以下来自于黑马程序员的视频笔记)
- 什么关系属于一对一
一个身份证对应着一个公民,一个公民也对应着一张身份证
- 一对一的建表原则:
两种对应方式:
唯一外键对应:在任意一方加一个外键,然后加个unique约束条件。本文中采用这种
主键对应:就是主键对主键
- 什么样关系属于一对多?
一个用户对应多个订单,一个订单只能属于一个用户
一个客户对应多个联系人,一个联系人只能属于某一个客户。
- 一对多的建表原则:
在多的一方创建外键指向一的一方的主键
这里用到的是两张表,订单表(Orders)和用户表(User)。一个订单对应一个用户,一个用户可以对应着多个订单。虽然这是一对多关系,但是如果从订单角度看,一个订单只能对应一个用户。勉强可以算作一对一,本文中为了缩减代码量,就直接用两个表演示一对一和一对多。下面进入案例。有些代码是重复的,因此得先把这些准备工作做了
2.准备
2.1创建两张表
创建User表,插入三条数据
CREATE TABLE `user` (
`uid` 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=27 DEFAULT CHARSET=UTF8MB4;
INSERT INTO `user` VALUES ('1', '王五', null, '2', null);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州');
创建Orders表,插入两条数据
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=UTF8MB4;
INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);
2.2 创建工程
可以创建java工程也可以创建javaEE项目,反正测试使用Junit。具体如何创建可以参考我另外一篇文章https://blog.csdn.net/Vibugs/article/details/85246420
项目结构图
导入jar包:
2.3 创建实体类
创建Order实体类
根据一对一的实体类原则,在没有外键的一方实体类中包含外键一方的对象 , 因此Order类中要有User的对象
创建User类
根据一对多的建立实体类的原则,在没有外键的一方(user)的实体类创建另一方的List集合(List<order>),因此 User类中带有order类的list集合。
2.4 创建Mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 通过properties导入外部配置文件-->
<properties resource="MySql.properties"/>
<!--配置别名
* 1:可以直接设置别名;
* 2:如果实体类的别名太多了,可以将实体类都放在一个包下,然后这里直接导入包就好了
-->
<typeAliases>
<typeAlias type="cn.svllen.mybatis.domain.User" alias="user"/>
<!-- <package name="cn.svllen.mybatis.domain" /> -->
</typeAliases>
<!-- 和spring整合后 environment配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC"/>
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入map文件 -->
<mappers>
<mapper resource="cn/svllen/mybatis/domain/UserMapper.xml"/>
<mapper resource="cn/svllen/mybatis/domain/OrderMapper.xml"/>
</mappers>
</configuration>
MySql.properties配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=12315
2.5 创建SessionFactory的Util类
package cn.svllen.mybatis.util;
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 java.io.InputStream;
/**
* @program: MybatisDay01
* @description: 创建SessionFactory的util类
* @author: svllen
* @create: 2019-03-23 10:52
**/
public class SessionFactoryUtil {
public static SqlSessionFactory sqlSessionFactory;
static{
try{
InputStream inputStream = Resources.getResourceAsStream("Mybatis-config.xml");
//构建工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (Exception e){
e.printStackTrace();
}
}
public static SqlSession getCurrentSession(){
return sqlSessionFactory.openSession();
}
}
3. 实现一对一
需求:使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息。在sql映射文件中用resultMap标签输出
实现思路:根据一对一的建表原则,在任意一方(订单表)建立唯一外键指向另一方的主键,确定要查询的唯一订单和唯一用户。再根据一对一建立实体类的原则,在没有外键的一方实体类中包含外键一方的对象,显示时通过这个对象获取到关联的用户信息。
实现过程:sql查询语句中设置两个条件:
条件1 订单的外键user_id =用户的主键id
条件2: 订单的主键id=你要查询的值。
结果用resultMap结果集返回,类型是Order型,order实体类中有user对象,因此关联的用户信息就可以从里面获取到(即order.getUser()) 。实体类时关联了,但是映射的时候还是得指明关联关系,因此需要在resultMap标签中使用association标签指定。
3.1 创建订单类的sql映射文件:OrderMapper.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.svllen.mybatis.domain.OrderMapper">
<!-- 使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息-->
<select id="OneToOneByOrder" parameterType="int" resultMap="oneToOneOrderResultMap">
SELECT
o.id as order_id,
user_id, number, createtime, note,
u.id as user_id,
username, birthday, sex, address
FROM orders o, user u WHERE o.user_id = u.id and o.id=#{id}
</select>
<!-- 使用resultMap输出结果-->
<resultMap id="oneToOneOrderResultMap" type="cn.svllen.mybatis.domain.Order" >
<!--要输出的Order的信息 -->
<id column="order_id" property="id" />
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 一对一的关系映射,需要使用association标签
* property:对象属性的名称
* javaType:对象属性的类型
* column:所对应的外键字段名称
* select:使用另一个查询封装的结果
-->
<association property="user" javaType="User" column="user_id" resultMap="UserMap" />
</resultMap>
<!-- 将一对一的User抽出来,封装重用-->
<resultMap id="UserMap" type="User">
<!--要输出的User的信息 -->
<id column="user_id" property="uId" javaType="int" />
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</resultMap>
</mapper>
select标签:
1.我给两个表的主键id都起了别名,因为两个表的主键都是一样的名称,当你把结果映射到resultMap的时候会出现显示错误的bug。具体可查看我另一篇文章 https://blog.csdn.net/Vibugs/article/details/86540185
2.我们的查询条件有两个,一个是通过order_id查询用户信息,第二是通过外键查询用户信息。因此在sql语句中查询条件是 o.user_id = u.id(即订单表中的user_id外键 = 用户表中的id主键)。但是要插入匹配符#{}的应该是订单的id主键,毕竟我们是通过订单id查询订单信息。
- 属性:id的值就是OrderMapper接口类 的方法名,parameterType是输入的参数,这里是int型。resultMap:将结果以结果集的形式显示。
- SQL语句:这里有两个点需要知道。
resultMap标签:
属性
id | 当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
type | 类的完全限定名, 或者一个类型别名(就是决定resultMap的类型) |
autoMapping | 如果设置这个属性,MyBatis将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset) |
字段
- constructor - 用于在实例化类时,注入结果到构造方法中
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
- 嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个
- collection – 一个复杂类型的集合
- 嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个
- discriminator – 使用结果值来决定使用哪个 resultMap
- case – 基于某些值的结果映射
- 嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个
- case – 基于某些值的结果映射
本例中两个resultMap的type不一样,select标签指定的oneToOneOrderResultMap是Order类型的,而association关联的UserMap是User类型的。那输出时怎么输出呢,是输出order对象还是user对象呢。其实是输出Order对象,我们在Order实体类中不是定义了user对象吗,可以直接通过order.getUser.getXxx()方法输出User信息。
association标签:
这个标签上面代码注释里面有对它几个属性的解释。在这里就不多说了
记得将OrderMapper.xml导入到Mybatis-config.xml文件中
3.2 创建接口类:OrderMapper
package cn.svllen.mybatis.domain;
/**
* @program: MybatisDay01
* @description: orderMapper的接口类,用来跟OrderMapper.xml关联起来。里面的一个select标签(也可以回其他标签,insert,update)对应这里的一个方法
* @author: svllen
* @create: 2019-03-26 15:30
**/
public interface OrderMapper {
//使用一对一的查找方式,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息
Order OneToOneByOrder(int orderId);
}
3.3 测试类:MapperTest
public class MapperTest {
/**
* @Description: 一对一的测试,输入订单id查询订单信息,嵌套用户顺便查询出订单所属的用户信息
* @Param:
* @return:
* @Author: svllen
* @Date: 2019/3/27
*/
@Test
public void OneToOneByOrderTest(){
SqlSession sqlSession = SessionFactoryUtil.getCurrentSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
int orderId = 4;
//查询结果
Order order = orderMapper.OneToOneByOrder(orderId);
//显示结果
System.out.println("查询到的订单为:" + order);
System.out.println("该订单用户为:" + order.getUser());
sqlSession.commit();
sqlSession.close();
}
}
结果:
4.实现一对多
需求:通过输入用户id查询多个订单信息,用户信息也要返回
实现思路:根据一对多建表原则(在order创建外键指向user的主键),通过order的外键确定要查找的唯一用户和至少一个的订单的关系,再根据一对多建实体类的原则,在user实体类中有order的List<order>集合,通过这个集合获取唯一用户下的所有订单。
过程很相似,直接附上步骤
创建userMapper接口类:
package cn.svllen.mybatis.domain;
import java.util.List;
/**
* @program: MybatisDay01
* @description: usermapper的接口类,实现Mapper动态代理开发。取代传统的Dao,DaoImpl这种开发模式。与userMapper形成关联
* @author: svllen
* @create: 2019-03-25 10:03
**/
public interface UserMapper {
/*为了跟mapper.xml文件产生关联,这里有四个原则
* 1. 接口方法名等于id名
* 2. 返回值类型与resultType的一样
* 3. 方法的形参跟parameterType一样
* 4. 在.xml文件中,命名空间的值是接口类的全路径
*/
//测试一对多的关系,通过输入用户id查询多个订单信息,用户信息也要返回
User oneToManyByUser(int userId);
}
创建userMapper.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">
<!--写sql语句的 -->
<mapper namespace="cn.svllen.mybatis.domain.UserMapper">
<!--一对多的测试,通过输入用户id查询多个订单信息,用户信息也要返回 -->
<select id="oneToManyByUser" parameterType="int" resultMap="userResultMap">
select
o.id as order_id,
user_id, number, createtime, note,
u.id as user_id,
username, birthday, sex, address
FROM orders o ,user u WHERE o.user_id = u.id and u.id=#{id}
</select>
<resultMap id="userResultMap" type="user">
<id column="user_id" property="uId"/>
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 一对多的关系。使用collection标签
* property:指定集合属性的名(User实体类中list集合名)
* ofType:指的是集合中元素的值,一般是类的完全限定名
-->
<collection property="orderList" ofType="order" resultMap="orderResultMap" />
</resultMap>
<!--可重用的order的resultMap -->
<resultMap id="orderResultMap" type="order">
<id column="order_id" property="id" />
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
</resultMap>
</mapper>
创建测试方法:
/**
* @Description: 一对多的测试, 通过输入用户id查询多个订单信息,用户信息也要返回
* @Param:
* @return:
* @Author: svllen
* @Date: 2019/3/29
*/
@Test
public void oneToManyByUserTest(){
SqlSession sqlSession = SessionFactoryUtil.getCurrentSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//设置查询条件
int userId = 16;
User user = userMapper.oneToManyByUser(userId);
System.out.println("你查到的用户信息为:" + user +"\n该用户下的订单有:");
List<Order> orderList = user.getOrderList();
for (Order order : orderList){
System.out.println(order);
}
sqlSession.commit();
sqlSession.close();
}
测试结果:
结束语:希望各位点赞