MyBatis 笔记
MyBatis 是基于 Java 语言的开源的持久层框架,利用实体类和数据表之间产生了关联,通过映射文件中的 sql 语句对数据库进行操作
使用 maven 创建工程需要导入 mybatis 的依赖:
org.mybatis mybatis3.5.5
简单实例演示
mybatis的持久层开发有两种方式,一种是原始的 dao 开发,比较繁琐,还有一种是基于接口的 JDK 动态代理开发,也是最普遍的一种开发模式
原始dao开发模式
使用原始 dao 开发模式,首先第一步: 创建 mybatis 的核心配置文件,一般取名为 mybatis-config.xml
在dataSource标签中配置数据库连接,在mapper标签中配置xml映射文件目录,然后去书写xml映射文件
第二步:在上面的映射文件目录对应的位置创建文件
mapper 标签的 namespace 属性自定义书写,select 标签的 id 不能与其他 id 重复,resultType 是和数据库表对应的实体类的全限定路径名,实体类仅仅只有和表中字段对应的属性及 get set toString 方法而已
第三步:创建dao类,通过dao接口类来操作 mapper 文件中的增删改查
dao类想要操作mapper文件需要使用工厂对象,工厂对象暂时就不在这里创建了,由调用者给他对象让他使用就行
操作mapper文件需要使用mapper文件的 ==namespace.标签id==,例如 ==test1.findAll==
public class BeanDao {
private SqlSessionFactory factory ;
public BeanDao( SqlSessionFactory factory ){
this.factory = factory;
}
public List findAll(){
SqlSession sqlSession = factory.openSession();
List list = sqlSession.selectList("test1.findAll");
return list;
}
}
第四步:创建测试类,对写好的代码进行测试
因为调用一个dao都会使用一个工厂对象,所以这里就直接创建一个供所有dao使用,节省资源
public class AppTest {
private static InputStream is = null;
private static SqlSessionFactory factory = null;
@Before
public void before(){
try {
is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (Exception e) {
System.out.println("获取配置文件失败!");
}
factory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void tes1(){
BeanDao dao = new BeanDao(factory);
List list = dao.findAll();
for (Bean bean : list)
System.out.println(bean);
}
}
基于接口的动态代理开发
使用动态代理开发模式,首先第一步: 创建 mybatis 的核心配置文件,一般取名为 mybatis-config.xml
这里和 dao 开发的配置文件一模一样,可以去上面 copy
第二步:在上面的映射文件目录对应的位置创建映射文件
这里和上面几乎一模一样,但是动态代理的 mapper 的 namespace 指向的必须是接口的全限定名称
第三步:按照上面映射文件目录创建接口
这里和上面不同的是不需要太多繁琐的结构,只需要创建一个方法即可对应mapper中的增删改查,但是需要注意一点:==接口方法名必须和mapper的CRUD标签id一致,否则会报错==
public interface BeanDao {
List findAll();
}
第四步:创建测试类,对写好的代码进行测试
public class TestDemo {
private SqlSessionFactory factory = null;
@Before
public void before(){
InputStream is = null;
try {
is = Resources.getResourceAsStream("mybatis-config.xml");
}catch (Exception e){
System.out.println("加载核心配置文件失败");
System.exit(0);
}
this.factory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void test1(){
SqlSession session = factory.openSession();
BeanDao mapper = session.getMapper(BeanDao.class);
List list = mapper.findAll();
for (Bean bean : list)
System.out.println(bean);
}
}
注意:在 openSession 的时候是可以传入一个 boolean 参数的,如果不传参数默认为 false,如果要执行 DML 语句进行增删改的话,一定要传入 true 做为参数,否则他不会自动提交 ( 你可以手动提交 ),查询无需提交所以不需要设置
Mybatis 的核心配置文件
mybatis 的核心配置文件,一般都起名为 mybatis-config.xml 或者是 sqlMapConfig.xml ,在核心配置文件中可以通过某些标签对 mybatis 进行一些配置
==注意:mybatis 的核心配置文件内不可以书写中文注释 (英文或数字可以),否则报错==
mybatis 提供了很多标签进行配置,但是这些标签也有着先后顺序,如果标签不按顺序书写也是会报错的
- configuration(配置文件的根标签)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
上面来自 mybatis 官方文档,这里会挑常用的标签进行笔记
Environments 环境配置
1. 环境的选择
在 mybatis 配置文件中使用 environments 标签配置环境,在他下面可以有一个或多个 environment 子标签,每个都代表一个环境,然后在 environments 标签中使用 default 属性对环境进行选择
这里使用 default 对环境进行选择,目前选择的是MySQL的环境
这是MySQL的环境 这是oracle的环境
除开配置文件内的默认环境,也可以在打开打开会话工厂的时候传入指定环境的id进行选择环境
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is, "mysql");
注意: 尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
2. 事务管理器
在每个 environment 子标签环境中有 transactionManager 标签的 type 属性可以对 SQL 进行事务控制管理,管理事务的管理器有两种:【 JDBC 】【 MANAGED 】
| 事务管理器 | 说明 |
|---|---|
| JDBC | 直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。 |
| MANAGED | 什么都不做,让容器来管理事务的整个生命周期,不建议使用 |
建议使用 JDBC 来管理事务
3. 数据源
数据源是配置 mybatis 和数据库进行连接的相关配置,他由 environment 标签下的 dataSource 标签进行设置
通过 dataSource 标签的 type 属性可以选择数据源类型:
| 类型 | 说明 |
|---|---|
| UNPOOLED | 只是每次被请求时打开和关闭连接,反应较慢,适用于简单小型项目 |
| POOLED | 相对于UNPOOLED相比使用了 数据库连接池 的感念 |
| JNDI | 能在如 EJB 或应用服务器这类容器中使用 |
数据源最基本的四个属性:
- 【driver】 数据库连接驱动类
- 【url】 数据库链接地址
- 【username】 用户名
- 【password】 密码
最常见的 environments 标签配置
Mappers 映射标签
书写的 mapper 文件需要被映射到核心配置文件中,映射的标签就是 mappers ,他下面有两个子标签,分别是 mapper 和 package 映射方法如下:
1. 映射的文件在resources目录下
>
2. 映射的文件在dao包下
当 mapper 文件在 dao 包下的时,mapper 和接口必须是同名的,或者在 resources 目录下有着同级的目录
3. 接口注解开发
注意!当mapper文件在dao包下的时候,需要在maven的pom文件中添加如下代码,否则xml文件可能不会被打包发布
src/main/resources ***.xml true src/main/java ***.xml true
Properties 属性
我们在使用传统 JDBC 的时候都会把 driver,url 等等放在一个 propertis 后缀的文件中,这样可以降低代码的耦合性,方便环境切换,mybatis 中也支持这种做法,使用 properties 标签就可以实现
1. 手写 properties 配置文件
driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1/库名 username=root password=*****
2. 使用 properties 标签将配置文件引入进来
TypeAliases 别名
在我们的 mapper 映射文件中,CRUD 标签中的参数,返回结果 resultType 通常都是全限定名,例如:
这个时候我们就可以使用 typeAliases 标签下的 typeAlias 子标签==配置类别名==,可以简化全限定名书写格式
abc就是这个类的类别名,就可以在映射文件中使用了
typeAlias 标签中的 alias 属性也可以忽略不写,默认已类名作为别名 (别名首字母小写即可)
但如果是个较大的项目,表和实体类比较多,如果逐个实体类配置的话会很麻烦,这时可以使用 package 子标签
将指定包下的所有类全部配置别名,别名为类本身名字 (首字母小写)
Settings 全局设置
在 mybatis 中可以对全局进行一些设置,设置需要用 settings 下的 setting 子标签实现,格式体现为:
常用的设置有:
| 设置名 | 作用描述 | 参数 |
|---|---|---|
| cacheEnabled | 设置全局缓存的开启和关闭 | boolean |
| defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 |
| mapUnderscoreToCamelCase | 是否开启驼峰命名规则 | boolean |
| logImpl | 配置日志信息,个人推荐使用 STDOUT_LOGGING 日志 | @官方文档 |
Mapper 映射文件
mapper 映射文件,用来负责和数据库交互的 sql 代码,主要是增删改查四个标签为主:
增删改标签
增删改标签是用来书写插入 sql 的标签,比较常用的几个属性如下:
| 属性 | 作用 |
|---|---|
| id | 用来作为标签的标识符,使用id可以直接找到当前标签调用 |
| parameterType | 接受的参数的类型,如果参数类型是集合,那么就填集合内元素的类型 |
| flushCache | 增删改标签默认设置为true,只要语句被调用,就会清空本地和二级缓存 |
| timeout | 抛出异常之前,驱动程序等待响应的时间,默认为空值 |
查询标签
select 标签是用来专门书写查询语句的标签,比较常用的几个属性如下:
除开增删改相同的属性之外,查询有着自己的属性
| 属性 | 作用 |
|---|---|
| resultType | 用来设置查询返回的数据对应的实体类 |
| resultMap | 属于高级的parameterType,他可以手动配置实体类属性和数据库字段的映射 |
| useCache | select 默认为 true,执行查询后将查询结果缓存在二级缓存中 |
添加时返回 ID
这里记一个十分好用的小功能,当我们执行 insert 之后,想要立刻操作这行记录,但是他的主键是自增的,我们想要操作他需要获取他的 ID,这个时候我们可以通过属性设置回显 ID
假设当前有一个 bean,属性为 userid,username,password
insert into t_user values(#{username}, #{password})
@Test
public void test(){
User user = new User();
user.setUsername("张");
user.setPassword("123");
userDao.addUser(user);
// 当我们执行插入语句后,如果正常运行,那么userid就会成功赋值
System.out.println(user.getUserid());
}
SQL 代码片段
可以被用来定义可重用的 SQL 代码段,例如:
定义了 id 为 t_user 的 sql 代码片段,在下面的增删改查中就可以使用 include 来调用了
userid, username, usersex, usertel
${ } 和 #{ } 的区别
${ }和#{ }他们都是用来接收传入参数的,只不过有些细节需要留意,假设现在有一行SQL语句:
select * from t_user where userid = 1;
其中作为查询条件的 1 是需要接收传入参数的,那么获取到传入参数有两种方法:
- ${ },拼接SQL字符串
- #{ },作为SQL占位符
如果使用 ${} 的话,通过日志可以看到,它以拼接字符串的方式执行,这样的话就很容易收到SQL注入的攻击
select * from t_user where userid = 1;
但是如果使用 #{} 的话,他是将 SQL 代码预处理之后在执行之前进行参数替换,传入的参数都将作为普通参数对待,通过执行日志就可以看到:
select * from t_user where userid = ? ;
这是二者最明显的差距,通过占位符可以有效防止SQL注入攻击,推荐使用#{ }
ResultMap 标签
一般使用 resultType 返回对象的时候,是数据库自动检测实体类中和字段匹配的属性进行封装,但是如果实体类中的属性和表中的字段截然不同的话,就需要手动配置他们之间的映射关系了
property="tel" javaType="club.hanzhe.pojo.Tel">
动态 SQL
通过传入的参数对 sql 进行灵活化使用,类似 Java 一样,通过 判断循环 来控制 SQL 的最终执行
if 判断标签
如果接收的username参数不为空的话,就追加当前标签内的sql语句
where 过滤标签
使用 where 标签之后,如果 where 内有符合 if 条件的会自动添加 where 关键字,如果没有符合的就不添加
如果有多个 if 符合条件,需要在前面加上 and 或者 or 等判断条件,如果是第一个成立的条件会默认省略前面的 and 或 or,后续的会添加
choose 标签
choose 标签有着和 switch 类似的功能,他要求当前判断至少成立一个才可以结束,如果没有成立的会执行otherwise
set 标签
使用 set 标签之后,如果 set 内有符合 if 条件的会自动添加 set 关键字,如果没有符合的就不添加,需要注意一点的是,==set → if 内的 sql 结尾一定要带上逗号==,系统会自动省略但不会自动追加。
update t_user where userid = #{userid } username = #{username }, usersex = #{usersex }, usertel = #{usertel },
foreach 标签
foreach 标签用来遍历传进来的集合或数组容器,多用于批量删除操作,里面的属性解释如下:
delete from bean where id in #{ i }
- collection:传进来被遍历的那个容器的名字,可以是数组或集合
- open:循环开始前需要添加的前缀
- close:循环结束后需要添加的后置
- separator:每循环一次时需要在中间添加的分隔符
- item:被循环出来的每个字段,在标签内直接使用即可
cache 缓存标签
一级缓存
在 mybatis 中一级缓存是默认开启的,在进行 openSession 的时候就自动打开了一级缓存,且对于一级缓存的操作,只能清空,不能禁用,一级缓存的作用范围是当前的 SqlSession 实例
在同一个 SqlSession 实例中,两次同样的查询返回的结果是一致的,但是如果是两个SqlSession那么进行相同的查询结果就会不同,这里就不写了
public void test4(){
SqlSession session = this.factory.openSession(true);
BeanDao mapper = session.getMapper(BeanDao.class);
Bean bean1 = mapper.findById(7844);
Bean bean2 = mapper.findById(7844);
System.out.println(bean1 == bean2);
}
二级缓存
二级缓存的范围是基于当前的 mapper.xml 配置文件的 mapper 标签,也就是当前的 namespace 下,一个二级缓存可以横跨多个一级缓存,而二级缓存默认是关闭的,开启二级缓存的方法就是==使用 cache标签==
没错,只要在 mapper 下面加上这么一句代码,二级缓存就开启完成了,简单粗暴
在开启二级缓存后还不能立刻使用,会报错,==需要让该 mapper 相关的所有 bean 实现 Serializable 序列化接口==
Mybatis 注解开发
CRUD 注解
在 mybatis 中也可以不使用 mapper 映射文件,改为使用注解开发,分别相对着增删改查四个注解
@Insert("insert into t_user values(null,#{name},#{sex},#{address})")
public int saveUser(User user);
@Delete(“DELETE FROM t_user WHERe u_id = #{abc}”)
public int removeUser(Integer id);
@Update(“update t_user set u_address=#{address} where u_name = #{name}”)
public int updateUser(User user);
@Select(“select * from t_user where u_id = #{abc}”)
public User findUserById( Integer id );
注意!使用 mapper 文件的时候需要扫描 mapper 文件所在位置,然后才可以使用,使用注解开发也需要扫描:
将所有带有注解的接口都扫描
Result 注解
使用 xml 开发时,会遇到字段与实体类属性名不匹配造成封装失败的情况,起别名解决是一种方法,但是一般都使用 resultMap 结果映射,将字段与属性匹配,注解开发也可以使用 resultMap,对应的注解是 @Results
使用注解的方式匹配字段与属性查询
@Results(
id = "r1", // id:给当前results起名字,像resultMap一样继承使用
value = { // value:配置多个映射
@Result( column = "u_id" , property = "id" ),
@Result( column = "u_name" , property = "name" ),
@Result( column = "u_sex" , property = "sex" ),
@Result( column = "u_address" , property = "address" )
}
)
public User findUserById( Integer id );
如果有另一个查询与他的规则相同,那么可以引用之前写的Results的id属性
@Select("select * from t_user where u_name = #{abc}")
@ResultMap("r1")
public User findUserByName( String name );
扩展功能
Mybatis 逆向工程
逆向工程是根据数据库中表的信息进行自动创建实体类,接口,映射文件的技术
1. 环境配置
使用逆向工程必须引入他的依赖或jar包
org.mybatis.generator mybatis-generator-core1.3.5
2. 逆向工程配置文件
你可以在任意位置 ( 包括项目外 ) 创建逆向工程所需要的配置文件,需要手动配置的地方有以下几点:
- properties 标签中的配置文件需要自己书写,也可以书写该标签及文件
- jdbcConnection 标签需要修改,修改需参考上面的 properties 配置文件
- javaModelGenerator 标签需要修改,他是实体类生成的目标包路径
- sqlMapGenerator 标签需要修改,他是 mapper 映射文件生成的目标路径
- javaClientGenerator 标签需要修改,他是接口生成的目标包路径
- table 标签的 tableName 属性需要修改,他是逆向工程所参考的数据表
- 如有多个表被参考就书写多个 table 标签,可以直接复制修改 tableName 属性
3. 使用逆向工程
在环境配置完成后使用下面这段模板代码,运行后就会将实体类,接口,mapper文件自动生成
需要在改代码中手动设置配置文件的文件名及位置
import org.junit.Test; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Generator {
@Test
public void create(){
try {
List
boolean overWriter = true;
// 这里加载刚刚创建的逆向工程配置文件
File configFile = new File(“文件位于磁盘的绝对路径”);
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overWriter);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}catch (Exception e){
System.out.println(“操作失败!”);
System.out.println(“==================================================”);
System.out.println(e);
e.printStackTrace();
}
}
}
PageHelper 分页插件
pageHelper 插件是针对 mybatis 设计的一款分页插件,通过拦截器对 sql 进行操作,使其拥有分页的能力
1. 环境配置
使用 pageHelper 分页插件需要导入他的 maven 依赖或者导入他的 jar 包
com.github.pagehelper pagehelper5.0.0
在 maven 依赖添加完成后,还需要在 mybatis 的核心配置文件中使用 plugins 标签注册 pageHelper 插件
2. 使用PageHelper
PageHelper 依赖引入后,在程序中就有了 PageHelper 类,调用它的 startPage 静态方法,后紧跟的第一条查询语句就是分页查询
startPage 的静态方法需要两个参数,第一个是查询的页码,第二个是每页的查询数量
public void test1(){
// 查询第二页,每页5条记录
PageHelper.startPage(2, 5);
List list = mapper.findAll();
for (Bean bean : list)
System.out.println(bean);
System.out.println(list);
}
分页除了 PageHelper 类,还有一个 PageInfo 可以使用,将查询的结果封装到 PageInfo 中,可以查询一些和分页相关的信息
public void test1(){
SqlSession session = factory.openSession();
BeanDao mapper = session.getMapper(BeanDao.class);
PageHelper.startPage(2, 5, true);
PageInfo info = new PageInfo(mapper.findAll());
System.out.println("总页码:" + info.getPages());
System.out.println("总记录数:" + info.getTotal());
System.out.println("结果集:");
List list = info.getList();
for (Bean bean : list)
System.out.println(bean);
}
3. PageInfo常用参数
在将结果集封装到 PageInfo 中的时候,可以可选性的传入一个导航页码的参数,可以获取到和当前页相邻的页码
public void test1(){
SqlSession session = factory.openSession();
BeanDao mapper = session.getMapper(BeanDao.class);
PageHelper.startPage(201, 5, true);
// 在创建info对象的时候可以跟随一个int类型,这个值是当前页附近的页码的数量
PageInfo info = new PageInfo(mapper.findAll(), 7);
System.out.println("当前页:" + info.getPageNum());
System.out.println("每页显示数量:" + info.getPageSize());
System.out.println("当前页信息:");
for (Object obj : info.getList())
System.out.println(obj);
System.out.println("总页码:" + info.getPages());
System.out.println("总记录数:" + info.getTotal());
System.out.println("上一页:" + info.getPrePage());
System.out.println("下一页:" + info.getNextPage());
System.out.println("是否首页:" + info.isIsFirstPage());
System.out.println("是否尾页:" + info.isIsLastPage());
System.out.println("导航条页码:");
for (int page : info.getNavigatepageNums() )
System.out.print( page + " " );
System.out.println("n导航条中首页" + info.getNavigateFirstPage());
System.out.println("导航条中尾页" + info.getNavigateLastPage());
}
- 这里需要注意!==每页显示数量是指分页是设置的数量==,例如设置每页只显示5条,获取到的就是5,而不是最后一页只显示三条就返回三,这是错误的
合理化查询
在网页中往往有些人会钻空子乱改数据,例如只有20页数据,他修改了参数请求到了第50页,这种情况经常会发生,所以在注册 pageHelper 的时候可以添加一个参数使数据变得合理化
Lombok 插件
在我们进行查询的时候,需要将查询的结果封装到指定的实体类中,而对应的实例类需要书写响应的 get set 方法,构造方法及 toString 方法,这样比较麻烦,这是可以使用 lombok 插件进行自动生成功能
1. 环境配置
使用 lombok 需要引入他的 maven 依赖
org.projectlombok lombok1.18.8
在依赖引入后还需要在编辑器中安装 lombok 的插件 ( IDEA 编辑器为例 ) File → settings → plugins → install JetBrains plugin... 在里面搜索 lombok 下载安装,而后在 file → settings → build,Exe.. → Compiler → Annotation Pro... 里面选中当前项目,在右面的 Enable annotation processing 选项打勾
2. 使用 Lombok 插件
在目标实体类上,只需要书写好属性即可,无需书写其他多余方法,使用 lombok 提供的注解用来一键生成:
| 注解 | 作用 |
|---|---|
| @Getter | 一键生成所有 get 方法 |
| @Setter | 一键生成所有 set 方法 |
| @ToString | 一键生成 toString 方法 |
| @Data | 一键生成 get set toString 等方法 |
| @NoArgsConstructor | 生成无参构造方法 |
| @AllArgsConstructor | 生成有参构造方法 |
更多学习资料请微信搜万神小栈



