前言
在上一期的博客中我与老铁们分享了有关Mybatis与Spring的集成,领悟到了Spring给 Mybatis带来的便利以及魅力。今天我来带老铁们进入有关Mybatis关联关系映射的世界,一起来领悟这个世界带给我们的震撼,希望老铁们能有所收获。
一、关联关系映射的简介
1. 概念
Mybatis的关联关系映射是一种将数据库表之间的关联关系映射到Java对象之间的技术。它通过配置文件或注解的方式,将数据库表的列与Java对象的属性进行映射,从而实现对象与数据库之间的数据交互。这种映射关系可以是一对一、一对多、多对一或多对多的关系。通过Mybatis的关联关系映射,我们可以方便地进行数据库操作,而不需要手动编写SQL语句。
2. 特点
Mybatis关联关系映射的特点
特点 说明 关联关系映射灵活 Mybatis的关联关系映射非常灵活,可以根据实际需求定义各种类型的关联关系,包括一对一、一对多、多对一和多对多等。 方便开发人员配置 通过Mybatis的配置文件或者注解,可以方便地定义关联关系映射,无需编写繁琐的SQL语句,减少了开发人员的工作量。 有利于项目性能优化 Mybatis的关联关系映射可以进行懒加载和延迟加载,即在需要时才去查询关联对象,提高了系统性能,并减少了不必要的查询操作。 提高效率,减少数据库访问压力 Mybatis支持一级缓存和二级缓存,可以缓存关联对象,提高查询效率,减少数据库的访问压力。 使项目具有扩展性 Mybatis的关联关系映射可以通过自定义TypeHandler等扩展点,满足特定需求的定制化开发。
3. 使用场景
- 对象关系映射(ORM):Mybatis可以将数据库表与Java对象进行映射,方便进行对象的持久化操作,简化了数据库操作的编写和管理。
- 复杂查询:通过关联关系映射,可以方便地进行复杂的查询操作,包括多表关联查询、嵌套查询等,减少了手动编写SQL语句的工作量。
- 数据库事务管理:Mybatis的关联关系映射可以与数据库事务管理结合使用,实现数据的一致性和完整性。
- 缓存管理:通过关联关系映射,可以方便地进行缓存管理,提高查询效率,减少数据库的访问压力。
- 分布式系统:在分布式系统中,Mybatis的关联关系映射可以帮助实现数据的一致性和分布式事务管理。
二、Mybatis中的一对一&一对多关联关系配置
1. 导入相关的数据库表
以上就是我们案例演示所需要的数据库表。
2. 多表查询
resultType"java.yutils.Map"方式:
该方式多表查询不推荐在公司的项目中使用,针对于自己开发人员开发是有利的,但是针对于公司开发、项目研发团队来说是不利的。
好处:在针对客户需求发生改变的时候(需求1-->需求2(加字段)-->需求3(加字段) ),针对客户的需求不管怎么变,但我的代码都不会发生改变只需要加个字段即可。
弊端:这种方式在公司或者研发团队中是不利于后期维护和扩展的。
3. 案例部署准备
3.1 生成所需数据库对应的实体类
genreatorConfig.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:\\MYsoftware\\Maven\\mvn_repsoitory\\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.yx.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.yx.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.yx.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_mvc_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_book_category" domainObjectName="HBookCategory" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> <table schema="" tableName="t_hibernate_category" domainObjectName="HCategory" 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.1 一对多的关联关系案例演示
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="com.yx.mapper.OrderMapper" > <resultMap id="BaseResultMap" type="com.yx.model.Order" > <constructor > <idArg column="order_id" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="order_no" jdbcType="VARCHAR" javaType="java.lang.String" /> </constructor> </resultMap> <sql id="Base_Column_List" > order_id, order_no </sql> <resultMap id="OrderVoMap" type="com.yx.vo.OrderVo" > <result column="order_id" property="orderId"></result> <result column="order_no" property="orderNo"></result> <collection property="orderItems" ofType="com.yx.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> </mapper>
OrderMapper.java
package com.yx.mapper; import com.yx.model.Order; import com.yx.vo.OrderVo; import org.apache.ibatis.annotations.Param; public interface OrderMapper { OrderVo selectByOid(@Param("oid") Integer oid); }
实体类代码
package com.yx.model; import lombok.ToString; @ToString public class Order { private Integer orderId; private String orderNo; public Order(Integer orderId, String orderNo) { this.orderId = orderId; this.orderNo = orderNo; } public Order() { super(); } public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } } //=========================以上是Order实体类代码======================================== //=========================以下是OrderItem实体类代码======================================== package com.yx.model; import lombok.ToString; @ToString public class OrderItem { private Integer orderItemId; private Integer productId; private Integer quantity; private Integer oid; public OrderItem(Integer orderItemId, Integer productId, Integer quantity, Integer oid) { this.orderItemId = orderItemId; this.productId = productId; this.quantity = quantity; this.oid = oid; } public OrderItem() { super(); } public Integer getOrderItemId() { return orderItemId; } public void setOrderItemId(Integer orderItemId) { this.orderItemId = orderItemId; } public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public Integer getOid() { return oid; } public void setOid(Integer oid) { this.oid = oid; } }
接口类及接口实现类代码
package com.yx.biz; import com.yx.vo.OrderVo; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 10:14 */ public interface OrderBiz { OrderVo selectByOid(Integer oid); } //============================以上是接口类代码========================== //============================以下是接口实现类代码====================== package com.yx.biz.impl; import com.yx.biz.OrderBiz; import com.yx.mapper.OrderMapper; import com.yx.vo.OrderVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 10:15 */ @Service public class OrderBizImpl implements OrderBiz { @Autowired private OrderMapper orderMapper; @Override public OrderVo selectByOid(Integer oid) { return orderMapper.selectByOid(oid); } }
OrderVo类代码
package com.yx.vo; import com.yx.model.Order; import com.yx.model.OrderItem; import java.util.ArrayList; import java.util.List; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 9:05 */ public class OrderVo extends Order { private List<OrderItem> orderItems =new ArrayList<OrderItem>(); public List<OrderItem> getOrderItems() { return orderItems; } public void setOrderItems(List<OrderItem> orderItems) { this.orderItems = orderItems; } }
测试类代码
package com.yx.biz.impl; import com.yx.biz.OrderBiz; import com.yx.vo.OrderVo; 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 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 10:19 * 订单测试类 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class OrderBizImplTest { @Autowired private OrderBiz orderBiz; @Test public void selectByOid() { OrderVo orderVo = orderBiz.selectByOid(7); System.out.println(orderVo); orderVo.getOrderItems().forEach(System.out::println); } }
测试结果
3.2一对一的关联关系案例演示
OrderItemMapper.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.yx.mapper.OrderItemMapper" > <resultMap id="BaseResultMap" type="com.yx.model.OrderItem" > <constructor > <idArg column="order_item_id" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="product_id" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="quantity" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="oid" jdbcType="INTEGER" javaType="java.lang.Integer" /> </constructor> </resultMap> <sql id="Base_Column_List" > order_item_id, product_id, quantity, oid </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from t_hibernate_order_item where order_item_id = #{orderItemId,jdbcType=INTEGER} </select> <resultMap id="OrderItemVoMap" type="com.yx.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.yx.model.Order"> <result column="order_id" property="orderId"></result> <result column="order_no" property="orderNo"></result> </association> </resultMap> <select id="selectByOrderItemId" resultMap="OrderItemVoMap" 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> </mapper>
OrderItemMapper.java
package com.yx.mapper; import com.yx.model.OrderItem; import com.yx.vo.OrderItemVo; import org.apache.ibatis.annotations.Param; public interface OrderItemMapper { OrderItemVo selectByOrderItemId(@Param("oiid") Integer oiid); }
实体类代码
package com.yx.model; import lombok.ToString; @ToString public class Order { private Integer orderId; private String orderNo; public Order(Integer orderId, String orderNo) { this.orderId = orderId; this.orderNo = orderNo; } public Order() { super(); } public Integer getOrderId() { return orderId; } public void setOrderId(Integer orderId) { this.orderId = orderId; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } } //=========================以上是Order实体类代码======================================== //=========================以下是OrderItem实体类代码======================================== package com.yx.model; import lombok.ToString; @ToString public class OrderItem { private Integer orderItemId; private Integer productId; private Integer quantity; private Integer oid; public OrderItem(Integer orderItemId, Integer productId, Integer quantity, Integer oid) { this.orderItemId = orderItemId; this.productId = productId; this.quantity = quantity; this.oid = oid; } public OrderItem() { super(); } public Integer getOrderItemId() { return orderItemId; } public void setOrderItemId(Integer orderItemId) { this.orderItemId = orderItemId; } public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public Integer getOid() { return oid; } public void setOid(Integer oid) { this.oid = oid; } }
OrderItemVo类代码
package com.yx.vo; import com.yx.model.Order; import com.yx.model.OrderItem; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:07 */ public class OrderItemVo extends OrderItem { private Order order; public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
接口类及接口实现类代码
package com.yx.biz; import com.yx.vo.OrderItemVo; import org.apache.ibatis.annotations.Param; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:10 */ public interface OrderItemBiz { OrderItemVo selectByOrderItemId(@Param("oiid") Integer oiid); } //============================以上是接口类代码========================== //============================以下是接口实现类代码====================== package com.yx.biz.impl; import com.yx.biz.OrderItemBiz; import com.yx.mapper.OrderItemMapper; import com.yx.vo.OrderItemVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:11 */ @Service public class OrderItemBizImpl implements OrderItemBiz { @Autowired private OrderItemMapper orderItemMapper; @Override public OrderItemVo selectByOrderItemId(Integer oiid) { return orderItemMapper.selectByOrderItemId(oiid); } }
测试类代码
package com.yx.biz.impl; import com.yx.biz.OrderItemBiz; import com.yx.vo.OrderItemVo; 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 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:13 * 测试类 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class OrderItemBizImplTest { @Autowired private OrderItemBiz orderItemBiz; @Test public void selectByOrderItemId() { OrderItemVo orderItemVo = orderItemBiz.selectByOrderItemId(27); System.out.println(orderItemVo); System.out.println(orderItemVo.getOrder()); } }
测试结果
3.3 多对多的关联关系案例演示
HBookMapper.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.yx.mapper.HBookMapper" > <resultMap id="BaseResultMap" type="com.yx.model.HBook" > <constructor > <idArg column="book_id" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="book_name" jdbcType="VARCHAR" javaType="java.lang.String" /> <arg column="price" jdbcType="REAL" javaType="java.lang.Float" /> </constructor> </resultMap> <sql id="Base_Column_List" > book_id, book_name, price </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from t_hibernate_book where book_id = #{bookId,jdbcType=INTEGER} </select> <resultMap id="HBookVoMap" type="com.yx.vo.HBookVo"> <result column="book_id" property="bookId"></result> <result column="book_name" property="bookName"></result> <result column="price" property="price"></result> <collection property="categories" ofType="com.yx.model.HCategory"> <result column="category_id" property="categoryId"></result> <result column="category_name" property="categoryName"></result> </collection> </resultMap> <!-- 根据书籍的id查询出书籍的信息即所属类别--> <select id="selectByBookId" resultMap="HBookVoMap" 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 b.book_id=#{bid} </select> </mapper>
HBookMapper.java
package com.yx.mapper; import com.yx.model.HBook; import com.yx.vo.HBookVo; import org.apache.ibatis.annotations.Param; public interface HBookMapper { HBookVo selectByBookId(@Param("bid") Integer bid); }
实体类代码
package com.yx.model; import lombok.ToString; @ToString public class HBook { private Integer bookId; private String bookName; private Float price; public HBook(Integer bookId, String bookName, Float price) { this.bookId = bookId; this.bookName = bookName; this.price = price; } public HBook() { super(); } public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } } //==========================以上是HBook实体类代码========================================== ==========================以下是HCategory实体类代码========================================== package com.yx.model; import lombok.ToString; @ToString public class HCategory { private Integer categoryId; private String categoryName; public HCategory(Integer categoryId, String categoryName) { this.categoryId = categoryId; this.categoryName = categoryName; } public HCategory() { super(); } public Integer getCategoryId() { return categoryId; } public void setCategoryId(Integer categoryId) { this.categoryId = categoryId; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } }
HBookVo代码
package com.yx.vo; import com.yx.model.HBook; import com.yx.model.HCategory; import java.util.List; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:34 */ public class HBookVo extends HBook { private List<HCategory> categories; public List<HCategory> getCategories() { return categories; } public void setCategories(List<HCategory> categories) { this.categories = categories; } }
接口类及接口实现类代码
package com.yx.biz; import com.yx.vo.HBookVo; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:41 */ public interface HBookBiz { HBookVo selectByBookId(Integer bid); } //======================以上是接口类代码=================================== //======================以下是接口实现类代码=============================== package com.yx.biz.impl; import com.yx.biz.HBookBiz; import com.yx.mapper.HBookMapper; import com.yx.vo.HBookVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:42 */ @Service public class HBookBizImpl implements HBookBiz { @Autowired private HBookMapper hBookMapper; @Override public HBookVo selectByBookId(Integer bid) { return hBookMapper.selectByBookId(bid); } }
测试类代码
package com.yx.biz.impl; import com.yx.biz.HBookBiz; import com.yx.vo.HBookVo; 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 君易--鑨 * @site www.yangxin.com * @company 木易 * @create 2023-09-04 11:43 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class HBookBizImplTest { @Autowired private HBookBiz hBookBiz; @Test public void selectByBookId() { HBookVo hBookVo =this.hBookBiz.selectByBookId(8); System.out.println(hBookVo); hBookVo.getCategories().forEach(System.out::println); } }
测试结果