一、什么是MyBatis?
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apachesoftware foundation迁移到了google code,并且改名为MyBatis 。2013年11日转移到Github。
可以看作是jdbc的一个高级封装
二、解决的主要问题?
减轻使用JDBC的复杂性,不用编写重复的创建Connetion ,Statement ;不用编写关闭资源代码。
直接使用java对象,表示结果数据。让开发者专注SQL的处理。其他分心的工作由MyBatis代劳。
SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
三、使用步骤
0 创建数据库和表
1 新建一个maven项目
2 修改pom.xml文件
1)加入mybatis依赖和MySQL驱动等
<!-- 依赖列表 -->
<dependencies>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
2)在build标签中加入资源插件
<build>
<!-- 资源插件:处理src/main/java目录下的xml -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3 根据数据库表创建实体类
4 创建dao接口,定义操作数据库的方法
5 创建mapper文件(.xml)
Mybatis框架推荐是把sql语句和java代码分开
推荐mapper文件:定义和dao接口在同一目录下,并且名字和dao接口的名字一致,一个表对应一个mappper文件
mapper文件内部主要介绍:
<?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="com.zhiyou.mybatis.dao.StudentMapper">
<!-- 1.约束文件
http://mybatis.org/dtd/mybatis-3-mapper.dtd
约束文件作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序。
2.mapper是根标签
namespace:命名空间,必须有值,不能为空。唯一值。推荐使用Dao接口的全限定名称。
作用:参与识别sql语句的作用。
3.在mapper里面可以写<insert> , <update>,<delete>,<select>等标签。
<insert>里面是insert语句,表示执行的insert操作
<update>里面是 update语句
<delete>里面是delete语句
<select>里面是 select语句
-->
</mapper>
mapper文件中直接写sql语句即可
<select>:表示查询操作,里面是select语句
id:要执行的sql语句的唯一标识,是一个自定义宁符串。推荐使用dao接口中的方法名称
resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象。
resultType的值现在使用的java对象的全限定名称
6 创建mybatis的主配置文件(xml文件),放在resources目录下
1)定义创建连接实例的数据源对象(DataSource)
2)引入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>
<!--添加日志设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--起别名,只给一个实体类起别名-->
<!-- <typeAliases >-->
<!-- <typeAlias type="com.zhiyou.mybatis.entity.Student" alias="student"></typeAlias>-->
<!-- </typeAliases>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_source"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--
使用mapper的resource属性指定mapper文件的路径。这个路径是从target/classes路径开启的
使用注意:
resource=""mapper文件的路径,使用/分割路径”一个mapper resource指定一个mapper文件
-->
<mappers>
<!-- <mapper resource="com/zhiyou/mybatis/dao/StudentMapper.xml"/>-->
<!-- <mapper resource="com/zhiyou/mybatis/dao/MyClassMapper.xml"/>-->
<!-- <package name="com.zhiyou.mybatis.dao"/>-->
<mapper resource="com/zhiyou/mybatis/dao/DongTaiSqlMapper.xml"></mapper>
</mappers>
</configuration>
7 创建测试类进行测试
public class TestFindOne {
public static void main(String[] args) throws IOException {
//mybatis核心类
//1 定义mybatis配置文件的位置,从类路径开始的相对路径
String config="mybatis.xml";
//2 读取主配置文件,使用mybatis中的Resources类
InputStream inputStream = Resources.getResourceAsStream(config);
//3 创建SqlSessionFactory对象,使用SqlSessionFactoryBuilder类
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//4 获取SqlSession对象
SqlSession sqlSession = factory.openSession();
//5 指定要执行的sql语句的id
//sql的id= namespace+"."+select等标签内的id属性值
String sqlId="com.zhiyou.mybatis.dao.StudentDao"+"."+"findStudentById";
//6 通过sqlSession的方法执行sql语句
Student student=sqlSession.selectOne(sqlId);
System.out.println(student);
//7 关闭sqlSession
sqlSession.close();
}
}
8 日志的设置
设置日志,运行代码时可以看到sql语句和sql的执行结果
<!--添加日志设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
9 事务的提交
mybatis默认执行sql语句是手动提交事务,在 insetrt update delete 操作后要手动进行事务的提交
方式1:手动提交
方式2:创建sqlsession对象时,设置自动提交
10 工具类和模板的创建
首先设置mapper的文件模板
同样主配置文件的模板也是这样创建的
创建工具类,新创建一个包util,然后再在包里创建一个类
11 mybatis 的 mapper代理方式
代理技术:由mybatis创建dao接口的实现类Proxy(DaoImpl)
使用框架创建的实现类代替你自己手动创建的实现类的功能,不用开发人员写dao接口的实现类
使用要求:mapper标签中的namespace必须设置为dao的全限定名称
标签内的id 必须为dao接口中的方法名称
实现方式
使用sqlSession对象的方法getmapper(dao.class)
使用代理模式后,在主配置文件中引入mapper文件的方式可以用以下方式
直接写mapper文件所在的包的全限定名称,但是必须要求mapper文件的名字和mapper接口名字一致
四、mybatis中的一些重要的对象介绍
1 Resource:mybatis框架中的对象,一个作用就是读取配置文件的信息
2 SqlSessionFactoryBuilder:负责创建SqlSessionFatory对象
3 SqlSessionFatory:它的对象是一个重要的对象
它是一个重量级的对象,创建此对象需要更多的时间和资源。因此在项目中有一个就够了
其实它是一个接口:作用是SqlFactory的工厂,就是创建SqlSession对象
他的真正实现类是DefaultSqlSessionFactory
SqlSessionFatory的方法
OpenSession()获取一个默认的SqlSession对象,默认是需要手动提交事务的
OpenSession(boolean)boolean参数表示是否需要自动提交
True 为自动提交,false为手动提交
4 SqlSession对象 :是通过SqlSessionFatory获取的,SqlSession本身就是一个接口
DefaultSqlSession:实现类
SqlSession作用是提供了大量的执行sql语句的方法
五、parameterType入参类型
表示参数的类型,指dao方法的形参数据类型。这个参数的数据类型是给mybatis使用,mybatis在给sql语句的参数赋值时使用。
第一个用法:Java类型的全限定名称 paramType="java.lang.Integer"
第二个用法:mybatis定义的java类型的别名 paramType="int"
paramType:mybatis可通过反射机制获取dao接口方法参数的类型,可以不写
1、dao接口方法是一个简单类型的参数时
2、dao接口方法是多个简单类型的参数时
方式1:需要使用 @Param:命名参数,在方法的形参前面使用,定义参数名,这个名称可以用在mapper文件中
方式2:也可以不用@Param注解,使用索引,从0开始
3、dao接口方法的参数是一个Java对象时
一个Java对象作为方法的参数时,使用该对象的属性作为参数值使用
简单的语法:#{属性名}
Mybatis内部是这样工作的: 他会去调用此属性的getXxx()方法获取属性值
4、dao接口方法的参数是一个map集合时
六、占位符的使用
mybatis的占位符有 #{} 和 ${} 两种
#{} 语法:#{字符}
Mybatis处理 #{ } 占位符使用的对象是PrepareStatement对象
#{}占位符处理后的sql语句为: insert into student values (?,?,?,?,?)
特点:
- 使用的PrepareStatement对象执行sql语句效率高
- 使用的PrepareStatement对象,能避免sql注入,sql语句执行更安全
- #{}常作为列值来使用的,位于等号的右侧,#{}位置的值和数据类型有关的
${} 语法:${字符}
${}表示字符串连接,把sql语句的其他内容和${}内容使用字符串(+)连接方式连在一起
特点
1)使用Statement对象,执行sql语句,效率低
2)${}占位符的值,使用的字符串连接方式,有sql注入的风险。有代码安全的问题
3)${}数据是原样使用的,不会区分数据类型。
4)${}常用作表名或者列名,在能保证数据安全的情况下使用${}
Ps:使用该占位符时 1 方法形参处要用@Param注解 2 如果传参为字符串类型的数据时要手动加上引号
七、结果集
主要有resultType和resultMap两个
resultType属性:在执行select时使用,作为<select>标签的属性出现的。表示结果类型,mysql执行sql语句,得到java对象的类型。它的值有两种 1)java的全限定名称 2) 起别名
1、resultType
1) java 的全限定名称
resultType :现在使用java类型的全限定名称。表示的意思mybatis执行sq1,把ResultSet中的数据转为student类型的对象。 mybatis会做以下操作: 1.调用com.zhiyou.mybatis.entity.Student的无参数构造方法,创建对象。 Student student = new student(); //使用反射创建对象 2.同名的列赋值给同名的属性。 student.setId( rs.getInt("id")); student.setName(rs.getString("name")); 3.得到iava对象,如果dao接口返回值是List集合,mybatis把student对象放入到List集合。 所以执行Student mystudent = dao.selectById(1001);得到数据库中 id=1001这行数据, 这行数据的列值,赋给了mystudent对象的属性。你能得到mystudent对象。就相当于是id=1001这行数据。 |
2)使用别名
mybatis提供的对java类型定义简短,好记名称。
自定义别名的步骤:
1) 在mybatis主配置文件,使用typeAliase标签声明别名
2)在mapper文件中, resultType="别名"
也可以使用 <package name="com.zhiyou.dao"/> 这样就会给该包下面所有类起别名,默认为类名开头小写
2、resultMap
常用于列名和属性不同的情况,列名和属性名相同也可以使用
id:给resultmap起一个别名
type:java实体类的全限定名称,包名+类名,如果定义别名了,写别名即可
<id> 标签:常用于设置主键列 <result>标签:常用于设置非主键列
column是数据库中表的列,property是Java实体类中的属性
八、多表联查
1、多对一 或者 一对一
常用 association 标签
现有两张表,学生表和班级表,学生表与班级表的对应关系是多个学生对应一个班级,或者也可以说一个学生对应一个班级
实体类
方式1:类似子查询方式
<!--方式1: 类似子查询方式-->
<resultMap id="studentMapper1" type="com.zhiyou.mybatis.entity.Student">
<!-- property 指的是学生表中的班级属性名 column指的是学生表中查询出来的与班级表中的关联的班级id的值 select指定哪个select标签 -->
<association property="myClass" column="scid" select="getClass" >
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
<result column="cbanXun" property="cbanXun"></result>
</association>
</resultMap>
<!-- 查询一条学生记录 -->
<select id="getOne" resultMap="studentMapper1">
select * from tab_stu where sid = #{id}
</select>
<!-- 查询一条班级记录,根据学生表中查询的scid -->
<select id="getClass" resultType="com.zhiyou.mybatis.entity.MyClass">
select * from tab_class where cid=#{cid}
</select>
方式2:联合查询方式
<!-- 方式2 : 联合查询方式 -->
<resultMap id="studentMapper2" type="com.zhiyou.mybatis.entity.Student">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
<result column="sex" property="sex"></result>
<result column="sage" property="sage"></result>
<result column="scid" property="scid"></result>
<association property="myClass" >
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
<result column="cbanXun" property="cbanXun"></result>
</association>
</resultMap>
<select id="getOne2" resultMap="studentMapper2">
select * from tab_stu s,tab_class c where s.sid=#{id} and s.scid=c.cid
</select>
2、一对多
常用 collection 标签
现有两张表,班级表和学生表,一个班级可以对应多个学生,因此结果集需要映射封装
实体类
方式1 类似子查询方式
<!-- 方式1 : 类似子查询方式 -->
<resultMap id="classMapper1" type="com.zhiyou.mybatis.entity.MyClass">
<id column="cid" property="cid"></id>
<!-- property 是班级里面学生集合的属性名 ofType 是集合里面存储的数据类型 -->
<collection property="list" column="cid" ofType="com.zhiyou.mybatis.entity.Student" select="getStudent">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
<result column="sex" property="sex"></result>
<result column="sage" property="sage"></result>
<result column="scid" property="scid"></result>
</collection>
</resultMap>
<select id="getOneClass" resultMap="classMapper1">
select * from tab_class where cid=#{cid}
</select>
<select id="getStudent" resultType="com.zhiyou.mybatis.entity.Student">
select * from tab_stu where scid=#{cid}
</select>
方式2 联合查询方式
<!-- 方式2 : 联合查询方式 -->
<resultMap id="getClassmap" type="com.zhiyou.mybatis.entity.MyClass">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
<result column="cbanXun" property="cbanXun"></result>
<collection property="list" ofType="com.zhiyou.mybatis.entity.Student">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
<result column="sex" property="sex"></result>
<result column="sage" property="sage"></result>
<result column="scid" property="scid"></result>
</collection>
</resultMap>
<select id="getOneClass2" resultMap="getClassmap">
select * from tab_stu s,tab_class c where c.cid=#{cid} and s.scid=c.cid
</select>
九、动态sql
什么是动态sql:同一个dao方法,根据不同的条件可以表示不同的sql语句,主要是where部分有变化,使用mybatis提供的标签,实现动态sql的能力,主要讲if,where,foreach,sql
1、if标签
语法如下:
<!-- List<Student> getAll3(Student s); -->
<!--如果参数分数合理 把参数分数作为最低分来查-->
<select id="getAll3" resultType="Student">
select * from student where 1=1
<if test="score != null and score gte 0 and score lte 100">
and score >=#{score}
</if>
</select>
2、where标签
使用if标签时,容易引起sql语句语法错误。使用where标签解决if产生的语法问题。
使用where时 ,里面是一个或多个if标签,当有一个if标签判断条件为true,where标签会转为WHERE关键字附加到sql语句的后面。如果if 没有一个条件为true ,忽略where和里面的if
语法格式:
<where>
<if test=”条件”> sql语句 </if>
</where>
<!-- where标签:智能判断是否加where -->
<!-- List<Student> getAll5(Student s); -->
<select id="getAll5" resultType="Student">
select * from student
<where>
<if test="score != null and score gte 0 and score lte 100">
score >=#{score}
</if>
</where>
</select>
3、foreach标签
使用foreach可以遍历集合和数组,一般用在in语句中
<!-- List<Student> getAll9(int[] ids); -->
<select id="getAll9" parameterType="int[]" resultType="Student">
<!-- select * from student where sid in(1,2,3,4) -->
select * from student where sid in
<foreach collection="array" item="id" separator="," open="(" index="i" close=")" >
#{id}
</foreach>
</select>
4、set标签
用于更新操作,可以自动判断是否去除最后一个逗号
<!-- int updateOne1(Student s); -->
<!-- 如果s中的属性有值 就更改数据库中此属性的值 -->
<update id="updateOne1" parameterType="Student">
update student
<set>
<if test="sname != null">
sname=#{sname},
</if>
<if test="sex eq '男'.toString() or sex eq '女'.toString() ">
sex=#{sex},
</if>
<if test="score != null and score gte 0 and score lte 100">
score=#{score},
</if>
<if test="sdy != null">
sdy=#{sdy},
</if>
</set>
where sid = #{sid}
</update>
5、trim标签
<!--trim标签: prefix:整体前面加前缀 suffix:整体后面加后缀
prefixOverrides:智能判断去除片段的前缀,suffixOverrides:智能判断去除片段的后缀-->
<select id="getAll8" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and | or" suffix=" and 1=1 ">
<if test="sex != null">
and sex=#{sex}
</if>
<if test="score != null">
or score=#{score}
</if>
<if test="sdy != null">
and sdy=#{sdy}
</if>
<if test="sname != null">
or sname=#{sname}
</if>
</trim>
</select>
6、choose标签
<!-- List<Student> getAll7(Student s); -->
<!-- 如果性别有值 把性别作为唯一条件 如果性别没值 再判断党员 如果党员没值 再判断分数 如果分数也每值 就差stid=1作为条件 -->
<!--使用choose标签实现-->
<select id="getAll7" resultType="Student">
select * from student
<where>
<choose>
<when test="sex != null">
sex=#{sex}
</when>
<when test="sdy != null">
sdy=#{sdy}
</when>
<when test="score != null">
score >= #{score}
</when>
<otherwise>
stid=1
</otherwise>
</choose>
</where>
</select>
7、代码片段
sql标签标示一段sql代码,可以是表名,几个字段, where条件都可以,可以在其他地方复用sql标签的内容。
使用方式:
- 在mapper文件中定义 sq1代码片段
<sql id="唯一字符串">部分sq1语句</sq1>
2)在其他的位置,使用include标签引用某个代码片段
8、if标签中test值的书写问题
1)当参数是一个简单类型的参数时
2)当方法参数是多个简单类型的参数时
3)当方法参数是一个对象时
十、缓存
当对数据库进行查询时 如果两次的sql语句一致 不再重复去服务数据库 直接使用上一次的查询结果
作用:提高程序效率+降低数据库服务器的压力
1、一级缓存
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。
所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
-
一级缓存默认是开启的
-
一级缓存针对于同一个sqSession
-
一级缓存使用前提:缓存不能刷新
-
期间不能执行dml
一级缓存生命周期
a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。
d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
2、二级缓存
二级缓存是application级别的缓存/SqlSessionFactory级别的缓存
mybatis的二级缓存默认是关闭的
二级缓存的数据会存储在硬盘上
在使用缓存的mapper映射文件中添加配置
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
在使用缓存的mapper映射文件的sql标签中添加配置:(默认是true:可以不用配置)
<!-- List<Student> getAll1();-->
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭-->
<select id="getAll1" resultType="Student" useCache="true">
select * from student
</select>
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭-->
<select id="getAll2" resultType="Student" useCache="false">
select * from student
</select>
在核心配置文件中添加配置
<settings>
....
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
<setting name="cacheEnabled" value="true" />
</settings>