Mybatis原本是apache的一个开源项目(当时叫iBatis), 2010年6月这个项目由ApacheSoftware Foundation 迁移到了 Google Code,随着开发团队转投GoogleCode 旗下, iBatis3.x正式更名为MyBatis。
Mybatis是一款优秀的持久性框架。
Mybatis几乎避免了所有的手动jdbc设置以及手动获得结果集的操作。
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性。
MyBatis 可以通过简单的 XML 或注解(@insert,@delete等)来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Mybatis支持动态sql以及数据缓存。
Mybatis 中文官网 https://mybatis.org/mybatis-3/zh/getting-started.html
Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,比如用一个Java的Student实体类,去对应数据库中的一张student表,类中的属性和表中的列一一对应()。Student类就对应student表,一个Student对象就对应student表中的一行数据。
在idea中建立一个与数据库表对应的实体类。
//配置mysql
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.16version>
dependency>
//配置mybatis
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.2version>
dependency>
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">
<dataSource type="POOLED">
<property name="driver" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value=""/>
dataSource>
environment>
environments>
configuration>
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="接口地址">
定义 sql 语句
mapper>
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory是Mybatis的关键对象,SqlSessionFactory单个数据映射关系经过编译后的内存镜像,SqlSessionFactory实例由SqlSessionFactoryBuilder对象获得,Mybatis以SqlSessionFactory为核心,并且SqlSessionFactory是线程安全的,SqlSessionFactory一旦被创建,在整个的执行过程中都是存在的,在运行期间不建议多次创建,建议使用单例模式:
package com.ffyc.mybatisdemo.util;
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 MyBatisUtil {
static SqlSessionFactory sqlSessionFactory =null;
//当MybatisUtil类第一次加载时,静态代码块执行,创建SqlSessionFactory
static{
//读取全局配置文件
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
//创建sqlSessionFactory,由sqlSessionFactory构建sqlsession
//由于创建sqlSessionFactory开销较大,用于封装数据库的连接信息,所以没有必要每次都创建
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
//获得通过静态代码块建立的创建sqlSessionFactory来建立sqlsession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
SqlSession sqlSession = sessionFactory.openSession()
SqlSession是MyBatis实现数据持久化的关键对象,类似于JDBC的connection,SqlSession是应用层与持久层之间执行交互操作的一个单线程对象.每个线程都应该有它自己的SqlSession实例.SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,绝对不能讲SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.也绝不能将SqlSession实例的引用放在任何类型的管理范围中,在该线程执行(与数据库会话结束后)完后必须关SqlSession。
sqlSession.getMapper(接口.class)
动态为接口创建一个代理对象,由Mybatis框架自动生成,由代理对象调用Mapper中的方法;
具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
package com.ffyc.mybatisdemo.dao;
import com.ffyc.mybatisdemo.model.Admin;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface AdminDao {
//方法名与对应的mapper中的标签id相同,
//方法的参数值,返回值类型要与mapper中的标签中定义的参数值和返回类型相同
Admin findAdminById(int id);
Admin login(@Param("account") String account, @Param("password") String password);
Admin login2(Admin admin);
int saveAdmin(Admin admin);
int updateAdmin(Admin admin);
int deleteAdmin(Admin admin);
Admin findAdminById1(int id);
Admin findAdminById2(int id);
int findAdminCount();
List<Admin> findAdminList();
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ffyc.mybatisdemo.dao.AdminDao">
<update id="updateAdmin" parameterType="Admin">
update admin set account=#{account}, password=#{password},gender=#{gender} where id=#{id}
update>
<delete id="deleteAdmin">
delete from admin where id=#{id}
delete>
<select id="findAdminById" parameterType="int" resultType="Admin">
select * from admin where id = #{id}
select>
<select id="login" resultType="Admin">
select*from admin where account=#{account} and password=#{password}
select>
<select id="login2" parameterType="Admin" resultType="Admin">
select*from admin where account=#{account} and password=#{password}
select>
<select id="findAdminById1" parameterType="int" resultType="Admin">
select account,password,gender xb from admin where id=#{id}
select>
<select id="findAdminCount" parameterType="int" resultType="java.lang.Integer">
select count(*) from admin
select>
<resultMap id="adminMap" type="admin">
<result column="gender" property="xb">result>
resultMap>
<select id="findAdminById2" parameterType="int" resultMap="adminMap">
select * from admin where id = #{id}
select>
<resultMap id="adminListMap" type="admin">
<result column="gender" property="xb">result>
resultMap>
<select id="findAdminList" resultMap="adminListMap">
select * from admin
select>
<insert id="saveAdmin" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into admin(account,password,gender)values(#{account},#{password},#{gender})
insert>
mapper>
driverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai
username=root
password=root
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="config.properties">properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="com.ffyc.mybatisdemo.model"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driverName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mappers/AdminMapper.xml"/>
mappers>
configuration>
package com.ffyc.mybatisdemo.test;
import com.ffyc.mybatisdemo.dao.AdminDao;
import com.ffyc.mybatisdemo.model.Admin;
import com.ffyc.mybatisdemo.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class Test2 {
@Test
public void login() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.login("admin1","111");
sqlSession.close();
}
@Test
public void login2() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin("admin1","111","男");
adminDao.login2(admin);
sqlSession.close();
}
@Test
public void save() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin("admin2","222","女");
adminDao.saveAdmin(admin);
sqlSession.commit();
sqlSession.close();
}
@Test
public void update() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin("admin5","555","男");
admin.setId(2);
adminDao.updateAdmin(admin);
sqlSession.commit();
sqlSession.close();
}
@Test
public void delete() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin("admin5","555","男");
admin.setId(2);
adminDao.deleteAdmin(admin);
sqlSession.commit();
sqlSession.close();
}
}

