Введение в Fluent Mybatis 3: сложный запрос и запрос к объединенной таблице

Java

Быстрая обработка mybatis может сэкономить много времени на ручном кодировании в простом CRUD, но даже сложные запросы легко обрабатываются без каких-либо проблем. В этой статье мы продемонстрируем следующие возможности:

  • В подзапросе
column IN (select column from ... )
  • СУЩЕСТВУЕТ подзапрос
EXISTS (select 1 from .... )
  • запрос JOIN (включая INNER JOINm, LEFT JOIN, RIGHT JOIN)
select ... from table1 a join table2 b on a.xxx = b.xxx where ...;

select ... from table1 a left join table2 b on a.xxx = b.xxx where ...;

select ... from table1 a right join table2 b on a.xxx = b.xxx where ...;

Не только поддерживает связанный запрос двух таблиц, но также поддерживает любую таблицу JOIN.

  • ПРИСОЕДИНЯЙТЕСЬ, И, ИЛИ большую рукопашную

настройки сцены

Чтобы облегчить объяснение этих сложных функций запросов беглого mybatis, мы создаем 3 таблицы: информационная таблица студента, административная область и стенограмма студента.

CREATE TABLE student
(
    id             bigint(21) unsigned auto_increment primary key COMMENT '主键id',
    age            int          DEFAULT NULL COMMENT '年龄',
    grade          int          DEFAULT NULL COMMENT '年级',
    user_name      varchar(45)  DEFAULT NULL COMMENT '名字',
    gender_man     tinyint(2)   DEFAULT 0 COMMENT '性别, 0:女; 1:男',
    birthday       datetime     DEFAULT NULL COMMENT '生日',
    phone          varchar(20)  DEFAULT NULL COMMENT '电话',
    bonus_points   bigint(21)   DEFAULT 0 COMMENT '积分',
    status         varchar(32)  DEFAULT NULL COMMENT '状态(字典)',
    home_county_id bigint(21)   DEFAULT NULL COMMENT '家庭所在区县',
    address        varchar(200) DEFAULT NULL COMMENT '家庭详细住址',
    gmt_created    datetime     DEFAULT NULL COMMENT '创建时间',
    gmt_modified   datetime     DEFAULT NULL COMMENT '更新时间',
    is_deleted     tinyint(2)   DEFAULT 0 COMMENT '是否逻辑删除'
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '学生信息表';

CREATE TABLE county_division
(
    id           bigint(21) unsigned auto_increment primary key COMMENT '主键id',
    province     varchar(50) DEFAULT NULL COMMENT '省份',
    city         varchar(50) DEFAULT NULL COMMENT '城市',
    county       varchar(50) DEFAULT NULL COMMENT '区县',
    gmt_created  datetime    DEFAULT NULL COMMENT '创建时间',
    gmt_modified datetime    DEFAULT NULL COMMENT '更新时间',
    is_deleted   tinyint(2)  DEFAULT 0 COMMENT '是否逻辑删除'
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '区县';

create table `student_score`
(
    id           bigint auto_increment primary key COMMENT '主键ID',
    student_id   bigint               NOT NULL COMMENT '学号',
    school_term  int                  NULL COMMENT '学期',
    subject      varchar(30)          NULL COMMENT '学科',
    score        int                  NULL COMMENT '成绩',
    gmt_create   datetime             NOT NULL COMMENT '记录创建时间',
    gmt_modified datetime             NOT NULL COMMENT '记录最后修改时间',
    is_deleted   tinyint(2) default 0 NOT NULL COMMENT '逻辑删除标识'
) engine = InnoDB default charset = utf8 COMMENT = '学生成绩';

Демонстрация присоединения в статье основана на свободном использовании mybatis версии 1.8.7.

<dependencies>
    <!-- fluent mybatis依赖-->
    <dependency>
        <groupId>com.github.atool</groupId>
        <artifactId>fluent-mybatis</artifactId>
        <version>1.8.7</version>
    </dependency>
    <dependency>
        <groupId>com.github.atool</groupId>
        <artifactId>fluent-mybatis-processor</artifactId>
        <version>1.8.7</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

В подзапросе

  • сценарий спроса

Найти все студенты 4 класса в Ханчжоу, провинция Чжэцзян

  • Сценарий SQL
selct * from student
where grade = 4
and is_deleted = 0
and home_county_id in 
    (select id from county_division 
     where is_deleted = 0
     and province = '浙江省'
     and city = '杭州市')
  • Реализация FluentMybatis
@RunWith(SpringRunner.class)
@SpringBootTest(classes = QuickStartApplication.class)
public class InSelectDemo {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void test_in_select() {
        StudentQuery query = new StudentQuery()
            .where.isDeleted().isFalse()
            .and.grade().eq(4)
            .and.homeCountyId().in(new CountyDivisionQuery()
                .selectId()
                .where.isDeleted().isFalse()
                .and.province().eq("浙江省")
                .and.city().eq("杭州市")
                .end()
            ).end();
        List<StudentEntity> students = mapper.listEntity(query);
    }
}
  • Проверьте вывод журнала консоли
DEBUG - ==>  Preparing: SELECT id, gmt_modified, is_deleted, address, age, birthday, bonus_points, gender_man, gmt_created, grade, home_county_id, phone, status, user_name 
    FROM student WHERE is_deleted = ? AND grade = ? 
    AND home_county_id IN (SELECT id FROM county_division 
            WHERE is_deleted = ? AND province = ? AND city = ?)  
DEBUG - ==> Parameters: false(Boolean), 4(Integer), false(Boolean), 浙江省(String), 杭州市(String) 
DEBUG - <==      Total: 0 

СУЩЕСТВУЕТ подзапрос

  • сценарий спроса

Запросите список студентов, которые не сдали экзамены по китайскому языку или математике в семестре 2019 года.

  • Реализация SQL
select * from student
 where is_deleted = 0
 and exists (select 1 from student_score
     where is_deleted = 0
     and score < 60
     and school_term = 2019
     and subject in ('语文', '数学')
     and student_id = student.id
 )
  • свободная реализация mybatis
@SpringBootTest(classes = QuickStartApplication.class)
public class InSelectDemo {
    @Autowired
    private StudentMapper mapper;
    @Test
    public void test_exists() {
        StudentQuery query = new StudentQuery()
            .where.isDeleted().isFalse()
            .and.exists(new StudentScoreQuery()
                .selectId()
                .where.isDeleted().isFalse()
                .and.schoolTerm().eq(2019)
                .and.score().lt(60)
                .and.subject().in(new String[]{"语文", "数学"})
                .and.studentId().apply("= student.id")
                .end()
            ).end();
        List<StudentEntity> students = mapper.listEntity(query);
    }
}
  • Проверьте вывод журнала консоли
DEBUG - ==>  Preparing: SELECT id, gmt_modified, is_deleted, address, age, birthday, bonus_points, gender_man, gmt_created, grade, home_county_id, phone, status, user_name FROM student 
    WHERE is_deleted = ? 
    AND EXISTS (SELECT id FROM student_score 
        WHERE is_deleted = ? 
        AND school_term = ? 
        AND score < ? 
        AND subject IN (?, ?) 
        AND student_id =student.id
)  
DEBUG - ==> Parameters: false(Boolean), false(Boolean), 2019(Integer), 60(Integer), 语文(String), 数学(String) 
DEBUG - <==      Total: 0 

ВНУТРЕННЕЕ СОЕДИНЕНИЕ запрос

  • Требования к сценарию

Семья оказалась студентом мужского пола из города Ханчжоу, провинция Чжэцзян. Имя, возраст и район/округ, где он живет.

  • Реализация SQL
select a.user_name, a.age, a.gender_man, b.province, b.city, b.county
from student a join county_division b
on a.home_county_id = b.id
where a.is_deleted = 0
and a.gender_man = 1
and b.is_deleted = 0
and b.province = '浙江省'
and b.city = '杭州市'
  • Свободный синтаксис соединения Mybatis

Основной синтаксис запроса на соединение выглядит следующим образом:

Parameters parameters = new Parameters();
IQuery query = JoinBuilder
.from(new 左表Query("别名").where(设置左表查询条件).end())
.join(new 右表1Query("别名").where(设置右表1查询条件).end())
.on(l->{左表on字段}, r->{右表1on字段})
.join(new 右表2Query("别名").where(设置右表2查询条件).end())
.on(l->{左表on字段}, r->{右表2on字段})
.builder();
  • свободная реализация mybatis
public class JoinSelectDemo {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void test_join_student_county() {
        IQuery query = JoinBuilder
            .<StudentQuery>from(new StudentQuery("t1")
                .select.userName().age().genderMan().end()
                .where.isDeleted().isFalse()
                .and.genderMan().eq(1).end())
            .join(new CountyDivisionQuery("t2")
                .select.province().city().county().end()
                .where.isDeleted().isFalse()
                .and.province().eq("浙江省")
                .and.city().eq("杭州市").end())
            .on(l -> l.where.homeCountyId(), r -> r.where.id()).endJoin()
            .build();
        mapper.listMaps(query);
    }
}
  • Проверьте вывод журнала консоли
DEBUG - ==>  Preparing: SELECT t1.user_name, t1.age, t1.gender_man, t2.province, t2.city, t2.county 
    FROM student t1 
    JOIN county_division t2 
    ON t1.home_county_id = t2.id 
    WHERE t1.is_deleted = ? 
    AND t1.gender_man = ? 
    AND t2.is_deleted = ? 
    AND t2.province = ? 
    AND t2.city = ?  
DEBUG - ==> Parameters: false(Boolean), 1(Integer), false(Boolean), 浙江省(String), 杭州市(String) 
DEBUG - <==      Total: 0 

Мы видим, что fluent mybatis автоматически собирает запрос соединения в соответствии с настройками, устанавливает условие on и устанавливает псевдоним для таблицы запросов, а также устанавливает условие в соответствии с псевдонимом.

LEFT JOIN & RIGHT JOIN запрос

Запрос LEFT JOIN & RIGHT JOIN аналогичен запросу JOIN, просто поместите

JoinBuilder
    .from(new StudentQuery("t1").where("条件设置").end())
    .join(new CountyDivisionQuery("t2").where("条件设置").end())
    .on(l->l.where.左表字段(), r->r.where.右表字段()).endJoin()

заменить на соответствующий

JoinBuilder
    .from(new StudentQuery("t1").where("条件设置").end())
    .leftJoin(new CountyDivisionQuery("t2").where("条件设置").end())
    .on(l->l.where.左表字段(), r->r.where.右表字段()).endJoin()

или

JoinBuilder
    .from(new StudentQuery("t1").where("条件设置").end())
    .rightJoin(new CountyDivisionQuery("t2").where("条件设置").end())
    .on(l->l.where.左表字段(), r->r.where.右表字段()).endJoin()

JOIN-запрос из 3 таблиц

В приведенном выше примере показана связанная операция запроса между двумя таблицами.Что делать, если с запросом связано более двух таблиц? Связанный запрос нескольких таблиц также довольно прост для беглого mybatis, просто установите еще одну операцию соединения.

В реальном бизнесе старайтесь избегать операций сопоставления нескольких таблиц.

  • сценарий спроса

Актуальный запрос для получения информации о студентах с баллом 90 и выше по китайскому языку или математике в Ханчжоу, провинция Чжэцзян, в семестре 2019 года.

  • Реализация SQL
select a.user_name, c.subject, c.score
from student a
join county_division b on a.home_county_id = b.id
join student_score c on a.id = c.student_id
where a.is_deleted = 0
and b.is_deleted = 0
and b.province = '浙江省'
and b.city = '杭州市'
and c.is_deleted = 0
and c.school_term = 2019
and c.subject in ('语文', '数学')
and c.score >=90
  • свободная реализация mybatis
@RunWith(SpringRunner.class)
@SpringBootTest(classes = QuickStartApplication.class)
public class JoinSelectDemo {
    @Autowired
    private StudentMapper mapper;

	@Test
    public void test_3_table_join() {
        IQuery query = JoinBuilder
            .from(new StudentQuery("t1")
                .select.userName().end()
                .where.isDeleted().isFalse().end())
            .join(new CountyDivisionQuery("t2")
                .where.isDeleted().isFalse()
                .and.province().eq("浙江省")
                .and.city().eq("杭州市").end())
            .on(l -> l.where.homeCountyId(), r -> r.where.id()).endJoin()
            .join(new StudentScoreQuery("t3")
                .select.subject().score().end()
                .where.isDeleted().isFalse()
                .and.schoolTerm().eq(2019)
                .and.subject().in(new String[]{"语文", "数学"})
                .and.score().ge(90).end())
            .on(l -> l.where.id(), r -> r.where.studentId()).endJoin()
            .build();
        mapper.listMaps(query);
    }
}
  • Проверить журнал вывода консоли
DEBUG - ==>  Preparing: SELECT t1.user_name, t3.subject, t3.score 
    FROM student t1 
    JOIN county_division t2 ON t1.home_county_id = t2.id 
    JOIN student_score t3 ON t1.id = t3.student_id 
    WHERE t1.is_deleted = ? 
    AND t2.is_deleted = ? AND t2.province = ? AND t2.city = ? 
    AND t3.is_deleted = ? AND t3.school_term = ? AND t3.subject IN (?, ?) AND t3.score >= ?  
DEBUG - ==> Parameters: false(Boolean), false(Boolean), 浙江省(String), 杭州市(String), false(Boolean), 2019(Integer), 语文(String), 数学(String), 90(Integer) 
DEBUG - <==      Total: 0 

Мы видим, что fluent mybatis устанавливает 2 операции соединения и настройки условий, а также устанавливает псевдонимы для каждой таблицы соединения. Условия where трех таблиц также устанавливаются в соответствии с псевдонимами.

ПРИСОЕДИНЯЙТЕСЬ, И, ИЛИ большую рукопашную

Fluent mybatis поддерживает запросы AND, IN (подзапрос), EXISTS (подзапрос), JOIN. В то же время вы также можете использовать И (множественные условия ИЛИ) и ИЛИ (несколько условий И), чтобы комбинировать сложные условия по мере необходимости. Например, для вышеуказанного языка или математики мы заменяем операцию IN на условие OR для простой демонстрации.

  • И (Условие 1 ИЛИ Условие 2) свободная реализация mybatis
@RunWith(SpringRunner.class)
@SpringBootTest(classes = QuickStartApplication.class)
public class AndOrDemo {
    @Autowired
    private StudentMapper mapper;

    @Test
    public void test_and_or() {
        IQuery query = JoinBuilder
            .from(new StudentQuery("t1")
                .select.userName().end()
                .where.isDeleted().isFalse().end())
            .join(new CountyDivisionQuery("t2")
                .where.isDeleted().isFalse()
                .and.province().eq("浙江省")
                .and.city().eq("杭州市").end())
            .on(l -> l.where.homeCountyId(), r -> r.where.id()).endJoin()
            .join(new StudentScoreQuery("t3")
                .select.subject().score().end()
                .where.isDeleted().isFalse()
                .and.schoolTerm().eq(2019)
                .and(iq -> iq
                    .where.subject().eq("语文")
                    .or.subject().eq("数学").end())
                .and.score().ge(90).end())
            .on(l -> l.where.id(), r -> r.where.studentId()).endJoin()
            .build();
        mapper.listMaps(query);
    }
}
  • Проверьте вывод журнала консоли
DEBUG - ==>  Preparing: SELECT t1.user_name, t3.subject, t3.score 
    FROM student t1 
    JOIN county_division t2 ON t1.home_county_id = t2.id 
    JOIN student_score t3 ON t1.id = t3.student_id 
    WHERE t1.is_deleted = ? 
    AND t2.is_deleted = ? AND t2.province = ? AND t2.city = ? 
    AND t3.is_deleted = ? AND t3.school_term = ? 
    AND ( subject = ? OR subject = ? ) 
    AND t3.score >= ?  
DEBUG - ==> Parameters: false(Boolean), false(Boolean), 浙江省(String), 杭州市(String), false(Boolean), 2019(Integer), 语文(String), 数学(String), 90(Integer) 

Мы заметили, что вывод оператора в журнале содержитAND ( subject = ? OR subject = ? )

Адрес примера кода в тексте

Суммировать

Для сложных запросов Fluent mybatis также обеспечивает нулевое кодирование xml и mapper, отсутствие кодирования волшебных строк и плавную работу в стиле API.Вы думаете, Fluent mybatis является артефактом Java-формы? Если вы считаете, что беглый mybatis — это неплохо, помогите поставить лайк и переслать статью.

Введение в Fluent MyBatis

Введение в FluentMybatis II

Сравнение функций Fluent Mybatis, родного Mybatis и Mybatis Plus

Свободная документация и примеры Mybatis

Fluent Mybatis Gitee

Fluent Mybatis GitHub