mprelation注解实现MP一对一、一对多、多对一、多对多的自动关联查询

对于一对一,一对多,多对一,多对多的关联查询,MyBatis-Plus 在处理时,需要编写关联查询方法及配置resultMap,并且书写SQL。对于这种操作,我们可以使用注解来进行简化。

MyBatis-Plus-Relation ( mprelation ) : 实现 mybatis-plus 一对一、一对多、多对一、多对多的自动关联查询,注解方式。

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层不需要再添加 getLinkByIdselectLinkById之类的方法来关联映射。
  • 重写过的ServiceImpl各种内置的查询方法都自动关联查询,非内置方法可以调用autoMapper相关方法进行自动或手动关联。
  • 所有执行采用非join方式查询,同时解决关联处理的1+n问题。

缺点

  • 目前只针对SqlSession / Mapper形式有效(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)。
  • 如果确实需要延迟加载,可使用ServiceImplAutoMapper 相关的initialize()方法一次性加载所有需要的被延迟的属性(只需要创建额外的一个SqlSession,毕竟SqlSession之前已经关闭)。

实体类配置 & 相关注解的使用

一对多(多对一)

  • 一对多(多对一)表结构:
    • company:(compnay_id, name)
    • man:(man_id, name, company_id)

Company实体类

@Data
public class Company {

    @TableId(value = "company_id")
    private Long id;
    private String name;

    // 一对多
    @TableField(exist = false)
    @OneToMany    // 一对多默认为延迟加载,即@Lazy/@Lazy(true)/或此时不标注
    // name:本类中的关联字段名,referencedColumnName:Man中的关联字段名
    @JoinColumn(name = "company_id", referencedColumnName = "company_id")    
    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)或此时不标注
    // Man中相关的@TableField与Company中的@TableId名称一致时@JoinColumn可省略
    @JoinColumn(name = "company_id", referencedColumnName = "company_id")   
    private Company company;
}

一对一

  • 一对一表结构:(实际可以减少一方)
    • woman:(woman_id, name, lao_gong_id)
    • man:(man_id, name, lao_po_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;
}

多对多

  • 多对多表结构:
    • course:(course_id, name)
    • child:(child_id, name)
    • student_course:(id, student_id, course_id)

Course实体类

@Data
public class Course {
    
    @TableId(value = "course_id")
    private Long id;
    
    private String name;

    // 多对多
    @TableField(exist = false)
    @ManyToMany // 多对多默认为延迟加载,即@Lazy(true)或此时不标注
    // 第三方命名为StudentCourseMapper或CourseStudentMapper时@JoinTable注解一般可省略
    @JoinTable(targetMapper = StudentCourseMapper.class)    
    @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;
}

StudentCourse中间类(多对多必须要有,如果命名为StudentCourse或CourseStudent,则上边的@JoinTable可省略)

@Data
public class StudentCourse {
    
    // 可以有也可以无此ID
    private Long id;

    @TableField("student_id")
    private Long studentId;

    @TableField("course_id")
    private Long courseId;
}

mprelation 关联查询的使用

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"});
    }
}

在实体类中配置注解(更多注解参见上面注解部分,此处只列出其中一个)

@Data
// 不需要手动触发加载延迟属性,当延迟属性被调用时自动创建Session进行获取。可见如果有多个延迟属性需要被使用时,会造成多次创建Session,此时可以标注为AutoLazy(false)或不标注,然后采用initialze方法手动一次性加载需要的属性
@AutoLazy
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;
}

Service层、Mapper层的使用

以下是基于MyBatis-Plus官方示例修改而来的测试程序

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); // 原MP的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=xxx2, 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对象的mapperEntitymapperEntityListmapperEntitySetmapperEntityPage等方法来操作:

// 获取(填充)waWas 的关联
List waWas = man.getWaWas();
autoMapper.mapperEntityList(waWas); 

AutoMapper类中的常用方法

方法名称方法说明
mapperEntity(entity)对一个实体类,实现自动关联
mapperEntityList(entity_list)对一个实体类List,实现自动关联
mapperEntitySet(entity_set)对一个实体类Set,实现自动关联
mapperEntityCollection(entity_list_or_set)对一个实体类SetList,实现自动关联
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);
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在mybatis中,一对多查询可以使用注解实现。具体步骤如下: 1. 定义实体类 定义一个实体类,该实体类包含一个一对多的关联关系,如下所示: ```java public class Order { private Long id; private Date createTime; private List<OrderItem> orderItems; // 一对多关联关系 // getter和setter方法省略 } public class OrderItem { private Long id; private Long orderId; private String productName; private Integer quantity; // getter和setter方法省略 } ``` 2. 创建Mapper接口 创建一个Mapper接口,并使用注解定义一对多查询语句,如下所示: ```java public interface OrderMapper { @Select("select * from order where id = #{id}") @Results({ @Result(property = "id", column = "id"), @Result(property = "createTime", column = "create_time"), @Result(property = "orderItems", column = "id", many = @Many(select = "com.example.OrderItemMapper.findByOrderId")) }) Order findById(Long id); } public interface OrderItemMapper { @Select("select * from order_item where order_id = #{orderId}") List<OrderItem> findByOrderId(Long orderId); } ``` 在上面的注解中,@Results定义了查询结果的映射关系,@Result用于映射查询结果中的字段到实体类中的属性。@Many注解用于定义一对多关联关系,指定了查询OrderItem的方法,以及通过哪个字段进行关联。 3. 调用Mapper接口 在业务逻辑中调用Mapper接口,并传入查询参数,如下所示: ```java Order order = orderMapper.findById(1L); System.out.println(order.getId() + " " + order.getCreateTime()); for (OrderItem item : order.getOrderItems()) { System.out.println(item.getId() + " " + item.getProductName() + " " + item.getQuantity()); } ``` 通过调用OrderMapper的findById方法,可以查询到指定id的Order对象及其关联的OrderItem对象列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦翼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值