什么是Mybatis?
- Mybatis是一款优秀的持久层框架,它支持定制化SQL,存储过程以及高级映射。
- Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- Mybatis可以使用简单的XML或注解配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects:普通老式Java对象)为数据库中的记录。
导入依赖
<!--Mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
使用XML的方式配置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?useSSL=true&useUnicode=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- <mappers>-->
<!-- <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
<!-- </mappers>-->
</configuration>
获得sqlSession对象
public class MybatsiUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步,获取sqlSessionFactory对象
String resource = "mybatis-config.xml";//这是配置Mybatis的文件名
InputStream inputStream = Resources.getResourceAsStream(resource);//通过流的形式获得xml文件的信息,传给获取sqlSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了获取sqlSessionFactory,顾名思义,我们就可以从中获得sqlsession的实例了
public static SqlSession getSqlSession(){
//sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法,这就有点像connection
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
开始第一个Mybatis程序
- 创建实体类User
- 创建dao层,创建一个接口,IUserMapper(不用IUserDao)
- 不用写UserMapperImpl,Mybatis会帮我们做连接数据库的相关操作,直接写Mapper配置文件mybatis-config.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">
<!--namespace:绑定对应的IUserMapper接口-->
<mapper namespace="com.cwx.dao.IUserMapper">
<!--select 查询语句,id:IUserMapper接口文件的方法名,resultType:返回结果的类型,必须写全路径,parameterType:参数的类型,若没带参数就不用写-->
<select id="getUserList" resultType="com.cwx.entity.User">
select * from test
</select>
<select id="getUserListByID" parameterType="int" resultType="com.cwx.entity.User">
select * from test where id = #{id}<!--这里用的id是mapper接口中的参数名,要一致-->
</select>
<!--插入一个数据,参数名用实体类定义的私有变量名,没有返回值-->
<insert id="addUser" parameterType="com.cwx.entity.User">
insert into test (name,age,pwd) values (#{name},#{age},#{pwd})
</insert>
<!--更新一个数据,没有返回值-->
<update id="updateUser" parameterType="com.cwx.entity.User">
update test set age=#{age},pwd=#{pwd} where name=#{name}
</update>
</mapper>
- 在MapperRegister中注册Mapper(别忘了!),在mybatis-config.xml文件中配置
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
- 测试是否查询成功
@Test
public void mybatisTest(){
//第一步,获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession;
//第二步,利用sqlSession实现执行语句
UserMapper userMapper = sqlSession.getMapper(IUserMapper.class);//获得dao层的Mapper接口,放入工厂中
List<User> userList = userMapper.getUserList();//执行配置文件里的方法,若方法里有参数就在这写入
for(User user : userList){
System.out.println(user);
sqlSession.close();//必须要关闭,sqlSession线程是不安全的
}
}
Tips:使用增删改操作需要添加事务,即测试时最后要提交事务
sqlSession.commit()
Mybatis使用Map
当方法的参数有多个时使用map或者当我们实体类或数据库中的字段或参数比较多时,当我们进行修改时,只修改某个参数就可以使用map,使用map可以只用写我们需要修改的值,其他的不用动或者。
<update id="updateUser2" parameterType="map"><!--参数类型为map-->
update test pwd=#{pwd} where name=#{name}<!--这里只用修改pwd-->
</update>
测试类:
@Test
public void testUpdate(){
//第一步,获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession;
//第二步,利用sqlSession实现执行语句
UserMapper userMapper = sqlSession.getMapper(IUserMapper.class);//获得dao层的Mapper接口,放入工厂中
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","cwx");
map.put("pwd",123456);
userMapper.updateUser2(map)
sqlSession.commit();
sqlSession.close();
}
环境配置(environments)
Tips:mybatis默认事务是JDBC(还有一个是MANAGED),默认数据源是POOLED,Mybatis可以配置多个环境,但每个Sqlsession实例只能选择一种环境。
属性(properties)
我们可以通过properties属性来引用配置文件,例如:
<properties resource="db.properties">
<property name ="username" value="root" />
<property name ="password" value="111111" />
</properties>
【注意点】: 若db.properties文件中和这里的property属性有一样的字段,优先使用db.properties里的,其它地方通过#{}
符号引用属性值。
Tips:
${}
和#{}
区别:两者都可以获得参数,但是#{}
能够更大程度的防止SQL注入问题,而${}
无法防止SQL注入。
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<typeAliases>
<typeAlias alias="User" type="com.cwx.entity.User"/>
</typeAliases>
<!--或者使用包名,当实体类有很多时用这种-->
<!--MyBatis 会在包名下面搜索需要的 Java Bean,别名是开头字母小写,如:user-->
<typeAliases>
<package name="com.cwx.entity"/>
</typeAliases>
Tips:MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用,此时Mybatis-plus应运而生,更加简化了操作。
映射器(mappers)
在MapperRegister中注册mapper,一般就使用<mapper resource="mapper/UserMapper.xml"/>
方式,其他方式和注意点到 Mybatis-3官方文档里看映射器的方式 里面查看。
生命周期和作用域(Scope)
- SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory,就不再需要它了,类似于局部变量。 - SqlSessionFactory
一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例,可以想象为数据库连接池。 - SqlSession
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域,可以把它想象为一个请求,请求完后需要关闭。
ResultMap结果集映射
- 解决属性名和字段名不一致的问题
在UserMapper.xml文件中进行配置:
<!--实现结果集映射,保证实体类中的名和数据库字段名对应-->
<resultMap id="UserMap" type="com.cwx.entity.User">
<!-- column:是数据库中的字段,property:实体类中的属性名-->
<result column="id" property="id" /> <!--名字相同可以不用写-->
<result column="name" property="name" /> <!--名字相同可以不用写-->
<result column="pwd" property="password" /> <!--这里的名字不一样,这都是简单版的结果映射-->
</resultMap>
<select id="getUserById" resultMap="UserMap">
select * from test where id=#{id}
</select>
- resultMap 元素是 MyBatis 中最重要最强大的元素,它的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
- 上面的只是一些简单的结果映射,如果世界有这么简单就好了。
分页
- limit分页:语法:
select * from test limit startIndex,pageSize;
- 1.接口:
List<User> getUserLimit(Map<String,Integer> map)
(多个参数就使用map) - 2.配置UserMapper.xml文件
<select id="getUserLimit" paramType="map" resutlMap="userMap">
select * from test limit #{startIndex},#{pageSize}
</select>
- 3.测试:步骤和上面差不多,不再赘写。
- 分页插件:PageHelper
,更加简化了操作。
使用注解开发
- 面向接口编程:在一个面向对象的系统中,系统的各个功能是由许多不同的对象协作完成的,在这中情况下,各个对象的内部是如何具体实现的对系统设计员来说就不那么重要了,按照这种思想编程就是面向接口编程,实际开发中,架构师只会给定相应的接口,具体实现就让码农干了。
- 为什么使用面向接口编程?
根本原因:解耦,可拓展,提高可复用,分层开发中,上层不用管具体实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。 - 1.在接口上写注解
@Select(select * form test)
List<User> getUserList();//如果方法中有多个参数,就使用`@Param()`注解
- 2.在mapper注册中心绑定接口
<mappers>
<mapper class="com.cwx.dao.UserMapper"/><!--这里用的是class,以前是用resources-->
</mappers>
- 测试:步骤和上面差不多,不再赘写。
- 本质:反射机制,底层是动态代理。
Mybatis执行流程(分析源码)
- 1.Resources获得配置文件 (用流来获取文件)
- 2.实例化
SqlSessionFactoryBuilder()
构造器,返回SQLSessionFactory
- 3.解析配置文件流
XMLConfigBuilder
- 4.
Configuration
对象包含所有的配置信息 - 5.
SqlSessionFactory
实例化,返回一个SqlSession
- 6.
Transaction
事务管理 - 7.创建
execute
执行器 - 8.创建一个
SqlSession
- 9.实现CRUD(若执行失败,则回滚)
- 10.若执行成功则
commit
数据关系之多对一查询
举例:有个student实体类,包含id,name,Teacher(老师的对象)多个学生对应一个老师,要求查出所有学生和(一个)老师
<!--方式一(常用): 按照结果嵌套处理 -->
<select id="getStudent" resultMap="StudentTeacher">
select s.name sname,s.id sid,t.name tname
from student s ,teacher t
where s.tid=t.id;
</select>
<resultMap id="StudentTeacher" type="Studenet">
<result property="id" column="sid">
<result property="name" column="sname">
<association property="teacher" javaType="Teacher">
<result property="name" column="sname">
</association>
</resultMap>
<!--方式二: 按照查询方式处理 -->
<select id="getStudent" resultMap="StudentTeacher2">
select * from student
</select>
<resultMap id="StudentTeacher2" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher">
</resultMap>
<select id="getTeacher" type="Teacehr">
select * from Teacher where id =#{tid}
</select>
总结:主要是利用了<association>
,当要查询的东西属于多的一方用<association>
,当要查询的东西属于一的一方用collection
。
数据关系之一对多查询
还是上面的例子,这次的角度站在老师这一方,就是一对多关系,Student类中的teacher对象,变为tid,Teacher类加一个List<Student>对象。
<!--方式一:按照结果嵌套处理-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.name sname,s.tid stid,t.id tid, t.name tname
from sutdent s,teacher t
where t.id=s.tid and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname">
<result property="id" column="tid">
<collection property="student" ofType="Studnet"><!--这里区别是使用了ofType,为了识别集合中的类型-->
<result property="name" cloumn="sname">
<result property="id" cloumn="sid">
</collection>
</resultMap>
<!--方式:按照查询方式嵌套处理-->
<select id="getTeacehr" resultMap="TeacherStudent2">
select * from teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--这里的column=id是为了给下面的条件用的id传值-->
<collection property="student" javaType="ArrayList" ofType="Student" select="getStudent" column="id">
</resultMap>
<select id="getStudent" type="Student">
select * from student where id =#{tid}
</select>
数据关系之多对多查询
思路:利用中间表
【面试高频】
- MySQL引擎
- InnoDB底层原理
- 索引
- 索引优化
动态SQL
- 动态SQL:指在不同的条件下生成不同的SQL语句。
- IF
<select id="quryBlogIf" type="Blog" parameType="map"><!--这里的参数是map类型,有可能会传多个值-->
select * from Blog where state ='ACTIVE'
<if test="title !=null">
and titile=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</select>
- 其他标签可以看 官方文档,超级详细!
- SQL片段:说白了就是讲公共的部分抽取出来,利用
<sql id="xxx">
标签,引用是用<include refid="xxx">
,这就就可以减少代码重复。 - Foreach标签:(当条件需要传一个集合时,直接用遍历,如id in(1,2,3),查找id在这里面的博客 )
- collection:传值过来的集合名字。
- item:从集合中取的单个的名字。
- open:扫描集合开始的位置一般用"("
- close:扫描集合结束的位置一般用")"
- separator:集合里面的值的分隔符,一般是","
缓存
- 什么是缓存?
缓存是存在内存中的临时数据,将经常查询的数据放在缓存中,用户就不用再去磁盘上查询,从而提高查询效率,减少系统开销,解决了高并发系统的性能问题。 - 数据库:当流量多了,需要多个数据库,需要实现:读写分离,主从复制(保持一致性)。
Mybatis缓存
- Mybatis 包含了一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,Mybatis中默认定义了两个缓存:一级缓存 和 二级缓存。
- 默认情况下,一级缓存自动开启,属于SqlSession级别的缓存,也称为本地缓存(查询完后数据会被缓存,关闭后缓存就消失)。
- 二级缓存需要手动开启和配置,它属于namespace级别的缓存(一个Mapper接口中所有的数据)。
- 为了提高扩展性,Mybatis定义了缓存接口Cache,可以通过实行这个接口自定义二级缓存。
一级缓存
- 只在SqlSession作用域中有缓存,一旦SqlSession.close()后缓存就没了。
- 缓存失效的情况:
- 当实现增删改操作时,缓存会被刷新,即清空。
- 当手动清除缓存时,使用
sqlSession.clearCache()
方法。
二级缓存
二级缓存开启前先要保证Mybatis是否开启了全局缓存,全局缓存默认是开启的。
- 二级缓存也叫全局缓存,一级缓存的作用域太低了,一个nameSpace空间对应一个二级缓存。
- 工作机制
- 一个会话查询一条数据,这个数据会被放在以及缓存中,我们要做的事将这个数据放入到二级缓存中,这样一级缓存关闭后数据还存在二级缓存中。(注意!这里只有当一级缓存关闭后才会给到二级缓存,若没有关闭则二级缓存没有数据!)
- 新的会话查询信息时,可以从二级缓存中获取相同的内容。
- 不同的Mapper查出的数据会放在自己对应的缓存(Map)中。
- 开启二级缓存:直接在Mapper.xml中配置
<cache/>
。
Tips:我们要将实体类序列化,实现Serializable接口,不然会报错
NoSerializableException
Mybatis缓存原理
- 查找缓存的顺序
- 先从二级缓存查找,若没有,在到一级缓存找,若还没有则从数据库中找。