浅了解mybatis-plus给个泛型就可以使用的原理

前言

在看一个项目源码的时候发现一个很有趣的封装 是对mybatis-plus进行了二次封装 来达到一个减少controller重复crud代码量的一个封装 于是抱着学习的态度学习了一下

首先我们知道对于一个javaweb后端来一般都有三层 controller service dao 不同的业务会有很多相似的代码,比如crud 一般来说大部分业务都有一些添加删除修改的功能 这些功能大多又有着相同的套路,就是说完全可以通过改变量名的方式来重复利用这些代码,利用mybatis-plus我们可以很简单实现从数据库中取得数据和编写service的crud(甚至不需要写代码 只需要继承一个类就是) 但是没有提供controller层 那么如何利用 类似的思想来实现controller层的呢?

先说结论 (压缩版:创建一个基类 利用的泛型来调用方法 使用的时候直接把泛型赋值成下一层对应的对象 这样就相当于基类就是模板 使用的时候套个模板就行)我们创建基类的时候设计可以传入的泛型包括 下一层的对象 和实体类 基类中方法都是通过下一层对象的泛型来调用的 当我们继承这个基类的时候给出两个泛型参数 然后程序就会将基类中的泛型参数变成我们传入的对象,那么其实就是下一层的那个对象来调用的这些方法 要实现先必须实现传入泛型的限制 我们可以设置 < M extends Mapper> 所有的公共方法都由Mapper来定义 这样只要是继承 Mapper的对象都会有这些公共方法就不会报错

这里我们写一个demo来模拟一下

首先我们会编写一个类来模拟一个mybatis-plus的Mapper来提供数据这里为了简单并没有对Mapper添加泛型 故所有类调用返回的都是一个结果

public interface Mapper{
    default String get(){
        return "我是BaseMapper里的get-----------" ;
    }
}

首先我们先建立一个包来模拟我们封装的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AShrAkum-1642484343638)(C:\Users\sun\AppData\Local\Temp\1642429976039.png)]

实体类的抽象基类 所有的实体类都需要基础该类

public abstract class BaseEntity {
    protected int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

当我们的BaseDao继承了Mapper接口自然就有了Mapper中默认实现的方法

package kj.dao;

import kj.bean.BaseEntity;
import mybatisplus.BaseMapper;

public interface BaseDao extends Mapper{

}

现在如果有一个我们业务的dao继承BaseDao那么自然就拥有了 Mapper中的默认方法

这是一个工具类目的就是获取当前类的父级的泛型类型,就是说调用这个工具中的方法就可以获得我们传入的 T 的类型

package kj.uitl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 通用实效
 *   用来获取实现父类
 * @author sun
 * @date 2022/01/15
 */
public class GenericUtil {
//获取第一个泛型类型
   public static <T> Class<T> getSuperGenericClass(Class<?> clz) {
      Class<T> result = null;
      //得到当前对象的父类"泛型类型"(也叫参数化类型)
      //superclass == GenericDao<Dept>成为参数化类型
      //superclass == BaseDao不是参数化类型
      Type superclass = clz.getGenericSuperclass();
      //判断类型是否为参数化类型
      if (superclass instanceof ParameterizedType) {
         //把父类类型转换成参数化类型(泛型类型)
         //只有ParameterizedType才能通过getActualTypeArguments得到参数
         ParameterizedType parameterizedType = (ParameterizedType) superclass;
         //得到参数化类型类型的参数
         //types == GenericDao<Dept>的"<Dept>"参数
         Type[] types = parameterizedType.getActualTypeArguments();
         //返回子类传递的类型
         result = (Class<T>) types[0];
      }
      return result;
   }
//获取第二个泛型类型
   public static <T> Class<T> getSuperGenericClass2(Class<?> clz) {
      Class<T> result = null;
      //得到当前对象的父类"泛型类型"(也叫参数化类型)
      //superclass == GenericDao<Dept>成为参数化类型
      //superclass == BaseDao不是参数化类型
      Type superclass = clz.getGenericSuperclass();
      //判断类型是否为参数化类型
      if (superclass instanceof ParameterizedType) {
         //把父类类型转换成参数化类型(泛型类型)
         //只有ParameterizedType才能通过getActualTypeArguments得到参数
         ParameterizedType parameterizedType = (ParameterizedType) superclass;
         //得到参数化类型类型的参数
         //types == GenericDao<Dept>的"<Dept>"参数
         Type[] types = parameterizedType.getActualTypeArguments();
         //返回子类传递的类型
         result = (Class<T>) types[1];
      }
      return result;
   }


}

随口再提一下给自己长点印象 现在有两个对象有这样的关系