编写mapper.xml,书写SQL,并定义好SQL的输入参数,和输出参数
编写全局配置文件,配置数据源,以及要加载的mapper.xml文件
通过全局配置文件,创建SqlSessionFactory
每次进行CRUD时,通过SqlSessionFactory创建一个SqlSession
调用SqlSession上的selectOne,selectList,insert,delete,update等方法,传入mapper.xml中SQL标签的id,以及输入参数
注意:全局配置文件中,各个标签要按照如下顺序进行配置,因为mybatis加载配置文件的源码中是按照这个顺序进行解析的,每有新建并要使用的mapper.xml文件一定要在mappers中进行配置。
<configuration>
properties
settings
typeAliases
<typeAliases>
<package name="com.ffyc.mybatisdemo.model"/>
typeAliases>
mappers
-->
configuration>
在使用Dao类中的接口与Mappers.xml文件进行绑定时要注意:
mapper中命名空间必须唯一(必须指向该Dao类)
<mapper namespace="com.ffyc.mybatisdemo.dao.AdminDao">
使用接口与xml进行绑定时 接口方法要与mapper.xml中id保持一致
int updateAdmin(Admin admin);
<update id="updateAdmin" parameterType="Admin">
update admin set account=#{account}, password=#{password},gender=#{gender} where id=#{id}
update>
Dao接口方法的入参,出参类型与mapper.xml标签中定义的参数值和返回类型相同
int updateAdmin(Admin admin);
parameterType="Admin"
注解在没有嵌套查询的情况下,是很便捷的,但是使用注解会让SQL语句注入到运行代码中,若是要修改SQL语句就要去修改代码,可维护性不强。
在使用注解时不需要使用mapper.xml文件去进行数据处理,之间在Dao中编写SQL语句对数据进行操作。
常见的操作(增,删,改,查)如下:
@Insert("insert into grade(name) value(#{name}) ")
int saveGrade(Grade grade);
@Delete("delete from grade where id=#{id}")
int deleteGrade(int id);
@Update("update grade set name=#{name} where id=#{id}")
int updateGrade(Grade grade);
@Select("select * from grade where id=#{id}")
Grade findGradeById(int id);
当我们在数据库中进行插入操作时,数据库的主键通常会自增,而我们要如何得到在数据库中生成的新主键?
使用 useGeneratedKeys=“true” 支持取出数据库生成的主键 和 keyColumn=“id” 指定主键列
<insert id="saveAdmin" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into admin(account,password,gender)values(#{account},#{password},#{gender})
insert>
主要是动态SQL标签的使用,注意如果入参类型parameterType是List的话,则在标签体内引用这个List,只能用变量名list,如果parameterType是数组,则只能用变量名array
<select id="findStudentList1" resultType="Student" parameterType="List">
select * from student where no in
<foreach collection="list" open="(" item="no" separator="," close=")">
#{no}
foreach>
select>
Dao:
List<Student> findStudentList1(List<Integer> list);
Test:
@Test
public void findStudentList(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao1 studentDao1 = sqlSession.getMapper(StudentDao1.class);
List<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
studentDao1.findStudentList1(list);
sqlSession.close();
}
结果:

在mapper中使用sql语句并要对查询的数据进行筛选等操作,我们就要利用动态Sql。
在没有使用MYbatis之前,我们对于该类语句是如何处理的?
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/smartschool?serverTimezone=Asia/Shanghai", "root", "root");
String sql="SELECT \n" +
"d.id,\n" +
"d.dorm_num,\n" +
"d.num dnum,\n" +//人数
"d.floor_num,\n" +
"d.state,\n" +
"b.name bname,\n" +
"da.name daname,\n" +
"b.num bnum,\n" +//楼层数
"a.account,\n" +
"d.oper_time\n" +
"FROM dorm d LEFT JOIN building b ON d.buildingid= b.id\n" +
"LEFT JOIN admin a ON d.admin = a.id " +
"LEFT JOIN dorm_admin da ON b.dorm_adminid=da.id" +
" where 1=1";
if(dorm_num!=null&&dorm_num!=""){
sql+=" and d.dorm_num='"+dorm_num+"'";
}
由上述代码可以看出,我们在之前想要对查询的数据进行比对,筛选时为了避免Sql异常通常选择在sql语句的末端拼上where 1=1。
而在引入动态Sql后完全可以避免此类语句的注入。
Mybatis中的动态标签:
where if
select * from student
<where>
<if test="name!=null&name!=''">
name=#{name}
if>
<if test="no!=null&no!=''">
and no=#{no}
if>
where>
一旦where标签中有if条件成立会自动生成where,否则不生成若是第一位if条件不成立,而第二位if成立,则if语句中的and会自动被(mybatis)去除
tirm
select * from student
<trim prefix="where" prefixOverrides="and|or"> /*可添加前缀或覆盖多余字段*/
<choose>
<when test="name!=null">
name=#{name}
when>
<otherwise>
name="李四"
otherwise>
choose>
trim>
trim prefix="自定义前缀" prefixOverrides="覆盖指定的关键字
choose when otherwise
<trim prefix="where" prefixOverrides="and|or"> /*可添加前缀或覆盖多余字段*/
<choose>
<when test="name!=null">
name=#{name}
when>
<otherwise>
name="李四"
otherwise>
choose>
trim>
choose 分支执行when test="条件" 成立执行 否则 otherwise
set
<update id="updateStudent">
update student
<set>
<if test="name!=null">
name=#{name},
if>
<if test="gender!=null">
gender=#{gender}
if>
set>
where id=#{id}
update>
在至少有一个子元素返回了SQL语句时,才会向SQL语句中添加SET,并且如果SET之后是以`,`开头的话,会自动将其删掉
foreach
<select id="findStudentList1" resultType="Student" parameterType="List">
select * from student where no in
<foreach collection="list" open="(" item="no" separator="," close=")">
#{no}
foreach>
select>
用来做迭代拼接的,通常会与SQL语句中的IN查询条件结合使用,注意,到parameterType为List(链表)或者Array(数组),后面在引用时,参数名必须为list或者array。如在foreach标签中,collection属性则为需要迭代的集合,由于入参是个List,所以参数名必须为list
sql
<sql id="selectStudent">
SELECT
s.id,
s.name sname,
s.gender,
s.no,
a.account,
g.name gname
FROM
student s
LEFT JOIN admin a
ON s.adminid = a.id
LEFT JOIN grade g
ON s.gradeid = g.id
sql>
<select id="findStudentList" resultMap="studentMap">
<include refid="selectStudent">include>
select>
可将重复的SQL片段提取出来,然后在需要的地方,使用``标签进行引用
嵌套查询通常使用在一个实体类中包含另一个类,若这个被包含的类在该类中的形式是集合或数组则在mapper中使用嵌套查询时要使用collection标签否则使用association。
使用这个嵌套查询,需要注意的是collection和association有如下属性
<resultMap id="GradeMap1" type="grade">
<id column="id" property="id">id>
<result column="name" property="name">result>
<collection property="students" javaType="list" ofType="Student" column="id" select="findStudentByGradeId">collection>
resultMap>
<select id="findGradeList1" resultMap="GradeMap1">
SELECT
id,
name
FROM
grade
select>
默认开启,同一个SqlSesion级别共享的缓存,在一个SqlSession的生命周期内,执行2次相同的SQL查询,则第二次SQL查询会直接取缓存的数据,而不走数据库,当然,若第一次和第二次相同的SQL查询之间,执行了DML(INSERT/UPDATE/DELETE),则一级缓存会被清空,第二次查询相同SQL仍然会走数据库。
一级缓存在下面情况会被清除
在同一个SqlSession下执行增删改操作时(不必提交),会清除一级缓存
SqlSession提交或关闭时(关闭时会自动提交),会清除一级缓存
对mapper.xml中的某个CRUD标签,设置属性flushCache=true,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement)
在全局配置文件中设置 ,这样会使一级缓存失效,二级缓存不受影响
默认关闭,可通过全局配置文件中的开启二级缓存总开关,然后在某个具体的mapper.xml中增加,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中