Предисловие:
mybatis по-прежнему относительно популярен в структуре уровня сохраняемости, а общие проекты основаны на ssm. Хотя mybatis может напрямую управлять базой данных с помощью операторов SQL в формате xml, он очень гибок. Однако, поскольку операция должна выполняться с помощью операторов SQL, необходимо писать большое количество файлов xml, что очень хлопотно. mybatis-plus очень хорошо решает эту проблему.
Введение
MyBatis-Plus (сокращенно MP) — это инструмент расширения для MyBatis, который только дополняет, а не изменяет на основе MyBatis, создан для упрощения разработки и повышения эффективности. Официальный сайт:mp.baomidou.com/guide
Интегрировать
1. Создайте таблицу
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
user_name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
age INT(11) DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '修改时间'
) ENGINE=INNODB CHARSET=UTF8;
Данные инициализации
INSERT INTO user (id, user_name, age, email,create_time,update_time) VALUES
(1, 'Jone', 18, 'test1@baomidou.com',now(),now()),
(2, 'Jack', 20, 'test2@baomidou.com',now(),now()),
(3, 'Tom', 28, 'test3@baomidou.com',now(),now()),
(4, 'Sandy', 21, 'test4@baomidou.com',now(),now()),
(5, 'Billie', 24, 'test5@baomidou.com',now(),now());
Функция now() получает текущее время
2. Создайте проект Springboot и импортируйте зависимости
Импортированная здесь версия mysql — 8.0, а модифицированная версия mysql — 5.1.47 (см. личную версию mysql)
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
Зависимости MyBatis-Plus:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
Последняя версия официального сайта:3.3.2
, используя загрузочную версию как2.2.4
Не используйте последнюю версию, ха-ха.
3. Конфигурация
1. Добавьте соответствующую конфигурацию базы данных в файл конфигурации application.yml:
server:
port: 1998
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
password: 980402
driver-class-name: com.mysql.jdbc.Driver
mysql5 и mysql8 будут немного отличаться при подключении: (1), драйвер mysql 5 отличается от com.mysql.jdbc.Driver (2), mysql 8 управляет другим com.mysql.cj.jdbc.Driver, необходимо увеличить конфигурацию часового пояса serverTimezone=GMT+8
4. Кодирование
1. Напишите класс сущности User.java
//生成getter,setter等函数
@Data
//生成全参数构造函数
@AllArgsConstructor
//生成无参构造函数
@NoArgsConstructor
public class User {
private Long id;
private String userName;
private Integer age;
private String email;
private Date createTime;
private Date updateTime;
}
2. Напишите интерфейс Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gongj.mybatisplus.entity.User;
//继承基本的类 BaseMapper
public interface UserMapper extends BaseMapper<User> {
}
3. Добавьте аннотацию @MapperScan в класс запуска Spring Boot для сканирования каталога, в котором находится Mapper.
@SpringBootApplication
@MapperScan("com.gongj.mybatisplus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
5. Тест
5.1 Тестовый запрос для всех
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void testSelect(){
// 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
5.2, настроить журнал sql в yml
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
запросить еще раз
5.3, добавить тест
@Test
void testInsert() {
User user = new User();
user.setUserName("gongj");
user.setEmail("19908488818@163.com");
user.setAge(23);
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
int insert = userMapper.insert(user);//帮我们自动生成id
System.out.println("id=" + user.getId()); //id会自动回填
}
MyBatisplus Первичная ключевая стратегия:
public enum IdType {
AUTO(0), //数据库id自增
NONE(1), // 未设置主键id
INPUT(2), // 用户输入id
//以下三种只有当插入对象ID 为空,才自动填充。
ID_WORKER(3), // 默认的全局唯一id
UUID(4), //全局唯一uuid
ID_WORKER_STR(5); //字符串全局唯一ID ID_WORKER的字符串表示方式
}
Изменить стратегию первичного ключа по умолчанию Аннотируйте поле класса сущности: @TableId(type = xxx)
@TableId(type = IdType.AUTO)
private Long id;
Стратегия первичного ключа была изменена на автоматическое увеличение, и поля базы данных должны автоматически увеличиваться!
5.4 Тест автозаполнения
В приведенной выше операции добавления мы вручную присваиваем значения времени создания и времени модификации. Можно ли это сделать автоматически? MyBatisPlus предоставляет такую конфигурацию.
1. Пользовательский класс реализацииMyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
2. Добавьте аннотации к атрибутам полей класса сущностей.
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
Тип заполнения:
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
Протестируйте операцию добавления еще раз:
@Test
void testInsert() {
User user = new User();
user.setUserName("gongj2");
user.setEmail("19908488818@163.com");
user.setAge(223);
int insert = userMapper.insert(user);//帮我们自动生成id
System.out.println("id=" + user.getId());
}
Эффект потрясающий.
5.5. Изменить тест
@Test
void testUpdate() {
User user = new User();
user.setId(1276042184872341506L);
user.setUserName("ggggggggggg");
user.setAge(555);
int insert = userMapper.updateById(user);
System.out.println("id=" + user.getId());
}
Все sql автоматически настраиваются динамически для вас! Вы также можете видеть, что автозаполнение updateTime также вступает в силу.
5.6, оптимистичная блокировка
Когда запись должна быть обновлена, я надеюсь, что эта запись не была обновлена другими
Оптимистичные способы блокировки: 1, при удалении записи, получение текущей версии 2, когда обновленная версия этого пояса 3, при выполнении обновлений, установите версию = NewVersion, где версия = OFFVERSION 4, если версия неверна, то обновление не удается
Увеличьте поля базы данных версии 5.6.1!
5.6.2 Класс Entity добавляет атрибут версии и добавляет аннотацию!
@Version
private Integer version;
5.6.3 Конфигурация плагина
@Configuration
public class MybatisPlusConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
5.6.4 Тестовая модификация
@Test
void testVserion(){
// 1、查询用户信息
User user = userMapper.selectById(1276056567094583298L);
user.setAge(55);
user.setUserName("yuanj");
//user 就是前台传入的值
userMapper.updateById(user);
}
Обратим внимание на оператор sql update
UPDATE user SET create_time=?, update_time=?, user_name=?, version=?, email=?, age=? WHERE id=? AND version=?
версия делается для изменения условий.Специальное примечание:
- Только поддерживаемые типы данных: int, Integer, long, Long, Date, Timestamp, LocalDateTime.
- Целочисленный тип
newVersion = oldVersion + 1
-
newVersion
отпишусь вentity
середина - поддерживается только
updateById(id)
иupdate(entity, wrapper)
метод - существует
update(entity, wrapper)
метод,wrapper
Повторное использование невозможно!!!
5.7. Запрос
5.7.1 Запрос по идентификатору
@Test
void selectById(){
userMapper.selectById(1276056567094583298L);
}
5.7.2 Пакетный запрос по идентификатору
@Test
public void selectBatchIds(){
// 根据id批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1276056567094583298L, 5, 3));
}
5.7.3. Запрос на основе пользовательских условий карты
@Test
public void selectByMap(){
// 根据Map 自定义条件查询
Map map = new HashMap();
map.put("user_name","yuanj");
List<User> users = userMapper.selectByMap(map);
}
5.8 Пейджинговый запрос
5.8.1 Настройка компонента перехватчика
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
5.8.2 Кодирование
@Test
public void selectPage(){
Page<User> userPage = new Page<>();
userPage.setCurrent(1L); //当前是第几页 默认为1
userPage.setSize(2); //每页大小
IPage<User> userIPage = userMapper.selectPage(userPage, null);
System.out.println("当前页" + userIPage.getCurrent()); //当前页
System.out.println("总页数" + userIPage.getPages()); //总页数
System.out.println("返回数据" + userIPage.getRecords()); //返回数据
System.out.println("每页大小" + userIPage.getSize()); //每页大小
System.out.println("满足符合条件的条数" + userIPage.getTotal()); //满足符合条件的条数
System.out.println("下一页" + userPage.hasNext()); //下一页
System.out.println("上一页" + userPage.hasPrevious()); //上一页
}
5.9. Удалить
Удаление делится на физическое удаление и логическое удаление.
5.9.1. Физическое удаление
1. Физическое удаление по id
@Test
void deleteById(){
//根据id物理删除
int i = userMapper.deleteById(1L);
}
2. Физически удалять пачками по id
@Test
void deleteBatchIds(){
//批量删除
int i = userMapper.deleteBatchIds(Arrays.asList(2L, 4L));
System.out.println(i); //受影响的行数为2
}
5.9.2, логическое удаление
1. Добавить удаленное поле в таблицу данных
2. Добавьте атрибуты и добавьте аннотации к классам сущностей
@TableLogic
private Integer deleted;
3. Тест
выполнить операцию удаления еще раз
@Test
void deleteById(){
//根据id删除
int i = userMapper.deleteById(3L);
System.out.println(i);
}
Вы можете видеть, что sql является оператором модификации.
4. Выполните запрос:
@Test
void testSelect(){
// 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
Видно, что данные с id 3 не могут быть запрошены.
5.10 Условный конструктор
1. Запросите данные, время создания которых от 2020-6-15 до 2020-7-5 и почтовый ящик не равен нулю и не равен нулю
@Test
void select1(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//查询创建时间在2020-6-15到2020-7-5并且邮箱不等于空并不等于null的数据
queryWrapper.between("create_time","2020-06-01","2020-07-05").ne("email","").isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age FROM
user WHERE deleted=0 AND (create_time BETWEEN ? AND ? AND
email <> ? AND email IS NOT NULL)
2. Нечеткий запрос
@Test
void select2(){
//模糊查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.likeLeft("user_name","J"); //左模糊 %J
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
3. Подзапрос и обратный порядок по id
@Test
void select3(){
//子查询并根据id倒序
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id","select id from user where id < 4");
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach((System.out::println));
}
Preparing: SELECT id,deleted,create_time,update_time,user_name,version,email,age
FROM user WHERE deleted=0 AND (id IN (select id from user where id < 4))
ORDER BY id DESC
4. Пейджинговый запрос с несколькими условиями
//分页多条件查询
@Test
void select4(){
//查询年龄大于等于28岁的信息并分页
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age",28);
Page<User> userPage = new Page<>();
userPage.setCurrent(1L);
userPage.setSize(2);
IPage<User> userIPage = userMapper.selectPage(userPage, queryWrapper);
List<User> records = userIPage.getRecords();
records.forEach((System.out::println));
}
SELECT COUNT(1) FROM user WHERE deleted = 0 AND (age >= ?)
SELECT id,deleted,create_time,update_time,user_name,version,email,age,cid
FROM user WHERE deleted=0 AND (age >= ?) LIMIT ?,?
5.11 Ассоциативный запрос
1. Создайте новый карточный стол
CREATE TABLE card (
cid BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
cname VARCHAR(30) DEFAULT NULL COMMENT '卡片名'
) ENGINE=INNODB CHARSET=UTF8;
INSERT INTO card (cid,cname) VALUES
(1,'健身卡'),
(2,'KTV卡'),
(3,'校园卡')
2. Создайте столбец cid в пользовательской таблице.
и свяжите пользовательскую таблицу с карточной таблицей
3, кодирование
3.1, сущность карты
//生成getter,setter等函数
@Data
//生成全参数构造函数
@AllArgsConstructor
//生成无参构造函数
@NoArgsConstructor
public class Card {
private Long cid;
private String cname;
}
3.2, пользовательский объект
Добавьте атрибуты cid иcards
private Long cid;
private List<Card> cards;
3.3, новый метод UserMapper
List<User> userJoinCard();
3.4. UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gongj.mybatisplus.mapper.UserMapper">
<resultMap id="userJoinCardMap" type="com.gongj.mybatisplus.entity.User">
<id column="id" property="id"/>
<result property="userName" column="user_name"></result>
<result property="age" column="age"></result>
<result property="createTime" column="create_time"></result>
<result property="updateTime" column="update_time"></result>
<result property="email" column="email"></result>
<result property="email" column="email"></result>
<result property="version" column="version"></result>
<result property="deleted" column="deleted"></result>
<result property="cid" column="cid"></result>
<collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</collection>
</resultMap>
<select id="userJoinCard" resultMap="userJoinCardMap">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid,c.cid,c.cname from user u left join card c on u.cid = c.cid
</select>
</mapper>
Элементы, содержащиеся в resultMap:
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
Если тег коллекции использует вложенные запросы, формат выглядит следующим образом: Примечание. Столбец в теге. Параметры, которые будут переданы в оператор SELECT SIERY, если передаются несколько параметров, формат является столбцем = "{параметр 1 = поле таблицы 1, имя параметра 2 = поле таблицы 2};
<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" >
</collection>
呜呜 .cn Блог на .com / Ken Home / Fear / 7 ...
3.5, звоните
@Test
void joinQuery(){
List<User> users = userMapper.userJoinCard();
users.forEach((System.out::println));
}
Видно, что это связанный запрос sql, а затем выполняется сопоставление полей в resultMap.
3.6 Еще один способ написания коллекции, его также можно назвать распределенным запросом
CardMapper
public interface CardMapper extends BaseMapper<Card> {
public Card selectById();
}
CardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gongj.mybatisplus.mapper.CardMapper">
<resultMap id="CardMap" type="com.gongj.mybatisplus.entity.Card">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
</resultMap>
<select id="selectById" resultMap="CardMap">
select * from card where cid = #{cid}
</select>
</mapper>
userMapper
List<User> userJoinCard2();
UserMapper.xml
<resultMap id="userJoinCardMap2" type="com.gongj.mybatisplus.entity.User">
<id column="id" property="id"/>
<result property="userName" column="user_name"></result>
<result property="age" column="age"></result>
<result property="createTime" column="create_time"></result>
<result property="updateTime" column="update_time"></result>
<result property="email" column="email"></result>
<result property="email" column="email"></result>
<result property="version" column="version"></result>
<result property="deleted" column="deleted"></result>
<result property="cid" column="cid"></result>
<collection property="cards" column="cid" ofType="com.gongj.mybatisplus.entity.Card" select="com.gongj.mybatisplus.mapper.CardMapper.selectById">
</collection>
</resultMap>
<select id="userJoinCard2" resultMap="userJoinCardMap2">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
</select>
select="com.gongj.mybatisplus.mapper.CardMapper.selectById"
Используйте встроенные запросы.
перечислить
@Test
void joinQuery2(){
List<User> users = userMapper.userJoinCard2();
users.forEach((System.out::println));
}
5.12, связанный запрос и пейджинг
UserMapper
List<User> userJoinCardPage(Page page);
UserMapper.xml
<select id="userJoinCardPage" resultMap="userJoinCardMap2">
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
</select>
контрольная работа
@Test
void joinQueryPage(){
Page<User> userPage = new Page<>();
userPage.setSize(1);
userPage.setCurrent(1);
List<User> users = userMapper.userJoinCardPage(userPage);
users.forEach((System.out::println));
}
Было довольно просто, нужно только передать на него объект Page.
Дополнение 1:
Мы обнаружили, что если мы вызовем результат однотабличного запроса, то есть встроенного запроса, возникнет исключение.
@Test
void testSelect(){
// 参数是一个 Wrapper ,条件构造器,这里我们先不用 null 查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
Как это решить? Добавьте аннотации к свойствам класса сущностей, существующие по умолчанию имеют значение true.
@TableField(exist = false)
private List<Card> cards;
К свойству бина добавляется аннотация, указывающая, что текущее свойство не является полем базы данных, а должно использоваться в проекте, поэтому при добавлении или запросе бина mybatis-plus проигнорирует это и не сообщит об ошибке .
Дополнение 2: Условный запрос многотабличного пейджинга
//多表分页查询
@Test
void joinQueryPage3(){
//查询年龄大于等于20、名称以j开头的信息并分页
Page<User> userPage = new Page<>();
userPage.setSize(2);
userPage.setCurrent(1);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age",20);
queryWrapper.likeRight("user_name","j");
IPage<User> userIPage = userMapper.userJoinCardPageQuery2(userPage,queryWrapper);
System.out.println("当前页" + userIPage.getCurrent()); //当前页
System.out.println("总页数" + userIPage.getPages()); //总页数
System.out.println("每页大小" + userIPage.getSize()); //每页大小
System.out.println("满足符合条件的条数" + userIPage.getTotal()); //满足符合条件的条数
System.out.println("返回数据===="); //返回数据
userIPage.getRecords().forEach((System.out::println));
}
执行结果:
当前页1
总页数1
每页大小2
满足符合条件的条数1
返回数据====
User(id=2, userName=Jack, age=20, email=test2@baomidou.com, createTime=Sat Oct 31 07:50:19 CST 2020, updateTime=Sat Oct 31 07:50:19 CST 2020, version=0, deleted=0, cid=1, cards=[Card(cid=1, cname=健身卡)])
UserMapper
IPage<User> userJoinCardPageQuery2(Page page, @Param(Constants.WRAPPER)QueryWrapper<User> queryWrapper);
UserMapper.xml
<select id="userJoinCardPageQuery2" resultMap="userJoinCardMap2">
<!--带上${ew.customSqlSegment 就可以实现查询-->
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u
${ew.customSqlSegment}
</select>
SQL для выполнения
SELECT COUNT(1) FROM user u WHERE (age >= ? AND user_name LIKE ?)
select u.id,u.user_name,u.email,u.age,u.version,u.create_time,u.update_time,u.deleted,u.cid from user u WHERE (age >= ? AND user_name LIKE ?) LIMIT ?,?
select * from card where cid = ?