MyBatis关联关系映射
MyBatis是一种ORM(Object-Relational Mapping)框架,支持将Java对象和关系数据库之间的数据进行映射。在MyBatis中,关联关系映射是一种处理实体对象之间关联关系的方法
配置mybatis关联映射
使用ehcache做为二级缓存,这个工具类是由桥接包提供的
注意:与hibernate二级缓存不同之处:
hibernate默认会缓存单条、不会缓存多条,想要缓存多条,那么需要通过代码手动开启使用缓存
mybatis默认就会缓存单条以及多条,如果不想缓存多条,那么在具体的方法sql添加属性值userCache=false
一对多关系配置 :
注意事项,使用左外连接而非内连接!
实体类:
在多的一方(例如Order表)中,需要定义一个表示一的一方(例如User表)的属性,通常是一个对象或集合。
在表示一的一方(例如User表)的实体类中,不需要再定义对应多的一方(例如Order表)的属性。
使用注解或XML文件来配置映射关系时,需要在多的一方(例如Order表)的实体类中,使用一对多的注解或XML配置来指定对应的一的一方(例如User表)的属性,并指定双方的关联列。
在进行一对多关系配置时,需注意双方的关联列(例如Order表中的user_id列与User表中的id列)需要保持一致。
public class Order { private Long id; private String orderNo; private List<OrderItem> orderItems; // 省略getter和setter方法 } public class OrderItem { private Long id; private String productName; // 省略getter和setter方法 } package com.yuan.vo; import com.yuan.model.Order; import com.yuan.model.OrderItem; import java.util.ArrayList; import java.util.List; /** * @author zhanghao * @site * @company s 集团 * @create 2023-09-04 10:48 */ public class OrderVO extends Order { private List<OrderItem> list = new ArrayList<>(); public List<OrderItem> getList() { return list; } public void setList(List<OrderItem> list) { this.list = list; } } public class Order { private Long id; private String orderNo; private List<OrderItem> orderItems; // 省略getter和setter方法 } public class OrderItem { private Long id; private String productName; // 省略getter和setter方法 } package com.yuan.vo; import com.yuan.model.Order; import com.yuan.model.OrderItem; import java.util.ArrayList; import java.util.List; /** * @author zhanghao * @site * @company s 集团 * @create 2023-09-04 10:48 */ public class OrderVO extends Order { private List<OrderItem> list = new ArrayList<>(); public List<OrderItem> getList() { return list; } public void setList(List<OrderItem> list) { this.list = list; } }
Mapper接口和XML文件
- Mapper接口名称必须与对应的XML文件名称相同,并且位于同一个包下。
- Mapper接口中的方法名必须与对应的XML文件中的SQL语句ID相同。
- 在Mapper接口中定义方法时,需要与XML文件中对应的SQL语句的参数类型、返回值类型、方法名都一致。
- 在使用Mapper接口进行CRUD操作时,需要在XML文件中定义对应的SQL查询语句、参数映射以及结果集映射。
- 在使用Mapper接口进行查询操作时,需要定义对应的查询结果映射,通常可以使用ResultMap或ResultType来实现。
- 在进行Mapper接口和XML文件编写时,需要注意SQL语句的参数名与实体类属性名需要一致,否则可能会出现SQL执行异常。
- 当进行多表查询时,需要在XML文件中使用join语句,同时需要注意多表查询的性能问题。
<!-- OrderMapper.xml --> <mapper namespace="com.zhanghao.mapper.OrderMapper"> <resultMap id="OrderVoMap" type="com.zhanghao.vo.OrderVO" > <result column="order_id" property="orderId"></result> <result column="order_no" property="orderNo"></result> <collection property="list" ofType="com.zhanghao.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="demo1" resultMap="OrderVoMap" parameterType="java.lang.Integer" > select * from t_hibernate_order o ,t_hibernate_order_item oit where o.order_id = oit.oid and o.order_id = #{oid} </select> </mapper>
查询的方法:
package com.zhanghao.Biz; import com.zhanghao.vo.OrderVO; /** * @author zhanghao * @site * @company s集团 * @create 2023-09-04 10:08 */ public interface OrderBiz { OrderVO demo1(Integer oid); }
一对多的查询:
package com.zhaghao.Biz.Impl; import com.zhaghao.Biz.OrderBiz; import com.zhaghao.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 zhaghao * @site * @company s集团 * @create 2023-09-04 10:13 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class OrderBizImplTest { @Autowired private OrderBiz orderBiz; @Test public void demo1() { OrderVO orderVO = orderBiz.demo1(7); System.out.println(orderVO); orderVO.getList().forEach(System.out::println); } }
多对一 关系配置 :
实体类
- 实体类中需要包含与关联表对应的属性,并且需要使用相应的注解进行注解。一般来说,多对一关系中需要使用@ManyToOne注解表示多个表与一个表的关联关系。
- 在使用@ManyToOne注解时,需要指定关联关系的属性,如@JoinColumn注解表示关联外键的名称。
- 在实体类中还需要定义与关联表相关的getter和setter方法。
- 在多对一关系中,由于多个表关联同一个表,因此需要在实体类中使用关联表的实体类作为属性类型。
- 实体类中还需要定义与关联表相关的getter和setter方法。
- 在多对一关系中,需要使用@OneToMany注解表示关联表与多个表的关系。
- 在使用@OneToMany注解时,需要指定关联关系的属性,如@JoinColumn注解表示关联外键的名称。
- 在进行多对一关系的实体类编写时,需要注意两张表之间的外键关系是否正确,否则可能会出现数据一致性问题。
public class Order { private Long id; private String orderNo; private List<OrderItem> orderItems; // 省略getter和setter方法 } public class OrderItem { private Long id; private String productName; // 省略getter和setter方法 } package com.zhanghao.vo; import com.zhanghao.model.Order; import com.zhanghao.model.OrderItem; /** * @author zhanghao * @site * @company s集团 * @create 2023-09-04 10:58 */ public class OrderItemVo extends OrderItem { private Order order; public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
Mapper接口和XML文件
- 在Mapper接口中定义方法时,需要使用@Result注解映射查询结果到实体类。通常情况下,@Result注解需要指定两个属性,一个是column,表示查询结果中的列名,另一个是property,表示映射到实体类的属性名。
- 在Mapper接口中定义查询方法时,需要使用@Select注解指定SQL语句,并使用@ResultMap注解指定查询结果映射关系。
- 在XML文件中,需要定义一个<resultMap>元素,用于映射查询结果到实体类。<resultMap>元素需要指定id属性、type属性和<id>、<result>子元素。
- 在<resultMap>元素中定义<association>元素,用于映射关联表的查询结果到对应实体类的属性上。<association>元素需要指定属性名、column属性和javaType属性,以及<id>、<result>子元素。
- 在XML文件中定义查询语句时,需要使用<include>元素引用<resultMap>元素,并且还需要使用<join>元素进行关联查询。
- 在<join>元素中需要指定关联表名、关联条件,以及使用<association>元素映射关联表查询结果到实体类的属性。
package com.zhanghao.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; } } package com.yuan.model; import lombok.ToString; @ToString public class HBookCategory { private Integer bcid; private Integer bid; private Integer cid; public HBookCategory(Integer bcid, Integer bid, Integer cid) { this.bcid = bcid; this.bid = bid; this.cid = cid; } public HBookCategory() { super(); } public Integer getBcid() { return bcid; } public void setBcid(Integer bcid) { this.bcid = bcid; } public Integer getBid() { return bid; } public void setBid(Integer bid) { this.bid = bid; } public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } } package com.yuan.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; } } package com.zhaghao.vo; import com.zhaghao.model.HBook; import com.zhaghao.model.HCategory; import java.util.List; /** * @author zhaghao * @site * @company s集团 * @create 2023-09-04 10:35 */ 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.zhanghao.Biz.Impl; import com.zhanghao.Biz.HBookBiz; import com.zhanghao.mapper.HBookMapper; import com.zhanghao.vo.HbookVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author zhanghao * @site * @company s集团 * @create 2023-09-04 11:23 */ @Service public class HBookBizImpl implements HBookBiz { @Autowired private HBookMapper hBookMapper; @Override public HbookVo demo3(Integer bid) { return hBookMapper.demo3(bid); } }
多对多关系查询
package com.zhanghao.Biz.Impl; import com.zhanghao.Biz.HBookBiz; import com.zhanghao.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 zhanghao * @site * @company s集团 * @create 2023-09-04 11:24 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-context.xml"}) public class HBookBizImplTest { @Autowired private HBookBiz hBookBiz; @Test public void demo3() { HbookVo hbookVo = hBookBiz.demo3(8); System.out.println(hbookVo); hbookVo.getCategories().forEach(System.out::println); } }
总结:
在使用一对多关系映射的时候,使用group by和聚合函数会影响查询性能,数据库中大量数据可能会使查询变慢。此时可以使用MyBatis提供的延迟加载机制,即在查询一端的时候只查询一端的属性,待需要获取多端的数据时再进行查询。