//用于扫描mapper接口所在的包
//或者所用于扫描指定包下的mapper接口
@MapperScan("com.atguigu.mapper") @Repository public interface UserMapper extends BaseMapper{ // 根据id查询用户信息,返回map集合 Map selectMapById(Long id); }
@Test
public void selectMapById(){
Map map = userMapper.selectMapById(1L);
System.out.println(map);
} public interface UserMapper extends BaseMapper{ }
public interface UserService extends IService{ }
/** * ServiceImpl实现了IService,提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现 */ @Service public class UserServiceImpl extends ServiceImplimplements UserService { }

@Test
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email");
//注意1:username、ageemail为数据库表的字段名
//注意2:链式结构的调用
//注意3:between也可以用gt和lt
//注意4:逻辑删除is_deleted=0
List list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
} //按年龄降序查询用户,如果年龄相同则按id升序排列 //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC queryWrapper.orderByDesc("age").orderByAsc("id");
public void test03(){
//删除email为空的用户
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
//注意:如果加上了逻辑删除,那么得到的sql语句是update语句
System.out.println("受影响的行数:" + result);
} @Test
public void test04() {
QueryWrapper queryWrapper = new QueryWrapper<>();
//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("username", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
} @Test
public void test042() {
QueryWrapper queryWrapper = new QueryWrapper<>();
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
//lambda表达式内的逻辑优先运算
//从源码中可以看出:这里的" i(Consumer consumer 》Wrapper) "其实就是Wraapper(条件构造器)
queryWrapper.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
} @Test
public void test05() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List @Test
public void test06() {
//查询id小于等于3的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN (select id from t_user where id <= 3))
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
List list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
} @Test
public void test07() {
//将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
//组装set子句以及修改条件
UpdateWrapper updateWrapper = new UpdateWrapper<>();
//lambda表达式内的逻辑优先运算
updateWrapper
.set("age", 18)
.set("email", "user@atguigu.com")
.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
//UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
//User user = new User();
//user.setName("张三");
//int result = userMapper.update(user, updateWrapper);
//UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
int result = userMapper.update(null, updateWrapper);
System.out.println(result);
} @Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择(如下拉框、复选框))
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
List users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
} @Test
public void test08UseCondition() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
queryWrapper
.like(StringUtils.isNotBlank(username), "username", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
List users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
} @Test
public void test09() {
//定义查询条件,有可能为null(用户未输入)
String username = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
//避免使用字符串表示字段,防止运行时错误
//函数式接口。 这里通过函数直接访问到实体类属性对应的表字段名。
//其中:getName是属性,getAge是属性
lambdaQueryWrapper
.like(StringUtils.isNotBlank(username), User::getName, username)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
} @Test
public void test10() {
//组装set子句
LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@atguigu.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda表达式内的逻辑优先运算
User user = new User();
int result = userMapper.update(user, lambdaUpdateWrapper);
System.out.println("受影响的行数:" + result);
} @Getter // lombok自动生成get方法(不需要设置set方法)
public enum SexEnum {
MALE(1, "男"), // 值1
FEMALE(2, "女"); // 值2
@EnumValue // 要把枚举的哪个属性的值插入到数据中,就在这个属性上加上@EnumValue注解
private Integer sex; // 属性1
private String sexName; // 属性2
SexEnum(Integer sex, String sexName) { // 构造器
this.sex = sex;
this.sexName = sexName;
}
} @Test
public void test(){
User user = new User();
user.setName("Enum");
user.setAge(20);
//设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
user.setSex(SexEnum.MALE);
//INSERT INTO t_user ( username, age, sex ) VALUES ( ?, ?, ? )
//Parameters: Enum(String), 20(Integer), 1(Integer)
int result = userMapper.insert(user);
System.out.println("resutl="+result);
}
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.203.5.185:3306/fLearn?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
username: root
password: MySQL#567890
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置mybatis类型别名所对应的包
#此配置后,默认把类名当做此类型的别名,且不区分大小写(resultType="User"和resultType="user"都是指向User.java类型)
type-aliases-package: com.guigu.pojo
# 配置扫描通用枚举所在的包
type-enums-package: com.guigu.enums - @Configuration
- @MapperScan("com.guigu.mapper") //可以将启动类中的注解移到此处
- public class MybatisPlusConfig {
- /**
- *
- * 1 怎么来配置mybatis-plus中的插件?
- * 这里所需要的类型是MybatisPlusInterceptor,这是mybatis-plus的一个拦截器,用于配置mybatis-plus中的插件的。
- * 2 为什么要使用拦截器MybatisPlusInterceptor呢?
- * 这里边的原理和mybatis分页插件的功能是一样的,工作流程如下 :
- * (1)第一步:执行查询功能。
- * (2)第二步:拦截器对查询功能进行拦截。
- * (3)第三步:拦截器对查询功能的基础上做了额外的处理,达到分页的效果(功能)。
- * 3 对比配置mybatis中的插件
- * 用的也是拦截器的方式,如配置mybatis分页插件。
- *
- * @return MybatisPlusInterceptor
- */
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor() {
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- //参数:要配置的具体插件:new PaginationInnerInterceptor(DbType.MYSQL)是专门为mysql定制实现的内部的分页插件
- interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
- return interceptor;
- }
- }
@Test
public void testPage(){
//设置分页参数:页码、显示的条数
Page page = new Page<>(2, 3);
//第1个参数:分页对象
//第2个参数:条件构造器,如果设置为null,即查询所有
//返回值就是一个Page对象,即返回上面所new出来的page对象,数据都在其中
//对应的sql语句:SELECT id,name,age,email FROM user LIMIT ?
userMapper.selectPage(page, null);
//获取当前页数据
List list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
} @Repository public interface UserMapper extends BaseMapper{ /** * 需求:根据年龄查询用户列表,分页显示 * * 第一步:xml自定义分页,Mapper接口方法 * 第1步:如果想要mybatis-plus的分页插件来作用于我们自定义的sql语句的话,第一个参数必须得是一个分页对象:Page page。 * 第二步:因为Mapper接口方法有2个参数的话 * 方案1:使用mybatis提供的访问方式 * 方案2:也可以使用@param来设置命名参数,来规定参数的访问规则 * * @param page mybatis-plus提供的分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位。虽然在自定义的sql语句中不会用到Page page,但是如果想在自定义的sql语句中使用mybatis-plus的分页插件使用分页功能,那么第一个参数必须得是Page。 * @param age 年龄 * @return Page */ Page selectPageVo(@Param("page") Page page, @Param("age") Integer age); }
id,username,age,email
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.203.5.185:3306/fLearn?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
username: root
password: MySQL#567890
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置mybatis类型别名所对应的包
#此配置后,默认把类名当做此类型的别名,且不区分大小写(resultType="User"和resultType="user"都是指向User.java类型)
type-aliases-package: com.guigu.pojo @Test
public void testSelectPageVo(){
//设置分页参数
Page page = new Page<>(1, 5);
//sql语句:SELECT id,name,age,email FROM user WHERE age > ? LIMIT ?
userMapper.selectPageVo(page, 20);
//获取分页数据
List list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
} @SpringBootTest
public class LeguanTest {
@Autowired
private ProductMapper productMapper;
@Test
public void testConcurrentUpdate() {
//0、商品成本价80
//1、小李获取:当前价格
Product p1 = productMapper.selectById(1L);
System.out.println("小李取出的价格:" + p1.getPrice());
//2、小王获取:当前价格
Product p2 = productMapper.selectById(1L);
System.out.println("小王取出的价格:" + p2.getPrice());
//3、小李修改:将价格加了50元,存入了数据库
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改结果:" + result1);
//4、小王修改:将商品减了30元,存入了数据库
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改结果:" + result2);
//5、老板查询最后的结果:价格覆盖,最后的结果:70(小于成本价,亏)
Product p3 = productMapper.selectById(1L);
System.out.println("老板查询最后的结果:" + p3.getPrice());
}
}
@Data
@TableName("t_product")
public class Product {
private Long id;
private String name;
private Integer price;
@Version //标识:数据库表中,哪个字段做为乐观锁版本号字段(这里是version)
private Integer version;
} @SpringBootTest
public class LeguanTest {
@Autowired
private ProductMapper productMapper;
@Test
public void testConcurrentUpdate() {
//第1步:小李获获取:当前的价格,100
Product p1 = productMapper.selectById(1L);
//第2步:小王获获取:当前的价格,100
Product p2 = productMapper.selectById(1L);
//第3步:小李修改 + 50
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改的结果:" + result1);
//第4步:小王修改 - 30
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改的结果:" + result2);
if(result2 == 0){
//第5步:操作失败,重试:重新获取version并更新
p2 = productMapper.selectById(1L);p2.setPrice(p2.getPrice() - 30);
result2 = productMapper.updateById(p2);
}
System.out.println("小王修改重试的结果:" + result2);
//第6步:老板看价格,120,满意
Product p3 = productMapper.selectById(1L);
System.out.println("老板看价格:" + p3.getPrice());
}
}
spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、lombok、mysql-connector-java
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
username: root
password: root
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 
spring:
# 配置数据源信息
datasource:
dynamic:
# 设置默认的数据源或者说主数据源,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false未匹配到指定数据源时使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/fLearn?characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: MySQL#567890
#从数据源
slave_1:
url: jdbc:mysql://localhost:3306/fLearn2?characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: MySQL#567890 public class FastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://10.203.5.185:3306/fLearn?characterEncoding=utf-8&userSSL=false", "root", "MySQL#567890")
// 全局配置
.globalConfig(builder -> {
builder.author("atguigu") // 设置作者
//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件(代码重新且覆盖生成)
.outputDir("D://mybatis_plus"); // 指定输出目录
})
// 包设置
// 如下图所示,所有的代码都会生成到com.atguigu.mybatisplus包下。
.packageConfig(builder -> {
builder.parent("com.atguigu") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));// 设置映射文件XxxMapper.Xml的生成路径
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("t_user") // 设置需要逆向生成的表的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}