一、MyBatis简介
MyBatis是一个半自动化的持久层框架。
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis避免了几乎所有JDBC代码和手动设置参数以及获取结果集。
- MyBatis可以使用间的的xml或注解用于配置和原始映射,将接口和java的POJO映射成数据库的记录。
二、MyBatis与JDBC、Hibernate
jdbc:
- sql夹在java代码块中,耦合度高。
- 维护不易,sql时常变化,需要平凡修改
Hibernate:
- 长难复杂的sql,Hibernate也不容易处理;
- 内部会自动生成sql,不容易做优化;
- 基于全映射的全自动框架,进行部分映射比较困难,导致数据库性能下降。
Mybatis是一个半自动框架,使得sql与java编码分开,一个专注业务,一个专注数据。
三、MyBatis配置文件
-
全局配置文件:mybatis-config.xml
- 指导mybatis正确运行的一些全局设置
<?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> <!-- 和Spring的context、property-placeholder相似,都是引用外部配置文件 resource:从类路径下开始引用 url:引用磁盘路径或网络路径的资源 --> <!-- <properties url=""></properties>--> <properties resource="dbconfig.properties"></properties> <!-- setting是MyBatis中极为重要的调整设置,其中的属性会改变MyBatis的运行时行为--> <settings> <!-- name:配置项的key; value:配置项的值 mapUnderscoreToCamelCase:开启驼峰命名自动映射(如果javabean的属性和数据库的字段满足驼峰命名法) --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!-- typeAliases(类型别名),为常用的类型起别名--> <typeAliases> <!-- typeAlias:为一个javaBean起别名,别名默认就是类名(不区分大小写)--> <!-- alias:指定一个别名--> <!-- <typeAlias type="com.bean.Employee" alias="emp"></typeAlias>--> <!-- 批量起别名 ,默认别名就是类名--> <package name="com.bean"/> <!-- 一般使用全类名,能快速锁定--> </typeAliases> <!-- 插件是mybatis的强大功能--> <!-- --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!-- 引入自己编写的每一个接口的实现文件--> <!-- 写好的sql映射文件需要使用mappers注册进来--> <mappers> <!-- resource:在类路径下找sql映射文件 url:从磁盘或者网络路径引用 class:直接引用接口的全类名 1.可以将xml放在dao接口同目录下,而且文件名和接口名一致 2.配合注解使用 --> <!-- <mapper class="com.dao.EmployeeDao"></mapper>--> <!-- <mapper url=""></mapper>--> <!-- <mapper resource="com/dao/EmployeeDao.xml"/>--> <!-- <mapper class="com.dao.EmployeeDaoAnnotation"></mapper>--> <!-- 批量注册--> <package name="com.dao"/> </mappers> </configuration>
注:当使用mappers批量注册时,需要将xxxDao.xml与dao接口放在同一个包下,且名字相同。
-
SQL映射文件:xxxDao.xml
- 相当于是对Dao接口的一个实现描述。
<?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:名称空间,写接口的全类名,相当于告诉mybatis这个配置文件是实现哪个接口的 --> <mapper namespace="com.dao.EmployeeDao"> <!-- 用来定义一个查询操作 id:方法名,相当于这个配置是对于某个方法的实现 resultType:指定方法运行后的返回值类型(查询操作必须指定的) --> <select id="getEmpById" resultType="com.bean.Employee"> select * from t_employee where id = #{id} </select> <select id="getEmpByName" resultType="com.bean.Employee"> select * from t_employee where empName=#{name} </select> <!-- 增删改不用写返回值类型,增删改是返回影响多少行 MyBatis自动判断,如果是数字(int.long) 如果是Boolean(影响0行自动封装false,否则true) #{属性名}:从传入的参数对象中取出对应属性的值 --> <update id="updateEmployee" > update t_employee set empname=#{empName},gender=#{gender},email=#{email} where id=#{id} </update> <insert id="insertEmployee"> insert into t_employee(empname,gender,email) values (#{empName},#{gender},#{email}) </insert> <delete id="deleteEmployee"> delete from t_employee where id=#{id} </delete> </mapper>
四、plugins插件
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为、插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。
五、注解的使用
MyBatis支持注解,可以直接在dao接口的方法上面标注注解,而不用写配置文件。
示例代码:
public interface EmployeeDaoAnnotation {
@Select("select * from t_employee where id = #{id}")
public Employee getEmpById(Integer id);
@Select("select * from t_employee where empName=#{name}")
public Employee getEmpByName(String name);
@Select("update t_employee\n" +
" set empname=#{empName},gender=#{gender},email=#{email}\n" +
" where id=#{id}")
public int updateEmployee(Employee employee);
@Select("delete from t_employee\n" +
" where id=#{id}")
public Boolean deleteEmployee(Integer id);
@Select("insert into t_employee(empname,gender,email)\n" +
" values (#{empName},#{gender},#{email})")
public int insertEmployee(Employee employee);
}
mybatis全局配置文件中声明一下:
<mappers>
<!--
resource:在类路径下找sql映射文件
url:从磁盘或者网络路径引用
class:直接引用接口的全类名
1.可以将xml放在dao接口同目录下,而且文件名和接口名一致
2.配合注解使用
-->
<!-- <mapper class="com.dao.EmployeeDao"></mapper>-->
<!-- <mapper url=""></mapper>-->
<mapper resource="EmployeeDao.xml"/>
<mapper class="com.dao.EmployeeDaoAnnotation"></mapper>
</mappers>
注:重要的dao可以写配置,简单的可以使用注解
六、简单使用
首先引入全局配置文件mybatis-config.xml,然后创建一个SqlSessionFactory,用于生产SqlSession,通过SqlSession获取dao接口的实现,然后就可以操作数据库。
@Test
public void test1() throws IOException {
//根据全局配置文件创建出一个sqlSessionFactory
//SqlSessionFactory:是sqlSession工厂,负责创建sqlSession对象
//sqlSession:sql会话(代表和数据库的一次会话)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession=null;
Employee emp=null;
try {
//获取和数据库的一次会话:getConnection()
sqlSession = sqlSessionFactory.openSession();
//使用sqlSession操作数据库,获取dao接口的实现
EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
emp = employeeDao.getEmpById(1);
} finally {
sqlSession.close();
}
System.out.println(emp);
}
七、参数的各种取值
-
单个参数(基本数据类型)
取值:#{随便写}
-
多个参数:
取值:可以用0,1(参数索引)或者param1,param2…
原因:只要传入的多个参数,mybatis会自动的将这些参数封装在一个map中,封装时使用的key就是参数的索引和参数的第几个表示。
Map<String,Object> map=new HashMap<>(); map.put("1",传入的值); map.put("2",传入的值);
-
@param:为参数指定key,命名参数;
可以告诉mybatis,封装参数map的时候使用我们指定的key
-
传入pojo
取值:#{pojo的属性值}
-
传入map:将多个要使用的参数封装起来
取值:#{key}
EmployeeDao:
public Employee getEmpByIdAndName(@Param("name") String name, Integer id); public Employee getEmployeeByIdAndName(Map<String,Object> map);
EmployeeDao.xml:
<select id="getEmpByIdAndName" resultType="com.bean.Employee" >
select * from t_employee where empName=#{name} and id=${1}
</select>
<select id="getEmployeeByIdAndName" resultType="com.bean.Employee" >
select * from t_employee where empName=#{name} and id=${id}
</select>
Test:
import com.bean.Employee;
import com.dao.EmployeeDao;
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 org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public class MyBatisTest {
SqlSessionFactory sqlSessionFactory;
@Before
public void getSqlSessionFactory() throws IOException {
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void test4(){
SqlSession sqlSession=null;
try {
sqlSession = sqlSessionFactory.openSession(true);
EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
Employee emp = employeeDao.getEmpByIdAndName("admin", 1);
System.out.println(emp);
} finally {
sqlSession.close();
}
}
@Test
public void test5(){
SqlSession sqlSession=null;
try {
sqlSession = sqlSessionFactory.openSession(true);
EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
Map<String,Object> map=new HashMap<>();
map.put("id","1");
map.put("name","admin");
Employee employee = employeeDao.getEmployeeByIdAndName(map);
System.out.println(employee);
} finally {
sqlSession.close();
}
}
}
八、#{}与${}的区别
在mybatis中,两种取值方式:
- #{属性名}:是参数预编译的方式,参数的位置都是用”?“替代,参数后来都是预编译社会设置进去的;安全,不会有sql注入问题,推荐使用
- ${属性名}:不是参数预编译,而是直接和sql语句拼串;不安全
sql语句只有参数位置时支持预编译的。
一般都是使用#{},安全;在不支持参数预编译的位置要进行取值就用${},比如表名。
九、联合查询与分布查询
我们可以使用association与collection标签进行复杂对象的映射(对象的属性包含有对象或集合)。
<!--自定义封装规则,使用级联属性封装联合查询出的结果-->
<resultMap id="myResult" type="com.bean.Key">
<id property="id" column="id"></id>
<result property="keyName" column="key_name"></result>
<result property="lock.id" column="lid"></result>
<result property="lock.LockName" column="lock_name"></result>
</resultMap>
使用association标签:
<!-- 使用mybatis推荐的association标签-->
<resultMap id="myResult" type="com.bean.Key">
<id property="id" column="id"></id>
<result property="keyName" column="key_name"></result>
<!-- javaType:指定这个属性的类型-->
<association property="lock" javaType="com.bean.Lock">
<id property="id" column="lid"></id>
<id property="LockName" column="lock_name"></id>
</association>
</resultMap>
字段丢失的解决办法:不要直接用select * ,而是把每个字段都写出来,并起别名与javaBean的属性名相对应。
十、动态sql
if标签
where标签与if标签配合,可以动态查询。
where标签可以帮我们去除sql语句中多余的and,但是and要写在每一个sql语句的前面。
if标签中的test写判断语句,有些符号要使用转义字符。
<select id="getStudentByCondition" resultMap="studentMap">
select * from student
<where>
<if test="id!=null and !id.equals("")">
id>#{id}
</if>
<if test="name!=null">
and student_name like #{name}
</if>
<if test="english!=null and english>=0 and english <=100">
and english < #{english};
</if>
</where>
</select>
forEach标签
<!--
collection:指定要遍历的集合的key
open:以什么开始
close:以什么结束
separator:以什么作为分隔符
item:给每次遍历出来的元素起一个变量名
-->
<select id="getStudentByCollection" resultMap="studentMap">
select * from student where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
choose标签
<!-- choose标签,当进入任何一个一个when标签的时候,就不会进入其它标签了-->
<select id="getStudentByConditionChoose" resultMap="studentMap">
select * from student
<where>
<choose>
<when test="id!=null and !id.equals("")">
id=#{id}
</when>
<when test="name!=null and !name.equals("")">
student_name like #{name}
</when>
<when test="chinese">
chinese>#{chinese}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
set标签
<!-- 测试动态更新,set标签可以去掉多余的,-->
<update id="updateStudent">
update student
<set>
<if test="name!=null and !name.equals("")">
student_name = #{name},
</if>
<if test="chinese!=null and chinese>=0 and chinese <=100">
chinese = #{chinese},
</if>
<if test="math!=null and math>=0 and math <=100">
math = #{math},
</if>
<if test="english!=null and english>=0 and english <=100">
english = #{english}
</if>
</set>
<where>
id=#{id}
</where>
</update>
十一、缓存机制
MyBatis缓存简介
MaBatis缓存机制:Map,能保存查询出的一些数据。
- 一级缓存:线程级别的缓存,本地缓存,sqlSession级别的缓存,不同的sqlSession使用不同的一级缓存。
- 二级缓存:全局范围的缓存,除过当前线程、sqlSession能用外其他也可以调用。
机制:只要之前查询过数据,mybatis就会保存在一个缓存中(Map),下次获取直接从缓存中拿,但是如果在这之间执行了其它增删改操作,mybatis就会清空一级缓存。
一级缓存失效的情况
- 不同的sqlSession使用不同的一级缓存。
- 同一个方法,不同的参数,可能之前没查询过,会重新发sql
- 在这个sqlSession期间执行任何一次增删改操作,此sqlSession的一级缓存就会被清空。
- 手动清空缓存。
二级缓存
二级缓存:全局缓存,namespace级别的缓存。
sqlSession关闭或提交以后,一级缓存会放在二级缓存中,mybatis默认是没有使用的。
二级缓存的使用:
-
全局配置开启二级缓存
<!-- 开启全局缓存开关--> <setting name="cacheEnabled" value="true"></setting>
-
配置某个dao.xml文件,让其使用二级缓存
<!-- 使用二级缓存--> <cache></cache>
-
让 对应的javaBean实现序列化,即实现Serializable接口。
缓存的查询数据
- 不会出现一级缓存和二级缓存中有同一个数据
- 一级缓存关闭就会有二级缓存
- 查一个数据,二级缓存中没有此数据,就会看一级缓存,一级缓存没有就会查数据库,数据库查询后的结果放在一级缓存中。
- 任何时候都是先看二级缓存,再看一级缓存,如果都没有,就会去数据库查询。
缓存有关的设置
十二、整合第三方缓存
整合Ehcache
-
导包
ehcache-core-2.6.8.jar
log4j-1.2.17.jar
mybatis-3.4.1.jar
mybatis-ehcache-1.0.3.jar
mysql-connector-java-8.0.24.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.2.jar -
ehcache得有一个配置文件,文件名叫ehcache.xml,放在类路径的根目录下
-
在mapper.xml中配置使用自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
-
如果别的dao还要用这个缓存
<cache-ref namespace="com.dao.StudentDao"/>