myBatis-07 案例实践
案例表结构:
1、用户表
2、订单表
3、订单明细表
4、商品表
表结构关系相对比较清楚。
0、项目代码结构
1、表结构创建
客户表
-- ---------------------------- -- Table structure for ex_customer -- ---------------------------- DROP TABLE IF EXISTS `ex_customer`; CREATE TABLE `ex_customer` ( `custId` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `custName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `telephone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `sex` char(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`custId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ex_customer -- ---------------------------- INSERT INTO `ex_customer` VALUES ('C001', '顾问', '15345187579', '男', '顾问'); INSERT INTO `ex_customer` VALUES ('C002', '曹阳', '15345187577', '男', '曹阳'); SET FOREIGN_KEY_CHECKS = 1;
订单表
-- ---------------------------- -- Table structure for ex_order -- ---------------------------- DROP TABLE IF EXISTS `ex_order`; CREATE TABLE `ex_order` ( `orderId` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `custId` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `orderDate` datetime NULL DEFAULT NULL, `totalMoney` decimal(10, 2) NULL DEFAULT NULL, `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`orderId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ex_order -- ---------------------------- INSERT INTO `ex_order` VALUES ('T0001', 'C001', '2021-01-01 00:00:00', 7000.00, '测试订单'); SET FOREIGN_KEY_CHECKS = 1;
订单明细表
-- ---------------------------- -- Table structure for ex_orderdetail -- ---------------------------- DROP TABLE IF EXISTS `ex_orderdetail`; CREATE TABLE `ex_orderdetail` ( `detailId` bigint(20) NOT NULL AUTO_INCREMENT, `orderId` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `prodId` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `buyPrice` decimal(10, 2) NULL DEFAULT NULL, `buyCount` int(11) NULL DEFAULT NULL, `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`detailId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ex_orderdetail -- ---------------------------- INSERT INTO `ex_orderdetail` VALUES (1, 'T0001', 'P001', 2000.00, 2, NULL); INSERT INTO `ex_orderdetail` VALUES (2, 'T0001', 'P002', 3000.00, 1, NULL); SET FOREIGN_KEY_CHECKS = 1;
商品表
-- ---------------------------- -- Table structure for ex_product -- ---------------------------- DROP TABLE IF EXISTS `ex_product`; CREATE TABLE `ex_product` ( `prodId` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `prodName` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `sellPrice` decimal(10, 2) NULL DEFAULT NULL, `imgUrl` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`prodId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of ex_product -- ---------------------------- INSERT INTO `ex_product` VALUES ('P001', '手机', 2000.00, NULL, NULL); INSERT INTO `ex_product` VALUES ('P002', '平板', 3000.00, NULL, NULL); SET FOREIGN_KEY_CHECKS = 1;
2、模型类定义
2.1、用户模型
package rui.db.Model; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.List; //客户模型 @Component public class ex_Customer implements Serializable { private String custId; private String custName; private String telephone; private String sex; private String remark; //用户采购订单列表 1对多 private List<ex_Order> orderList; //用户采购商品列表 多对多 private List<ex_Product> prodList; public String getCustId() { return custId; } public void setCustId(String custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public List<ex_Product> getProdList() { return prodList; } public void setProdList(List<ex_Product> prodList) { this.prodList = prodList; } @Override public String toString() { String result = "ex_customer{" + "custId='" + custId + '\'' + ", custName='" + custName + '\'' + ", telephone='" + telephone + '\'' + ", sex='" + sex + '\'' + ", remark='" + remark + '\'' + '}'; if (prodList != null) { result += "\n采购商品列表"; for (ex_Product item : prodList) { result += "\n" + item.toString(); } } if (orderList != null) { result += "\n采购订单列表"; for (ex_Order item : orderList) { result += "\n" + item.toString(); } } return result; } }
2.2、订单模型
package rui.db.Model; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Date; import java.util.List; //订单模型 @Component public class ex_Order implements Serializable { private String orderId; private String custId; private Date orderDate; private double totalMoney; private String remark; //包含客户表引用 private ex_Customer orderCust; //包含订单明细 private List<ex_OrderDetail> detailList; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getCustId() { return custId; } public void setCustId(String custId) { this.custId = custId; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } public double getTotalMoney() { return totalMoney; } public void setTotalMoney(double totalMoney) { this.totalMoney = totalMoney; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public ex_Customer getOrderCust() { return orderCust; } public void setOrderCust(ex_Customer orderCust) { this.orderCust = orderCust; } public List<ex_OrderDetail> getDetailList() { return detailList; } public void setDetailList(List<ex_OrderDetail> detailList) { this.detailList = detailList; } @Override public String toString() { String result = "ex_order{" + "orderId='" + orderId + '\'' + ", cusId='" + custId + '\'' + ", orderDate=" + orderDate + ", totalMoney=" + totalMoney + ", remark='" + remark + '\'' + '}'; if (orderCust != null) result += "\n" + orderCust.toString(); if (detailList != null) { result += "\n订单包含如下的订单明细数据"; for (ex_OrderDetail detail : detailList) { result += "\n" + detail.toString(); } } return result; } }
2.3、订单明细模型
package rui.db.Model; import org.springframework.stereotype.Component; import java.io.Serializable; //订单明细 @Component public class ex_OrderDetail implements Serializable { private long detailId; private String orderId; private String prodId; private double buyPrice; private int buyCount; private String remark; //包含订单商品引用 private ex_Product orderProd; public long getDetailId() { return detailId; } public void setDetailId(long detailId) { this.detailId = detailId; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getProdId() { return prodId; } public void setProdId(String prodId) { this.prodId = prodId; } public double getBuyPrice() { return buyPrice; } public void setBuyPrice(double buyPrice) { this.buyPrice = buyPrice; } public int getBuyCount() { return buyCount; } public void setBuyCount(int buyCount) { this.buyCount = buyCount; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public ex_Product getOrderProd() { return orderProd; } public void setOrderProd(ex_Product orderProd) { this.orderProd = orderProd; } @Override public String toString() { String result = "ex_Orderdetail{" + "detailId=" + detailId + ", orderId='" + orderId + '\'' + ", prodId='" + prodId + '\'' + ", buyPrice=" + buyPrice + ", buyCount=" + buyCount + ", remark='" + remark + '\'' + '}'; if (orderProd != null) { result += "\n" + orderProd.toString(); } return result; } }
2.4、商品模型
package rui.db.Model; import org.springframework.stereotype.Component; import java.io.Serializable; //产品模型 @Component public class ex_Product implements Serializable { private String prodId; private String prodName; private double sellPrice; private String imgUrl; private String remark; public String getProdId() { return prodId; } public void setProdId(String prodId) { this.prodId = prodId; } public String getProdName() { return prodName; } public void setProdName(String prodName) { this.prodName = prodName; } public double getSellPrice() { return sellPrice; } public void setSellPrice(double sellPrice) { this.sellPrice = sellPrice; } public String getImgUrl() { return imgUrl; } public void setImgUrl(String imgUrl) { this.imgUrl = imgUrl; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "ex_product{" + "prodId='" + prodId + '\'' + ", prodName='" + prodName + '\'' + ", sellPrice=" + sellPrice + ", imgUrl='" + imgUrl + '\'' + ", remark='" + remark + '\'' + '}'; } }
3、访问层接口
定义两个数据访问接口:
3.1、订单Dao
从订单角度查询数据,包含订单用户,订单明细列表,订单明细商品
package rui.db.Dao; import rui.db.Model.ex_Order; import org.springframework.stereotype.Repository; import java.util.List; //订单接口 @Repository(value = "ex_OrderDao") public interface ex_OrderDao { public ex_Order findByKey(String orderId); public List<ex_Order> findAll(); }
3.2、订单Mapper
<?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"> <!--自动代理生成模式,namespace值必须和接口保证一致--> <mapper namespace="rui.db.Dao.ex_OrderDao"> <!--查询单条数据--> <select id="findByKey" resultMap="orderResult"> Select ex_order.*, custName, telephone, sex, ex_customer.remark as ex_customer_remark, detailId, ex_orderdetail.prodId, buyCount, buyPrice, prodName, sellPrice, imgUrl from ex_order left join ex_customer on ex_order.custId = ex_customer.custId left join ex_orderdetail on ex_order.orderId = ex_orderdetail.orderId left join ex_product on ex_orderdetail.prodId = ex_product.prodId where ex_order.orderId = #{orderId} </select> <!--查询多条数据--> <select id="findAll" resultMap="orderResult"> Select ex_order.*, custName, telephone, sex, ex_customer.remark as ex_customer_remark, detailId, ex_orderdetail.prodId, buyCount, buyPrice, prodName, sellPrice, imgUrl from ex_order left join ex_customer on ex_order.custId = ex_customer.custId left join ex_orderdetail on ex_order.orderId = ex_orderdetail.orderId left join ex_product on ex_orderdetail.prodId = ex_product.prodId where 1 = 1 </select> <!-- 定义订单查询结果映射 --> <resultMap id="orderResult" type="rui.db.Model.ex_Order"> <id property="orderId" column="orderId"/> <result property="custId" column="custId"/> <result property="orderDate" column="orderDate"/> <result property="totalMoney" column="totalMoney"/> <result property="remark" column="remark"/> <!--客户关联,通过javaType设置映射的类型--> <association property="orderCust" javaType="rui.db.Model.ex_Customer" resultMap="customerResult"> </association> <!--订单明细集合,通过ofType设置映射的类型--> <collection property="detailList" ofType="rui.db.Model.ex_OrderDetail" resultMap="orderDetailResult"> </collection> </resultMap> <!--客户映射--> <resultMap id="customerResult" type="rui.db.Model.ex_Customer"> <id property="custId" column="custId"/> <result property="custName" column="custName"/> <result property="telephone" column="telephone"/> <result property="sex" column="sex"/> <!--列明重复时解决方案--> <result property="remark" column="ex_customer_remark"/> </resultMap> <!--订单明细映射--> <resultMap id="orderDetailResult" type="rui.db.Model.ex_OrderDetail"> <id property="detailId" column="detailId"/> <result property="orderId" column="orderId"/> <result property="prodId" column="prodId"/> <result property="buyPrice" column="buyPrice"/> <result property="buyCount" column="buyCount"/> <result property="remark" column="remark"/> <!--商品关联--> <association property="orderProd" javaType="rui.db.Model.ex_Product" resultMap="productResult"> </association> </resultMap> <!--商品映射--> <resultMap id="productResult" type="rui.db.Model.ex_Product"> <id property="prodId" column="prodId"></id> <result property="prodName" column="prodName"/> <result property="sellPrice" column="sellPrice"/> <result property="imgUrl" column="imgUrl"/> </resultMap> </mapper>
3.3、用户Dao
从用户角度查询数据,包含商品列表,订单列表
package rui.db.Dao; import rui.db.Model.ex_Customer; import org.springframework.stereotype.Repository; //用户接口 @Repository(value = "ex_CustomerDao") public interface ex_CustomerDao { //主键查询 public ex_Customer findByKey(String custId); }
3.4、用户Mapper
<?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"> <!--自动代理生成模式,namespace值必须和接口保证一致--> <mapper namespace="rui.db.Dao.ex_CustomerDao"> <cache/> <!--查询单条数据--> <select id="findByKey" resultMap="customerResult"> SELECT ex_customer.*, ex_order.orderId, ex_order.orderDate, ex_order.totalMoney, ex_orderdetail.detailId, ex_orderdetail.prodId, ex_orderdetail.buyPrice, ex_orderdetail.buyCount, ex_product.prodName, ex_product.sellPrice, ex_product.imgUrl FROM ex_customer LEFT JOIN ex_order ON ex_customer.custId = ex_order.custId LEFT JOIN ex_orderdetail ON ex_order.orderId = ex_orderdetail.orderId LEFT JOIN ex_product ON ex_orderdetail.prodId = ex_product.prodId where ex_customer.custId = #{custId} </select> <!-- 定义客户结果映射 --> <resultMap id="customerResult" type="rui.db.Model.ex_Customer"> <id property="custId" column="custId"/> <result property="custName" column="custName"/> <result property="telephone" column="telephone"/> <result property="sex" column="sex"/> <result property="remark" column="remark"/> <!--商品集合,通过ofType设置映射的类型--> <collection property="prodList" ofType="rui.db.Model.ex_Product" resultMap="productResult"> </collection> <!--订单集合,通过ofType设置映射的类型--> <collection property="orderList" ofType="rui.db.Model.ex_Order" resultMap="orderResult"> </collection> </resultMap> <!--订单映射--> <resultMap id="orderResult" type="rui.db.Model.ex_Order"> <id property="orderId" column="orderId"/> <result property="custId" column="custId"/> <result property="orderDate" column="orderDate"/> <result property="totalMoney" column="totalMoney"/> <result property="remark" column="remark"/> <!--订单明细集合--> <collection property="detailList" ofType="rui.db.Model.ex_OrderDetail" resultMap="orderDetailResult"> </collection> </resultMap> <!--订单明细映射--> <resultMap id="orderDetailResult" type="rui.db.Model.ex_OrderDetail"> <id property="detailId" column="detailId"/> <result property="orderId" column="orderId"/> <result property="prodId" column="prodId"/> <result property="buyPrice" column="buyPrice"/> <result property="buyCount" column="buyCount"/> <result property="remark" column="remark"/> <!--商品关联--> <association property="orderProd" javaType="rui.db.Model.ex_Product" resultMap="productResult"> </association> </resultMap> <!--商品映射--> <resultMap id="productResult" type="rui.db.Model.ex_Product"> <id property="prodId" column="prodId"></id> <result property="prodName" column="prodName"/> <result property="sellPrice" column="sellPrice"/> <result property="imgUrl" column="imgUrl"/> </resultMap> </mapper>
5、测试查询
5.1、测试1
//从订单角度查询数据,获取订单明细,订单明细关联商品 - 单记录查询
@Test public void TestMyBatis_01() throws IOException { //1 读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //2 构建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3 构建SqlSession,并执行mapper内操作 try (SqlSession session = sqlSessionFactory.openSession()) { //获取接口代理实现 ex_OrderDao dao = session.getMapper(ex_OrderDao.class); ex_Order entry = dao.findByKey("T0001"); System.out.println(entry.toString()); } System.out.println("测试完成1"); }
5.2、测试2
//从订单角度查询数据,获取订单明细,订单明细关联商品 - 多记录查询
@Test public void TestMyBatis_02() throws IOException { //1 读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //2 构建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3 构建SqlSession,并执行mapper内操作 try (SqlSession session = sqlSessionFactory.openSession()) { //获取接口代理实现 ex_OrderDao dao = session.getMapper(ex_OrderDao.class); List<ex_Order> list = dao.findAll(); for (ex_Order item : list) { System.out.println("--------------"); System.out.println(item); } } System.out.println("测试完成2"); }
5.3、测试3
//从客户角度查询,能够获取客户采购的商品列表和订单列表
@Test public void TestMyBatis_03() throws IOException { //1 读取配置文件 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //2 构建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //3 构建SqlSession,并执行mapper内操作 try (SqlSession session = sqlSessionFactory.openSession()) { //获取接口代理实现 ex_CustomerDao dao = session.getMapper(ex_CustomerDao.class); ex_Customer item = dao.findByKey("C001"); System.out.println(item.toString()); } System.out.println("测试完成3"); }
7、延迟加载
延迟加载,就是关联表数据推迟加载,如果需要关联表数据时再进行数据加载。
而Mapper内的association、collection就具备延迟加载功能。
延迟需要先从单表查询数据,关联表数据通过另一个Select再取获取。
这里以用户表为例介绍延迟加载。
7.1、开启延迟加载
myBatis全局配置文件
<!-- 全局配置参数,相关的参数参照myBatis文档 --> <settings> <!-- 是否开启缓存 --> <setting name="cacheEnabled" value="true"/> <!-- 是否开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!--将积极加载修改成消极加载--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
7.2、延迟加载的Mapper配置
<?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"> <!--自动代理生成模式,namespace值必须和接口保证一致--> <mapper namespace="rui.db.Dao.ex_CustomerDao"> <cache/> <!--延迟加载的方法--> <select id="findByKey" resultMap="customerResult"> SELECT ex_customer.* FROM ex_customer where ex_customer.custId = #{custId} </select> <!-- 定义客户结果映射 --> <resultMap id="customerResult" type="rui.db.Model.ex_Customer"> <id property="custId" column="custId"/> <result property="custName" column="custName"/> <result property="telephone" column="telephone"/> <result property="sex" column="sex"/> <result property="remark" column="remark"/> <!--商品集合,通过ofType设置映射的类型--> <collection property="prodList" ofType="rui.db.Model.ex_Product" > </collection> <!--订单集合,通过ofType设置映射的类型--> <!--延迟加载订单 调用外部select,并指定要传入的字段列--> <collection property="orderList" ofType="rui.db.Model.ex_Order" select="lazyOrderResult" column="custId" > </collection> </resultMap> <!--延迟加载订单数据--> <select id="lazyOrderResult" resultMap="orderResult"> Select ex_order.*, detailId, ex_orderdetail.prodId, buyCount, buyPrice, prodName, sellPrice, imgUrl from ex_order left join ex_customer on ex_order.custId = ex_customer.custId left join ex_orderdetail on ex_order.orderId = ex_orderdetail.orderId left join ex_product on ex_orderdetail.prodId = ex_product.prodId where ex_order.custId = #{custId} </select> <!--订单映射--> <resultMap id="orderResult" type="rui.db.Model.ex_Order"> <id property="orderId" column="orderId"/> <result property="custId" column="custId"/> <result property="orderDate" column="orderDate"/> <result property="totalMoney" column="totalMoney"/> <result property="remark" column="remark"/> <!--订单明细集合--> <collection property="detailList" ofType="rui.db.Model.ex_OrderDetail" resultMap="orderDetailResult"> </collection> </resultMap> <!--订单明细映射--> <resultMap id="orderDetailResult" type="rui.db.Model.ex_OrderDetail"> <id property="detailId" column="detailId"/> <result property="orderId" column="orderId"/> <result property="prodId" column="prodId"/> <result property="buyPrice" column="buyPrice"/> <result property="buyCount" column="buyCount"/> <result property="remark" column="remark"/> <!--商品关联--> <association property="orderProd" javaType="rui.db.Model.ex_Product" resultMap="productResult"> </association> </resultMap> <!--商品映射--> <resultMap id="productResult" type="rui.db.Model.ex_Product"> <id property="prodId" column="prodId"></id> <result property="prodName" column="prodName"/> <result property="sellPrice" column="sellPrice"/> <result property="imgUrl" column="imgUrl"/> </resultMap> </mapper>
7.3、通过日志验证延迟
mybatis对接log4J
1)pom导入包
<!--单元测试--> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
2)mybatis主配置文件
log4j.properties配置内容
## 设置Logger输出级别和输出目的地 ### log4j.rootLogger=DEBUG,CONSOLE,LOGFILE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%5p [%t] - %m%n log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.file=log4j.log log4j.appender.LOGFILE.appand=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] - %m%n
执行非加载模式日志输出:
执行懒加载模式日志输出: