Часть 1Введение в FluentMybatis, представляет методы Mapper, предоставляемые платформой, из которых несколько важных методов используют объекты IQuery и IUpdate. Эти два объекта являются строительными классами FluentMybatis для реализации сложного и динамического sql.Благодаря этим двум объектам Fluent Mybatis может создавать более сложные бизнес-операторы sql непосредственно через java API без написания определенных файлов xml, чтобы достичь логики кода и логического единства sql. .
Сравнение функций Fluent Mybatis, родного Mybatis и Mybatis Plus
Свободная документация и примеры Mybatis
В следующей статье рассказывается, как определить мощные динамические операторы SQL с помощью IQuery и IUpdate.
Сначала создайте бизнес-сценарий
- Структура таблицы
Если есть структура таблицы отчета студента следующим образом:
create table `student_score`
(
id bigint auto_increment comment '主键ID' primary key,
student_id bigint not null comment '学号',
gender_man tinyint default 0 not null comment '性别, 0:女; 1:男',
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 default 0 not null comment '逻辑删除标识'
) engine = InnoDB default charset=utf8;
- необходимость
Сейчас нужны:Статистика с 2000 по 2019 год, баллы по трем предметам («английский язык», «математика», «китайский язык») по семестрам, минимальный балл предметной статистики, наивысший балл и средний балл, статистические результаты, отсортированные по семестрам и предметам.Оператор SQL для достижения этого требования выглядит следующим образом.
select school_term, subject, count(score), min(score), max(score), avg(score)
from student_score
where school_term between 2000 and 2019
and subject in ('英语', '数学', '语文')
and is_deleted = 0
group by school_term, subject
order by school_term, subject
Теперь реализуем через FluentMybatis
- Определите интерфейс в классе StudentScoreDao.
@Data
public class ScoreStatistics {
private int schoolTerm;
private String subject;
private long count;
private Integer minScore;
private Integer maxScore;
private BigDecimal avgScore;
}
public interface StudentScoreDao extends IBaseDao<StudentScoreEntity> {
/**
* 统计从fromYear到endYear年间学科subjects的统计数据
*
* @param fromYear 统计年份区间开始
* @param endYear 统计年份区间结尾
* @param subjects 统计的学科列表
* @return 统计数据
*/
List<ScoreStatistics> statistics(int fromYear, int endYear, String[] subjects);
}
- Реализовать бизнес-логику на StudentScoreDaoImpl
@Repository
public class StudentScoreDaoImpl extends StudentScoreBaseDao implements StudentScoreDao {
@Override
public List<ScoreStatistics> statistics(int fromSchoolTerm, int endSchoolTerm, String[] subjects) {
return super.listPoJos(ScoreStatistics.class, super.query()
.select.schoolTerm().subject()
.count("count")
.min.score("min_score")
.max.score("max_score")
.avg.score("avg_score")
.end()
.where.isDeleted().isFalse()
.and.schoolTerm().between(fromSchoolTerm, endSchoolTerm)
.and.subject().in(subjects)
.end()
.groupBy.schoolTerm().subject().end()
.orderBy.schoolTerm().asc().subject().asc().end()
);
}
}
В реализации DaoImpl помимо возврата статистических результатов в соответствии с условиями, результаты также автоматически преобразуются в объекты ScoreStatistics по правилу подчеркивания для горба.
- Напишите тест для проверки
@RunWith(SpringRunner.class)
@SpringBootTest(classes = QuickStartApplication.class)
public class StudentScoreDaoImplTest {
@Autowired
private StudentScoreDao dao;
@Test
public void statistics() {
List<ScoreStatistics> list = dao.statistics(2000, 2019, new String[]{"语文", "数学", "英语"});
System.out.println(list);
}
}
- Просмотр вывода консоли
DEBUG - ==> Preparing: SELECT school_term, subject, count(*) AS count, MIN(score) AS min_score, MAX(score) AS max_score, AVG(score) AS avg_score
FROM student_score
WHERE is_deleted = ?
AND school_term BETWEEN ? AND ?
AND subject IN (?, ?, ?)
GROUP BY school_term, subject
ORDER BY school_term ASC, subject ASC
DEBUG - ==> Parameters: false(Boolean), 2000(Integer), 2019(Integer), 语文(String), 数学(String), 英语(String)
DEBUG - <== Total: 30
[ScoreStatistics(schoolTerm=2000, subject=数学, count=17, minScore=1, maxScore=93, avgScore=36.0588),
...
ScoreStatistics(schoolTerm=2009, subject=语文, count=24, minScore=3, maxScore=100, avgScore=51.2500)]
В приведенном выше примере примерно показана логика динамического построения запроса FluentMybatis. Давайте объясним это подробно.
где условная конструкция
Общее, где условная конструкция
Синтаксис настройки условия where следующий: он начинается с where и заканчивается методом end(). После встроенной переменной where поля таблицы (методы), которые могут быть установлены, могут быть автоматически перечислены, а за полем может следовать компаратор и значение сравнения.
.where
.字段().条件(条件参数)
.end()
Уравнение: равный =?
- Пример установки условия равенства
@Test
public void column_eq_value() {
UserQuery query = new UserQuery()
.where.userName().eq("darui.wu")
.and.eMail().eq("darui.wu@163.com").end();
mapper.findOne(query);
}
- Используйте метод условного суждения: eq(значение объекта, предикат когда)
Когда прогноз когда верен, будет выполнено суждение eq(value)
Пример:
/**
* 根据条件查询用户列表
* 如果设置了积分, 加上积分条件
* 如果设置了状态, 加上状态条件
*
* @return
*/
@Override
public List<UserEntity> findByBirthdayAndBonusPoints(Date birthday, Long points, String status) {
UserQuery query = super.query()
.where.birthday().eq(birthday)
.and.bonusPoints().ge(points, If::notNull)
.and.status().eq(status, If::notBlank).end();
return mapper.listEntity(query);
}
Приведенный выше пример эквивалентен следующим общим условным настройкам Java.
public List<UserEntity> findByBirthdayAndBonusPoints(Date birthday, Long points, String status) {
UserQuery query = super.query();
query.where.birthday().eq(birthday);
if (points != null) {
query.where.bonusPoints().ge(points);
}
if (status != null && !status.trim().isEmpty()) {
query.where.status().eq(status).end();
}
return mapper.listEntity(query);
}
В качестве альтернативы используйте условные настройки mybatis
<select id="findByBirthdayAndBonusPoints" parameterType="java.util.Map" resultMap="UserEntity">
SELECT ... FROM user
WHERE birthday = #{birthday}
<if test="bonus_points != null ">
AND bonus_points = #{bonusPoints}
</if>
<if test="status != null and status != '' ">
AND status = #{status}
</if>
</select>
Использование условного оператора fluent mybatis может достичь того же эффекта, что и вышеприведенное суждение java и суждение xml.В то же время, если пустая строка передана для дня рождения, она также будет считаться пустой, и будет выдано исключение. Предотвратите непреднамеренные условные запросы, вызванные неправильной передачей параметров.
ne: не равно ?
@Test
public void ne() {
UserQuery query = new UserQuery()
.where
.age().ne(34)
.end();
mapper.count(query);
}
gt: больше > ?
@Test
public void gt() {
UserQuery query = new UserQuery()
.where.age().gt(34).end();
mapper.count(query);
}
ge: больше или равно >= ?
@Test
public void ge() {
UserQuery query = new UserQuery()
.where.age().ge(34).end();
mapper.count(query);
}
lt: меньше, чем
@Test
public void lt() {
UserQuery query = new UserQuery()
.where.age().lt(34).end();
mapper.count(query);
}
le: меньше или равно
@Test
public void le() {
UserQuery query = new UserQuery()
.where.age().le(34).end();
mapper.count(query);
}
between: BETWEEN ? AND ?
@Test
public void between() {
UserQuery query = new UserQuery()
.where.age().between(23, 40).end();
mapper.count(query);
}
notBetween: NOT BETWEEN ? AND ?
@Test
public void not_between() {
UserQuery query = new UserQuery()
.where.age().notBetween(23, 40).end();
mapper.count(query);
}
например: НРАВИТСЯ ?, ? это "%value%"
@Test
void like() {
mapper.listEntity(new UserQuery()
.where
.userName().like("abc") // like '%abc%'
.end()
);
}
notLike: НЕ НРАВИТСЯ ?, ? равно "%value%"
@Test
void not_like() {
mapper.listEntity(new UserQuery()
.where
.userName().notLike("abc") // not like '%abc%'
.end()
);
}
likeLeft: НРАВИТСЯ?,? это "значение%"
likeRight: НРАВИТСЯ ?, ? равно "%value"
isNull: column IS NULL
@Test
void isNull() {
mapper.listEntity(new UserQuery()
.where.age().isNull().end()
);
}
isNotNull: column IS NOT NULL
@Test
void isNotNull() {
mapper.listEntity(new UserQuery()
.where.age().isNotNull().end()
);
}
в: в IN (?, ..., ?)
@Test
public void in_collection() {
UserQuery query = new UserQuery()
.where
.age().in(Arrays.asList(34, 35))
.end();
mapper.count(query);
}
@Test
public void in_array() {
UserQuery query = new UserQuery()
.where
.age().in(new int[]{34, 35})
.end();
mapper.count(query);
}
notIn: не в... НЕ В (?, ..., ?)
Вложенные условные конструкции
@Test
public void lt() {
UserQuery query = new UserQuery()
.where.age().lt(34).end();
mapper.count(query);
}
@Test
public void le() {
UserQuery query = new UserQuery()
.where.age().le(34).end();
mapper.count(query);
}
between: BETWEEN ? AND ?
@Test
public void between() {
UserQuery query = new UserQuery()
.where.age().between(23, 40).end();
mapper.count(query);
}
notBetween: NOT BETWEEN ? AND ?
@Test
public void not_between() {
UserQuery query = new UserQuery()
.where.age().notBetween(23, 40).end();
mapper.count(query);
}
например: НРАВИТСЯ ?, ? это "%value%"
@Test
void like() {
mapper.listEntity(new UserQuery()
.where
.userName().like("abc") // like '%abc%'
.end()
);
}
notLike: НЕ НРАВИТСЯ ?, ? равно "%value%"
@Test
void not_like() {
mapper.listEntity(new UserQuery()
.where
.userName().notLike("abc") // not like '%abc%'
.end()
);
}
likeLeft: НРАВИТСЯ?,? это "значение%"
likeRight: НРАВИТСЯ ?, ? равно "%value"
isNull: column IS NULL
@Test
void isNull() {
mapper.listEntity(new UserQuery()
.where.age().isNull().end()
);
}
isNotNull: column IS NOT NULL
@Test
void isNotNull() {
mapper.listEntity(new UserQuery()
.where.age().isNotNull().end()
);
}
в: в IN (?, ..., ?)
@Test
public void in_collection() {
UserQuery query = new UserQuery()
.where
.age().in(Arrays.asList(34, 35))
.end();
mapper.count(query);
}
@Test
public void in_array() {
UserQuery query = new UserQuery()
.where
.age().in(new int[]{34, 35})
.end();
mapper.count(query);
}
notIn: не в... НЕ В (?, ..., ?)
Вложенные условные конструкции
FluentMybatis также поддерживает построение вложенных условий.
в (выберите подзапрос)
- Вложенная таблица запросов аналогична основной таблице запросов.
.column().in(запрос-> {установить условия для запроса})
Вам нужно только обратиться к лямбда-выражению в in, а входным параметром лямбда-выражения является запрос с тем же именем. Параметр where может быть установлен для этого входного параметра.
@DisplayName("嵌套查询和主查询的表是同一个")
@Test
void test_in_same_table_query() {
UserQuery query = new UserQuery()
.where.id().in(q -> q.selectId()
.where.id().eq(3L).end())
.and.userName().like("user")
.and.age().gt(23).end();
List list = mapper.listEntity(query);
}
Приведенная выше логика выполняет следующую инструкцию SQL
SELECT id, gmt_create, gmt_modified, is_deleted, account, age, avatar, birthday, bonus_points, e_mail, password, phone, status, user_name
FROM user WHERE id IN (SELECT id FROM user WHERE id = ?)
AND user_name LIKE ?
AND age > ?
- Вложенная таблица запросов — это сцена другой таблицы
.column().in(new XyzQuery(){установить условия для запроса})
Если вложенный запрос не является одной и той же таблицей и одной и той же таблицей, вам необходимо явно объявить тип класса объекта Query в методе in, и следующее использование будет таким же, как в методе 1.
@DisplayName("嵌套查询和主查询的表是不同")
@Test
void test_in_difference_table_query() {
UserQuery query = new UserQuery()
.selectId()
.where.addressId().in(new ReceivingAddressQuery().selectId()
.where.id().in(new int[]{1, 2}).end())
.end();
mapper.listEntity(query);
}
выполнить sql-оператор
SELECT id FROM user
WHERE address_id IN (SELECT id
FROM receiving_address WHERE id IN (?, ?)
)
- не во вложенном запросе: используйте тот же метод, что и во вложенном запросе
существует (выберите подзапрос)
- Вложенная таблица запросов аналогична основной таблице запросов.
В запросе Exists не нужно указывать поля, а на метод exists можно ссылаться непосредственно в запросе where.
- существует (запрос-> {установить условия для запроса})
Если таблица, запрашиваемая с помощью exists, совпадает с основным запросом, вы можете напрямую использовать параметр запроса того же типа в лямбда-выражении, а использование параметра такое же, как и в методе in.
- существует (новый XyzQuery {установить условия для запроса})
Если таблица, запрошенная с помощью exists, не соответствует основному запросу, укажите тип запроса в первом параметре метода exists, а второй параметр такой же, как в методе 1.
конкретный пример
@DisplayName("EXISTS查询")
@Test
void test_exists_query() {
UserQuery query = new UserQuery()
.where.exists(new ReceivingAddressQuery()
.where.detailAddress().like("杭州")
.and.id().apply(" = user.address_id").end())
.end();
mapper.listEntity(query);
}
выполнить sql-оператор
SELECT id, gmt_create, gmt_modified, is_deleted, account, address_id, age, avatar, birthday, bonus_points, e_mail, password, phone, status, user_name
FROM user
WHERE EXISTS (SELECT * FROM receiving_address
WHERE detail_address LIKE ?
AND id = user.address_id
)
группировать по условной конструкции
Синтаксис установки условия groupBy следующий, начиная с groupBy и заканчивая методом end().
.groupBy.column1().column2()
.end()
Пример 1:
@Test
public void test_groupBy() throws Exception {
UserQuery query = new UserQuery()
.selectId()
.where.id().eq(24L).end()
.groupBy.userName().age().end();
mapper.listEntity(query);
}
выполнить sql-оператор
SELECT id FROM t_user WHERE id = ? GROUP BY user_name, age
состояние
.groupBy.column1().column2().end().end()
.having
.聚合函数1.column1().条件(条件值)
.聚合函数2.column2().条件(条件值)
.end()
Условия здесь аналогичны части where, поддерживающей eq, ge, gt и т. д., пример
.where.id().eq(24L).end()
.groupBy.id().end()
.having.sum.age().between(2, 10)
.and.count.id().gt(2)
.and.avg.age().in(new int[]{2, 3})
.and.min.age().gt(10)
.and.max.age().lt(20)
.end();
Соответствующий оператор sql
GROUP BY id
HAVING SUM(age) BETWEEN ? AND ?
AND COUNT(id) > ?
AND AVG(age) IN (?, ?)
AND MIN(age) > ?
AND MAX(age) < ?
порядок по условной конструкции
Синтаксис установки условия orderBy следующий, начиная с orderBy и заканчивая методом end().
.orderBy
.字段().asc()
.字段().desc()
.end()
Пример кода 1
@Test
public void test_orderBy() throws Exception {
UserQuery query = new UserQuery()
.selectId()
.where.id().eq(24L).end()
.orderBy.id().asc().age().desc().end();
mapper.listEntity(query);
}
Соответствующий оператор sql
SELECT id FROM t_user WHERE id = ? ORDER BY id ASC, age DESC
установить значение обновления
Синтаксис: начать с set, обновить значение соответствующего поля, закончить с помощью end()
.set.字段1().is(设置值)
.set.字段2().is(设置值)
.set.字段3().is(设置值)
.end()
.where.条件设置.end()
образец кода
@Test
void test_update() {
mapper.updateBy(new UserUpdate()
.set.age().is(34).end()
.where.id().eq(2).end()
);
}
Соответствующий оператор sql
UPDATE t_user SET gmt_modified = now(), age = ? WHERE id = ?
инструкция: В примере явно указано значение обновления поля age, но поле gmt_modified устанавливает значение обновления по умолчанию now(), Таким образом, поле gmt_modified обновляется одновременно с выполнением инструкции. вывод журнала консоли
...UserMapper.updateBy - ==> Preparing: UPDATE t_user SET gmt_modified = now(), age = ? WHERE id = ?
...UserMapper.updateBy - ==> Parameters: 34(Integer), 2(Integer)