一、关联关系映射
MyBatis是一款优秀的持久层框架,它提供了强大的关联关系映射能力。在MyBatis中,我们可以使用XML或注解来定义关联关系映射。
1.1 一对一
一对一关联映射:
- 在XML中,我们可以使用<resultMap>元素来定义一对一关联映射,可以通过<association>来指定关联对象的映射规则。
- 注解中,可以使用@One注解来定义一对一关联映射。
1.2 一对多
一对多关联映射:
- 在XML中,我们可以使用<resultMap>元素来定义一对多关联映射,可以通过<collection>来指定关联对象集合的映射规则。
- 注解中,可以使用@Many注解来定义一对多关联映射
1.3 多对多
多对多关联映射:
- 在XML中,我们可以使用<resultMap>元素来定义多对多关联映射,可以通过<collection>来指定中间表关联对象的映射规则。
- 注解中,可以使用@Many注解来定义多对多关联映射
同时,MyBatis还提供了一些常见的关联关系映射的配置选项,例如延迟加载、级联操作等,可以根据具体需求进行配置。
总的来说,MyBatis的关联关系映射定义非常灵活,可以根据需求使用XML或注解来进行配置,提供了丰富的选项来满足不同场景下的需求。
二、处理关联关系的方式
在MyBatis中,处理关联关系的方式主要有两种:嵌套查询和嵌套结果。
2.1 嵌套查询
嵌套查询(Nested Selects):
这种方式是通过在SQL语句中使用子查询来实现关联查询。我们可以使用<collection>或<association>元素来配置关联关系。例如,在一个订单表中,可以通过子查询查询与订单相关的用户信息。
2.2 嵌套结果
嵌套结果(Nested Results):
这种方式是通过在查询结果中嵌套关联对象的结果集来实现关联查询。我们可以使用<resultMap>元素中的<association>或<collection>元素来配置关联关系。例如,在一个订单表和订单项表的关系中,可以将订单项作为订单对象的一个属性。
关联查询的处理方式取决于具体的需求和数据模型。嵌套查询适用于复杂的关联关系和需要在各部分之间添加额外条件的情况,而嵌套结果适用于简单的关联关系和需要携带完整对象信息的情况。
无论使用哪种方式,MyBatis都提供了灵活的配置选项来满足各种关联查询需求。使用MyBatis的关联映射机制,我们可以轻松地处理对象之间的关系,并在数据库操作中提供更强大的功能和效率。
三、一对一关联映射
3.1 建表
创建名为 t_hibernate_book (书籍表) 数据表
创建名为 t_hibernate_book_category (书籍类别表) 数据表
其中名为 bid 的属性字段为 t_hibernate_book (书籍表) 的 bid(主键) 的外键
其中名为 cid 的属性字段为 t_hibernate_category (类别表) 的 category_id (主键) 的外键
创建名为 t_hibernate_category (类别表) 数据表
创建名为 t_hibernate_order (订单表) 数据表
创建名为 t_hibernate_order_item (订单详情表) 数据表
其中名为 order_id 的属性字段为 t_hibernate_order (订单表) 的 oid (主键) 的外键
3.2 配置文件
修改名为 generatorConfig.xml 的 配置文件,修改后的所有代码如下 :
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration> <!-- 引入配置文件 --> <properties resource="jdbc.properties"/> <!--指定数据库jdbc驱动jar包的位置--> <classPathEntry location="D:\\temp2\\mvn_repository\\mysql\\mysql-connector-java\\5.1.44\\mysql-connector-java-5.1.44.jar"/> <!-- 一个数据库一个context --> <context id="infoGuardian"> <!-- 注释 --> <commentGenerator> <property name="suppressAllComments" value="true"/><!-- 是否取消注释 --> <property name="suppressDate" value="true"/> <!-- 是否生成注释代时间戳 --> </commentGenerator> <!-- jdbc连接 --> <jdbcConnection driverClass="${jdbc.driver}" connectionURL="${jdbc.url}" userId="${jdbc.username}" password="${jdbc.password}"/> <!-- 类型转换 --> <javaTypeResolver> <!-- 是否使用bigDecimal, false可自动转化以下类型(Long, Integer, Short, etc.) --> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- 01 指定javaBean生成的位置 --> <!-- targetPackage:指定生成的model生成所在的包名 --> <!-- targetProject:指定在该项目下所在的路径 --> <javaModelGenerator targetPackage="com.Kissship.model" targetProject="src/main/java"> <!-- 是否允许子包,即targetPackage.schemaName.tableName --> <property name="enableSubPackages" value="false"/> <!-- 是否对model添加构造函数 --> <property name="constructorBased" value="true"/> <!-- 是否针对string类型的字段在set的时候进行trim调用 --> <property name="trimStrings" value="false"/> <!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 --> <property name="immutable" value="false"/> </javaModelGenerator> <!-- 02 指定sql映射文件生成的位置 --> <sqlMapGenerator targetPackage="com.Kissship.mapper" targetProject="src/main/java"> <!-- 是否允许子包,即targetPackage.schemaName.tableName --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- 03 生成XxxMapper接口 --> <!-- type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 --> <!-- type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 --> <!-- type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 --> <javaClientGenerator targetPackage="com.Kissship.mapper" targetProject="src/main/java" type="XMLMAPPER"> <!-- 是否在当前路径下新加一层schema,false路径com.oop.eksp.user.model, true:com.oop.eksp.user.model.[schemaName] --> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- 配置表信息 --> <!-- schema即为数据库名 --> <!-- tableName为对应的数据库表 --> <!-- domainObjectName是要生成的实体类 --> <!-- enable*ByExample是否生成 example类 --> <!--<table schema="" tableName="t_book" domainObjectName="Book"--> <!--enableCountByExample="false" enableDeleteByExample="false"--> <!--enableSelectByExample="false" enableUpdateByExample="false">--> <!--<!– 忽略列,不生成bean 字段 –>--> <!--<!– <ignoreColumn column="FRED" /> –>--> <!--<!– 指定列的java数据类型 –>--> <!--<!– <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> –>--> <!--</table>--> <table schema="" tableName="t_hibernate_book" domainObjectName="HBook" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> <table schema="" tableName="t_hibernate_category" domainObjectName="Category" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> <table schema="" tableName="t_hibernate_book_category" domainObjectName="HBookCategory" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> <table schema="" tableName="t_hibernate_order" domainObjectName="Order" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> <table schema="" tableName="t_hibernate_order_item" domainObjectName="OrderItem" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> </context> </generatorConfiguration>
3.3 代码生成
自动生成代码的目录列表如下:
然后将每个实体类对象里面重写一下toString()方法即可。
3.4 编写测试
创建一个 名为 OrderItemVo 的类,继承OrderItem类,及属性有Order对象
package com.Kissship.vo; import com.Kissship.model.Order; import com.Kissship.model.OrderItem; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-10:24 */ public class OrderItemVo extends OrderItem { private Order order; public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
在自动生成的OrderItemMapper.xml配置文件中进行增加以下配置:
<resultMap id="OrderItemMap" type="com.Kissship.vo.OrderItemVo" > <result column="order_item_id" property="orderItemId" ></result> <result column="product_id" property="productId" ></result> <result column="quantity" property="quantity" ></result> <result column="oid" property="oid" ></result> <association property="order" javaType="com.Kissship.model.Order"> <result column="order_id" property="orderId" ></result> <result column="order_no" property="orderNo" ></result> </association> </resultMap> <select id="selectByBiid" resultMap="OrderItemMap" parameterType="java.lang.Integer" > SELECT * FROM t_hibernate_order o , t_hibernate_order_item oi WHERE o.order_id = oi.oid AND oi.order_item_id = #{oiid} </select>
在自动生成的 OrderItemMapper 接口中进行增加以下代码:
OrderItemVo selectByBiid(@Param("oiid") Integer oiid);
创建一个接口名为 : OrderItemBiz 接口
package com.Kissship.biz; import com.Kissship.vo.OrderItemVo; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-10:31 */ public interface OrderItemBiz { OrderItemVo selectByBiid(Integer oiid); }
实现以上创建的接口,创建一个实现了名为 OrderItemBizImpl 类
package com.Kissship.biz; import com.Kissship.mapper.OrderItemMapper; import com.Kissship.vo.OrderItemVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-10:42 */ @Service public class OrderItemBizImpl implements OrderItemBiz { @Autowired private OrderItemMapper orderItemMapper; @Override public OrderItemVo selectByBiid(Integer oiid) { return orderItemMapper.selectByBiid(oiid); } }
创建一个测试类 名为 Test01 ,用来进行方法测试
package com.Kissship.biz; import com.Kissship.vo.OrderItemVo; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-10:42 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class Test01 { @Autowired private OrderItemBiz orderItemBiz; @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } @Test public void selectByBiid() { OrderItemVo orderItemVo = orderItemBiz.selectByBiid(27); System.out.println(orderItemVo); System.out.println(orderItemVo.getOrder()); } }
执行测试类中selectByBiid()的方法,结果如图 :
四、一对多关联映射
创建一个 名为 OrdeVo 的类,继承Order类,及属性有List<OrderItem>集合
用来存储查询到的OrderItem对象,因为是一对多所有查询出有多个对象,如下:
package com.Kissship.vo; import com.Kissship.model.Order; import com.Kissship.model.OrderItem; import java.util.ArrayList; import java.util.List; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-10:24 */ public class OrderVo extends Order { private List<OrderItem> orderItems = new ArrayList<>(); public List<OrderItem> getOrderItems() { return orderItems; } public void setOrderItems(List<OrderItem> orderItems) { this.orderItems = orderItems; } }
在自动生成的 OrderMapper.xml 配置文件中增加以下配置,如下:
<resultMap id="OrderVoMap" type="com.Kissship.vo.OrderVo"> <result column="order_id" property="orderId" ></result> <result column="order_no" property="orderNo" ></result> <collection property="orderItems" ofType="com.Kissship.model.OrderItem"> <result column="order_item_id" property="orderItemId" ></result> <result column="product_id" property="productId" ></result> <result column="quantity" property="quantity" ></result> <result column="oid" property="oid" ></result> </collection> </resultMap> <select id="selectByOid" resultMap="OrderVoMap" parameterType="java.lang.Integer" > SELECT * FROM t_hibernate_order o , t_hibernate_order_item oi WHERE o.order_id = oi.oid AND o.order_id = #{oid} </select>
在自动生成的 OrderMapper接口中进行增加以下代码,如下:
OrderVo selectByOid(@Param("oid") Integer oid);
创建一个接口名为 : OrderBiz 接口
package com.Kissship.biz; import com.Kissship.vo.OrderVo; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:12 */ public interface OrderBiz { OrderVo selectByOid(Integer oid); }
在实现以上 OrderBiz 接口,创建一个实现类,名为 OrderBizImpl
package com.Kissship.biz; import com.Kissship.mapper.OrderMapper; import com.Kissship.vo.OrderVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:14 */ @Service public class OrderBizImpl implements OrderBiz { @Autowired private OrderMapper orderMapper; @Override public OrderVo selectByOid(Integer oid) { return orderMapper.selectByOid(oid); } }
在测试类( Test01 )中增加以下测试方法及接口:
@Autowired private OrderBiz orderBiz; @Test public void selectByOid() { OrderVo orderVo = orderBiz.selectByOid(7); System.out.println(orderVo); orderVo.getOrderItems().forEach(System.out::println); }
执行其中的方法进行测试,结果为如图 :
五、多对多关联映射
在自动生成的 HBookMapper.xml 配置文件中增加以下配置:
<resultMap id="CategoryVoMap" type="com.Kissship.vo.CategoryVo"> <result column="category_id" property="categoryId"></result> <result column="category_name" property="categoryName"></result> <collection property="hbooks" ofType="com.Kissship.model.HBook"> <result column="book_id" property="bookId"></result> <result column="book_name" property="bookName"></result> <result column="price" property="price"></result> </collection> </resultMap> <select id="selectByCategoryId" resultMap="CategoryVoMap" parameterType="java.lang.Integer" > SELECT * FROM t_hibernate_book b, t_hibernate_book_category bc , t_hibernate_category c WHERE b.book_id = bc.bid AND bc.cid = c.category_id AND c.category_id = #{cid} </select>
在自动生成的 HBookMapper 接口 中增加以下方法:
HbookVo selectByBookId(@Param("bid") Integer bid);
创建一个接口名为 HBookBiz
package com.Kissship.biz; import com.Kissship.vo.HbookVo; import org.apache.ibatis.annotations.Param; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:25 */ public interface HBookBiz { HbookVo selecByBookId(@Param("bid") Integer bid); }
然后再实现以上 HBookBiz 接口,创建一个实现类,名为 HBookBizImpl
package com.Kissship.biz; import com.Kissship.mapper.HBookMapper; import com.Kissship.vo.HbookVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:26 */ @Service public class HBookBizImpl implements HBookBiz { @Autowired private HBookMapper hBookMapper; @Override public HbookVo selecByBookId(Integer bid) { return hBookMapper.selectByBookId(bid); } }
在测试类( Test01 )中增加以下测试方法及接口:
@Autowired private HBookBiz hbookBiz; @Test public void selectByBookId() { HbookVo hBookVo = hbookBiz.selecByBookId(8); System.out.println(hBookVo); hBookVo.getCategories().forEach(System.out::println); }
执行其中的方法进行测试,结果为如图 :
在自动生成的 CategoryMapper.xml 配置文件中增加以下配置:
<resultMap id="CategoryVoMap" type="com.Kissship.vo.CategoryVo"> <result column="category_id" property="categoryId"></result> <result column="category_name" property="categoryName"></result> <collection property="books" ofType="com.Kissship.model.HBook"> <result column="book_id" property="bookId"></result> <result column="book_name" property="bookName"></result> <result column="price" property="price"></result> </collection> </resultMap> <select id="selectByCategoryId" resultMap="CategoryVoMap" parameterType="java.lang.Integer" > SELECT * FROM t_hibernate_book b, t_hibernate_book_category bc , t_hibernate_category c WHERE b.book_id = bc.bid AND bc.cid = c.category_id AND c.category_id = #{cid} </select>
在自动生成的 CategoryMapper 接口 中增加以下方法:
CategoryVo selectByCategoryId(@Param("cid") Integer cid);
创建一个接口名为 CategoryBiz
package com.Kissship.biz; import com.Kissship.vo.CategoryVo; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:37 */ public interface CategoryBiz { CategoryVo selectByCategoryId(Integer cid); }
然后再实现以上 CategoryBiz 接口,创建一个实现类,名为 CategoryBizImpl
package com.Kissship.biz; import com.Kissship.mapper.CategoryMapper; import com.Kissship.vo.CategoryVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Kissship * @site www.Kissship.com * @company xxx公司 * @create 2023-09-04-11:38 */ @Service public class CategoryBizImpl implements CategoryBiz { @Autowired private CategoryMapper categoryMapper; @Override public CategoryVo selectByCategoryId(Integer cid) { return categoryMapper.selectByCategoryId(cid); } }
在测试类( Test01 )中增加以下测试方法及接口
@Autowired private CategoryBiz categoryBiz; @Test public void selectByCategoryId() { CategoryVo categoryVo = categoryBiz.selectByCategoryId(8); System.out.println(categoryVo); categoryVo.getHbooks().forEach(System.out::println); }
执行其中的方法进行测试,结果为如图 :
六、小结
- 使用 <resultMap> 元素配置结果集的映射关系,指定数据库列和 Java 对象属性之间的映射。
- 可以使用 <result> 元素将数据库列映射到 Java 对象的属性,并指定属性的类型、映射关系及相关配置。
- 可以使用 <association> 元素配置关联对象的映射关系,用于映射复杂对象之间的关系。
- 可以使用 <collection> 元素配置集合类型对象的映射关系,用于映射一对多或多对多的关系。
- 使用 <sql> 元素定义可重用的 SQL 片段,提供了组织和共享 SQL 语句的能力。
最后Mybatis关联关系映射与表对象之间的关系就到这里,祝大家在敲代码的路上一路通畅!