SpringBoot教程(十一) | SpringBoot集成Mybatis

网友投稿 806 2022-05-30

上一篇文章我们介绍了springboot集成JdbcTemplate.简单体验了一下JdbcTemplate框架的用法,今天的内容比较重要,我们来介绍一下SpringBoot集成Mybatis的步骤。

1、 Mybatis 介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集。MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

Mybatis特点:

1、Mybatis实现了接口绑定,使用更加方便。

2、对象关系映射的改进,效率更高

3、MyBatis采用功能强大的基于OGNL的表达式来消除其他元素。

优点:

1、简单易学

mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活

mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合

通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的orm字段关系映射

5、提供对象关系映射标签,支持对象关系组建维护

6、提供xml标签,支持编写动态sql。

缺点:

1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。

2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

4、二级缓存机制不佳

上面的内容都是从网上拷贝的,因为我想大多数的人应该都用过mybatis,并且我们本文的侧重点主要是SpringBoot的集成方式,而不是从头介绍Mybatis,如果大家对Mybatis的使用不太了解,建议先去学习一下Mybatis的用法。

2、集成步骤

接下来我们开始进行集成。为了方便操作,由于我们上次刚刚集成过JdbcTemplate, 代码中共存多个DAO层框架可能会有问题,我们在原有项目基础上拉取一个新的分支来进行开发,分支名就叫 feature/mybaits。完成的代码都会托管到gitCode上,大家可在文末获取地址。

2.1 引入依赖

首先我们先引入Mybatis所需依赖,mybatis本身已经提供了用于适配springBoot的Starter, 同时我们还需要引入 mysql-connector. 在pom.xml中添加:

org.projectlombok lombok 1.18.22 provided org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.1 mysql mysql-connector-java runtime

2.2 配置数据库连接

在spring的配置文件中,配置我们需要访问的数据库的连接信息,这个配置和前面jdbcTemplate的配置一样

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_learning username: root password: root

2.3 开发Mapper

在JdbcTemplate中我们访问数据库的这一层使用dao表示的。但是在mybatis中,我们一般都把这一层称之为mapper, 并且一般类名也用这个结尾,其实代表的都是一个意思,就是使用习惯的问题。在Mybatis中的Mapper也是分为接口和实现,比较特殊的是mapper的实现一般使用xml文件的形式来体现。我们的sql也都是写在xml文件中。

我们在项目中创建一个mapper的文件夹,用来存放所有的Mapper接口。我们在里边创建UserMapper用来处理user表的增删改查操作。

@Mapper public interface UserMapper { /** * 删除操作 * @param id * @return */ int deleteByPrimaryKey(Integer id); /** * 插入操作 * @param record * @return */ int insert(User record); /** * 插如操作 * @param record * @return */ int insertSelective(User record); /** * 根据id查询操作 * @param id * @return */ User selectByPrimaryKey(Integer id); /** * 更新操作 * @param record * @return */ int updateByPrimaryKeySelective(User record); /** * 更新操作 * @param record * @return */ int updateByPrimaryKey(User record); }

然后在resources 资源目录下(application.yml同级目录下)创建一个mapper文件夹,用于存放xml格式的mapper实现。在里面写一个UserMapper.xml

id, name, age,address,create_time, update_time delete from t_user where id = #{id,jdbcType=INTEGER} insert into t_user ( name, age,address,create_time ,update_time ) values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER},#{address},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP} ) insert into t_user id, name, age, address, create_time, update_time, #{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{address}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}, update t_user name = #{name,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, address = #{address}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP} where id = #{id,jdbcType=INTEGER} update t_user set name = #{name,jdbcType=VARCHAR}, age = #{age,jdbcType=INTEGER}, address = #{address,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=TIMESTAMP}, update_time = #{updateTime,jdbcType=TIMESTAMP} where id = #{id,jdbcType=INTEGER}

然后在Service层中引用mapper.

2.4 配置Mapper的路径

我们现在是把xml类型的文件都放在了resources下的mapper文件夹中了。但是这个路径对与我们的项目中的mybatis来说他是不知道的,所以我们需要告诉它这个路径的位置。怎么告诉呢,就是在application.yml中配置一下。

