通用Service应用 #
这里会出现 publicKey is now allowed ,在数据库连接语句后面加上这句话即可 allowPublicKeyRetrieval=true
spring: #配置数据源 datasource: #配置数据源类型 type: com.zaxxer.hikari.HikariDataSource #配置数据源各个信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&&useSSL=false&&allowPublicKeyRetrieval=true username: root password: 123456
查询
@Test public void testList(){ //List<User> list = userService.list(); long count = userService.count(); System.out.println("总条数:"+count); }
SQL执行语句
==> Preparing: SELECT COUNT( * ) FROM user ==> Parameters: <== Columns: COUNT( * ) <== Row: 5 <== Total: 1
批量添加
@Test public void batchInsert(){ List<User> users=new ArrayList<>(); for(int i=0;i<10;i++){ User user=new User(); user.setName("name"+i); user.setEmail("email"+i); users.add(user); } boolean b = userService.saveBatch(users); System.out.println("result:"+b); }
sql日志输出
==> Preparing: INSERT INTO user ( id, name, email ) VALUES ( ?, ?, ? ) ==> Parameters: 1532579686881243138(Long), name0(String), email0(String) ==> Parameters: 1532579687124512770(Long), name1(String), email1(String) ==> Parameters: 1532579687128707074(Long), name2(String), email2(String) ==> Parameters: 1532579687128707075(Long), name3(String), email3(String) ==> Parameters: 1532579687132901377(Long), name4(String), email4(String) ==> Parameters: 1532579687137095681(Long), name5(String), email5(String) ==> Parameters: 1532579687137095682(Long), name6(String), email6(String) ==> Parameters: 1532579687141289985(Long), name7(String), email7(String) ==> Parameters: 1532579687145484289(Long), name8(String), email8(String) ==> Parameters: 1532579687145484290(Long), name9(String), email9(String) result:true
注意,这里是一个个的insert into ,而不是一条(单个的sql语句进行循环添加)
MyBatis-Plus常用注解1 #
现在将mysql数据库表user名改为t_user 会提示下面的报错
Cause: java.sql.BatchUpdateException: Table 'mybatis_plus.user' doesn't exist
说明mybatis plus查询的时候会去找实体类名一样的表
使用@TableName(“t_user”) 设置实体类对应的表名
@Data @TableName("t_user") public class User { private Long id; private String name; private Integer age; private String email; }
- 修改后执行成功
统一添加
mybatis-plus: configuration: global-config: db-config: table-prefix: t_
指定主键名 假设现在把数据库列名和bean的属性名id改为uid,此时新增一条记录
Field 'uid' doesn't have a default value ; Field 'uid' doesn't have a default value; nested exception is java.sql.SQLException: Field 'uid' doesn't have a default value
- 说明此时没有为uid赋值
使用@TableId告诉mybatis-plus那个字段为主键,让mybatis-plus为他赋默认值
@Data public class User { @TableId private Long uid; private String name; private Integer age; private String email; }
sql打印
==> Preparing: INSERT INTO t_user ( uid, name, age ) VALUES ( ?, ?, ? ) ==> Parameters: 1532582462671618050(Long), 张三(String), 18(Integer) <== Updates: 1
@TableId的value属性 #
用于指定绑定的主键的字段 假设此时将bean的主键属性名为id,数据库主键名是uid
此时运行,会提示
### SQL: INSERT INTO t_user ( id, name, age ) VALUES ( ?, ?, ? ) ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'id' in 'field list'
他会拿bean的属性来生成sql语句
加上@TableId(value=“uid”)后运行正常
@TableId的value属性 #
/** * 生成ID类型枚举类 * * @author hubin * @since 2015-11-10 */ @Getter public enum IdType { /** * 数据库ID自增 * <p>该类型请确保数据库设置了 ID自增 否则无效</p> */ AUTO(0), /** * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) */ NONE(1), /** * 用户输入ID * <p>该类型可以通过自己注册自动填充插件进行填充</p> */ INPUT(2), /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 分配ID (主键类型为number或string), * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法) * * @since 3.3.0 */ ASSIGN_ID(3), /** * 分配UUID (主键类型为 string) * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-","")) */ ASSIGN_UUID(4); private final int key; IdType(int key) { this.key = key; } }
//使用自增 @TableId(value="uid",type = IdType.AUTO ) private Long id;
然后将数据库主键设置为自动递增
新增后id为6
通过全局属性设置主键生成策略 #
全局配置设置
mybatis-plus: global-config: db-config: id-type: auto
雪花算法 #
- 数据库扩展方式:主从复制、业务分库、数据库分表
- 数据库拆分:水平拆分、垂直拆分
- 水平分表相对垂直分表,会引入更多的复杂性,比如要求唯一的数据id该怎么处理
- 可以给每个分表都给定一个范围大小,但是这样分段大小不好取
- 可以取模,但是如果增加了机器,原来的值主键(怎么处理是个问题
- 雪花算法,由Twitter公布的分布式主键生成算法 能够保证不同表的主键的不重复性,以及相同表的主键的有序性
- 核心思想
MyBatis-Plus常用注解2 #
此时数据库字段名为name,如果现在实体类的名字改为userName,那么会报错
INSERT INTO t_user ( user_name, age ) VALUES ( ?, ? )
又一次证明了MyBatis-plus通过实体类属性猜测数据库表的相关字段
使用@TableFiled来指定对应的字段名
@TableField(value = "name") private String userName;
查询
代码
@Test public void selectTest() { User user = userService.getById(5L); System.out.println("结果:" + user); }
sql执行语句
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE uid=? AND is_deleted_ly=0 ==> Parameters: 5(Long) <== Columns: id, userName, age, email, is_deleted_ly <== Row: 5, Billie, 24, email被修改了, 0 <== Total: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5e048149] 结果:User(id=5, userName=Billie, age=24, email=email被修改了, isDeletedLy=0)
逻辑删除(主要是允许数据的恢复) 这里增加一个isDeletedLy字段(这里为了测试,一般是isDeleted)
在User类添加下面的字段
@TableLogic private Integer isDeletedLy;
逻辑删除
代码
@Test public void deleteLogic() { boolean save = userService.removeBatchByIds(Arrays.asList(1L,2L,3L)); System.out.println("结果:" + save); }
sql执行语句 注意,这里使用了is_deleted_ly=0是因为在下面的步骤加入了逻辑删除注解
==> Preparing: UPDATE t_user SET is_deleted_ly=1 WHERE uid=? AND is_deleted_ly=0 ==> Parameters: 1(Long) ==> Parameters: 2(Long) ==> Parameters: 3(Long)
结果
条件构造器 #
结构
- 解释
- 解释
查看BaseWrapper源码
/** * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能 * <p>这个 Mapper 支持 id 泛型</p> * * @author hubin * @since 2016-01-23 */ public interface BaseMapper<T> extends Mapper<T> { /** * 插入一条记录 * * @param entity 实体对象 */ int insert(T entity); /** * 根据 ID 删除 * * @param id 主键ID */ int deleteById(Serializable id); /** * 根据实体(ID)删除 * * @param entity 实体对象 * @since 3.4.4 */ int deleteById(T entity); /** * 根据 columnMap 条件,删除记录 * * @param columnMap 表字段 map 对象 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根据 entity 条件,删除记录 * * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 删除(根据ID或实体 批量删除) * * @param idList 主键ID列表或实体列表(不能为 null 以及 empty) */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList); /** * 根据 ID 修改 * * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity); /** * 根据 whereEntity 条件,更新记录 * * @param entity 实体对象 (set 条件值,可以为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); /** * 根据 ID 查询 * * @param id 主键ID */ T selectById(Serializable id); /** * 查询(根据ID 批量查询) * * @param idList 主键ID列表(不能为 null 以及 empty) */ List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); /** * 查询(根据 columnMap 条件) * * @param columnMap 表字段 map 对象 */ List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根据 entity 条件,查询一条记录 * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p> * * @param queryWrapper 实体对象封装操作类(可以为 null) */ default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) { List<T> ts = this.selectList(queryWrapper); if (CollectionUtils.isNotEmpty(ts)) { if (ts.size() != 1) { throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records"); } return ts.get(0); } return null; } /** * 根据 Wrapper 条件,判断是否存在记录 * * @param queryWrapper 实体对象封装操作类 * @return */ default boolean exists(Wrapper<T> queryWrapper) { Long count = this.selectCount(queryWrapper); return null != count && count > 0; } /** * 根据 Wrapper 条件,查询总记录数 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * <p>注意: 只返回第一个字段的值</p> * * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录(并翻页) * * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录(并翻页) * * @param page 分页查询条件 * @param queryWrapper 实体对象封装操作类 */ <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); }
Wrapper条件组装
queryWrapper测试
@Test public void test01() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); //链式结构调用 userQueryWrapper.like("name", "a") .between("age", 10, 30) .isNotNull("email"); List<User> users = userMapper.selectList(userQueryWrapper); users.forEach(System.out::println); }
sql日志打印
//注意,这里出现了逻辑删除条件 ==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL) ==> Parameters: %a%(String), 10(Integer), 30(Integer) <== Columns: id, userName, age, email, is_deleted_ly <== Row: 4, Sandy, 21, test4@baomidou.com, 0 <== Row: 5, Billiea, 24, email被修改了, 0 <== Total: 2 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@19650aa6] User(id=4, userName=Sandy, age=21, email=test4@baomidou.com, isDeletedLy=0) User(id=5, userName=Billiea, age=24, email=email被修改了, isDeletedLy=0)
使用排序
@Test public void test02() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.orderByDesc("age") .orderByAsc("uid"); List<User> users = userMapper.selectList(userQueryWrapper); users.forEach(System.out::println); }
sql日志打印
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 ORDER BY age DESC,uid ASC ==> Parameters: <== Columns: id, userName, age, email, is_deleted_ly <== Row: 7, 张三6, 38, test6@baomidou.com, 0 <== Row: 5, Billiea, 24, email被修改了, 0 <== Row: 4, Sandy, 21, test4@baomidou.com, 0 <== Row: 6, 张三5, 18, test5@baomidou.com, 0 <== Row: 8, 张三a, 18, null, 0 <== Total: 5 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7158daf2] User(id=7, userName=张三6, age=38, email=test6@baomidou.com, isDeletedLy=0) User(id=5, userName=Billiea, age=24, email=email被修改了, isDeletedLy=0) User(id=4, userName=Sandy, age=21, email=test4@baomidou.com, isDeletedLy=0) User(id=6, userName=张三5, age=18, email=test5@baomidou.com, isDeletedLy=0) User(id=8, userName=张三a, age=18, email=null, isDeletedLy=0)
条件逻辑删除
代码
@Test public void test03() { QueryWrapper<User> userQueryWrapper=new QueryWrapper<>(); userQueryWrapper.isNull("email"); int deleted = userMapper.delete(userQueryWrapper); System.out.println(deleted); }
sql日志输出
==> Preparing: UPDATE t_user SET is_deleted_ly=1 WHERE is_deleted_ly=0 AND (email IS NULL) ==> Parameters: <== Updates: 1
修改
@Test public void test04() { QueryWrapper<User> userQueryWrapper=new QueryWrapper<>(); //(age>23且用户名包含a) 或 (邮箱为null) userQueryWrapper.gt("age",23) .like("name","a") .or() .isNull("email"); User user=new User(); user.setUserName("被修改了"); int deleted = userMapper.update(user,userQueryWrapper); System.out.println(deleted); }
sql日志打印
==> Preparing: UPDATE t_user SET name=? WHERE is_deleted_ly=0 AND (age > ? AND name LIKE ? OR email IS NULL) ==> Parameters: 被修改了(String), 23(Integer), %a%(String) <== Updates: 1
条件优先级
@Test public void test05() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); //(age>23且用户名包含a) 或 (邮箱为null) userQueryWrapper .like("name", "a") //and里面是一个条件构造器 .and( userQueryWrapper1 -> userQueryWrapper1.gt("age", 20) .or() .isNull("email") ); User user = new User(); user.setUserName("被修改了"); int deleted = userMapper.update(user, userQueryWrapper); System.out.println(deleted); }
sql日志输出
==> Preparing: UPDATE t_user SET name=? WHERE is_deleted_ly=0 AND (name LIKE ? AND (age > ? OR email IS NULL)) ==> Parameters: 被修改了(String), %a%(String), 20(Integer) <== Updates: 1
注意 or也有优先级的参数
只查询某些字段
@Test public void test06() { QueryWrapper<User> userQueryWrapper =new QueryWrapper<>(); userQueryWrapper.select("uid","name"); List<Map<String, Object>> maps = userMapper.selectMaps(userQueryWrapper); System.out.println(maps); }
sql输出
==> Preparing: SELECT uid,name FROM t_user WHERE is_deleted_ly=0 ==> Parameters: <== Columns: uid, name <== Row: 4, 被修改了 <== Row: 5, 被修改了 <== Row: 6, 张三5 <== Row: 7, 张三6 <== Total: 4
子查询 假设需要完整下面的sql查询
代码
@Test public void test7(){ //查询id小于等于100 QueryWrapper<User> userQueryWrapper=new QueryWrapper<>(); userQueryWrapper.inSql("uid", "select uid from t_user where uid <= 100"); List<User> users = userMapper.selectList(userQueryWrapper); users.forEach(System.out::println); }
sql输出
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (uid IN (select uid from t_user where uid <= 100)) ==> Parameters: <== Columns: id, userName, age, email, is_deleted_ly <== Row: 4, 被修改了, 21, test4@baomidou.com, 0 <== Row: 5, 被修改了, 24, email被修改了, 0 <== Row: 6, 张三5, 18, test5@baomidou.com, 0 <== Row: 7, 张三6, 38, test6@baomidou.com, 0 <== Total: 4
UpdateWrapper
@Test public void test8(){ //(age>23且用户名包含a) 或 (邮箱为null) UpdateWrapper<User> updateWrapper=new UpdateWrapper<>(); updateWrapper.like("name","a") .and(userUpdateWrapper -> userUpdateWrapper.gt("age",23).or().isNotNull("email")); updateWrapper.set("name","小黑").set("email","abc@ly.com"); userMapper.update(null,updateWrapper); }
sql日志输出
==> Preparing: UPDATE t_user SET name=?,email=? WHERE is_deleted_ly=0 AND (name LIKE ? AND (age > ? OR email IS NOT NULL)) ==> Parameters: 小黑(String), abc@ly.com(String), %a%(String), 23(Integer) <== Updates: 0
模拟用户操作组装条件
@Test public void test9(){ String username=""; Integer ageBegin=null; Integer ageEnd=30; QueryWrapper<User> queryWrapper=new QueryWrapper<>(); if(StringUtils.isNotBlank(username)){ queryWrapper.like("user_name",username); } if( ageBegin!=null){ queryWrapper.gt("age",ageBegin); } if( ageEnd!=null){ queryWrapper.le("age",ageEnd); } userMapper.selectList(queryWrapper); }
sql日志打印
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (age <= ?) ==> Parameters: 30(Integer) <== Columns: id, userName, age, email, is_deleted_ly <== Row: 4, 被修改了, 21, test4@baomidou.com, 0 <== Row: 5, 被修改了, 24, email被修改了, 0 <== Row: 6, 张三5, 18, test5@baomidou.com, 0 <== Total: 3
使用condition处理条件
@Test public void test10(){ String username="abc"; Integer ageBegin=null; Integer ageEnd=30; QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.like(StringUtils.isNotBlank(username),"name",username) .ge(ageBegin!=null,"age",ageBegin); userMapper.selectList(queryWrapper); }
sql日志输出
==> Preparing: SELECT uid AS id,name AS userName,age,email,is_deleted_ly FROM t_user WHERE is_deleted_ly=0 AND (name LIKE ?) ==> Parameters: %abc%(String) <== Total: 0