Spring学习笔记(六):MyBatis集成

Easter79
• 阅读 632

1 概述

MyBaits是一个著名的持久层框架,本文首先介绍了MyBatis的简单使用,接着与Spring进行整合,最后简单地使用了Generator去自动生成代码。

2 MyBatis简介

MyBatis本来是Apache的一个开源项目——iBatis,2010年由Apaceh Software Foundation迁移到了Google Code,并改名为MyBatis

MyBatis是一个基于Java的持久层框架,提供的持久层框架包括SQL MapsData Access Objects,使用简单的XML或者注解用于配置映射,将接口和POJO映射成数据库中的记录,是一个小巧、方便、高效、简单、直接、半自动化的持久层框架。

3 工作原理

上图:

Spring学习笔记(六):MyBatis集成

  • 读取配置文件:mybatis-config.xml是全局MyBatis配置文件,配置了MyBatis运行环境信息
  • 加载映射文件:也就是SQL映射文件,配置了操作数据库的SQL语句
  • 构造会话工厂:通过配置文件构造会话工厂SqlSessionFactory
  • 创建会话对象:由上一步的会话工厂创建会话对象SqlSession
  • 获取MapperStatement:通过用户调用的apiStatement ID获取MapperStatement对象
  • 输入参数映射:通过ExecutorMapperStatement进行解析,将各种Java基本类型转化为SQL操作语句中的类型
  • 输出结果映射:JDBC执行SQL后,借助MapperStatement的映射关系将返回结果转化为Java基本类型并返回

4 MyBatis示例

首先先来看一下纯MyBaits的示例,没有整合Spring,一个简单的Maven工程,项目结构如下:

Spring学习笔记(六):MyBatis集成

4.1 依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

<!--驱动用的是MySQL,版本请自行修改-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>

Gradle

compile group: 'org.mybatis', name: 'mybatis', version: '3.5.5'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.21'

4.2 实体类

@Setter
@Getter
@Builder
public class User {
    private Integer id;
    private String name;

    @Override
    public String toString() {
        return "id:"+id+"\tname:"+name;
    }
}

4.3 映射文件

