Mybatis笔记
一、 mybatis框架
1.mybatis是什么?
mybatis是一个持久层框架,mybatis是一个不完全的ORM框架。sql语句需要程序员自己去编写,但是mybatis也有映射(输入参数映射、输出结果映射)。mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射).Mybatis 只是对于 jdbc 的封装。
2.mybatis框架
3.mybatis框架执行过程:
(1)配置mybatis的配置文件,SqlMapConfig.xml(名称不固定)
(2)通过配置文件,加载mybatis运行环境,创建SqlSessionFactory会话工厂
SqlSessionFactory在实际使用时按单例方式。
(3)通过SqlSessionFactory创建SqlSession
SqlSession是一个面向用户接口(提供操作数据库方法),实现对象是线程不安全的,建议sqlSession应用场合在方法体内。
(4)调用sqlSession的方法去操作数据。
如果需要提交事务,需要执行SqlSession的commit()方法。
(5)释放资源,关闭SqlSession
二、Dao开发方法
2.1 原始Dao开发方法
(1)映射文件
(2)Dao接口
(3)Dao接口实现类
Public class UserDaoImpl implements UserDao {
//注入SqlSessionFactory
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.setSqlSessionFactory(sqlSessionFactory);
}
private SqlSessionFactory sqlSessionFactory;
@Override
public User getUserById(int id) throws Exception {
SqlSession session = sqlSessionFactory.openSession();
User user = null;
try {
//通过sqlsession调用selectOne方法获取一条结果集
//参数1:指定定义的statement的id,参数2:指定向statement中传递的参数
user = session.selectOne("test.findUserById", 1);
System.out.println(user);
} finally{
session.close();
}
return user;
}
@Override
Public void insertUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
sqlSession.insert("insertUser", user);
sqlSession.commit();
} finally{
session.close();
}
}
}
(4)问题
原始Dao开发中存在以下问题:
******Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
******调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护
2.2 Mapper接口开发方法(Mapper动态代理方式)
(1)实现原理
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
【1】 Mapper.xml文件中的namespace与mapper接口的类路径相同。
【2】 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
【3】 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
【4】Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
(2)Mapper.xml映射文件
(3)接口文件
(4)加载Mapper.xml文件
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
(5)测试
InputStream is = Resources.getResourceAsStream("mybatis/SqlMapConfig.xml");
//通过配置文件SqlMapConfig.xml,创建SqlSessionFactory对象,然后产生SqlSession,执行SQL语句
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.selectById(1);
System.out.println(user);
//关闭session
sqlSession.close();
三、XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
<configuration>
<!-- 加载类路径下的属性文件 -->
<properties resource="jdbc.properties"/>
<!-- settings -->
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载(即按需加载) -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 打开全局缓存开关(二级缓存)默认值就是 true -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 设置类型别名 -->
<typeAliases>
<typeAlias type="ljh.entity.User" alias="user"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<!-- 设置一个默认的连接环境信息 -->
<environments default="mysql">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="JDBC"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件-->
<mappers>
<mapper resource="mybatis/mapper/UserMapper.xml"/>
</mappers>
</configuration>
SqlMapConfig.xml是mybatis核心配置文件,上边文件的配置内容为数据源、事务管理。
3.1配置内容
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
3.2配置详讲
(1)类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
(2)环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
(3)事务管理器(transactionManager)
(4)数据源(dataSource)
(5)映射器(mappers)
告诉 MyBatis 到哪里去找映射文件
*******<mapper resource=" " />使用相对于类路径的资源
*******<mapper url=" " />使用完全限定路径
*******<mapper class=" " />使用mapper接口类路径 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
*******<package name=""/>注册指定包下的所有mapper接口 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
四、XML映射文件
1、namespace等于mapper接口地址
2、输入映射(输入类型)
通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo,pojo的包装类型
3、输出映射(输出类型)
(1)resultType
******使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
******如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
******只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。不一致的属性值为null。
******输出单个pojo对象,方法返回值是单个对象类型
******输出pojo对象list,方法返回值是List<Pojo>
******如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身
(2)resultMap
mybatis中使用resultMap完成高级输出结果映射。一致
4、动态sql
(1)If
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意要做不等于空和空字符串校验。
(2)where
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<where />可以自动处理第一个and。
(3) foreach
向sql传递数组或List,mybatis使用foreach解析
* 需求:传入多个id查询用户信息,用下边两个sql实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%张%' id IN (10,89,16)
WHERE id=1 OR id=10 OR id=16
在查询条件中,查询条件定义成一个sql片段,需要修改sql片段。
(4) sql片段
需求:将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。方便程序员进行开发。
定义sql片段
引用sql片段
在mapper.xml中定义的statement中引用sql片段:
五、延迟加载
需要查询关联信息时,使用mybatis延迟加载特性可有效的减少数据库压力,首次查询只查询主要信息,关联信息等用户获取时再加载。【用的时候加载不用的时候不加载】
在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading
设置项
|
描述
|
允许值
|
默认值
|
lazyLoadingEnabled
|
全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。
|
true | false
|
false
|
aggressiveLazyLoading
|
当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。
|
true | false
|
true
|
在mybatis核心配置文件中配置
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
六、Mybatis连接池和事务
1、连接池
(1)mybatis连接池分类
可以看出 Mybatis 将它自己的数据源分为三类:
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源(最常用)
JNDI 使用 JNDI 实现的数据源
(2)Mybatis中数据源的配置
MyBatis 在初始化时,解析此文件,根据的 type 属性来创建相应类型的的数据源DataSource,即:type=”POOLED”:MyBatis 会创建 PooledDataSource 实例
type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
(3)Mybatis 中连接的获取过程分析
当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象
来创建 java.sql.Connection 对象。也就是说,java.sql.Connection 对象的创建一直延迟到执行 SQL 语句
的时候。
数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中。
2、Mybatis的事务控制
(1)JDBC中事务的控制在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以调整。
Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC
的 setAutoCommit()方法来设置事务提交方式的
SqlSession sqlSession = sqlSessionFactory.openSession(true);//手动将他的事务提交方式改为自动提交
掌握 Mybatis连接池技术、事务、复杂参数的传入、复杂结果集的封装,多表的关联查询。
七、查询缓存
1.SqlSessionFactoryBuilder、SqlSessionFacoty、SqlSession
sqlSessionFactory是mybatis中的一个中要的对象,通俗讲它是用来创建sqlSession对象的,而sqlSession用来操作数据库的。
(1)SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
(2)SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
(3)SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作,默认使用DefaultSqlSession实现类。
执行过程如下:
1. 加载数据源等配置信息
Environment environment = configuration.getEnvironment();
2. 创建数据库链接
3. 创建事务对象
4. 创建Executor,SqlSession所有操作都是通过Executor完成,mybatis源码如下:
5. SqlSession的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
结论:
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
2.Mybatis缓存介绍
(1)mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。mybaits提供一级缓存,和二级缓存。
如下图,是mybatis一级缓存和二级缓存的区别图解:
Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。Mybatis默认开启一级缓存。
Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
3.一级缓存
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
4.二级缓存
mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
在核心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true"/>
刷新缓存(就是清空缓存)
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。
八、 mybatis逆向
1、什么是逆向工程
mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml、po..)
企业实际开发中,常用的逆向工程方式:
由于数据库的表生成java代码。
2、使用方法(会用)
运行逆向工程, 生成代码配置文件,执行生成程序, 使用生成的代码