Mybatis框架

Mybatis框架
虹色轨迹🌠关于Mybatis框架
Mybatis框架解决了数据库编程相关的问题,主要是简化了数据库编程。
当使用Mybatis框架实现数据库编程时,只需要:
- 定义数据操作功能的抽象方法(此抽象方法必须在接口中)
- 配置以上抽象方法映射的SQL语句
Mybatis框架在实现过程中,会自动生成各接口的代理对象,所以,开发人员并不需要关注接口的实现问题。
使用Mybatis框架
在Spring Boot项目中,当需要使用Mybatis框架实现数据库编程时,需要添加:
mybatis-spring-boot-starter
- 数据库的依赖,例如
mysql-connector-java
所以,在pom.xml
中添加:
<!-- Mybatis框架 --> |
由于添加以上依赖后,Spring
Boot在启动时就会读取连接数据库的配置信息,如果未配置,则启动报错且失败,需要在src/main/resources
下的application.properties
中添加必要的配置:
spring.datasource.url=jdbc:mysql://localhast:8888 |
提示:以上配置中,属性名称是固定,以上示例值是错误值,但是,启动Spring Boot只是加载以上配置,并不会执行连接,所以,配置值错误并不影响启动项目。
创建数据库:create database mall_pms;
连接数据库的配置
在Spring Boot项目中,src/main/resources
下的application.properties
是默认的配置文件,项目在启动时,Spring
Boot会自动从此文件中读取相关的配置信息。
在许多配置过程中,需要在application.properties
中的配置的属性的名称是固定的!
在配置数据库的连接信息时,至少需要配置spring.datasource.url
、spring.datasource.username
、spring.datasource.password
这3个属性,分别表示连接数据库的URL、登录数据库的用户名、登录数据库的密码
spring.datasource.url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai |
为了检验配置值是否正确,可以在src/test/java
下默认的包下创建DatabaseConnectionTests
测试类,在测试类中编写测试方法,以尝试连接数据库,即可检验:
package cn.tedu.csmall.server; |
示例:create table user(
id bigint unsigned auto_increment comment '数据id',
age tinyint unsigned comment '年龄',
category int unsigned comment '分类',
username varchar(32) comment '用户名',
password char(32) comment '密码',
content text(32) comment '正文',
primary key(id)
)charset utf8mb4;
java: byte
short
int
long
String
mysql: tinyint
smallint
int
bigint
varchar/char/text
关于设计数据表
关于id
阿里巴巴的建议是:每张表都应该有id
字段,且是bigint unsigned
类型,其中,bigint
对应Java中的long
类型,unsigned
表示“无符号位”,
将使得此字段值不会出现负数,且取值区间是原正数的2倍……以tinyint
为例,没有添加unsigned
时,取值区间是[-128, 127]
,添加unsigned
以后,
取值区间是[0, 255]。
当把id
的类型设置为bigint
时,理论上id值足够使用,即使不添加unsigned
也不会出现id值不够用的情况,但仍推荐添加,其目的是为了表现语义。
关于编码
应该在创建表时指定编码,创建库时可以不指定编码。
在MySQL / MariaDB强烈推荐使用utf8mb4
。
关于字符串的字段类型
如果某个字符串类型的字段的值的长度变化可能较大,应该使用varchar
类型,例如用户名,如果某个字符串类型的字段的值的长度
变化不大,
应该使用char
类型。
注意:某些数据可能是纯数字的,但并不具备算术运算含义,也应该使用字符串类型,不要使用数值类型。
在使用varchar
时,指定的长度一定是“大于必要长度”的标准,例如,现行的标准是“用户名最多15个字符”,则建议设计为varchar(25)
或比25
更大的值,
但是,也不要过于夸张,避免影响语义。
使用Mybatis时定义的抽象方法
使用Mybatis时,定义的抽象方法都必须在接口中,通常,接口会使用Mapper
作为名称的最后一个单词,例如命令为BrandMapper
等。
关于抽象方法的声明原则:
- 返回值类型:如果需要执行的SQL是增、删、改类型的,统一使用
int
作为返回值类型,表示“受影响的行数”,其实也可以使用void
,但并不推荐,
如果需要执行的SQL是查询类型的,如果查询最多只返回1个结果,则只需要保证返回值类型可以装得下所需的查询结果即可,
如果查询返回的结果可能超过1条,则必须使用List
集合进行封装,且集合的元素类型依然只需要保证可以装得下所需的查询结果即可 - 方法名称:自定义
- 获取单个对象的方法用 get 做前缀
- 获取多个对象的方法用 list 做前缀
- 获取统计值的方法用 count 做前缀
- 插入的方法用 save/insert 做前缀
- 删除的方法用 remove/delete 做前缀
- 修改的方法用 update 做前缀
- 参数列表:如果需要执行的SQL语句中的参数数量较多,推荐将多个参数封装到自定义类中
关于@Mapper和@MapperScan
Mybatis框架只要求开发人员编写接口和抽象方法,不需要开发人员编写实现类,是因为Mybatis会通过代理模式自动生成接口的实现对象,
但是,它需要明确哪些接口是需要生成代理对象的。
可以在各接口上添加@Mapper
注解,在启动项目时,Mybatis会对整个项目进行扫描,对于已经添加此注解的接口,就会生成代理对象。
也可以在配置类上添加@MapperScan
注解,用于指定各接口所在的包,则Mybatis会扫描此包及其子孙包下的所有接口,并生成这些接口的代理对象。
关于@Mapper
和@MapperScan
这2种注解,只需要选择其中1种使用即可,通常推荐@MapperScan
。
注意:使用@MapperScan
时,一定只指向Mapper接口所在的包,并确保此包下无其它接口!
提示:Mybatis框架的@MapperScan
,与Spring框架的@ComponentScan
没有任何关系,且不会互相影响!
使用Mybatis实现插入数据
以实现“插入品牌数据”为例,需要执行的SQL语句大致是:
insert into pms_brand (name, pinyin, logo, description, keywords, sort, sales, product_count, comment_count, positive_comment_count, enable, gmt_create, gmt_modified) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); |
先在项目的默认包下创建pojo.entity.Brand
类,类中的属性应该与数据表对应:
public class Brand implements Serializable { |
接下来,准备接口和抽象方法,在项目的默认包下创建mapper.BrandMapper
接口,并在接口中添加抽象方法:
package cn.tedu.csmall.server.mapper; |
关于SQL语句,可以使用@Insert
等注解进行配置,但不推荐!
推荐使用XML文件配置SQL语句,此文件模版可以通过 http://doc.canglaoshi.org/config/Mapper.xml.zip 下载。
然后,在src/main/resources
下创建mapper
文件夹,将下载得到的zip文件解压,得到SomeMapper.xml
文件,将此XML文件复制到mapper
文件夹中。
先将SomeMapper.xml
重命名为BrandMapper.xml
。
关于此文件的配置:
- 根节名必须是
<mapper>
- 根节点必须配置
namespace
属性,取值为对应的接口的全限定名 - 在根节点内部,根据需要执行的SQL语句的类型不同,使用
<insert>
、<delete>
、<update>
、<select>
节点 - 在
<insert>
等节点上,必须配置id
属性,取值为抽象方法的名称(不包含括号及参数) - 在
<insert>
等节点内部,配置SQL语句,SQL语句不需要使用分号结束
例如配置为:
<?xml version="1.0" encoding="UTF-8" ?> |
最后,还需要补充一个配置,用于告诉Mybatis框架这类XML文件的位置!在application.properties
中添加:
mybatis.mapper-locations=classpath:mapper/*.xml |
另外,在插入数据时,还可以配置,得到自动编号的ID值,具体做法是在<insert>
节点上添加配置:
<!-- int insert(Brand brand); --> |
练习
目标:实现向pms_album
表中插入数据,并要求可以获取自动编号的id。
开发步骤:
- 在
entity
包创建Album
实体类,类中的属性与pms_album
表保持一致,且符合POJO规范 - 在
mapper
包中创建AlbumMapper
接口 - 在
AlbumMapper
接口中添加抽象方法:int insert(Album album);
(记得添加注释) - 在
src/main/resources
的mapper
文件夹下,通过复制粘贴得到AlbumMapper.xml
文件 - 配置
AlbumMapper.xml
中的根节点的namespace
- 在
AlbumMapper.xml
中,在根节点内部添加<insert>
节点,并配置id
、useGeneratedKeys
、keyProperty
属性,在<insert>
节点内部配置SQL语句 - 在
src/test/java
下的cn.tedu.csmall.server.mapper
包(你的包名可能不同)下创建AlbumMapperTests
测试类,在类中自动装配AlbumMapper
对象,并编写、执行测试方法
使用Mybatis实现删除数据
目标:根据id删除某个品牌
需要执行的SQL语句大致是:
delete from pms_brand where id=? |
在BrandMapper
接口中添加抽象方法:
/** |
在BrandMapper.xml
中配置SQL:
<!-- int deleteById(Long id); --> |
在BrandMapperTests
中编写并执行测试:
|
练习
目标:实现根据id删除pms_album
表中的数据。
使用Mybatis实现修改数据
目标:实现根据id修改pms_brand
表中某条数据的name
字段值。
需要执行的SQL语句大致是:
update pms_brand set name=? where id=? |
在BrandMapper
接口中添加抽象方法:
/** |
在BrandMapper.xml
中配置SQL:
<!-- int updateNameById(@Param("id") Long id, @Param("name") String name); --> |
在BrandMapperTests
中编写并执行测试:
|
练习
目标:实现根据id修改pms_album
表中某条数据的name
字段值。
使用Mybatis实现批量删除数据
在Mybatis中,有“动态SQL”的机制,它允许根据调用方法时传入的参数值不同,来生成不同的SQL语句。
目标:根据若干个id
一次性删除多个品牌。
需要执行的SQL语句大致是:
delete from pms_brand where id=? or id=? or id=?; |
或者:
delete from pms_brand where id in (?, ?, ?); |
注意:以上SQL语句中的id
的数量是不确定的。
在BrandMapper
接口中,抽象方法可以是:
int deleteByIds(Long... ids); // 注意:可变参数在处理时,本质上就是数组 |
或者:
int deleteByIds(Long[] ids); |
或者:
int deleteByIds(List<Long> ids); |
在BrandMapper.xml
中配置SQL语句:
<!-- int deleteByIds(Long... ids); --> |
由于需要对参数ids
(若干个id
)进行遍历,需要使用到动态SQL中的<foreach>
节点,此节点可以对数组或集合进行遍历!关于<foreach>
的配置:
collection
属性:表示被遍历的参数对象,当抽象方法的参数只有1个,且没有添加@Param
注解时,当参数值的类型是数组时,此属性值为array
,
当参数值的类型是List
时,此属性值为list
;否则,此属性值为@Param
注解中的参数值item
属性:表示被遍历到的元素的名称,是自定义的名称,在<foreach>
内部,使用#{}
格式的占位符时,也使用此属性来表示每个元素separator
属性:表示遍历过程中各元素值之间的分隔符号
最后,在BrandMapperTests
中编写并执行测试:
|
使用Mybatis实现动态SQL的修改数据
在动态SQL机制中,可以使用<if>
标签,可用于对某参数值进行判断,从而生成不同的SQL语句片段,常用于设计更新数据的操作。
目标:使用1个方法,实现多种不同的数据更新(想更新哪些字段就更新哪些字段,不想更新的字段值将保持不变)
需要执行的SQL语句大致是:
update pms_brand set name=?, pinyin=?, logo=? where id=? |
注意:以上SQL语句的修改的字段列表应该不是固定的,应该根据传入的参数值来决定。
先在BrandMapper
接口中添加抽象方法:
int updateById(Brand brand); |
然后,在BrandMapper.xml
中进行配置:
<!-- int updateById(Brand brand); --> |
需要注意的是,在Mybatis的动态SQL中,<if>
并没有对应的<else>
,如果一定要实现类似Java中的if...else
效果,需要使用<choose>
标签,其基本格式是:
<choose> |
或者,也可以使用2个条件完全相反的<if>
标签来实现类似效果(但是执行效率偏低),例如:
<if test="pinyin != null"> |
使用Mybatis实现查询数据
统计查询
目标:统计品牌表中的数据的数量
需要执行的SQL语句大致是:
select count(*) from pms_brand; |
在BrandMapper
接口中添加抽象方法:
int count(); |
在BrandMapper.xml
中配置SQL:
<!-- int count(); --> |
注意:所有查询节点(<select>
)必须配置resultType
或resultMap
这2个属性中的其中1个。
当使用resultType
声明封装结果的数据类型时,取值与抽象方法的返回值对应,如果是基本类型,直接写类型名称即可,
例如resultType="int"
,如果是引用数据类型,在java.lang
包下的可以直接写类名,其它包下的写全限定名。
指定条件的单一结果查询
目标:根据id查询品牌详情
需要执行的SQL语句大致是:
select id, name, pinyin ... from pms_brand where id=? |
由于不推荐使用星号表示字段列表,并且,在实际查询时,可能有部分字段是不需要体现在查询结果中的,推荐的做法是针对所需的查询字段,另外创建类型进行结果的封装。
例如,在pojo.vo
下创建BrandDetailVO
类:
public class BrandDetailVO implements Serializable { |
在BrandMapper
接口中添加抽象方法:
BrandDetailVO getById(Long id); |
然后,在BrandMapper.xml
中配置SQL:
<select id="getById" resultType="cn.tedu.csmall.server.pojo.vo.BrandDetailVO"> |
另外,在查询时,一定要明确几个概念:
- 字段(Field):在创建数据表时指定的名称(后续也能修改表结构时改名)
- 列(Column):查询的结果集中的每一竖排,列名默认情况下是字段名,如果查询时指定了别名,则列名就是指定的别名
- 属性( Property):类中的属性
Mybatis在执行查询时,会尝试自动的将结果集中的数据封装到返回结果类型的对象中,但是,它只能自动处理列名与属性名相同的部分,如果列名与属性名不同,默认并不能自动封装!
可以在查询的SQL语句中,自定义别名,使得列名与属性名相同,则可以实现自动封装,例如:
<select id="getById" resultType="cn.tedu.csmall.server.pojo.vo.BrandDetailVO"> |
以上的product_count AS productCount
自定义别名可以保证product_count
字段的值可以被自动封装!
更推荐使用<resultMap>
来指导Mybatis如何封装结果,它将与<select>
标签的resultMap
属性一起使用,例如:
<select id="xx" resultMap="DetailResultMap"> |
然后,在<resultMap>
内部,使用<result>
来配置列名与属性名的对应关系,例如:
<resultMap id="DetailResultMap" type="cn.tedu.csmall.server.pojo.vo.BrandDetailVO"> |
提示:在使用<resultMap>
配置时,从规范的角度出发,每个列与属性的关系都需要显式的配置出来(即使从功能实现的角度来看可能并不需要),另外,还应该使用<id>
节点对主键进行配置。
查询列表
目标:查询品牌列表
需要执行的SQL语句大致是(暂时使用星号表示字段列表):
select * from pms_brand order by id |
通常,查询列表时,所需要查询的字段与查单个数据可能是不同的,所以,可能需要自定义新的VO类作为List
中的元素类型,为了避免写完代码后发现某个VO不能复用于查询单个数据和查询列表这2个功能,推荐一开始就定义用于封装列表项结果的VO类,例如:
public class BrandListItemVO implements Serializable { |
在BrandMapper
接口中添加抽象方法:
List<BrandListItemVO> list(); |
然后,在BrandMapper.xml
中配置SQL:
<select id="list" resultMap="ListItemResultMap"> |
关于<sql>
与<resultMap>
在使用XML配置SQL语句时,可以使用<sql>
封装SQL语句片段,并使用<include>
进行引用,例如:
<select id="list" resultMap="ListItemResultMap"> |
通过,使用<sql>
封装字段列表,而<resultMap>
通常与之对应,所以,这2个节点的id
命名通常会使用相同的关键词,
例如<sql>
配置为id="ListItemQueryFields"
,并且<resultMap id="ListItemResultMap">
,甚至,在编码时,会把这2个节点放在相邻的位置。
提示:使用<sql>
封装字段列表时,IntelliJ IDEA可能会误判为错误的语法,将字段列表使用<if test="true">
框住(或将此<if>
添加在之前)即可避免出现这样的错误提示。
练习
完成以下查询功能:
- 统计相册的数量
- 统计类别的数量
- 根据id查询相册详情
- 除了
gmt_create
和gmt_modified
字段的值
- 除了
- 根据id查询类别详情
- 除了
gmt_create
和gmt_modified
字段的值
- 除了
- 查询相册的列表
- 除了
gmt_create
和gmt_modified
字段的值
- 除了
- 根据父级类别,查询子级类别列表(条件:
where parent_id=?
)- 除了
gmt_create
和gmt_modified
字段的值
- 除了
课后作业
在当前项目(jsd2203-csmall-server
)中,通过Mybatis实现:
- 向插入
pms_album
表、pms_attribute
表等共计12张表中插入数据(已完成的不必重复开发,但均需提交) - 根据
id
删除pms_album
表、pms_attribute
表等共计12张表中的数据(已完成的不必重复开发,但均需提交) - 根据若干个
id
删除pms_album
表、pms_attribute
表等共计12张表中的数据(已完成的不必重复开发,但均需提交)
在实现过程中,你可能需要创建必要的类、接口等文件,例如实体类、Mapper接口、XML文件、测试类,请按照课上标准命名,并放在正常的包或文件夹中。
以上所有功能必须有对应的测试。
Mybatis小结:
关于Mybatis使用
1.了解使用Mybatis时需要添加的依赖
mybatis-spring-boot-starter
mysql-connector-java
2.了解使用Mybatis时的一次性配置
application.properties: 数据库连接参数
使用@MapperScan配置接口文件所在的包
application.properties: 配置XML文件的位置
3.掌握抽象方法声明原则
返回值:增删改返回值为int,查询:单个结果-保证能封装结果即可、多条数据-使用List,元素类型保证能封装结果即可
方法名称:参考阿里手册
参数列表:先分析-打草稿(写sql),参数超过1个,声明多个参数-参数前添加@Param注解参数起别名;声明自定义POJO封装数据
4.掌握xml中配置个抽象方法的映射sql
xml文件固定声明 - 或用模板 或复制粘贴 或工具生成
必须在根节点
应根据sql语句来选择
动态
动态
5.掌握规范的sql语句
在insert语句中,应该显式的指定的字段列表
正例:insert into user(username,password) values(‘Tom’,’123123’);
反例:insert into user values(‘Tom’,’123123’);
在统计查询时,count()进行统计
在查询表数据时,不要使用星号()表示字段列表
当查询结果超过1条时,建议使用order by进行排序。
自动更新gmt_create和gmt_modified的Mybatis拦截器
MybatisConfiguration.java
|
InsertUpdateTimeInterceptor.java
package cn.tedu.mall.product.interceptor; |