mybatis: mapper-locations: classpath:mapper/*.xml 复制代码

mapper的位置位于 classpath下的mapper文件夹中的所有xml结尾的文件。

这里还有一个问题也提下。就是mapper的接口的位置我们并没有配置,他是怎么知道的呢,其实是我们在每个Mapper的接口上都加上了@Mapper的注解,所以他可以自动扫描到。如果要加这个注解,那么所有的Mapper接口上就都需要加,这其实是比较麻烦的。怎么办呢,我们可以都不加这个注解,然后在SpringBoot的启动类上,加上一个MapperScan注解,将mapper的包路径配置进去,就都不用加了。

我们去掉@Mapper注解,也不加任何配置会报错:

我们在主类上添加注解:

@SpringBootApplication @MapperScan("com.lsqingfeng.springboot.mapper") public class SpringBootLearningApplication { public static void main(String[] args) { SpringApplication.run(SpringBootLearningApplication.class, args); } }

再次启动:

成功了。

3、 接口测试

组装好项目后,我们来调用一下接口。继续使用之前的jdbc里写好的Controller进行测试。

调用成功后,观察数据库。

李四这条使我们刚刚插入进去的,只不过两个时间是空值,因为我们并没有给时间赋值。

再看看查询接口。

SpringBoot教程(十一) | SpringBoot集成Mybatis

4、mybatis自动填充时间

上面的案例中,由于我们没有对时间进行设置,导致创建时间和修改时间都是空的。其实Mybatis中为我们提供了-的机制,相当于可以对每次执行的sql进行拦截,这样我们就可以对对操作进行判断,如果是插入操作,就直接设置创建时间和修改时间为当前时间。如果是更新操作则设置更新时间为当前时间。

-代码如下。

import org.apache.ibatis.binding.MapperMethod.ParamMap; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.Date; /** * Mybatis-,用能与设置创建时间和更新时间 */ @Component @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) public class CreateTimeSetInterceptor implements Interceptor { private static final String CREATE_TIME_SETTER = "setCreateTime"; private static final String UPDATE_TIME_SETTER = "setUpdateTime"; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; Object parameter = invocation.getArgs()[1]; if (ms.getSqlCommandType() == SqlCommandType.INSERT) { setTimeIfNeccesary(parameter, CREATE_TIME_SETTER); setTimeIfNeccesary(parameter, UPDATE_TIME_SETTER); } else if (ms.getSqlCommandType() == SqlCommandType.UPDATE) { setTimeIfNeccesary(parameter, UPDATE_TIME_SETTER); } return invocation.getMethod().invoke(invocation.getTarget(), invocation.getArgs()); } private void setTimeIfNeccesary(Object param, String methodName) { Class cls = param.getClass(); if (cls == ParamMap.class) { @SuppressWarnings("unchecked") ParamMap map = (ParamMap) param; map.entrySet().forEach(entry -> { if (!entry.getKey().equals("et")) { setIfSetterExist(entry.getValue(), methodName); } }); } else { setIfSetterExist(param, methodName); } } private void setIfSetterExist(Object param, String methodName) { Class cls = param.getClass(); try { Method m = null; try { m = cls.getDeclaredMethod(methodName, new Class[] { Date.class }); if (m != null) { m.setAccessible(true); m.invoke(param, new Date()); } } catch (NoSuchMethodException e1) { m = cls.getDeclaredMethod(methodName, new Class[] { Timestamp.class }); if (m != null) { m.setAccessible(true); m.invoke(param, new Timestamp(System.currentTimeMillis())); } } } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } }

接下来我们再来执行一个插入操作》

再次观察数据库:

发现时间已经有值了。

5、 mybatis-generator 插件

我们上面通过User表创建了UserMapper接口和UserMapper的xml实现。其实这类文件中的内容是比较有规律的,所以如果每次没有都去自己写,是比较耗费时间的,所有mybatis为我们提供了mybatis-generator插件,通过这个插件,我们可以设置对应的数据库连接和表名,然后插件就会帮我们自动生成对应的实体,mapper接口和Mapper实现,里面的方法只有常用的增删改查操作。能够大大较少我们操作的工作量。 这个步骤,我们一般称之为 mybatis 逆向生成。 逆向生成的方法比较多,有兴趣的自己去找找吧,这里不想展开了,因为现在随着mybatisPlus的普及,使用mybatis-plus-generator的更多一些。

好了关于SpringBoot集成Mybatis我们就介绍这么多。欢迎大家一起交流,有问题随时留言。

另: 配套项目代码已托管中gitCode: gitcode.net/lsqingfeng/…

所有文章也会在微信公众号首发更新,欢迎关注: 一缕82年的清风

MyBatis Spring Boot

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:HarmonyOS之应用工程结构与设备模板
下一篇:React(一)react概述、组件、事件
相关文章