还在用MyBatis写CRUD?这款神器帮你5分钟写后台管理基本功能4️⃣
六、通用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接口方法。
- 自定义服务接口继承IService接口,其中泛型是实体类对象
public interface IEmployeeService extends IService<Employee> {
}
- 服务接口实现类集成IService接口实现类ServiceImpl同时实现自定义接口,泛型一是实体类的mapper接口,泛型二是实体类。
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
}
6.2.1、常用方法
- getBaseMapper():获取引用的XxxxMapper对象。
- getOne(wrapper):指定条件查询单个, 结果数据超过1个报错。
- 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的主要思想是:
- 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段
在类中都有相应的Field。 - ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD。
- 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 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的一些方法等。总体概括为:
- 拦截执行器的方法。
- 拦截参数的处理。
- 拦截结果集的处理。
- 拦截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);
}
执行后会发现控制台报错了,当执行全表更新时,会抛出异常,这样有效防止了一些误操作。
8.3、乐观锁插件
8.3.1、适用场景
当要更新一条记录的时候,希望这条记录没有被别人更新。
乐观锁的实现方式:
- 取出记录时,获取当前version。
- 更新时,带上这个version。
- 执行更新时, set version = newVersion where version = oldVersion。
- 如果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、说明
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime。
- 整数类型下 newVersion = oldVersion + 1。
- newVersion 会回写到 entity 中仅支持 updateById(id) 与 update(entity, wrapper) 方法。
- 在 update(entity, wrapper) 方法下, wrapper 不能复用。
- 点赞
- 收藏
- 关注作者
评论(0)