//用于扫描mapper接口所在的包 //或者所用于扫描指定包下的mapper接口 @MapperScan("com.atguigu.mapper")
@Repository public interface UserMapper extends BaseMapper{ // 根据id查询用户信息,返回map集合 Map selectMapById(Long id); }
@Test public void selectMapById(){ Mapmap = 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) QueryWrapperqueryWrapper = 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) QueryWrapperqueryWrapper = new QueryWrapper<>(); queryWrapper.isNull("email"); //条件构造器也可以构建删除语句的条件 int result = userMapper.delete(queryWrapper); //注意:如果加上了逻辑删除,那么得到的sql语句是update语句 System.out.println("受影响的行数:" + result); }
@Test public void test04() { QueryWrapperqueryWrapper = 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() { QueryWrapperqueryWrapper = 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 QueryWrapperqueryWrapper = 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)) QueryWrapperqueryWrapper = 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子句以及修改条件 UpdateWrapperupdateWrapper = 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; QueryWrapperqueryWrapper = 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; QueryWrapperqueryWrapper = 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; LambdaQueryWrapperlambdaQueryWrapper = 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子句 LambdaUpdateWrapperlambdaUpdateWrapper = 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(){ //设置分页参数:页码、显示的条数 Pagepage = 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(){ //设置分页参数 Pagepage = 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(); } }