文章目录
MyBatis一对一关联查询
一对一级联关系在现实生活中是十分常见的,例如一个大学生只有一张一卡通,一张一卡通只属于一个学生。再如人与身份证的关系也是一对一的级联关系。
MyBatis 如何处理一对一级联查询呢?在 MyBatis 中,通过 元素的子元素 处理这种一对一级联关系。
在 元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
下面以个人与身份证之间的关系为例讲解一对一级联查询的处理过程,读者只需参考该实例即可学会一对一级联查询的 MyBatis 实现。
1)创建数据表
CREATE TABLE 'idcard' (
'id' tinyint(2) NOT NULL AUTO_INCREMENT,
'code' varchar(18) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY ('id')
);
CREATE TABLE 'person'(
'id' tinyint(2) NOT NULL,
'name' varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
'age' int(11) DEFAULT NULL,
'idcard_id' tinyint(2) DEFAULT NULL,
PRIMARY KEY ('id'),
KEY 'idcard_id' ('idcard_id'),
CONSTRAINT 'idcard_id' FOREIGN KEY ('idcard_id') REFERENCES 'idcard'('id')
);
2)创建持久化类
在 myBatisDemo02 应用的 com.po 包中创建数据表对应的持久化类 Idcard 和 Person。
Idcard 的代码如下:
package com.mybatis.po;
public class Idcard {
private Integer id;
private String code;
// 省略setter和getter方法
/**
* 为方便测试,重写了toString方法
*/
@Override
public String toString() {
return "Idcard [id=" + id + ",code=" + code + "]";
}
}
Person 的代码如下:
package com.mybatis.po;
public class Person {
private Integer id;
private String name;
private Integer age;
// 个人身份证关联
private Idcard card;
// 省略setter和getter方法
@Override
public String toString() {
return "Person[id=" + id + ",name=" + name + ",age=" + age + ",card="
+ card + "]";
}
}
3)创建映射文件
首先,在 MyBatis 的核心配置文件 mybatis-config.xml(com.mybatis)中打开延迟加载开关,代码如下:
<!--在使用MyBatis嵌套查询方式进行关联查询时,使用MyBatis的延迟加载可以在一定程度上提高查询效率-->
<settings>
<!--打开延迟加载的开关-->
<setting name= "lazyLoadingEnabled" value= "true"/>
<!--将积极加载改为按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
然后,在 myBatisDemo02 应用的 com.mybatis 中创建两张表对应的映射文件 IdCardMapper.xml 和 PersonMapper.xml。在 PersonMapper.xml 文件中以 3 种方式实现“根据 id 查询个人信息”的功能,详情请看代码备注。
IdCardMapper.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="com.dao.IdCardDao">
<select id="selectCodeById" parameterType="Integer" resultType= "com.po.Idcard">
select * from idcard where id=#{id}
</select>
</mapper>
PersonMapper.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="com.dao.PersonDao">
<!-- 一对一根据id查询个人信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句)-->
<resultMap type="com.po.Person" id="cardAndPerson1">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<!-- 一对一级联查询-->
<association property="card" column="idcard_id" javaType="com.po.Idcard"
select="com.dao.IdCardDao.selectCodeByld"/>
</resultMap>
<select id="selectPersonById1" parameterType="Integer" resultMap=
"cardAndPerson1">
select * from person where id=#{id}
</select>
<!--对一根据id查询个人信息:级联查询的第二种方法(嵌套结果,执行一个SQL语句)-->
<resultMap type="com.po.Person" id="cardAndPerson2">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<!-- 一对一级联查询-->
<association property="card" javaType="com.po.Idcard">
<id property="id" column="idcard_id"/>
<result property="code" column="code"/>
</association>
</resultMap>
<select id="selectPersonById2" parameterType="Integer" resultMap= "cardAndPerson2">
select p.*,ic.code
from person p, idcard ic
where p.idcard_id=ic.id and p.id=#{id}
</select>
<!-- 一对一根据id查询个人信息:连接查询(使用POJO存储结果)-->
<select id="selectPersonById3" parameterType="Integer" resultType= "com.pojo.SelectPersonById">
select p.*,ic.code
from person p, idcard ic
where p.idcard_id = ic.id and p.id=#{id}
</select>
</mapper>
4)创建 POJO 类
在 myBatisDemo02 应用的 com.pojo 包中创建在第 3 步中使用的 POJO 类 com.pojo.SelectPersonById。
SelectPersonById 的代码如下:
package com.pojo;
public class SelectPersonById {
private Integer id;
private String name;
private Integer age;
private String code;
//省略setter和getter方法
@Override
public String toString() {
return "Person [id=" +id+",name=" +name+ ",age=" +age+ ",code=" +code+ "]";
}
}
5)创建数据操作接口
在 myBatisDemo02 应用的 com.dao 包中创建第 3 步中映射文件对应的数据操作接口 IdCardDao 和 PersonDao。
IdCardDao 的代码如下:
@Repository("idCardDao")
@Mapper
public interface IdCardDao {
public Idcard selectCodeById(Integer i);
}
PersonDao 的代码如下:
@Repository("PersonDao")
@Mapper
public interface PersonDao {
public Person selectPersonById1(Integer id);
public Person selectPersonById2(Integer id);
public SelectPersonById selectPersonById3(Integer id);
}
6)调用接口方法及测试
在 myBatisDemo02 应用的 com.controller 包中创建 OneToOneController 类,在该类中调用第 5 步的接口方法,同时创建测试类 TestOneToOne。
OneToOneController 的代码如下:
@Controller("oneToOneController")
public class OneToOneController {
@Autowired
private PersonDao personDao;
public void test(){
Person p1 = personDao.selectPersonById1(1);
System.out.println(p1);
System.out.println("=============================");
Person p2 = personDao.selectPersonById2(1);
System.out.println(p2);
System.out.println("=============================");
selectPersonById p3 = personDao.selectPersonById3(1);
System.out.println(p3);
}
}
TestOneToOne 的代码如下:
public class TestOneToOne {
public static void main(String[] args) {
ApplicationContext appcon = new ClassPathXmlApplicationContext("applicationContext.xml");
OneToOneController oto = (OneToOneController)appcon.getBean("oneToOne-Controller");
oto.test();
}
}
上述测试类的运行结果如图所示。
Mybatis一对多关联查询
在实际生活中一对多级联关系有许多,例如一个用户可以有多个订单,而一个订单只属于一个用户。
下面以用户和订单之间的关系为例讲解一对多级联查询(实现“根据 uid 查询用户及其关联的订单信息”的功能)的处理过程,读者只需参考该实例即可学会一对多级联查询的 MyBatis 实现。
1)创建数据表
本实例需要两张数据表,一张是用户表 user,一张是订单表 orders,这两张表具有一对多的级联关系。user 表在前面已创建,orders 表的创建代码如下:
CREATE TABLE `orders` (
`id` tinyint(2) NOT NULL AUTO_INCREMENT,
`ordersn` varchar(10) DEFAULT NULL,
`user_id` tinyint(2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2)创建持久化类
在 myBatisDemo02 应用的 com.po 包中创建数据表 orders 对应的持久化类 Orders,user 表对应的持久化类 MyUser 在前面已创建,但需要为 MyUser 添加如下属性:
// 一对多级联查询,用户关联的订单
private List<Orders> ordersList;
同时,需要为该属性添加 setter 和 getter 方法。
Orders 类的代码如下:
package com.po;
public class Orders {
private Integer id;
private String ordersn;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrdersn() {
return ordersn;
}
public void setOrdersn(String ordersn) {
this.ordersn = ordersn;
}
@Override
public String toString() {
return "Orders[id=" + id + ",ordersn=" + ordersn + "]";
}
}
3)创建映射文件
在 myBatisDemo02 应用的 com.mybatis 中创建两张表对应的映射文件 UserMapper.xml 和 OrdersMapper.xml。映射文件 UserMapper.xml 在前面已创建,但需要添加以下配置才能实现一对多级联查询(根据 uid 查询用户及其关联的订单信息):
<?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="com.mybatis.mapper.UserMapper">
<!-- 一对多 根据uid查询用户及其关联的订单信息:级联查询的第一种方法(嵌套查询) -->
<resultMap type="com.po.MyUser" id="userAndOrders1">
<id property="uid" column="uid" />
<result property="uname" column="uname" />
<result property="usex" column="usex" />
<!-- 一对多级联查询,ofType表示集合中的元素类型,将uid传递给selectOrdersByld -->
<collection property="ordersList" ofType="com.po.Orders"
column="uid" select="com.dao.OrdersDao.selectOrdersByld" />
</resultMap>
<select id="selectUserOrdersById1" parameterType="Integer"
resultMap="userAndOrders1">
select * from user where uid = #{id}
</select>
<!--对多根据uid查询用户及其关联的订单信息:级联查询的第二种方法(嵌套结果) -->
<resultMap type="com.po.MyUser" id="userAndOrders2">
<id property="uid" column="uid" />
<result property="uname" column="uname" />
<result property="usex" column="usex" />
<!-- 对多级联查询,ofType表示集合中的元素类型 -->
<collection property="ordersList" ofType="com.po.Orders">
<id property="id" column="id" />
<result property="ordersn" column="ordersn" />
</collection>
</resultMap>
<select id="selectUserOrdersById2" parameterType="Integer"
resultMap="userAndOrders2">
select u.*,o.id, o.ordersn from user u, orders o where u.uid
= o.user_id and
u.uid=#{id}
</select>
<!-- 一对多 根据uid查询用户及其关联的订单信息:连接查询(使用POJO存储结果) -->
<select id="selectUserOrdersById3" parameterType="Integer"
resultType="com.pojo.SelectUserOrdersById">
select u.*, o.id, o.ordersn from user u, orders o where
u.uid = o.user_id
and u.uid=#{id}
</select>
</mapper>
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="com.dao.OrdersDao">
<!-- 根据用户uid查询订单信息 -->
<select id="selectOrdersById" resultType="com.po.Orders"
parameterType="Integer">
select * from orders where user_id=#{id}
</select>
</mapper>
4)创建 POJO 类
在 myBatisDemo02 应用的 com.pojo 包中创建在第 3 步中使用的 POJO 类 com.pojo. SelectUserOrdersById。
SelectUserOrdersById 的代码如下:
package com.po;
public class SelectUserOrdersById {
private Integer uid;
private String uname;
private String usex;
private Integer id;
private String ordersn;
// 省略setter和getter方法
@Override
public String toString() { // 为了方便查看结果,重写了toString方法
return "User[uid=" + uid + ",uname=" + uname + ",usex=" + usex
+ ",oid=" + id + ",ordersn=" + ordersn + "]";
}
}
5)创建数据操作接口
在 myBatisDemo02 应用的 com.dao 包中创建第 3 步中映射文件对应的数据操作接口 OrdersDao 和 UserDao。
OrdersDao 的代码如下:
package com.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.po.Orders;
@Repository("ordersDao")
@Mapper
public interface OrdersDao {
public List<Orders> selectOrdersById(Integer uid);
}
UserDao 接口在前面已创建,这里只需添加如下接口方法:
package com.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.po.MyUser;
import com.po.SelectUserOrdersById;
@Repository("userDao")
@Mapper
public interface UserDao {
public MyUser selectOrdersById1(Integer uid);
public MyUser selectOrdersById2(Integer uid);
public List<SelectUserOrdersById> selectOrdersById3(Integer uid);
}
6)调用接口方法及测试
在 myBatisDemo02 应用的 com.controller 包中创建 OneToMoreController 类,在该类中调用第 5 步的接口方法,同时创建测试类 TestOneToMore。
OneToMoreController 的代码如下:
@Controller("oneToMoreController")
public class oneToMoreController {
@Autowired
private UserDao userDao;
public void test(){
//查询一个用户及订单信息
MyUser auser1 = userDao.selectUserOrderById1(1);
System.out.println(auser1);
System.out.println("=============================");
MyUser auser2 = userDao.selectUserOrderById2(1);
System.out.println(auser2);
System.out.println("=============================");
List<SelectUserOrdersById> auser3 = userDao.selectUserOrdersById3(1);
System.out.println(auser3);
System.out.println("=============================");
}
}
TestOneToMore 的代码如下:
public class TestOneToMore {
public static void main(String[] args) {
ApplicationContext appcon = new ClassPathXmlApplicationContext("applicationContext.xml");
OneToMoreController otm = (OneToMoreController)appcon.getBean("oneToMoreController");
otm.test();
}
}
测试类的运行结果如图所示。
MyBatis多对多关联查询
其实,MyBatis 没有实现多对多级联,这是因为多对多级联可以通过两个一对多级联进行替换。
例如,一个订单可以有多种商品,一种商品可以对应多个订单,订单与商品就是多对多的级联关系,使用一个中间表(订单记录表)就可以将多对多级联转换成两个一对多的关系。
下面以订单和商品(实现“查询所有订单以及每个订单对应的商品信息”的功能)为例讲解多对多级联查询。
1)创建数据表
订单表在前面已创建,这里需要创建商品表 product 和订单记录表 orders_detail,创建代码如下:
CREATE TABLE 'product'(
'id' tinyint(2) NOT NULL,
'name' varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
'price' double DEFAULT NULL,
PRIMARY KEY ('id')
);
CREATE TABLE 'orders_detail'(
'id' tinyint(2) NOT NULL AUTO_INCREMENT,
'orders_id' tinyint(2) DEFAULT NULL,
'product_id' tinyint(2) DEFAULT NULL,
PRIMARY KEY ('id'),
KEY 'orders_id' ('orders_id'),
KEY 'product_id' ('product_id'),
CONSTRAINT 'orders_id' FOREIGN KEY ('orders_id') REFERENCES 'orders' ('id'),
CONSTRAINT 'product_id' FOREIGN KEY ('product_id') REFERENCES 'product' ('id')
);
2)创建持久化类
在 myBatisDemo02 应用的 com.po 包中创建数据表 product 对应的持久化类 Product,而中间表 orders_detail 不需要持久化类,但需要在订单表 orders 对应的持久化类 Orders 中添加关联属性。
Product 的代码如下:
package com.po;
import java.util.List;
public class Product {
private Integer id;
private String name;
private Double price;
// 多对多中的一个一对多
private List<Orders> orders;
// 省略setter和getter方法
@Override
public String toString() { // 为了方便查看结果,重写了toString方法
return "Product[id=" + id + ",name=" + name + ",price=" + price + "]";
}
}
Orders 的代码如下:
package com.po;
import java.util.List;
public class Orders {
private Integer id;
private String ordersn;
// 多对多中的另一个一对多
private List<Product> products;
// 省略setter和getter方法
@Override
public String toString() {
return "Orders[id=" + id + ",ordersn=" + ordersn + ",products="
+ products + "]";
}
}
3)创建映射文件
本实例只需在 com.mybatis 的 OrdersMapper.xml 文件中追加以下配置即可实现多对多级联查询。
4)创建 POJO 类
该实例不需要创建 POJO 类。
5)添加数据操作接口方法
在 Orders 接口中添加以下接口方法:
public List<Orders> selectallOrdersAndProducts();
6)调用接口方法及测试
在 myBatisDemo02 应用的 com.controller 包中创建 MoreToMoreController 类,在该类中调用第 5 步的接口方法,同时创建测试类 TestMoreToMore。
MoreToMoreController 的代码如下:
package com.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.dao.OrdersDao;
import com.po.Orders;
@Controller("moreToMoreController")
public class MoreToMoreController {
@Autowired
private OrdersDao ordersDao;
public void test() {
List<Orders> os = ordersDao.selectallOrdersAndProducts();
for (Orders orders : os) {
System.out.println(orders);
}
}
}
TestMoreToMore 的代码如下:
package com.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMoreToMore {
public static void main(String[] args) {
ApplicationContext appcon = new ClassPathXmlApplicationContext(
"applicationContext.xml");
MoreToMoreController otm = (MoreToMoreController) appcon
.getBean("moreToMoreController");
otm.test();
}
}
上述测试类的运行结果如图所示。