MyBatis
高级映射涉及到联合查询,包括一对一、一对多、多对多等复杂查询。本文中所用的数据来自于MyBatis(二)—— 入门程序之单表增删查改。这里面有用户表、商品表、订单表、订单明细表。订单关联用户,订单明细关联订单和商品,以下是用visio2010画的数据库模型图。
根据上述模型图和表的实际含义,可以推测出下面的对应关系:
查询订单信息,并且关联与此订单相关的用户信息。本例中,没有查询所有的用户信息,只查询用户名、性别和地址信息。
1. POJO扩展类
这里需要用到的POJO
类是Orders
类,因为用户信息没有全部查询,所以不需要使用到User
类。但是因为是联合查询,所以单个POJO
类已经不能满足需求了,我们需要扩展POJO
类。
因为订单是主查询,所以在Orders
类的基础上做扩展。
public class OrdersCustom extends Orders {
private String sex;
private String username;
private String address;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "OrdersCustom [username=" + username + ", sex=" + sex
+ ", address=" + address + "]";
}
}
注意,如果要输出的话,建议重写一下toString
方法,要不然看不到对应的字段(本人就犯了这样低级的错误,还傻乎乎的查遍了所有地方,最后栽在一个toString
上……)。
2. Mapper.java
public interface OrdersMapper {
public List<OrdersCustom> findOdersAndUser() throws Exception;
}
3. Mapper.xml
<mapper namespace="com.xxx.mapper.OrdersMapper">
<select id="findOdersAndUser" resultType="com.xxx.pojo.OrdersCustom">
<include refid="selectOrdersAndUser">include>
select>
<sql id="selectOrdersAndUser">
select orders.*,
user.username,
user.sex,
user.address
from
orders, user
where
orders.user_id = user.id;
sql>
mapper>
4. 全局配置文件中加载映射文件
//others remain the same
<mappers>
<mapper resource="com/xxx/mapper/UserMapper.xml"/>
mappers>
5. 测试
public class OrdersMappertest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = org.apache.ibatis.io.Resources
.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testfindOdersAndUser() throws Exception {
//try-with-resources to auto close Sqlsession
try(SqlSession sqlSession = sqlSessionFactory.openSession();){
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersCustom> order_user = mapper.findOdersAndUser();
System.out.println( order_user );
}
}
}
1. POJO 扩展类。将原来单独列出的属性替换成User
属性,因为映射文件里需要用这个属性进行关联。
public class OrdersCustom extends Orders {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "OrdersCustom ["+ super.toString() + ",user=" + user + "]";
}
}
2. 映射文件Mapper.xml
<resultMap type="com.xxx.pojo.OrdersCustom" id="OrdersAndUserMap">
<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"/>
<association property="user" javaType="com.xxx.pojo.User">
<id column="user_id" property="id"/>
<result column="sex" property="sex"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
association>
resultMap>
<select id="findOdersAndUserByResultMap" resultMap="OrdersAndUserMap">
<include refid="selectOrdersAndUser">include>
select>
MyBatis
用
进行关联,property
属性指定POJO
中定义的属性,注意,要与POJO
中定义的属性对应起来;JavaType
指定这个属性的Java
类型。
3. Mapper.java
public List<OrdersCustom> findOdersAndUserByResultMap() throws Exception;
4. 测试
@Test
public void findOdersAndUserByResultMap() throws Exception {
try(SqlSession sqlSession = sqlSessionFactory.openSession();){
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersCustom> list = mapper.findOdersAndUserByResultMap();
System.out.println( list );
}
}
输出(截取部分):
注意,由于toString
的关系,输出时用户信息中会有字段显示为null
,因为我们输出的是整个user
但是没有查询里面的某些字段,无法映射上,所以显示为null
。
查询订单及该订单对应的明细,前面说过,一个订单可以包含多个明细,所以订单关联查询订单明细属于一对多的关系。
1. POJO扩展类。在Orders
的导出类中添加List
属性(OrderDetail
是订单明细表对应的POJO
类,这里就直接略过了),最终MyBatis
会将订单映射到Orders
中,订单明细映射到这个List
中。
public class OrdersCustom extends Orders {
List<Orderdetail> orderdetails = new ArrayList<Orderdetail>();
public List<Orderdetail> getOrderdetails() {
return orderdetails;
}
public void setOrderdetails(List<Orderdetail> orderdetails){
this.orderdetails = orderdetails;
}
}
2. Mapper.java
public List<OrdersCustom> findOrderAndOrderDetails() throws Exception;
3. Mapper.xml
<resultMap type="com.xxx.pojo.OrdersCustom" id="ordersAndOrderdetailResulMap" >
<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"/>
<collection property="orderdetails" ofType="com.xxx.pojo.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" />
collection>
resultMap>
<select id="findOrderAndOrderDetails" resultMap="ordersAndOrderdetailResulMap">
select orders.*,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
from
orders, orderdetail
where
orderdetail.orders_id = orders.id;
select>
需要说明一下,原来的教程里面,把用户表User
也关联进去了,个人觉得,作为案例来说没有必要,因此把关联查询用户表的那一部分进行了删减。当把用户也关联进去的时候,因为前面2.3
节已经定义了一个resultMap
,所以可以用extends
属性继承原有的resultMap
(
),这样就可以不用写订单和用户映射的部分了。
因为一个订单可能对应多个明细,所以用
来进行映射。
中的property
属性指定POJO
扩展类中定义的属性,最终多条记录会映射到这个属性上;ofType
指定List
的限定类型。本例中,在扩展类中定义了List
,所以property="orderdetails" ofType="com.shao.pojo.Orderdetail"
。
中的
指定订单明细表的唯一表示(即主键),所以查询时订单明细的id
不能忽略,但是因为订单表中有一个外键指向订单明细的id
,订单明细中又有一个id
,两个会重复,所以查询时给订单明细的id定义一个别名 orderdetail.id orderdetail_id,
。
4. 测试
@Test
public void findOrderAndOrderDetails() throws Exception {
try(SqlSession sqlSession = sqlSessionFactory.openSession( );){
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersCustom> list = mapper.findOrderAndOrderDetails();
System.out.println( list.get(0).getOrderdetails() );
}
}
查询用户和用户购买的商品。用户和商品没有直接的联系,但是订单关联用户,订单明细关联订单和商品,可以通过中间的这些关系把用户和商品对上。
1. POJO扩展类。因为几个表都要涉及到,所以几个POJO
类都要进行扩展。一个用户对应多个订单,在UserCustom
中添加List
,将订单和用户关联上;一个订单对应多条明细,在OrdersCustom
中添加List
,将订单和订单明细关联上;一条明细对应一个商品,在OrderdetailCustom
中添加Items
,将订单明细和商品关联上。绕了一圈之后,用户和商品关联上了。
public class UserCustom extends User {
List<OrdersCustom> orders = new ArrayList<OrdersCustom>();
public List<OrdersCustom> getOrders() {
return orders;
}
public void setOrders(List<OrdersCustom> orders) {
this.orders = orders;
}
}
public class OrdersCustom extends Orders {
List<OrderdetailCustom> orderdetailsList = new ArrayList<OrderdetailCustom>();
public List<OrderdetailCustom> getOrderdetailsList() {
return orderdetailsList;
}
public void setOrderdetailsList(List<OrderdetailCustom> orderdetailsList) {
this.orderdetailsList = orderdetailsList;
}
}
public class OrderdetailCustom extends Orderdetail {
Items items = new Items();
public Items getItems() {
return items;
}
public void setItems(Items items) {
this.items = items;
}
}
2. Mapper.java
public List<UserCustom> findUserAndItem() throws Exception;
3. Mapper.xml
<resultMap type="com.xxx.pojo.UserCustom" id="UserAndOrdersResultMap">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<collection property="orders" ofType="com.xxx.pojo.OrdersCustom">
<id column="id" property="id"/>
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<collection property="orderdetailsList" ofType="com.xxx.pojo.OrderdetailCustom">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
<association property="items" javaType="com.xxx.pojo.Items">
<id column="items_id" property="id"/>
<result column="name" property="name" />
<result column="price" property="price" />
<result column="pic" property="pic" />
<result column="createtime" property="createtime" />
<result column="detail" property="detail" />
association>
collection>
collection>
resultMap>
<select id="findUserAndItem" resultMap="UserAndOrdersResultMap">
select orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.id items_id,
items.name,
items.price,
items.pic,
items.createtime,
items.detail
from
orders, user, orderdetail,items
where
orders.user_id = user.id
and
orderdetail.orders_id = orders.id
and
orderdetail.items_id = items.id
select>
可以看到,因为嵌套了几层查询,所以配置会相对复杂一点,但其实有了前面的经验,这个配置应该也不难理解。用户关联订单,所以在里面嵌套了一个指定类型为订单的
;订单关联订单明细,所以在
里又嵌套了一个指定类型为订单明细的
;订单明细关联商品,所以用
进行关联。
4. 测试
@Test
public void testfindUserAndItem() throws Exception {
try(SqlSession sqlSession = sqlSessionFactory.openSession( );){
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<UserCustom> list = mapper.findUserAndItem();
System.out.println( list.get(0).getOrders().get(0).getOrderdetailsList().get(0).getItems() );
}
}
本文用实际案例讲解了MyBatis
中高级映射的开发方法,包含一对一映射、一对多映射和多对多映射。在最后的多对多映射案例中,因为映射双方没有直接的联系,所以需要通过中间的表将联系建立起来。
需要注意几点:
POJO
已经无法满足开发需求,所以只能扩展,但是,一定要导出一个子类并在子类上去做扩展,这样才符合开闭原则,对软件的健壮性和维护都是必要的;【1】传智 SpringMVC+MyBatis由浅入深 教程
【2】MyBatis User Guide