# Mybatis框架

Mybatis框架的主要作用

Mybatis框架主要实现了简化持久层编程的问题。

#持久层:实现数据持久化的一系列组件。

数据持久化:通常,在开发领域中,讨论的数据大多是在内存中的,而内存默认特指内存条(RAM:Random Access Memory),RAM的特性包含“一旦断电,数据将全部丢失”,且“正在执行的程序和数据都是在内存中的”,由程序处理的数据最终应该永久的保存下来,则不能将这些数据一直只存储在内存中,通常,会将数据存储到可以永久保存数据的存储介质中,典型的永久存储数据的存储介质有:硬盘、U盘、光盘等,所以,数据持久化就是将内存中的数据存储到硬盘等介质中,而硬盘中的数据是以文件的形式存在的,所以,通常可以将数据存储到文本文件中、XML文件、数据库中,这些存储方案中,只有数据库是便于实现增、删、改、查这4种操作的,所以,一般“数据持久化”默认指的就是将数据存储到数据库中。

在Java语言中,实现数据库编程需要先建立与数据库的连接,然后准备SQL语句,然后执行,然后获取执行结果并处理结果,最后,关闭或归还数据库连接,这是一套非常固定的流程,无论对哪个数据表执行哪种操作,其流程大致是固定的,所以,就产生了一些框架,用于简化这部分的编程。

在使用Mybatis时,只需要关注2点:

  • 在接口中定义抽象方法
  • 配置抽象方法映射的SQL语句

使用Mybatis的前期准备

在Spring Boot项目中,当需要使用Mybatis时,需要添加相关的依赖:

<!-- MySQL依赖项,仅运行时需要 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Mybatis整合Spring Boot的框架 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

当添加以上依赖项后,如果启动项目,会提示以下错误:

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

因为Spring Boot启动时,如果检测到当前已经添加数据库编程的依赖项,会自动读取连接数据库的配置信息,由于目前尚未配置这些信息,所以,启动会报错!

所以,需要在application.properties中添加配置:

连接数据库的参数

spring.datasource.url=jdbc:mysql://localhost:3306/mall_pms?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
由于Spring Boot在启动项目时只会读取以上配置并应用,并不会实际的连接数据库,所以,即使以上配置值是错误的,启动项目时并不会报告错误!

可以在测试类中添加方法,尝试连接数据库,以检查以上配置的“连接数据库的参数”是否正确:

@Autowired
DataSource dataSource; // 导包时注意:此接口是javax.sql包中的

@Test
void testConnection() throws Throwable {
    dataSource.getConnection();
}

使用Mybatis插入数据

*Mybatis要求抽象方法必须存在于接口中(因为其实现原理是基于接口的代理模式的),所以,在项目的根包下创建mapper.XXXXMapper接口。

提示:可以在接口上添加@Repository注解,避免在自动装配时IntelliJ IDEA误判而提示错误。

关于接口中的抽象方法:

  • 返回值类型:如果需要执行的SQL是增、删、改类型的,使用int作为返回值类型,表示“受影响的行数”,不建议使用void
  • 方法名称 :自定义,不要使用重载
    • 获取单个对象的方法用get做前缀
    • 获取多个对象的方法用list做前缀
    • 获取统计值的方法用count做前缀
    • 插入的方法用save/insert做前缀
    • 删除的方法用remove/delete做前缀
    • 修改的方法用update做前缀
  • 参数列表:如果需要执行的SQL语句有多个参数,应该将这些参数封装到自定义的数据类型中,并使用自定义的数据类型作为抽象方法的参数

首次使用

首次使用时,需要让Mybatis知道哪些接口是Mapper接口,可以(二选一):

  • 在每个接口上添加@Mapper注解
  • 配置类上添加@MapperScan并指定Mapper接口所在的包
    在根包下的任何类,添加了@Configuration注解,即是配置类
    可以在根包下创建config.MybatisConfiguration类,同时添加@Configuration@MapperScan("xxxx")即可

另外,在使用Mybatis时,还需要为每个抽象方法配置其映射的SQL语句,可以使用@Insert等注解来配置SQL语句,但不推荐,因为:

  • 不便于配置较长的SQL语句
  • 不便于做一些复杂的配置,特别是查询时
  • 不便于实现与DBA(Database Administrator)分工合作

建议的做法是使用XML文件来配置SQL语句,可以从 http://doc.canglaoshi.org/config/Mapper.xml.zip 下载得到所需的文件,然后,在项目的src/main/resources下创建mapper文件夹,将得下载、解压得到的XML文件复制到此文件夹中。

关于配置SQL的XML文件:

  • 根节点必须是<mapper>
  • <mapper>上必须配置namespace属性,取值为对应的接口的全限定名
  • <mapper>的子级,根据需要执行的SQL语句,选择使用<insert>(在配置<insert>节点时,配置useGeneratedKeyskeyProperty这2个属性,就可以得到自动编号的id)、<delete><update><select>中的某个节点,准备配置SQL语句,这些节点必须配置id属性,取值为抽象方法的名称(不包括括号和参数列表),并在这些节点内部配置SQL语句

首次使用时,需要在application.properties中配置以上XML文件的位置:

