對於一對一,一對多,多對一,多對多的關聯查詢,Mybatis-Plus 在處理時,需要編寫關聯查詢方法及配置resultMap,並且書寫SQL。
為了簡化這種操作,可以注解來簡化。
Mybatis-Plus-Relation ( mprelation ) : mybatis-plus 一對一、一對多、多對一、多對多的自動關聯查詢,注解方式。
交流QQ群: 1085077558
mprelation源碼地址 :
github: https://github.com/dreamyoung/mprelation.git
gitee: https://gitee.com/dreamyoung/mprelation.git
mprelation_demo 地址 :
github: https://github.com/dreamyoung/mprelation_demo.git
gitee: https://gitee.com/dreamyoung/mprelation_demo.git
POM引用 :
<dependency> <groupId>com.github.dreamyoung</groupId> <artifactId>mprelation</artifactId> <version>0.0.3.2-RELEASE</version> </dependency>
注解工具使用優缺點:
優點:
使用簡單,通過在實體類上添加@OneToOne / @OneToMany / @ManyToOne / @ManyToMany 等注解即可。
注解命名參考了Hibernate命名,如果使用過Hibernate則即刻可上手。
1對1、1對多、多對1、多對多映射時,可以不再寫SQL及XML配置文件,免去配置冗長的<resultMap>的麻煩。
Service層及Mapper層不需要再添加 getLinkById 、 selectLinkById 之類的方法來關聯映射
重寫過的ServiceImpl各種內置的查詢方法都自動關聯查詢,非內置方法可以調用autoMapper相關方法進行自動或手動關聯
所有執行采用非join方式查詢,同時解決關聯處理的1+n問題
缺點:
目前只針對SqlSession/Mappe形式有效(ActiveRecord形式暫未涉及修改,也沒有測試)
非事務下, 1個連接(1個SqlSession)只執行一條SQL,而自動獲取每個關聯屬性的sql都會創建1~2個SqlSession(並執行1~2條SQL)。如果需要使用非內置方法(即除ServiceImpl外的方法),必須配置只讀事務來減少SqlSession創建。
如果有多個延遲加載的關聯屬性,類上可注解@AutoLazy(false)或沒有標注該注解(默認),之后通過initialize方法在事務范圍內的一個SqlSession中同時加載需要的多個延遲加載的屬性。
使用注意點:
非ServiceImpl內置的業務查詢,配置事務管理,減少SqlSession的創建。
實體上可用注解@AutoLazy(true/false)來標注是否自動觸發延遲加載,該注解只針對需要延遲的屬性。
★ true或無值的話,則獲取延遲的關聯屬性時自動關聯。但每一個延遲屬性的獲取都消耗一個SqlSession。適合於只有一個延遲屬性的情況。
★ false或者不標注該注解的話,需要手動通過initialize()方法對延遲的關聯屬性進行獲取,否則不會自動關聯獲取,此時關聯為空。適合於有多個延遲屬性的情況。
如果可以,不使用延遲加載(延遲加載的使用是在SqlSession關閉后執行的,需要重新創建SqlSession)。
如果確實需要延遲加載,可使用ServiceImpl 或 AutoMapper 相關的initialize方法一次性加載所有需要的被延遲的屬性(只需要創建額外的一個SqlSession,畢竟SqlSession之前已經關閉)
注解使用:
一對多(多對一) :
Company實體類中配置:
@Data public class Company { @TableId(value = "company_id") private Long id; private String name; //一對多
@TableField(exist = false) @OneToMany //一對多默認為延遲加載,即@Lazy/@Lazy(true)/或此時不標注 @JoinColumn(name="company_id",referencedColumnName = "company_id")//@TableId與一方相關屬性中@TableField名稱保持一致時@JoinColumn可省略 private Set<Man> employees; }
Man實體類中配置:
@Data public class Man { @TableId(value = "man_id") private Long id; private String name;
//多對一
@TableField("company_id") private Long companyId; @TableField(exist = false) @ManyToOne //多對一默認為立即加載,即@Lazy(false)或此時不標注 @JoinColumn(name = "company_id", referencedColumnName = "company_id") //相關的@TableField與多方的@TableId名稱一致時@JoinColumn可省略 private Company company; }
一對多(多對一)表結構: company: (compnay_id, name) man: (man_id, name, company_id)
一對一:
Woman實體類配置:
@Data public class Woman { @TableId(value = "woman_id") private Long id; private String name;
//一對一
@TableField("lao_gong_id") private Long laoGongId; @TableField(exist = false) @OneToOne //一對一默認為立即加載,即@Lazy(false)或此時不標注 @JoinColumn(name = "lao_gong_id", referencedColumnName = "man_id")
private Man laoGong; }
Man實體類配置:
@Data public class Man { @TableId(value = "man_id") private Long id; private String name; //一對一
@TableField("lao_po_id")
private Long laoPoId; @TableField(exist = false) @OneToOne @JoinColumn(name = "lao_po_id", referencedColumnName = "woman_id") private Woman laoPo; }
一對一表結構:(實際可以減少一方) woman: (woman_id, name, lao_gong_id) man: (man_id, name, lao_po_id)
多對多:
Course實體類配置:
@Data public class Course { @TableId(value = "course_id") private Long id; private String name; //多對多
@TableField(exist = false) @ManyToMany //多對多默認為延遲加載,即@Lazy(true)或此時不標注 @JoinTable(targetMapper = StudentCourseMapper.class) //第三方命名為StudentCourseMapper或CourseStudentMapper時@JoinTable注解一般可省略 @JoinColumn(name = "course_id", referencedColumnName = "course_id") @InverseJoinColumn(name = "child_id", referencedColumnName = "student_id") private List<Child> students; }
Child實體類配置:
@Data public class Child { @TableId("child_id") private Long id; private String name;
//多對多
@TableField(exist = false) @ManyToMany @JoinTable(targetMapper=StudentCourseMapper.class) @JoinColumn(name = "child_id", referencedColumnName = "student_id") @InverseJoinColumn(name = "course_id", referencedColumnName = "course_id")
private List<Course> courses; }
StudenCourse中間類(多對多必須要有,如果命名為StudentCourse或CourseStudent,則上邊的@JoinTable可省略):
@Data public class StudentCourse { //可以有也可以無此ID
private Long id; @TableField("student_id") private Long studentId; @TableField("course_id") private Long courseId; }
多對多表結構:course: (course_id, name) child: (child_id, name) student_course:(id, student_id, course_id)
mprelation 關聯查詢,使用過程:
1. POM中引入mprelation:
<dependency> <groupId>com.github.dreamyoung</groupId> <artifactId>mprelation</artifactId> <version>0.0.3.2-RELEASE</version> </dependency>
配置 AutoMapper (只要是掃描被注解的實體類)
@Configuration public class AutoMapperConfig { @Bean public AutoMapper autoMapper() { return new AutoMapper(new String[] { "demo.entity","demo.bean" }); //配置實體類所在目錄(可多個,暫時不支持通過符*號配置) } }
2. 在實體類中配置注解(更多的注解配置見上邊注解部分,這里只列出其中一個)
@Data
@AutoLazy //不需要手動觸發加載延遲屬性,當延遲屬性被調用時自動創建Session進行獲取。可見如果有多個延遲屬性需要被使用時,會造成多次創建Session,此時可以標注為AutoLazy(false)或不標注,然后采用initialze方法手動一次性加載需要的屬性
public class Man {
@TableId(value = "man_id") private Long id; private String name; private Long laoPoId;
@TableField(exist = false) @OneToOne @JoinColumn(name = "lao_po_id", referencedColumnName = "woman_id") private Woman laoPo;
@TableField("company_id") private Long companyId; @TableField(exist = false) @ManyToOne @JoinColumn(name = "company_id", referencedColumnName = "company_id")
private Company company;
@TableField(exist = false) @OneToMany @JoinColumn(name = "man_id", referencedColumnName = "lao_han_id")
@Lazy(false)
private List<Child> waWa;
@TableField(exist = false) @OneToMany @JoinColumn(name = "man_id", referencedColumnName = "man_id") @Lazy(false) private Set<Tel> tels; }
3. 在Service層、Mapper層的使用,見下面:
以下是基於Mybatis-Plus官方示例修改而來的測試程序:
通過繼承工具類重寫過的IService / ServiceImpl 會自動執行關聯映射, 無須再寫gettLinkById之類的方法(可以使得各實現類沒有任何方法):
mapper接口:
public interface ManMapper extends BaseMapper<Man> {}
service接口:
public interface IManService extends IService<Man> {} // IService為重寫過的同名接口
Service實現:
@Service public class ManServiceImpl extends ServiceImpl<ManMapper, Man> implements IManService {} // ServiceImpl為重寫過的同名接口
測試調用:
public class ServiceTest { @Autowired ManService manService; @Test public void t_man_serviceImpl() { Man man = manService.getById(1); // 原Mybatis-Plus的ServiceImpl的各種查詢,被重寫過后,都可以自動關聯,
System.out.println(man);
} }
結果輸出:
Man(
id=1,
name=程序猿小明,
laoPoId=1,
laoPo=Woman(id=1, name=程序猿小明老婆, laoGongId=1, laoGong=null, waWa=null),
companyId=1,
company=Company(id=1, name=百度, employees=null),
waWa=[
Child(id=1,name=xxx1,lao_han_id=null, laoHan=null, lao_ma_id=null, laoMa=null, courses=null),
Child(id=2,name=xxxx2, lao_han_id=null, laoHan=null, lao_ma_id=null, laoMa=null, courses=null)
],
tels=[
Tel(id=1, tel=139xxxxxx, manId=1, laoHan=null),
Tel(id=4, tel=159xxxxxx, manId=1, laoHan=null),
Tel(id=2, tel=137xxxxxx, manId=1, laoHan=null)
]
)
如需需要對其關聯屬性對象的關聯屬性進行自動加載,可以繼續使用AutoMapper對象的mapperEntity、mapperEntityList、mapperEntitySet、mapperEntityPage來操作:
比如想獲取(填充)waWas 的關聯,則:
List waWas=man.getWaWas(); autoMapper.mapperEntityList(waWas);
AutoMapper類中的幾個常用方法說明:
mapperEntity(entity) 可以對一個實體類,實現自動關聯。
mapperEntityList(entity_list) 可以對一個實體類List,實現自動關聯。
mapperEntitySet(entity_set) 可以對一個實體類Set,實現自動關聯。
mapperEntityCollection(entity_list_or_set) 可以對一個實體類Set或List,實現自動關聯。
mapperEntityPage(entity_page) 可以對一個實體類Page,實現自動關聯。
mapper(entity_entityListOrSet_entityPage) 統一上邊的各種方法,直接對實體類,列表,分頁都可以實現自動關聯。
initialize(entity/entityList/entitySet/entityPage, OneOrMoreLazyPropertyName ...)
可以對一個實體類/實體類List/實體類Set/實體類Page,在事務范圍內,手動立即觸發其各個被@Lazy(true)標注的關聯屬性。
該方法在重寫過的ServiceImpl內也存在(供Controller層調用來加載延遲關聯的屬性)。
AutoMapper在重寫過的ServiceImpl類中已經自動注入可用(名為autoMapper),其它情況也可以手動注入:
public class MPRTest2 { @Autowired AutoMapper autoMapper; @Resource private ManMapper manMapper; @Test public void t_man() { Man man = manMapper.selectById(1L); autoMapper.mapperEntity(man); System.out.println(man); } }
最新版本POM:
<dependency> <groupId>com.github.dreamyoung</groupId> <artifactId>mprelation</artifactId> <version>0.0.3.2-RELEASE</version> </dependency>
github: https://github.com/dreamyoung/mprelation