 B extends A<T>

现在 在A中的 this指向是哪个类呢? 其实我记得不清记推断应该是B 但作为一个为新时代青年咋能迷迷糊糊呢 所以就写了一个测试 指向的 new的实例 (基础知识还是要多复习多补啊)

public class Text {
    public static void main(String[] args) {
        B b = new B();  //A   B@1b6d3586
    }
}
class A {
    public A(){
        System.out.println("A   "+this);
    }
}
class B extends A{
	
}

这个我们Service层的封装 BaseServiceImpl是所有service的基类

通过继承该类在传入相应的泛型就可以得到其方法的实现

package kj.service;

import kj.bean.BaseEntity;
import kj.uitl.GenericUtil;
import kj.dao.Dao;

public class BaseServiceImpl<T extends BaseEntity, M extends BaseDao> {
        M dao;
        Class<Object> t;
    public BaseServiceImpl() {
        //获取到泛型 M 的类型 创建出对象 这里因为用原生写的 如果使用spring就可以通过IOC注入
        Class<M> class2 = GenericUtil.getSuperGenericClass2(this.getClass());
        try {
            this.dao= class2.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        this.t = GenericUtil.getSuperGenericClass(this.getClass());
    }
	//在这里打印出t的类型
    public String get(){
        System.out.println("我是BaseServiceImpl里的 get----------"+t.toString());
       return dao.get();
    }

} 

假如现在一个serviceImpl继承了BaseServiceImpl 那么就会出现

serviceImpl extends BaseServiceImpl<实体类 Dog, 实际业务dao的实现类 DaoImpl>

我们new并调用serviceImpl.get() 会发生什么呢首先会去实例化 那么实例化的父类自然也就初始化了 别忘了 我们上面的测试哦 这个时候BaseServiceImpl的this指向的serviceImpl的 所以我们就获取到了 实体类 dao的实现类 并且通过 BaseServiceImpl 的构造函数赋值给 他的M dao;
Class t; 这个时候 不管是 dao 还是t 都是通过我们传入的类型了 t 做一些返回值什么的 调用的方法也就换成了我们传入的daoImpl来调用了 当然我们这里的dao没有做太多处理 但是道理是相同的 我们可以通过给dao层添加一些泛型来达到同样的效果 对controller 也是一样

下面就是我们的验证环节

bean

public class Dog extends BaseEntity {
}
public  class User extends BaseEntity {
}

dao

public interface DogBaseDao extends BaseDao {
}
public interface UserBaseDao extends BaseDao {
}

daoImpl

public class DogBaseDaoImpl implements DogBaseDao {
}
public class UserBaseDaoImpl implements UserBaseDao {
}

service

public class DogServiceImpl extends BaseServiceImpl<Dog, DogBaseDaoImpl> {
}
public class UserServiceImpl extends BaseServiceImpl<User, UserBaseDaoImpl> {
}

测试类

public class Main {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        String s = userService.get();
        System.out.println(s);
        DogServiceImpl dogService = new DogServiceImpl();
        String s1 = dogService.get();
        System.out.println(s1);
    }
}

打印结果:

我是BaseServiceImpl里的 get----------class test.bean.User
我是BaseMapper里的get-----------
我是BaseServiceImpl里的 get----------class test.bean.Dog
我是BaseMapper里的get-----------

我们如果对controller层进行同样的操作 写一个基类把大家都有的方法提取出来在去继承并且传入自己的ServiceImpl就可以不用写controller层crud (路由区别可以在子类加一个@RequestMapping)

public abstract class BaseController<S extends ICrudService<T>, T extends BaseEntity> {

    @Autowired
    protected S service;

    protected Logger LOG = LoggerFactory.getLogger(this.getClass());

    protected Class<T> entityClass;

    public BaseController(){
        this.entityClass = GenericUtil.getSuperGenericClass2(this.getClass());
    }

    /**
     * 加载
     *
     * @param id
     * @return
     * @throws Exception
     */
    @ApiOperation(value="加载", notes="根据ID加载")
    @GetMapping("/edit/{id}")
    public T edit(@PathVariable Long id) throws Exception {
        T entity = service.getById(id);
        afterEdit(entity);
        return entity;
    }