#Mybatis的配置SQL的XML文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml

MyBatis的动态SQL–foreach

动态SQL:根据参数值的不同,将生成不同的SQL语句。

假设存在需求:根据若干个id删除相册数据,即批量删除。

当实现以上功能时,关于抽象方法,可以设计为:

int deleteByIds(Long[] ids);
或者:
int deleteByIds(Long... ids);
或者:
int deleteByIds(List<Long> ids);

在配置SQL时,需要使用到<foreach>节点对参数进行遍历:

<!-- int deleteByIds(Long[] ids); -->
<delete id="deleteByIds">
    DELETE FROM pms_album
    WHERE id IN (
    	<foreach collection="array" item="id" separator=",">
            #{id}
    	</foreach>
    )
</delete>

关于<foreach>节点的配置:

  • collection属性:当抽象方法的参数只有1个且没有添加@Param注解时,当参数是数组类型时(包括类型为可变参数时),此属性取值为array,当参数是List集合类型时,此属性取值为list
  • item属性:遍历过程中的每个元素的变量名,是自定义的名称
  • separator属性:遍历过程中各元素之间的分隔符号

假设给需求:批量插入数据

添加抽象方法:

int insertBatch(List<Album> albums);

AlbumMapper.xml中配置SQL语句:

<!-- int insertBatch(List<Album> albums); -->
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO pms_album (
    	name, description, sort
    ) values 
    <foreach collection="list" item="album" separator=",">
        (#{album.name}, #{album.description}, #{album.sort})
    </foreach>
</insert>

使用Mybaits修改数据

通常,修改数据时,也会使用到动态SQL的机制,当传入某个字段对应的值时,SQL中才会包含修改此字段的部分,反之,如果没有传入某个字段对应的值,则SQL语句不会包含修改此字段的部分!

这样的功能可以通过动态SQL的<if>标签来实现!

假设需要实现修改相册数据,传入的参数中包含哪些数据,就修改哪些数据,不包含的部分将不会被修改。

int update(Album album);

AlbumMapper.xml中配置SQL语句:

<!-- int update(Album album); -->
<update id="update">
    UPDATE pms_album
    <set>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="description != null">
            description=#{description},
        </if>
        <if test="sort != null">
            sort=#{sort},
        </if>
    </set>
    WHERE id=#{id}
</update>

使用Mybatis查询–统计

假设需要实现:统计相册表中的数据的数量
关于抽象方法:在查询时,方法的返回值类型只要求能够存入查询结果即可。

则在AlbumMapper中添加抽象方法:

int count();

然后,在AlbumMapper.xml中配置SQL语句,将使用<select>节点,此节点必须配置resultTyperesultMap这2个属性中的某1个,当使用resultType时,此属性的值取决于抽象方法的返回值类型,如果是基本数据类型(例如int等),则resultType属性的值就是类型名,如果是引用数据类型(例如StringAlbum等),则resultType属性的值就是类型的全限定名(在java.lang包下的可以省略包名)。

<!-- int count(); -->
<select id="count" resultType="int">
    SELECT count(*) FROM pms_album
</select>

如果既没有配置resultType又没有配置resultMap,将会出现错误。

设需要实现:根据id查询相册详情

需要执行的SQL语句大致是:

select id, name, description, sort from pms_album where id=?

关于抽象方法的返回值类型,原则上,只需要能够“放得下”就行,所以,可以使用Album作为此次查询的返回值类型,但是,并不建议这样处理!通常,建议另创建类型,用于封装查询结果!另外创建的类型,通常并不会称之为实体类,并且,这种类型会添加一些后缀,关于后缀的使用,阿里的文档的参考

  • 数据对象:xxxDO,xxx 即为数据表名
  • 数据传输对象:xxxDTO,xxx 为业务领域相关的名称
  • 展示对象:xxxVO,xxx 一般为网页名称
  • POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO

关于以上后缀:

  • DO:Data Object
  • DTO:Data Transfer Object
  • VO:View Object / Value Object

对于本次查询,可以使用VO作为类型的后缀,完整的类名可以使用AlbumStandardVO,此类应该放在项目的根包的pojo.vo包下.

接下来,在AlbumMapper接口中添加抽象方法:

AlbumStandardVO getStandardById(Long id);

AlbumMapper.xml中配置SQL:

<!-- AlbumStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultType="xx.xx.xx.AlbumStandardVO">
    select id, name, description, sort from pms_album where id=#{id}
</select>

Mybatis在封装查询结果时,会自动的将**列名(Column)属性名(Property)**匹配的结果进行封装,例如查询结果中的name值将封装到返回值对象的name属性中去,对于名称不匹配的,将放弃。

可以在配置SQL时,为查询的字段自定义列名,使得“查询结果中的列名”与“封装结果的类型中的属性名”是一致的,例如:

<!-- BrandStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultType="cn.tedu.csmall.product.pojo.vo.BrandStandardVO">
    SELECT
        id, name, pinyin, logo, description,
        keywords, sort, sales, 
    
    	product_count AS productCount, 
    	comment_count AS commentCount,
        positive_comment_count AS positiveCommentCount, 
    
    	enable
    FROM pms_brand
    WHERE id=#{id}
</select>

提示:在SQL语句中,自定义别名时,AS关键字并不是必须的,只需要有1个空格即可。

除了以上做法以外,还可以在application.properties中添加配置,使得Mybatis能自动处理“全小写且使用下划线分隔的字段名对应的列名”与“驼峰命名法的属性名”之间的对应关系(例如此做法时,不必在查询时自定义别名):

mybatis.configuration.map-underscore-to-camel-case=true

或者,还可以选择自定义ResultMap,用于指导Mybatis如何封装查询结果,其基本方式是:

<resultMap id="自定义的ResultMap名称" type="封装查询结果的类型的全限定名">
    <!-- 配置 -->
</resultMap>

<select id="xxx" resultMap="自定义的ResultMap名称">
</select>

<resultMap>内部,使用<result>节点,配置其columnproperty属性,用于指定列名与属性名的对应关系,例如:

<resultMap id="自定义的ResultMap名称" type="封装查询结果的类型的全限定名">
    <result column="product_count" property="productCount" />
    <result column="comment_count" property="commentCount" />
    <result column="positive_comment_count" property="positiveCommentCount" />
</resultMap>

提示:在普通的单表查询中,列名与属性名本身就对应的部分,并不需要在<resultMap>中配置。

另外,在开发实践中,建议将查询的字段列表使用<sql>节点进行封装,然后,在配置的SQL语句中,使用<include>节点进行调用即可:

<!-- BrandStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultMap="StandardResultMap">
    SELECT
        <include refid="StandardQueryFields"/>
    FROM pms_brand
    WHERE id=#{id}
</select>

<sql id="StandardQueryFields">
    id, name, pinyin, logo, description,
    keywords, sort, sales, product_count, comment_count,
    positive_comment_count, enable
</sql>

使用Mybatis查询数据列表

查询列表与查询某1个数据的开发过程相差不大,主要区别在于:

  • 查询列表时,需要查询的字段通常更少
  • Mybatis会自动使用List集合来封装查询到的多个数据,所以,抽象方法的返回值类型必须是List类型的

假设需要实现:查询品牌列表(不考虑分页问题)

需要执行的SQL语句大致是:

select * from pms_brand order by sort desc, pinyin, id desc

注意:如果执行的查询的结果可能超过1条(即2条或以上),必须显式的指定order by进行排序!

然后,在BrandMapper接口中添加抽象方法:

List<BrandListItemVO> list();

BrandMapper.xml中配置SQL:

<!-- List<BrandListItemVO> list(); -->
<select id="list" resultMap="ListItemResultMap">
    SELECT
        <include refid="ListItemQueryFields"/>
    FROM pms_brand
    ORDER BY sort DESC, pinyin, id DESC
</select>

<sql id="ListItemQueryFields">
    id, name, logo
</sql>

<resultMap id="ListItemResultMap" type="cn.tedu.csmall.product.pojo.vo.BrandListItemVO">
</resultMap>

注意:即使是查询列表,无论使用resultType,还是配置<resultMap>,关于数据类型,都只需要指定为List中的元素类型即可!例如如下效果同上
BrandMapper.xml中配置SQL:

<!-- List<BrandListItemVO> list(); -->
<select id="list" resultType="cn.tedu.csmall.product.pojo.vo.BrandListItemVO">
    SELECT
        <include refid="ListItemQueryFields"/>
    FROM pms_brand
    ORDER BY sort DESC, pinyin, id DESC
</select>

<sql id="ListItemQueryFields">
    id, name, logo
</sql>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MyBatis是一种基于Java的持久层框架,它可以将SQL语句和Java对象进行映射,使得程序员可以使用面向对象的方式来访问关系型数据库。MyBatis框架主要包括以下几个组件: 1. SqlSessionFactory:SqlSessionFactory是MyBatis框架的核心组件,它是一个线程安全的对象,用来创建SqlSession对象。 2. SqlSession:SqlSession是MyBatis框架中的一个会话对象,它提供了对数据库的所有操作方法,包括增、删、改、查等操作。 3. Mapper接口:Mapper接口是MyBatis框架中的一个重要组件,它定义了针对某个数据表的增、删、改、查等操作方法。 4. Mapper XML文件:Mapper XML文件是MyBatis框架中的另一个重要组件,它提供了针对某个数据表的SQL语句,包括增、删、改、查等操作。 MyBatis框架的执行流程如下: 1. 加载SqlSessionFactory:程序首先通过MyBatis的配置文件(mybatis-config.xml)来创建SqlSessionFactory对象。 2. 获取SqlSession:程序通过SqlSessionFactory来获取SqlSession对象。 3. 执行SQL语句:程序通过SqlSession对象来执行SQL语句,可以通过Mapper接口或Mapper XML文件来执行SQL语句。 4. 返回结果:程序执行完SQL语句后,MyBatis框架会将查询结果封装成Java对象并返回。 总的来说,MyBatis框架是一种轻量级的持久层框架,它可以使得程序员可以使用面向对象的方式来访问关系型数据库,从而提高程序的开发效率和可维护性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值