Mybatis学习总结
1. 框架概述
1.1 三层框架
- 三层架构包含的三层:
- 界面层(User Interface layer)SpringMVC
- 业务逻辑层(Business Logic Layer)Spring
- 数据访问层(Data access layer)Mybatis
- 三层框架的功能:
- 界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和用户交互,手机 app 也就是表示层的,用户在 app 中操作,业务逻辑在服务器端处理。
- 业务逻辑层:接收表示传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。
- 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交
给业务层,同时将业务层处理的数据保存到数据库.
1.2 框架是什么
- 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种认为,框架是可被应用开发者定制的应用骨架、模板。简单的说,框架其实是半成品软件,就是一组组件,供你使用完成你自己的系统。从另一个角度来说框架一个舞台,你在舞台上做表演。在框架基础上加入你要完成的功能。框架是安全的,可复用的,不断升级的软件。
1.3 MyBatis 框架
1.3.1概述:
- MyBatis 框架:
MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。
iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAOs)
1.3.2 Mybaits框架的功能
- 减轻使用 JDBC 的复杂性,不用编写重复的创建 Connetion , Statement ; 不用编写关闭资源代码。直接使用 java 对象,表示结果数据。让开发者专注 SQL 的处理。 其他分心的工作由 MyBatis 代劳。
- 注册数据库的驱动,例如 Class.forName(“com.mysql.jdbc.Driver”))
- 创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象
- 从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象
- 关闭资源
2. Mybatis环境
1.下载
2.准备
2.1 创建mysql的数据库和表
2.2 创建maven工程
2.3 并且加入maven坐标
pom.xml 加入 maven 坐标:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependencies>
2.4 加入maven插件
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
2.5 编写实体类
实体类:
框架目录结构:
2.6 编写 Dao 接口 StudentDao
2.7 编写 Dao 接口 Mapper 映射文件 StudentDao.xml
要求:
- 在 dao 包中创建文件 StudentDao.xml
- StudentDao.xml 文件名称需要和接口 StudentDao 一样(区分大小写)
文件内容:
<?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">
<mapper namespace="org.example.dao.Studentdao">
<select id="selectStudent" parameterType="java.lang.Integer" resultType="Stu">
select * from student where sno = #{s}
</select>
</mapper>
- namespace:必须有值,自定义的唯一字符串 推荐使用:dao 接口的全限定名称
- select 查询数据, 标签中必须是 select 语句
id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称, 使用名称表示要执行的 sql 语句
resultType: 查询语句的返回结果数据类型,使用全限定类名
2.8 创建 MyBatis 主配置文件
- 项目 src/main 下创建 resources 目录,设置 resources 目录为 resources root
- 创建主配置文件:名称为 mybatis.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>
<!--
环境配置:数据库的配置信息
-->
<!--
生成查询日志
-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="org.example.domain.Student" alias="Stu"></typeAlias>
</typeAliases>
<environments default="development">
<!--
environment:一个数据库信息的配置
id:唯一值,自定义的,表示环境的名称
transactionManager :mybatis的事务类型
type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理)
default:必须和某个environment的id值一样。
告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库
-->
<environment id="development">
<transactionManager type="JDBC"/>
<!--
dataSource:表示数据源,连接数据库的
type:表示数据源的类型, POOLED表示使用连接池
-->
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/school"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--访问数据库的密码-->
<property name="password" value="qsc13579"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--一个mapper标签指定一个文件的位置。
从类路径开始的路径信息。 target/classes(类路径)
<mapper resource="org/example/dao/Studentdao.xml"/>
第二种方式,<package>
-->
<package name="org.example.dao"/>
</mappers>
</configuration>
加入日志输出的功能:
mybatis.xml 文件加入日志配置,可以在控制台输出执行的 sql 语句和参数
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
2.9 创建test类
例如:
public class test {
/*
测试插入
注意:mybatis是没有自动提交事务的,需要自己提交。
注意:mybatis是没有自动提交事务的,需要自己提交。
*/
@Test
public void testSelectStudent(){
/*
* 使用mybatis的动态代理机制, 使用SqlSession.getMapper(dao接口)
* getMapper能获取dao接口对应的实现类对象。
*/
SqlSession sqlSession = Mybatisutils.getSqlSession();
/*
创建SqlSessionFactory对象
会产生一个SqlSessionFactory,这个SqlSessionFactory携带着一个由Mybatis配置文件生解析而成的配置类(Configuration.java),
这个配置类里就包含着已注册Mapper的信息。
*/
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
//在调用getMapper()方法的时候会传一个Class类进去,这时,Configuration就会根据类型来匹配Mapper,这样就返回了相应的接口。
Student student = studentdao.selectStudent(3);
System.out.println("Student = " + student);
}
2.10 总结
- mapper文件
- dao包中的StudentDao.xml就是mapper文件。是sql映射文件,是用来写sql语句的,Mybatis会执行这些sql语句
- 约束文件的作用:限制,检查在当前文件中出现的标签,属性必须符合mybatis的要求
- mapper:是当前文件的根标签,是必须的
namespace:命名空间,值是唯一的,可以是自定义的字符串,但是最好使用dao接口的全限定名称
2. 对象分析:
@Test
public void testInsert() throws IOException {
//1.定义mybatis主配置文件的名称,从类路径的根开始
String config = "mybatis.xml";
//2.读取config表示的文件
InputStream inputStream = Resources.getResourceAsStream(config);
//3.创建sqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//4.创建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//5.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//6.指定要执行的sql语句的标识,namespace + . +标签的id
String SqlId = "org.example.dao.Studentdao" + "." + "insertStudent";
//7.通过SqlId执行sql语句(重要)
Student student = new Student();
student.setSno(7);
student.setSname("ycx");
student.setClassno(69);
int number = sqlSession.insert(SqlId,student);
//8.输出结果
//studentList.forEach( stu -> System.out.println(stu));
System.out.println("输出结果:" + number);
sqlSession.commit();
//9.关闭SqlSession对象
sqlSession.close();
}
}
其中
- Resources 类
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。 - SqlSessionFactoryBuilder 类
SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。 - SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
openSession(true):创建一个有自动提交功能的 SqlSession
openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
openSession():同 openSession(false) - SqlSession 接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭 - 其中指定要执行的sql语句的标识,namespace + . +标签的id.通过SqlId的值找到接口
String SqlId = "org.example.dao.Studentdao" + "." + "SelectStudents";
疑问: SqlId中的 “org.example.dao.Studentdao” + “.” + “SelectStudents”;对应的是mapper文件和其中的Id还是StudentDao和其中的方法
3 工具类
3.1创建 MyBatisUtil 类
- 路径:
- 将可以重复利用的代码写进工具类中
public class Mybatisutils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
String config = "mybatis.xml";//需要和项目中文件名一样
try {
InputStream inputStream = Resources.getResourceAsStream(config);
/*
创建SqlSessionFactory对象
会产生一个SqlSessionFactory,这个SqlSessionFactory携带着一个由Mybatis配置文件生解析而成的配置类(Configuration.java),
这个配置类里就包含着已注册Mapper的信息。
*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession的方法
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if(sqlSessionFactory != null){
sqlSession = sqlSessionFactory.openSession();//非自动提交事务
}
return sqlSession;
}
- 源码分析(重点 难点)
- 静态代码块的内容只需要调用一次类就可以一直使用并且创建
- 创建SqlSessionFactory对象会产生一个SqlSessionFactory,这个SqlSessionFactory携带着一个由Mybatis配置文件生解析而成的配置类(Configuration.java),这个配置类里就包含着已注册Mapper的信息。
- 我们在调用getMapper()方法的时候会传一个Class类进去,这时,Configuration就会根据类型来匹配Mapper,这样就返回了相应的接口。(这就是对应)
4 基本的 CURD步骤
select insert update delete 操作
- 在StudentDao接口中增加相应方法
-
public Student selectStudent(Integer sno);
-
public Student insertStudent(Integer sno);
-
public Student updateStudent(Integer sno);
-
public Student deleteStudent(Integer sno);
- 在StudentDao.xml中加入sql语句,以查找为例
<select id="selectStudent" parameterType="java.lang.Integer" resultType="Stu">
select * from student where sno = #{s}
</select>
- 增加测试方法,以查询为例
@Test
public void testSelectStudent(){
/*
* 使用mybatis的动态代理机制, 使用SqlSession.getMapper(dao接口)
* getMapper能获取dao接口对应的实现类对象。
*/
SqlSession sqlSession = Mybatisutils.getSqlSession();
/*
创建SqlSessionFactory对象
会产生一个SqlSessionFactory,这个SqlSessionFactory携带着一个由Mybatis配置文件生解析而成的配置类(Configuration.java),
这个配置类里就包含着已注册Mapper的信息。
*/
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
//在调用getMapper()方法的时候会传一个Class类进去,这时,Configuration就会根据类型来匹配Mapper,这样就返回了相应的接口。
Student student = studentdao.selectStudent(3);
System.out.println("Student = " + student);
}
5. 动态代理
5.1 传统Dao开发的缺点
- 在前面例子中自定义 Dao 接口实现类时发现一个问题:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。
- 所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB 进行操作。这种对 Dao 的实现方式称为 Mapper 的动态代理方式。
- Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的
5.2 使用Dao代理
- 去掉 Dao 接口实现类
- 使用getMapper() 获取代理对象,并且执行操作(以查询为例)
//工具类
SqlSession sqlSession = Mybatisutils.getSqlSession();
//getMapper()方法
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
Student student = studentdao.selectStudent(3);
System.out.println("Student = " + student);
- 执行原理在工具类中说明了
- 创建SqlSessionFactory对象会产生一个SqlSessionFactory,这个SqlSessionFactory携带着一个由Mybatis配置文件生解析而成的配置类(Configuration.java),这个配置类里就包含着已注册Mapper的信息。
- 我们在调用getMapper()方法的时候会传一个Class类进去,这时,Configuration就会根据类型来匹配Mapper,这样就返回了相应的接口。(这就是对应)
更多例子:
@Test
public void testSelectMore(){
SqlSession sqlSession = Mybatisutils.getSqlSession();
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
List<Student> studentList = studentdao.selectMore(3,"aaa");
for(Student s:studentList){
System.out.println("Student = " + s);
}
sqlSession.close();
}
@Test
public void testSelectLike(){
SqlSession sqlSession = Mybatisutils.getSqlSession();
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
String name = "%a%";
List<Student> studentList =studentdao.selectLike(name);
for(Student s:studentList){
System.out.println("Student = " + s);
}
sqlSession.close();
}
@Test
public void testSelectIf(){
SqlSession sqlSession = Mybatisutils.getSqlSession();
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
Student student = new Student();
student.setSname("aaa");
List<Student> studentList = studentdao.selectStudentIf(student);
for (Student s: studentList){
System.out.println("Student:" + student);
}
}
5.3 使用动态代理的要求:
- dao接口和mapper文件放在一起,同一个目录
- dao接口和mapper文件名称一致
- mapper文件中的namespace的值是dao接口的全限定名称
- mapper文件中的等标签的id是接口中的方法名称
6. Mybatis参数传递
- 从 java 代码中把参数传递到 mapper.xml 文件。
6.1 传递一个简单参数
- 简单类型:java的基本类型数据和String都叫简单类型
- 在mapper文件获取简单类型的一个参数的值,使用#{任意字符}
例如:
List<Student> selectLike(String name);
对应的mapper文件中的语句:
select * from student where sname like #{name}
6.2 多个参数-使用@Param
- 当 Dao 接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”),
- mapper 文件使用#{自定义参数名}。
接口方法:
<select id="selectLike" resultType="org.example.domain.Student">
select * from student where sname like #{name}
</select>
mapper文件:
<select id="selectMore" resultType="org.example.domain.Student">
select * from student where sname = #{myname} or sno = #{mysno}
</select>
6.3 多个参数-使用对象
- 使用 java 对象传递参数, java 的属性值就是 sql 需要的参数值。 每一个属性就是一个参数。
- 语法格式: #{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 }
javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。常用格式 #{ property }
例
- 接口方法:
List<Student> selectStudentIf(Student student);
- mapper文件
<select id="selectStudentIf" resultType="org.example.domain.Student">
<!--主sql-->
select * from student where
<if test="sname != null ">
sname = #{sname}
</if>
</select>
- 测试方法:
@Test
public void testSelectIf(){
SqlSession sqlSession = Mybatisutils.getSqlSession();
Studentdao studentdao = sqlSession.getMapper(Studentdao.class);
Student student = new Student();
student.setSname("aaa");
List<Student> studentList = studentdao.selectStudentIf(student);
for (Student s: studentList){
System.out.println("Student:" + student);
}
}
注意
selectStudentIf方法中的参数是一个Student对象,mapper文件中的参数传递是sname = #{sname}
,所以就不能像简单传入一个参数那样在#{}里面随意的更改名称,而是要和对应的Student属性名称一样,这样才能将参数传递进去,否则会报错
Student类(部分):
public class Student {
private int sno;
private String sname;
private int classno;
}
简单来说,就是通过值传对象的时候#{}里面可以是任意的字符串,而通过对象传参的时候#{}里面需要是对象的属性名
6.4 #和¥
- #:占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。这样做更安全,更迅速,通常也是首选做法
- $ 字符串替换,告诉 mybatis 包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。
例
通用方法,使用不同列作为查询条件
接口方法:
Student findByDiffField(@Param("col") String colunName,@Param("cval") Object
value);
mapper 文件:
<select id="findByDiffField" resultType="com.bjpowernode.domain.Student">
select * from student where ${col} = #{cval}
</select>
测试方法:
@Test
public void testFindDiffField(){
Student student1 = studentDao.findByDiffField("id",1002);
System.out.println("按 id 列查询:"+student1);
Student student2 = studentDao.findByDiffField("email","zhou@126.net");
System.out.println("按 email 列查询:"+student2);
总结
- #的结果: select * from student where sno = ?
$的结果: select * from student where sno = 100 - #做的是JDBC中preparedStatement = connection.prepareStatement(sql);的
- ¥做的是字符串的替换,使用Statement对象执行sql效率低且不安全,用户可能通过表单传入影响数据库的语句。一般用来替换列名
1.#是占位符 ¥是字符串连接方式
2.#是preparedStatement效率高安全性高 ¥是Statement效率低,有安全隐患(sql注入的风险)
3。#sql可以解决sql注入问题 ¥可以替换列名/表名
7. 动态SQL
7.1 动态SQL——if
- 对于该标签的执行,当 test 的值为 true 时,会将其包含的 SQL 片断拼接到其所在的 SQL 语句中。
语法:<if test=”条件”> sql 语句的部分 </if>
- 接口方法:
List<Student> selectStudentIf(Student student);
- mapper文件:
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
where 1=1
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</select>
7.2 动态SQL——where
<if/>标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句。因为,若 where 后
的所有<if/>条件均为 false,而 where 后若又没有 1=1 子句,则 SQL 中就会只剩下一个空的 where,SQL
出错。所以,在 where 后,需要添加永为真子句 1=1,以防止这种情况的发生。但当数据量很大时,会
严重影响查询效率。
使用<where/>标签,在有查询条件时,可以自动添加上 where 子句;没有查询条件时,不会添加
where 子句。需要注意的是,第一个<if/>标签中的 SQL 片断,可以不包含 and。不过,写上 and 也不错,
系统会将多出的 and 去掉。但其它<if/>中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student北京动力节点 www.bjpowernode.com
<where>
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</where>
</select>
7.3 动态SQL——foreach
签用于实现对于数组与集合的遍历。对其使用,需要注意:
- collection 表示要遍历的集合类型, list ,array 等。
- open、close、separator 为对遍历内容的 SQL 拼接。
语法:
<foreach collection="集合类型" open="开始的字符" close="结束的字符"
item="集合中的成员" separator="集合成员之间的分隔符">
#{item 的值}
</foreach>
mapper文件:
<select id="selectStudentForList"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")"
item="stuid" separator=",">
#{stuid}
</foreach>
</if>
</select>
7.4 数据库中列名和java类中列名不一样时
方法1:在mapper文件中使用resultMap标签来指定对应的列名和属性名
<resultMap>
<id column ="id" property = "stuId" >(主键用ID)
<result column ="name" property = "stuname" >
<result column ="classno" property = "stuclassno" >
列名 属性名
</resultMap>
方法2:在select中使用别名
select sname as stuname from student
列名 属性名
8. Mybatis配置文件
8.1 主配置文件: mybatis.xml
- 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>
包含:
- 定义别名
- 数据源
- mapper文件
- dataSource
Mybatis 中访问数据库,可以连接池技术,但它采用的是自己的连接池技术。在 Mybatis 的 mybatis.xml配置文件中,通过来实现 Mybatis 中连接池的配置。
- dataSource分为三类:
-
- UNPOOLED : 不使用连接池的数据源
-
- POOLED :使用连接池的数据源
-
- JNDI :使用 JNDI 实现的数据源
dataSource的配置:
- JNDI :使用 JNDI 实现的数据源
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/school"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--访问数据库的密码-->
<property name="password" value="qsc13579"/>
</dataSource>
8.2 mapper文件:
- 一个mapper标签指定一个文件的位置。从类路径开始的路径信息。 target/classes(类路径)
1)使用相对于类路径的资源,从 classpath 路径查找文件
例如:
2)指定包下的所有 Dao 接口
如:
注意:此种方法要求 Dao 接口名称和 mapper 映射文件名称相同,且在同一个目录中
8.3 事务
- 默认是需要手动提交的
- 设置自动提交的方式,factory 的 openSession() 分为有参数和无参数的。
有参数为 true,使用自动提交,可以修改 MyBatisUtil 的 getSqlSession()方法。
session = factory.openSession(true);
再执行 insert 操作,无需执行 session.commit(),事务是自动提交的
8.4 使用数据库属性配置文件
8.4.1 在 resources 目录创建 jdbc.properties 文件
8.4.2 使用 properties 标签
在主配置文件中加入
8.4.3 使用 key 指定值
8.5 类型别名
- 第一种方式:在mybatis主配置文件中定义,使定义别名。typeAlias中type:自定义类型的全限定名称,alias:别名
<typeAliases>
<typeAlias type="org.example.domain.Student" alias="Stu"></typeAlias>
</typeAliases>
- 第二种方式: name是包名,这个包中的所有类,类名就是别名 (推荐代码量多类名多的时候使用)
批量定义别名,扫描整个包下的类,别名为类名(首字母大写或小写都可以)
<package name="com.bjpowernode.domain"/>
<package name="...其他包"/>
9 细节以及源码分析
9.1 SqlSessionFactoryBuilder
我们以xml的形式创建了一个配置了数据库的基本信息,Mybatis的解析程序会将这个 mybatis-config.xml文件配置的信息解析到 Configuration类对象里面,然后利用SqlSessionFactoryBulider读取这个对象创建SqlSessionFactory
- 我们的xml配置文件 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>
<!--
环境配置:数据库的配置信息
-->
<!--
生成查询日志
-->
<!-- <settings>-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->
<typeAliases>
<typeAlias type="org.example.domain.Student" alias="Stu"></typeAlias>
</typeAliases>
<environments default="development">
<!--
environment:一个数据库信息的配置
id:唯一值,自定义的,表示环境的名称
transactionManager :mybatis的事务类型
type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理)
default:必须和某个environment的id值一样。
告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库
-->
<environment id="development">
<transactionManager type="JDBC"/>
<!--
dataSource:表示数据源,连接数据库的
type:表示数据源的类型, POOLED表示使用连接池
-->
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--访问数据库的密码-->
<property name="password" value="qsc13579"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--一个mapper标签指定一个文件的位置。
从类路径开始的路径信息。 target/classes(类路径)
-->
<mapper resource="org/example/dao/Studentdao.xml"/>
<mapper resource="org/example/dao/StudentClassdao.xml"/>
</mappers>
</configuration>
2.通过Resource中的getResourceAsReader方法底层使用IO流方式兑取配置文件
3.通过SqlSessionFactoryBuilder获取SqlSessionFactory对象。
(build方法)
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
这里插一下 SqlSessionFactoryBuilder 的源码,可以看到build有很多重载的build方法,参数不全的build都会直接return调用那个参数全的build,也就是说参数全的那个build是入口,注意看最后一个build方法的参数是一个Configuration 类型的参数
public class SqlSessionFactoryBuilder {
//参数不完整的build方法
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
//参数不完整的build方法
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
//参数不完整的build方法
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
//参数完整的build方法
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//参数不完整的build方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//参数不完整的build方法
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
//参数不完整的build方法
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
4.build 方法源码:
//SqlSessionFactoryBuilder.class
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//4.1进入解析XMLConfigBuilder的有参构造
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//4.2 进入parse方法
//4.3进入build方法中
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
4.1 进入解析XMLConfigBuilder的有参构造
这一步的作用是new出了一个XMLConfigBuilder 的对象 parser
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;//设置parsed为false,目的为了只让Xml文件只加载一次
this.environment = environment;
this.parser = parser;
}
4.2 根据上一步new出的对象parser ,调用其parse()方法,在parse()中,调用了parseConfiguration 方法,之后返回一个Configuration 类型的对象
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration:解析xml文件节点信息,将信息封装到Configuration对象中
//解析xml文件节点信息,将信息封装到Configuration对象中
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
4.3 进入build方法中,也就是把上一步的Configuration 对象带入,调用的就是
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
这个build方法了,返回了一个DefaultSqlSessionFactory,将其返回给SqlSessionFactory就完成了流程。
补充:
SqlSessionFactory在Mybatis中有两个实现类,分别是DefaultSqlSessionFactory和SqlSessionManager这里我们使用的是DefaultSqlSessionFactory
流程图