    /**
     * 分页查询
     * @param entity
     * @param page
     * @param rows
     * @return
     */
    @ApiOperation(value="分页查询", notes="分页查询")
    @PostMapping("/list-page")
    public IPage<T> listPage(T entity,
                          @RequestParam(name = "page", defaultValue = "1", required = false) long page,
                          @RequestParam(name = "rows", defaultValue = "10", required = false) long rows) {
        Page<T> p = new Page<>(page, rows);
        IPage<T> result = service.listPage(p, entity);
        return result;
    }

    /**
     * 查询所有
     * @return
     */
    @ApiOperation(value="查询", notes="查询所有")
    @RequestMapping(value = "/list", method = {RequestMethod.POST, RequestMethod.GET})
    public List<T> listAll() {
        List<T> list = service.list();
        return list;
    }

    /**
     * 增加,修改
     */
    @ApiOperation(value="保存", notes="ID存在修改,不存在添加")
    @PostMapping("/save")
    public ResponseBean save(T entity) throws Exception {
        ResponseBean rm = new ResponseBean();
        try {
            beforeSave(entity); //保存前处理实体类
            service.saveOrUpdate(entity);
            rm.setModel(entity);
        } catch (Exception e) {
            e.printStackTrace();
            rm.setSuccess(false);
            rm.setMsg("保存失败");
        }
        return rm;
    }

    /**
     * 删除
     */
    @ApiOperation(value="删除", notes="根据ID删除")
    @GetMapping("/delete/{id}")
    public ResponseBean delete(@PathVariable Long id) throws Exception {
        ResponseBean rm = new ResponseBean();
        try {
            service.removeById(id);
            rm.setModel(null);
        } catch (Exception e) {
            e.printStackTrace();
            rm.setSuccess(false);
            rm.setMsg("保存失败");
        }
        return rm;
    }

    /**
     * 批量删除
     */
    @ApiOperation(value="删除", notes="批量删除")
    @RequestMapping(value = "/delete", method = {RequestMethod.POST, RequestMethod.GET})
    public ResponseBean delete(@RequestParam List<Long> ids) {
        ResponseBean rm = new ResponseBean();
        try {
            service.removeByIds(ids);
        } catch (Exception e) {
            e.printStackTrace();
            rm.setMsg("删除失败");
            rm.setSuccess(false);
        }
        return rm;
    }

    /**
     * 保存前执行
     * @param entity
     * @throws Exception
     */
    public void beforeSave(T entity) throws Exception {
    }

    /**
     * 模板方法:在加载后执行
     * @param entity
     */
    public void afterEdit(T entity) {

    }

}

rintStackTrace();
rm.setMsg(“删除失败”);
rm.setSuccess(false);
}
return rm;
}

/**
 * 保存前执行
 * @param entity
 * @throws Exception
 */
public void beforeSave(T entity) throws Exception {
}

/**
 * 模板方法:在加载后执行
 * @param entity
 */
public void afterEdit(T entity) {

}

}


  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 MyBatis-Plus 可以使 Java 项目的数据库操作变得更加简单和高效。下面是使用 MyBatis-Plus 的基本步骤: 1. 添加 MyBatis-Plus 依赖 在项目的 `pom.xml` 文件中添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本号</version> </dependency> ``` 2. 配置数据源 在项目的 `application.properties` 或 `application.yml` 文件中配置数据源。 ```yml spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver ``` 3. 创建实体类 创建对应数据库表的实体类,并使用 MyBatis-Plus 提供的注解标记主键和表名。 ```java @Data @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; } ``` 4. 创建 Mapper 创建继承 `BaseMapper` 接口的 Mapper 接口,并指定泛型为对应的实体类。 ```java public interface UserMapper extends BaseMapper<User> { } ``` 5. 测试 在测试类中注入 `UserMapper` 并调用相应方法进行测试。 ```java @RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusTest { @Autowired private UserMapper userMapper; @Test public void insert() { User user = new User(); user.setName("test"); user.setAge(18); int result = userMapper.insert(user); System.out.println(result); } @Test public void select() { List<User> userList = userMapper.selectList(null); System.out.println(userList); } } ``` 以上就是使用 MyBatis-Plus 的基本步骤,可以根据具体需求进行更多的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值