还在用MyBatis写CRUD?这款神器帮你5分钟写后台管理基本功能4️⃣

举报
XiaoLin_Java 发表于 2022/02/22 15:14:56 2022/02/22
【摘要】 六、通用Service接口 6.1、传统方式    在以前的业务层中,我们需要写接口和实现类,其中有不少的接口都是重复的CRUD,没有任何技术含量。 6.1.1、Service接口public interface EmployeeService { void save(Employee employee); void update(Employee employee); ...

六、通用Service接口

6.1、传统方式

    在以前的业务层中,我们需要写接口和实现类,其中有不少的接口都是重复的CRUD,没有任何技术含量。

6.1.1、Service接口

public interface EmployeeService {
    void save(Employee employee);
    void update(Employee employee);
    void delete(Long id);
    Employee get(Long id);
    List<Employee> list();
}

6.1.2、ServiceImpl

@Service
public class EmployeeServiceImpl implements EmployeeService {
    @Autowired
    private EmployeeMapper mapper;

    @Override
    public void save(Employee employee) {
        mapper.insert(employee);
    }

    @Override
    public void update(Employee employee) {
        mapper.updateById(employee);  //必须全量更新
    }

    @Override
    public void delete(Long id) {
        mapper.deleteById(id);
    }

    @Override
    public Employee get(Long id) {
        return mapper.selectById(id);
    }

    @Override
    public List<Employee> list() {
        return mapper.selectList(null);
    }
}

6.2、MyBatis-Plus的通用Service接口

    既然需要重复写那么多没有技术含量的代码,那么肯定MyBatis-Plus会帮我们做好,我们只需要简单两部即可使用MyBatis-Plus给我们写好的CRUD方法,他会自动调用mapper接口方法。

  1. 自定义服务接口继承IService接口,其中泛型是实体类对象
public interface IEmployeeService extends IService<Employee> {
}
  1. 服务接口实现类集成IService接口实现类ServiceImpl同时实现自定义接口,泛型一是实体类的mapper接口,泛型二是实体类。
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
}

6.2.1、常用方法

  1. getBaseMapper():获取引用的XxxxMapper对象。
  2. getOne(wrapper):指定条件查询单个, 结果数据超过1个报错
  3. list(wrapper):指定条件查询多个。

6.2.2、分页

    分页所用的方法是:page(page, wrapper),他也可以配合高级查询一起。

6.2.2.1、配置分页插件

    我们需要在配置类中配置分页插件。

//分页

    @Bean

    public MybatisPlusInterceptor mybatisPlusInterceptor() {

        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);

        paginationInnerInterceptor.setOverflow(true); //合理化

        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        return interceptor;

    }

6.2.2.2、编写分页代码

    MyBatis-Plus的分页对象是IPage,分页信息封装对象,里面有各种分页相关信息等价于之前的PageInfo。

//需求:查询第2页员工信息, 每页显示3条, 按id排序
@Test
public void testPage(){
    EmployeeQuery qo = new EmployeeQuery();
    qo.setPageSize(3);
    qo.setCurrentPage(2);
    IPage<Employee> page = employeeService.query(qo);
    System.out.println("当前页:" + page.getCurrent());
    System.out.println("总页数:" + page.getPages());
    System.out.println("每页显示条数:" + page.getSize());
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("当前页显示记录:" + page.getRecords());
}

七、ActiveRecord

7.1、什么是ActiveRecord

    ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。

    ActiveRecord的主要思想是:

  1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段
    在类中都有相应的Field。
  2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD。
  3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;

    ActiveRecord(简称AR)一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,对于ActiveRecord 往往只能感叹其优雅,所以我们也在 AR 道路上进行了一定的探索,喜欢大家能够喜欢。

7.2、开启AR之旅

    在MP中,开启AR非常简单,只需要将实体对象继承Model即可。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee extends Model<Employee> {

  @TableId(type = IdType.AUTO)
  private Long id;
  private String username;
  private String name;
  private String password;
  private String email;
  private Integer age;
  private Boolean admin;
  private Long deptId;
  private Boolean status;
}

