通用Mapper的定义
通用Mapper 是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。开发人员可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法,极其方便的使用MyBatis单表的增删改查。
本文下从如下三个方面来介绍:
1.SpringBoot如何整合通用mapper
2.如何使用通用Mapper的方法
3.通用方法怎么来的
SpringBoot如何整合通用mapper
第一步:引入依赖
<!--mapper需要依赖jpa--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--MyBatis 通用 Mapper--> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>1.1.4</version> </dependency> <!-- SpringBoot - MyBatis 逆向工程 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> <!-- mysql连接需要的类 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
如上我们需要引入四个依赖一个是jpa依赖,一个是通用Mapper自身的依赖,一个是MyBatis逆向工程需要的依赖,最后就是连接mysql的依赖。依赖引入之后,接下来就是配置逆向工程。(PS:逆向工程就是帮助我们生成Model,Dao以及XML)。
第二步:配置逆向工程
首先我们需要在resources目录下新建一个属性文件config.properties用来定义数据库连接,生成类的目标包等信息。定义如下:
#jdbcConnection 连接 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatisdemo?useUnicode=true&characterEncoding=utf8 jdbc.user=root jdbc.password=admin #targetProject是包所在的位置 #mac 下 targetProject=/Volumes/Develop/WorkSpace/auto_java/auto-mapper-demo/src/main/ #model的目标包名 modelTargetPackage=com.jay.model #dao的目标包名 daoTargetPackage=com.jay.mapper
前面我们新建的属性文件其实是给后面的XML文件用的,接下来我们就来看看XML文件generatorConfig.xml吧。
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--引入属性文件--> <properties resource="config.properties"/> <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat"> <property name="beginningDelimiter" value="`"/> <property name="endingDelimiter" value="`"/> <!--配置生成类的插件--> <plugin type="tk.mybatis.mapper.generator.MapperPlugin"> <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/> <property name="caseSensitive" value="true"/> </plugin> <!--连接数据库--> <jdbcConnection driverClass="${jdbc.driverClass}" connectionURL="${jdbc.url}" userId="${jdbc.user}" password="${jdbc.password}"> </jdbcConnection> <!--指定model的生成路径--> <javaModelGenerator targetPackage="${modelTargetPackage}" targetProject="${targetProject}/java"/> <!--指定mapper的生成路径--> <sqlMapGenerator targetPackage="mapper" targetProject="${targetProject}/resources"/> <!--指定dao的生成路径--> <javaClientGenerator targetPackage="${daoTargetPackage}" targetProject="${targetProject}/java" type="XMLMAPPER"/> <!--指定需要生成Model,Dao,Xml的数据表--> <table tableName="classroom"></table> </context> </generatorConfiguration>
需要注意的是定义的属性名要与属性文件中的一致,比如jdbc.driverClass
第三步:定义逆向工程的启动类
将相关的必要的配置弄好之后,接下来我们就来定义逆向工程的启动类。这个启动类的代码也相对比较简单,代码如下所示:
public class Generator { public static InputStream getResourceAsStream(String path){ return Thread.currentThread().getContextClassLoader().getResourceAsStream(path); } public static void main(String[] args) throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(getResourceAsStream("generatorConfig.xml")); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); for (String warning : warnings) { System.out.println(warning); } } }
第四步 测试逆向工程
准备工作都做好之后,接下来,我们就来测试生成model,dao,XML等文件吧。我们直接运行Generator类正常的话就可以得到生成的Model,Dao和XML,生成结果如下图所示:
怎么用?
如何调用方法?
说完了逆向工程,生成了我们需要的Dao类之后,接下来我们就要看看怎么使用了Dao类,调用其拥有的通用方法,其实调用也相当简单。下面我先给出一个调用示例:
我们定义了一个ClassroomServiceImpl业务类,并且实现了三个方法,分别是调用insert方法保存班级,调用selectByPrimaryKey方法根据主键查找班级,调用select方法根据班级名称查找班级。这些方法都是ClassroomMapper接口中自带的方法。
@Service public class ClassroomServiceImpl implements ClassroomService { @Autowired private ClassroomMapper classroomMapper; //保存班级 @Override public boolean saveClassroom(Classroom classroom) { int result = classroomMapper.insert(classroom); return result == 1 ? true : false; } //根据主键查找 @Override public Classroom getClassroomById(int id) { Classroom classroom = classroomMapper.selectByPrimaryKey(id); return classroom; } //根据某个字段查找 @Override public List<Classroom> getClassroomByName(String name) { Classroom param = new Classroom(); param.setName(name); List<Classroom> classrooms = classroomMapper.select(param); return classrooms; } }
然后,我们在启动类中添加一个扫描Mapper的注解@MapperScan,这个注解一定要加,不然会找不到Mapper接口 。
@SpringBootApplication //使之可以扫描到mapper接口 @MapperScan(basePackages = "com.jay.mapper") public class AutoMapperDemoApplication { public static void main(String[] args) { SpringApplication.run(AutoMapperDemoApplication.class, args); System.out.println("********启动项目成功"); } }
添加完相关的方法之后,最后就是添加测试类测试这些方法是否起作用了,
@SpringBootTest @RunWith(SpringRunner.class) @Transactional public class ClassroomServiceImplTest { @Autowired private ClassroomService classroomService; @Before public void setUp() { Classroom classroom = new Classroom(); classroom.setId(3); classroom.setClassId(3); classroom.setClassType("0"); classroom.setName("优秀的三班"); classroomService.saveClassroom(classroom); } //测试保存班级 @Test public void saveClassroom() throws Exception { Classroom classroom = new Classroom(); classroom.setId(4); classroom.setClassId(4); classroom.setClassType("1"); classroom.setName("优秀的四班"); boolean b = classroomService.saveClassroom(classroom); Assert.assertTrue(b); } //测试主键查询 @Test public void getClassroomById() throws Exception { Classroom classroomById = classroomService.getClassroomById(3); Assert.assertEquals("优秀的三班", classroomById.getName()); } //测试按照名称查询 @Test public void getClassroomByName() throws Exception { List<Classroom> classrooms = classroomService.getClassroomByName("优秀的三班"); Assert.assertEquals("优秀的三班", classrooms.get(0).getName()); } }
运行测试用例的结果如下:
方法哪里来的
说完了怎么用之后,通用Mapper的使用方法我们就说完了。接下来我们来看看ClassroomMapper中的方法是怎么来的吧,不看代码我们想当然的会以为这些方法会在ClassroomMapper接口中有定义,但事实是这样子的么? 这里先留一个彩蛋,请听我慢慢道来。
首先,我们来看看生成的XML文件。
我们很惊奇的发现XML里面竟然一条SQL语句都莫得,那对应的ClassroomMapper接口中也应该没有定义相关的方法呀。那问题来了,这些个增删改查的方法是哪里来的呢?
这有点让人摸不着头脑,百思不得姐。话不多说,还是让我们接着看看
ClassroomMapper接口吧! public interface ClassroomMapper extends Mapper<Classroom> { }
果然,一个方法都没有定义,只是继承了一个Mapper<Classroom>接口,那么这些方法就有应该来源于是Mapper接口了,为了验证我们的猜测,让我们一起来看看这个自带神力的Mapper接口吧。
Mapper
/** * 通用Mapper接口,其他接口继承该接口即可 * <p/> * <p>这是一个例子,自己扩展时可以参考</p> * <p/> * <p>项目地址 : <a href="https://github.com/abel533/Mapper" target="_blank">https://github.com/abel533/Mapper</a></p> * * @param <T> 不能为空 * @author liuzh */ public interface Mapper<T> extends BaseMapper<T>, ExampleMapper<T>, RowBoundsMapper<T>, Marker { }
哦豁,这个Mapper接口里也没有金屋藏娇呀,同样的它也是一个方法都没有定义,只是很单纯的继承了BaseMapper,ExampleMapper,RowBoundsMapper和Marker这四个接口,其中Marker只是一个标记接口,暂不在我们的介绍范围内!(PS:不得不说这个框架的作者牛逼呀)按照上面分析的思路我们很自然的认为,这些方法应该是由上面的三个接口定义的。那么就让它们一一粉墨登场吧。
首先出场的是我们的 BaseMapper接口。
BaseMapper接口
/** * 通用Mapper接口,其他接口继承该接口即可 * <p/> * <p>这是一个例子,自己扩展时可以参考</p> * <p/> * <p>项目地址 : <a href="https://github.com/abel533/Mapper" target="_blank">https://github.com/abel533/Mapper</a></p> * * @param <T> 不能为空 * @author liuzh */ public interface BaseMapper<T> extends BaseSelectMapper<T>, BaseInsertMapper<T>, BaseUpdateMapper<T>, BaseDeleteMapper<T> { }
朋友们,这完全是跟Mapper接口一模一样的套路呀。BaseMapper接口里面也是啥方法都没有定义,同样是继承了好几个接口,一个是BaseSelectMapper接口,一个是BaseInsertMapper接口,一个是BaseUpdateMapper接口,最后一个是BaseDeleteMapper。从接口的命名我们不难猜测出这些个接口的作用。下面我们就分别介绍下他们。
从BaseSelectMapper 接口的定义来看,其主要就是定义基础的查询,最简单的查询请交就给它吧。我们进入它的身体里一探究竟。
/** * 通用Mapper接口,基础查询 * * @param <T> 不能为空 * @author liuzh */ public interface BaseSelectMapper<T> extends SelectOneMapper<T>, SelectMapper<T>, SelectAllMapper<T>, SelectCountMapper<T>, SelectByPrimaryKeyMapper<T>, ExistsWithPrimaryKeyMapper<T> { }
套路真的好深呀,进到BaseSelectMapper接还是没有看到有方法定义,同样的只是继承好些个接口。不要心急,不要烦躁,我们马上就可以看到真正干活的接口了。这里我选取SelectMapper接口做一个剖析。
/** * 通用Mapper接口,查询 * * @param <T> 不能为空 * @author liuzh */ public interface SelectMapper<T> { /** * 根据实体中的属性值进行查询,查询条件使用等号 * * @param record * @return */ @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL") List<T> select(T record); }
看到这儿朋友们是不是恍然大悟,这不就是前面classroomMapper.select(param)调用的这个方法么!!!这个方法是根据动态SQL(dynamicSQL)来执行SQL语句的。其只能根据实体中的属性值进行查询,并且查询条件使用等号。其他的接口也是一样的道理的。在此就不在赘述了。
总之就是一个接口里定义一个方法,并且这个方法的SQL是动态生成的。
ExampleMapper接口
说完了BaseMapper接口,接下来就让我们来看看ExampleMapper接口吧,这个接口的查询条件是传入Example来查询。例如:
可以直接带关键字查询,类似于动态的拼接SQL的方式。
@Override public List<Classroom> getClassByExample() { Example classRoomExample = new Example(Classroom.class); Example.Criteria criteria = classRoomExample.createCriteria(); criteria.andLike("name", "%班%"); criteria.andEqualTo("classType", "0"); List<Classroom> classrooms = classroomMapper.selectByExample(classRoomExample); return classrooms; }
同样的ExampleMapper接口也是继承了SelectByExampleMapper,SelectCountByExampleMapper,DeleteByExampleMapper,UpdateByExampleMapper已经UpdateByExampleSelectiveMapper这五个接口,每个接口里面都定义了一个方法。也是通过动态SQL来生成执行语句。在此就不在赘述了。
/** * 通用Mapper接口,Example查询 * * @param <T> 不能为空 * @author liuzh */ public interface ExampleMapper<T> extends SelectByExampleMapper<T>, SelectCountByExampleMapper<T>, DeleteByExampleMapper<T>, UpdateByExampleMapper<T>, UpdateByExampleSelectiveMapper<T> { }
最后,我们来看下RowBoundsMapper接口,这个接口继承了SelectByExampleRowBoundsMapper接口和SelectRowBoundsMapper接口。主要是配合分页插件PageHelper来实现分页查询的。分页查询可以详细看Mybatis-PageHelper分页插件的使用与相关原理分析。
RowBoundsMapper接口
/** * 通用Mapper接口,带RowBounds参数的查询 * <p/> * 配合分页插件PageHelper可以实现物理分页 * <p/> * PageHelper - http://git.oschina.net/free/Mybatis_PageHelper * * @param <T> 不能为空 * @author liuzh */ public interface RowBoundsMapper<T> extends SelectByExampleRowBoundsMapper<T>, SelectRowBoundsMapper<T> { }
总结
本文首先介绍了如何在SpringBoot中整合通用的Mapper,其中详细介绍了逆向工程的使用。接着就是介绍了通用方法的调用,通用Mapper运用动态SQL的方式,省去了编写SQL的繁琐,实现了单表的基本的增删改查方法,极大的提高了对单表操作的效率。最后就是介绍了通用Mapper内置的方法。希望对读者朋友们有所帮助。如有疑问欢迎与我联系。
参考
https://github.com/abel533/Mapper