MyBatis 框架
目录
1 总体概述
1.1 软件开发常用结构
1.1.1 三层架构
三层架构包括的三层:
界面层、业务逻辑层、数据访问层
三层的职责:
-
界面层:接受用户数据,显示请求的处理结果。(jsp,html,servlet)
-
业务逻辑层:接受界面层传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。(service)
-
数据访问层:与数据库打交道。实现对数据的增删改查。(dao)
三层对应的包:
-
界面层:controller (servlet)
-
业务逻辑层:service (service类)
-
数据访问层:dao (dao类)
三层中类的交互:
用户使用界面层——》业务逻辑层——》数据访问层——》数据库
三层对应的处理框架:
界面层——servlet——springmvc
业务逻辑层——service类——spring
数据访问层——dao类——mybatis
1.2 框架(Framework)
1.2.1 框架是一个模板
-
框架中定义好了一些功能,这些功能时可用的。
-
可以加入项目中自己的功能,这些功能可以利用框架中写好的功能。
框架是一个半成品软件,定义好了一些基础功能,需要加入自己的功能来完善。基础功能是可重复使用的,可升级的。
1.2.2 框架特点
-
框架是有局限的,不能干所有事情
-
框架是针对某一个领域有效。
-
框架是一个软件
1.3 JDBC的缺陷
使用JDBC的缺陷:
-
代码比较多,开发效率低
-
需要关注Connection,Statement,ResultSet对象创建和销毁
-
对Result查询的结果,需要自己封装为list
-
重复的代码比较多
-
业务代码和数据库的操作混在一起
总之,我们需要一个新的工具代替JDBC,以弥补JDBC的缺陷。那就是我们今天所学的MyBatis
1.4 MyBatis框架是什么
1.4.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.4.2 MyBatis
mybatis是MyBatis SQL Mapper Framework for Java (sql映射框架)
-
sql mapper:sql映射
可以把数据库表中的一行数据映射为一个java对象
一行数据可以看作是一个java对象,操作这个对象,就相当于操作表中的数据
-
Data Access Objects (DAOs):数据访问
对数据库执行增删改查
1.4.3 MyBatis提供了哪些功能
-
提供了创建Connection,Statement,ResultSet的能力,不用开发人员创建这些对象
-
提供了执行sql语句的能力,不用你执行sql
-
提供了循环sql,把sql的结果转为java对象List集合的能力
-
提供了关闭资源的能力,不用你关闭connection,statement,resulset
开发人员需要做的是:提供sql语句(工作量大幅减少了)
流程:开发人员提供sql语句——》mybatis处理sql——》开发人员得到List集合或者java对象
1.4.4 总结
mybatis是一个sql映射框架,提供数据库的操作能力。增强的JDBC,使用mybatis让开发人员集中精神写sql就可以了,不必关心Connection等的创建销毁,sql的执行。
2 快速入门
2.1 入门案例
2.1.1 使用MyBatis的准备
下载mybatis,此处我们使用的是3.5.1版本
Releases · mybatis/mybatis-3 · GitHub
2.1.2 创建一张表
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id int(11) NOT NULL,
name varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
age int(11) DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE=innoDB DEFAULT CHARSET=utf8;
INSERT INTO student VALUES(1001,"李四","lisi@163.com",20);
INSERT INTO student VALUES(1002,"张三","zhangsan@163.com",28);
创建好的表:
2.1.3 创建项目(略)
2.1.4 编辑POM文件增加MYSQL-connector和MyBatis依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
2.1.5 创建Student实体类和对应的Dao类(略)
2.1.6 创建MyBatis使用的配置文件——sql映射文件
sql映射文件:
-
一个xml文件
-
一般一个表对应一个sql映射表
-
应当存放在dao接口所在的目录
-
文件名称和接口保持一致
其作用在于:写与接口中方法对应的sql语句
<?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.mybatis.example.BlogMapper">
<!--
id:你要执行的sql语法的唯一标识,mybatis会使用这个id的值来找到要执行的sql语句
可以自定义,但是要求你使用接口中的方法名称
resultType:表示结果类型,是sql语句执行后得到的ResultSet
遍历这个Res得到的java对象的类型
值写的是类型的全限定名称
-->
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
<!-- 以上为官方文档的示例代码 -->
<!--
sql映射文件:写sql语句的,mybatis会执行这些sql
1.指定约束文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
mybatis-3-mapper.dtd 约束文件的作用,拓展名是dtd。
2.约束文件的作用:限制,检查在当前文件中出现的标签,属性必须符合mybatis要求
3.mapper 当前文件的根标签,必需的。
namespace 命名空间,唯一值,可以是自定义的字符串。
要求你使用dao接口的全限定名称。
4.在当前文件中,可以使用特定标签,表示数据库的特定操作。
<select> 查询,在标签内写select语句
<update> 更新,在标签内写update语句
...
基本与sql语句相同
-->
2.1.7 创建主配置文件
文件名可以自定义,文件类型为xml,文件应当被放置在resources的根目录下
其目的在于:
-
连接数据库
-
指定mapper文件的位置
<?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>
<!--
环境配置:数据库的连接信息
default:必须和某个environment的id值一样
告诉mybatis使用哪个数据库的连接信息。就是访问哪个数据库
-->
<environments default="development">
<!--
environment:一个数据库的配置,环境
id:一个唯一值,自定义,表示环境的名称
-->
<environment id="development">
<!--
transactionManager:mybatis的事务类型
type:JDBC(表示使用jdbc中的connection对象的commit,rollback做事务处理)
MANAGED:把mybatis的事务处理委托给其他容器(一个服务器软件,一个框架spring)
-->
<transactionManager type="JDBC"/>
<!--
dataSource:表示数据源,连接数据库
数据源:表示连接connection对象的;
在java中的规范中,实现了javax.sql.DataSource的都是数据源。
type:表示数据源类型
pooled:表示使用连接池,mybatis会创建pooledDataSource类
unpooled:不使用连接池,在每次执行sql语句,先创建连接,执行sql,再关闭连接
mybatis会创建一个unpooledDataSource类
JNDI:java命名和目录服务(windows注册表)
-->
<dataSource type="POOLED">
<!--
driver:数据库的驱动类型名
url:连接数据库的url字符串
username:访问数据库的用户名
password:访问数据库的密码
name是固定的不能修改
-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- sql mapper(sql映射文件)的位置 -->
<mappers>
<!--
一个mapper标签指定一个文件的位置
文件位置:要包括target/classes之后的文件路径
-->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
<mapper resource="org/mybatis/example/OrderMapper.xml"/>
<!--
指定多个mapper文件 可以一条一条mapper,如上面
也可以使用package,一次性添加多条mapper
-->
<!--
name:xml文件所在得包的名称,这个包中所有xml文件一次加载进mybatis
使用要求:
mapper文件名称需要和接口名称一样,区分大小写
mapper文件和dao接口需要在同一目录
-->
<package name="com.mybatis.example"/>
</mappers>
</configuration>
<!--
mybatis的主配置文件:主要定义了数据库的配置信息,sql映射文件的位置
1.约束文件
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
mybatis-3-config.dtd是约束文件的名称
2.<configuration>根标签。
-->
为了能在maven编译后读取到资源文件(sql映射文件)需要在pom加入一个插件
<resources>
<resource>
<directory>src/main/java</directory><!--所在目录-->
<includes><!--包括目录下的.properties,.xml文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
2.1.8 使用mybatis进行数据库的查询
使用mybatis的对象SqlSession,通过它的方法执行sql语句
public class MyApp {
public static void main(String[] args) throws IOException {
//访问mybatis读取student数据
//1.定义mybatis主配置文件的名称,从根目录开始(target/classes)
String config = "mybatis.xml";
//2.读取这个文件
InputStream in = Resources.getResourceAsStream(config);
//3.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
//5.**取SqlSession对象,从SqlSessionFactory中获取SqlSession**
SqlSession sqlSession = factory.openSession();
//6.拼接sql语句标识。sql映射文件中的namespace+"."+标签的id值
String sqlId = "com.pjh.dao.StudentDao"+"."+"findAll";
//7.执行sql语句,通过sqlId找到语句
List<Student> studentList = sqlSession.selectList(sqlId);
//8.输出结果
for(Student stu:studentList) {
System.out.println("查询的学生="+stu);
}
//9.关闭SqlSession对象
sqlSession.close();
}
}
得到结果:
查询的学生=Student{id=1001, name='李四', email='lisi@163.com', age=20} 查询的学生=Student{id=1002, name='张三', email='zhangsan@163.com', age=28}
2.1.9 使用MyBatis进行增删改
mybatis默认不会自动提交事务,需要手动进行提交
具体:
int num = sqlSession.insert(sqlId,student);
sqlSession.commit();//****
2.1.10 sql映射文件中sql语句的写法
sql语句中用#{字段名}来读取类的属性,并填入sql语句中
例如:
insert into student values(#{id},#{name},#{email},#{age})
2.2 主要类的介绍
-
Resources:
InputStream in = Resources.getResourceAsStream(config);
mybatis中的一个,负责读取主配置文件的类
-
SqlSessionFactoryBuilder:
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in);
负责创建SqlSessionFactory对象
-
SqlSessionFactory:需要使用大量资源,整个程序只需创建一次就够了;它是一个接口,实现类为DefaultSqlFactory。
作用:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
openSession()方法说明:
-
openSession():无参,获取非自动提交事务的SqlSession对象
-
openSession(boolean):为true时,获取自动提交事务的SqlSession;为false,获取非自动提交事务的SqlSession
-
-
SqlSession:接口,实现类为DefaultSqlSession
定义了操作数据的方法,例如selectOne,selectList,insert,delete等等
使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession()获取SqlSession对象,在执行完sql语句后,需要关闭它,执行SqlSession.close(),这样能保证它的使用是线程完全的
2.3 Mybatis工具类
以上入门案例使用的调用dao的方法十分繁琐,且有大量的代码复用问题,因此可以引入一个mybatis工具类,以减轻开发的繁琐程度。
package com.pjh.uitls;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtils {
private static SqlSessionFactory factory = null;
static {
String config = "mybatis.xml";
try {
InputStream in = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
//主要提供的方法,返回一个sqlSession
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
if(factory != null) {
sqlSession = factory.openSession();
}
return sqlSession;
}
}
再创建这个工具类后,2.1.8中的代码可以改写为
package com.pjh;
import com.pjh.domain.Student;
import com.pjh.uitls.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
import java.util.List;
public class MyApp2 {
public static void main(String[] args) throws IOException {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.pjh.dao.StudentDao.findAll";
List<Student> studentList = sqlSession.selectList(sqlId);
for(Student stu:studentList) {
System.out.println("查询的学生="+stu);
}
sqlSession.close();
}
}
3 深入理解
3.1 Mybatis动态代理
在引入了工具类后尽管稍微减少了开发的难度,但是仍然存在部分代码相似度比较高的情况,可以进一步优化,由此引出Mybatis的动态代理。
简要原理如下:
-
mybatis根据dao方法的调用,获取执行sql语句的信息。
-
mybatis根据你的dao接口,创建出一个dao接口的实现类,并创建这个类的对象。
-
完成sqlSession调用方法,访问数据库。
再次对2.1.8进行修改:
package com.pjh;
import com.pjh.dao.StudentDao;
import com.pjh.domain.Student;
import com.pjh.uitls.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class TestMyBatis {
/**
* 使用mybatis的动态代理机制,使用SqlSession.getMapper(dao接口)
* getMapper能获取dao接口对应的实现类对象
*/
public static void main(String[] args) {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//调用dao方法,执行数据库操作
List<Student> students = dao.findAll();
for(Student stu : students) {
System.out.println("学生="+stu);
}
}
}
使用动态代理后,程序员仅需创建Dao接口和对应的mapper文件,即可完成对数据库的操作。
3.2 传入参数
从java代码中把数据传入mapper文件的sql语句中。
-
parameterType:写在mapper文件中的一个属性。表示dao接口中方法参数的数据类型。其值为java数据类型的全限定名称或者mybatis定义的别名。
Alias Mapped Type _byte byte _longlong long _short short _int int _integer int _double double _float float _boolean boolean string String byte Byte long Long short Short int Integer integer Integer double Double float Float boolean Boolean date Date decimal BigDecimal bigdecimal BigDecimal object Object map Map hashmap HashMap list List arraylist ArrayList collection Collection iterator Iterator mapper文件中:
<!-- 全限定名称 --> <select id="selectById" parameterType="java.lang.Integer" resultType="com.pjh.domain.Student"> select * from student where id=#{id} </select> <!-- 别名 --> <select id="selectById" parameterType="int" resultType="com.pjh.domain.Student"> select * from student where id=#{id} </select>
但实际上parameterType可以不写,mybatis通过反射机制可以发现接口参数类型。
<select id="selectById" resultType="com.pjh.domain.Student"> select * from student where id=#{id} </select>
-
简单类型参数:mybatis把java的基本数据类型和string都叫做简单类型。
在mapper文件获取简单类型的一个参数的值,使用#{任意字符}
-
多个参数,使用@Param命名参数
//接口 public List<Student> selectMulitParam(String name, Integer age); //使用@Param("参数名") public List<Student> selectMulitParam(@Param("myname") String name, @Param("myage") Integer age);
mapper文件:
<select> select * from student where name=#{myname} and age=#{myage} </select>
-
多个参数,使用java对象参数
使用对象语法:#{属性名, javaType=类型名称, jdbcType=数据类型} 完整格式
javaType:指java中的属性数据类型
jdbcType:指数据库中的数据类型
JDBCTypes: BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED TINYINT REAL VARCHAR BINARY BLOB NVARCHAR SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB BIGINT DECIMAL TIME NULL CURSOR ARRAY 简化的格式:#{属性值},javaType和jdbcType的值mybatis反射可以获取,不用提供
-
多个参数,按参数位置传参
语法格式:#{arg0},其中0是参数的位置,其他是固定的
-
多个参数,使用Map传参
定义一个map:
Map<String,Object> map = new HashMap<>(); map.add("myname","张三"); map.add("myage",22);
Dao接口:
public List<Student> select(Map<String, Object> map);
语法格式:#{myname},其中大括号中写map中的key值
-
#与$
#:占位符,用来代替实际参数值,并使用PrepareStatement对象执行sql语句,#{}代替了sql语句中的?。这样做更加安全、迅速,建议使用。
$:字符串替换,使用的Statement对象执行,效率比ps低,且存在sql注入的问题。一般用于替换列名或者表名
3.3 Mybatis内部机制其实是JDBC
略
3.4 封装MyBatis输出结果
mybatis执行了sql语句,得到的java对象
3.4.1 resultType
结果类型,指sql语句执行完毕后,数据转为的java对象类型,该java类型是任意的。
处理方法:
-
mybatis执行sql语句,然后mybatis调用类的无参数构造方法,创建对象。
-
mybatis把ResultSet指定列值付给同名的属性。
类型:
-
简单类型:同3.2.2
-
定义自定义类型的别名
-
在mybatis主配置文件中定义,使用<typeAlias>定义别名
-
或者使用<package>,name是包名,这个包中的所有类,类名就是别名(类名不区分大小写)
-
可以在resultType中使用自定义的别名
-
3.4.2 查询返回Map
mybatis可以把查询结果组织成一个map并返回
注意:
-
列名是map的key,列值是map的value
-
只能返回一行记录,多余一行会报错
3.4.3 resultMap
结果映射,指定列名和java对象的属性对应关系的
用途:
-
自定义列值赋值给哪个属性
-
当你的列名和属性名不一样时,一定使用resultMap
语法格式:
<!-- 定义resultMap
id:自定义名称,表示你定义的这个resultMap
type:java类型的全限定名称
-->
<resultMap id="stuMap" type="com.pjh.domain.student">
<!--
列名和java属性将的关系
主键列,使用id标签
column:列名
property:java类型的属性名
-->
<id column="" property=""/>
<!-- 非主键列,使用result标签 -->
<result column="" property=""/>
</resultMap>
<!-- 在select标签中使用resultMap属性,其中写id名 -->
<select id="selectById" resultMap="stuMap">
select * from student where id=#{id}
</select>
注意:
-
resultType和resultMap不要一起用
-
除了使用resultMap来解决列名和属性名不一致的方法,还可以通过sql语句中的as给列名取和属性名相同的别名来解决。
3.5 模糊查询like
在mybatis1中有两种方法:
-
java代码中指定like的内容(推荐)
<select id="selectByName" resultType="com.pjh.domain.Student"> select * from student where name like #{name} </select>
此方法需要提前准备传入的name属性,例如:
String name = “%李%”
-
在mapper文件中拼接like的内容
<select id="selectByName" resultType="com.pjh.domain.Student"> select * from student where name like "%" #{name} "%" </select>
4 动态sql
sql的内容是变化的,可以根据条件获取到不同的sql语句,主要是where部分发生变化
动态sql的实现,主要使用mybatis提供的标签。
在使用动态sql时,使用java对象作为参数
4.1 <if>标签
if标签通过判断test中的判断结果是否为true,来判断是否将if标签中的sql语句加入
<if test="判断java对象的属性值">
部分sql语句
</if>
4.2 <where>标签
where标签用来包含多个if的,当多个if有一个成立,where会自动增加一个where关键字,并去掉if中多余的and,or等。
<where>
<if test="判断java对象的属性值">
部分sql语句
</if>
<if test="判断java对象的属性值">
部分sql语句
</if>
</where>
4.3 <foreach>标签
循环java中的数组,list集合,主要用在sql的in语句中。
例如:学生id是1001,1002,1003的三个学生
select * from student where id in (1001,1002,1003)
<foreach collection="" item="" open="" close="" separator="">
#{}
</foreach>
<!--
collection:表示接口中的方法参数的类型,如果是数组使用array,如果是list集合使用list
item:自定义的表示数组和集合成员的变量
open:循环开始的字符
close:循环结束的字符
separator:集合成员之间的分隔符
-->
4.4 代码片段
能够复用一些重复语句
步骤:
-
先定义
<sql id="自定义名称唯一">sql语句,表名,字段等</sql>
-
再使用
<include refid="id的值"/>
5 MyBatis配置文件
5.1 主配置文件,见2.1.7
settings:略
5.2 属性配置文件
把数据库连接信息放到一个单独的文件中。和mybatis主配置文件分开
便于修改,保存,处理多个数据库的信息。
步骤:
-
在resources目录中定义一个属性配置文件,xxxx.properties;在属性配置文件中,定义数据,格式是key=value。
key:一般使用'.'做多级目录的。例如jdbc.mysql.driver,jdbc.driver
-
在mybatis的主配置文件,使用<properties>指定文件位置
在需要使用值的地方,${key}
6 拓展——PageHelper
PageHelper可以便捷的帮你实现数据分页的功能,现已支持包括oricle,mysql在内的多达13种数据库。
安装步骤:
-
在pom中添加依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency>
记得加载一下pom
-
在mybatis主配置文件中的<environments>之间添加插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"/> </plugins>
-
在使用时调用PageHelper的静态方法startPage,会自动为你的sql语句加上limit
@test public void testSelectAll() { SqlSession sqlSession = MyBatisUtils.getSqlSession(); StudentDao dao = sqlSession.getMapper(StudentDao.class); //加入PageHelper的方法,分页 //pageNum表示:第几页P //pageSize:一页中有多少数据 PageHelper.startPage(1,2); List<Student> studentList = dao.selectAll(); for(Student stu : studentList) { System.out.println(stu); } }