7.2.1、查询所有

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test1 {

  @Autowired
  EmployeeMapper employeeMapper;

  /**
  * 用于测试查询所有
  */
  @Test
  public void test(){
    Employee employee = new Employee();
    List<Employee> employees = employee.selectAll();
    for (Employee employee1 : employees) {
      System.out.println(employee1);
    }
  }
}

7.2.2、根据id查询

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test2 {
  /**
   * 用于测试根据id查询
   */
  @Test
  public void test2(){
    Employee employee = new Employee();
    employee.setId(1L);
    System.out.println(employee.selectById());
  }
}

7.2.3、根据条件查询

  /**
  * 用于测试根据条件查询
  */
  @Test
  public void test4(){
    Employee employee = new Employee();
    QueryWrapper<Employee> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.le("password","123");
    List<Employee> employees = employee.selectList(userQueryWrapper);
    for (Employee employee1 : employees) {
      System.out.println(employee1);
    }
  }

7.2.4、新增数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAR {

  /**
   * 用于测试新增数据
   */
  @Test
  public void test3(){
    Employee employee = new Employee();
    employee.setId(11L);
    employee.setPassword("123");
    employee.insert();
  }
}

7.2.5、更新数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAR {
  /**
  * 用于测试更新
  */
  @Test
  public void test5(){
      Employee employee = new Employee();
      employee.setId(1L);
      employee.setPassword("123456789");
      employee.updateById();
  }
}

7.2.6、删除数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAR {
  /**
   * 用于测试删除
   */
  @Test
  public void test6(){
    Employee employee = new Employee();
    employee.setId(1L);
    employee.deleteById();
  }
}

八、插件机制

8.1、插件机制简介

    MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (prepare, parameterize, batch, update, query)

    我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的一些方法等。总体概括为:

  1. 拦截执行器的方法。
  2. 拦截参数的处理。
  3. 拦截结果集的处理。
  4. 拦截Sql语法构建的处理。

8.2、执行分析插件

    在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不适用于生产环境。我们首先要在启动类上配置:

  @Bean
  public SqlExplainInterceptor sqlExplainInterceptor(){
    SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
    List<ISqlParser> sqlParserList = new ArrayList<>();
	// 攻击 SQL 阻断解析器、加入解析链
    sqlParserList.add(new BlockAttackSqlParser());
    sqlExplainInterceptor.setSqlParserList(sqlParserList);
    return sqlExplainInterceptor;
  }

测试类:

@Test
public void testUpdate(){
Employee employee = new Employee();
employee.setPassword("123456");
int result = this.employeeMapper.update(employee, null);
System.out.println("result = " + result);
}

    执行后会发现控制台报错了,当执行全表更新时,会抛出异常,这样有效防止了一些误操作。

image-20210509210703826.png

8.3、乐观锁插件

8.3.1、适用场景

    当要更新一条记录的时候,希望这条记录没有被别人更新。

    乐观锁的实现方式:

  1. 取出记录时,获取当前version。
  2. 更新时,带上这个version。
  3. 执行更新时, set version = newVersion where version = oldVersion。
  4. 如果version不对,就更新失败。

8.3.2、插件配置

    我们需要在spring.xml中进行配置。

<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

    然后在singboot的启动类中配置。

Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
	return new OptimisticLockerInterceptor();
}

8.3.3、注解实体字段

为表添加version字段,并赋初始值为1

ALTER TABLE `employee`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';

为实体类添加version字段,并且添加@Version注解

@Version
private Integer version;

测试

@Test
public void testUpdate(){
	Employee employee = new Employee();
	user.setPassword("456789");
	user.setId(2L);
	user.setVersion(1); //设置version为1
	int result = this.userMapper.updateById(user);
	System.out.println("result = " + result);
}	

8.3.4、说明

  1. 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime。
  2. 整数类型下 newVersion = oldVersion + 1。
  3. newVersion 会回写到 entity 中仅支持 updateById(id) 与 update(entity, wrapper) 方法。
  4. 在 update(entity, wrapper) 方法下, wrapper 不能复用。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。