新建一个叫UserMapper.xml的映射文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">
    <select id="selectById" parameterType="Integer" resultType="pers.entity.User">
        select * from user where id=#{id}
    </select>
    <select id="selectAll" resultType="pers.entity.User">
        select * from user
    </select>

    <insert id="insert" parameterType="pers.entity.User">
        INSERT INTO `user` (`id`,`name`)
        VALUES (#{id},#{name})
    </insert>

    <update id="update" parameterType="pers.entity.User">
        UPDATE `user` set `name`=#{name} where id=#{id}
    </update>

    <delete id="delete" parameterType="Integer">
        DELETE FROM `user` WHERE `id` = #{id}
    </delete>
</mapper>

映射文件是一个XML文件,根元素为<mapper>,需要注意其中的namespace属性,调用的时候通过该namespace调用。其中的子元素表示SQL语句:

  • <select>:查询,id指定了这条语句的id号,调用时通过namespace.id的方式调用,比如该条select需要通过UserMapper.selectById调用,parameterType指定参数类型,这里是一个Integer的参数,resultType指定返回类型,实体类
  • <insert>/<update>/<delete>:对应的插入/修改/删除语句
  • 关于占位符:#{}表示是占位符,相当于传统JDBC中的?#{id}表示该占位符等待接收的参数名称为id

4.4 配置文件

MyBatis的配置文件,叫mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="test"/>
                <property name="password" value="test"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

指定了数据库的一些连接属性还有mapper的位置。

4.5 测试

public class Main {
    public static void main(String[] args) {
        try
        {
            InputStream inputStream = Resources.getResourceAsStream("config/mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession session = factory.openSession();
            User user = session.selectOne("UserMapper.selectById",1);
            System.out.println(user);
            User user1 = User.builder().name("test").build();
            session.insert("UserMapper.insert",user1);
            user1.setName("222");
            session.update("UserMapper.update",user1);
            List<User> list = session.selectList("UserMapper.selectAll");
            list.forEach(System.out::println);
            session.delete("UserMapper.delete",1);
            session.commit();
            session.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

主要流程如下:

  • 读取配置文件:根据org.apache.ibatis.io.Resources读取配置文件mybatis-config.xml,请注意配置文件的位置正确,这里的配置文件都放在resources下,mybatis-config.xml放在其中的config
  • 构建Session:根据配置文件构建SqlSessionFactory后,通过openSession创建Session
  • 业务操作:通过sessionselectOne/insert/update等进行业务操作,这类操作带两个参数,第一个参数是String,表示配置文件中的SQL语句,采用namespace.id的形式,比如这里的UserMapper.xml中声明namespaceUserMapper,其中带有一条idselectByIdselect语句,因此调用时使用UserMapper.selectById的形式,第二个参数是一个Object,表示要传递的参数,也就是绑定到配置文件中对应占位符的值
  • 提交与关闭:业务操作完成后提交事务并关闭session

示例测试结果:

Spring学习笔记(六):MyBatis集成

5 Spring整合示例

上面的例子只是为了演示MyBatis的基本使用,没有整合Spring,这里的例子是把Spring整合进来,流程也大概差不多,项目结构如下:

Spring学习笔记(六):MyBatis集成

5.1 依赖

分为5类JAR

  • MyBatis需要的JAR
  • Spring需要的JAR
  • MyBatisSpring整合的中间JAR
  • 数据库驱动JAR
  • 数据源JAR

完整依赖如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.5</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.8.0</version>
</dependency>

Gradle

compile group: 'org.springframework', name: 'spring-beans', version: '5.2.9.RELEASE'
compile group: 'org.springframework', name: 'spring-context', version: '5.2.9.RELEASE'
compile group: 'org.springframework', name: 'spring-core', version: '5.2.9.RELEASE'
compile group: 'org.springframework', name: 'spring-tx', version: '5.2.9.RELEASE'
compile group: 'org.springframework', name: 'spring-jdbc', version: '5.2.9.RELEASE'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.21'
compile group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.8.0'
compile group: 'org.mybatis', name: 'mybatis', version: '3.5.5'
compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.5'

5.2 配置文件

配置文件分为三类:

  • MyBatis映射文件:编写mapper的地方,也就是业务需要的SQL语句
  • MyBatis全局配置文件:由于整合了Spring,数据源的配置放在了Spring的配置文件中,而只需要保留mapper的查找位置
  • Spring配置文件:配置数据源+事务管理+MyBaitssqlSssionFactory+组件扫描

5.2.1 MyBatis映射文件

与上面的例子差不多,只是修改了namespace包名.类名的形式:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="pers.dao.UserDao">
    <select id="selectById" parameterType="Integer" resultType="pers.entity.User">
        select * from user where id=#{id}
    </select>
    <select id="selectAll" resultType="pers.entity.User">
        select * from user
    </select>

    <insert id="insert" parameterType="pers.entity.User">
        INSERT INTO `user` (`id`,`name`)
        VALUES (#{id},#{name})
    </insert>

    <update id="update" parameterType="pers.entity.User">
        UPDATE `user` set `name`=#{name} where id=#{id}
    </update>

    <delete id="delete" parameterType="Integer">
        DELETE FROM `user` WHERE `id` = #{id}
    </delete>
</mapper>

namespace需要与对应包名的带有@Mapper的类配置一致。

5.2.2 MyBatis配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

5.2.3 Spring配置文件

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd"
       >

    <context:component-scan base-package="pers.dao"/>
    <context:component-scan base-package="pers.service"/>
    <!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
        <!--最大连接数+最大空闲数+初始连接数-->
        <property name="maxTotal" value="30"/>
        <property name="maxIdle" value="10"/>
        <property name="initialSize" value="5"/>
    </bean>
    
    <!--事务管理-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource"  ref="dataSource"/>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="txManager" />

    <!--创建SqlSessionFactory Bean-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:config/mybatis-config.xml"/>
    </bean>

    <!--注解扫描,主要用于@Mapper,会扫描basePackge下的@Mapper注解的接口并自动装配为MyBatis的映射接口-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="pers.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>
</beans>

5.3 持久层

需要加上@Mapper注解,表示自动装配为MyBatis的映射接口,注意:

  • 映射文件中的namespace需要与包名.类名对应,比如这里的包为pers.dao,类名为UserDao,那么映射文件中的namespacepers.dao.UserDao

  • id需要与方法名对应,比如映射文件中的有一条select语句的idselectById,那么方法就需要命名为selectById,且参数类型需要对应一致

    @Repository @Mapper public interface UserDao { User selectById(Integer id); List selectAll(); int insert(User user); int update(User user); int delete(Integer id); }

5.4 业务层

@Service
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MyBatisService {
    private final UserDao dao;

    public void test(){
        User user = dao.selectById(13);
        System.out.println(user);
        dao.insert(User.builder().name("333").build());
        dao.update(User.builder().name("88888").id(13).build());
        dao.selectAll().forEach(System.out::println);
        dao.delete(12);
        dao.selectAll().forEach(System.out::println);
    }
}

注入UserDao后进行简单的测试,结果如下:

Spring学习笔记(六):MyBatis集成

6 自动生成代码

相信很多程序员也讨厌写又长又麻烦的XML配置文件,因此,MyBatis也提供了一个生成器插件,可以直接从表中生成实体类、dao接口以及映射文件,可以省去很多操作。

步骤如下:

  • 导入依赖
  • 编写Generator配置文件
  • 生成代码

6.1 依赖

其实就是加入一个插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.4.0</version>
            <configuration>
                <!-- 在控制台打印执行日志 -->
                <verbose>true</verbose>
                <!-- 重复生成时会覆盖之前的文件-->
                <overwrite>true</overwrite>
                <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.21</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

数据库驱动请对应修改。

至于Gradle版请看Kotlin版源码。

6.2 配置文件

这里是参考别人的配置文件,修改数据库连接、表名、包名即可:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- context 是逆向工程的主要配置信息 -->
    <!-- id:起个名字 -->
    <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 -->
    <context id="default" targetRuntime="MyBatis3">
        <!--optional,指在创建class时,对注释进行控制-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--jdbc的数据库连接 wg_insert 为数据库名字-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/test" userId="test" password="test" />
        <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 -->
            <!-- 不是 double 和 long 类型 -->
            <!-- 使用常用的基本类型代替 sql 包下的引用类型 -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- targetPackage:生成的实体类所在的包 -->
        <!-- targetProject:生成的实体类所在的硬盘位置 -->
        <javaModelGenerator targetPackage="pers.entity"
                            targetProject="src/main/java">
            <!-- 是否允许子包 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对modal添加构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>
        <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
        <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="pers.dao" targetProject="src/main/java">
        </javaClientGenerator>
        <!-- tableName是数据库中的表名,domainObjectName是生成的JAVA模型名,后面的参数不用改,要生成更多的表就在下面继续加table标签 -->
        <table tableName="user" domainObjectName="User"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false" />
    </context>
</generatorConfiguration>

6.3 生成代码

Spring学习笔记(六):MyBatis集成

双击生成即可:

Spring学习笔记(六):MyBatis集成

生成了实体类、dao接口以及mapper文件。

7 参考源码

Java版:

Kotlin版:

8 参考链接

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k