1. 基本使用方法
创建核心配置文件mybatis-config.xml
mybatis-config.xml中配置mybatis的核心配置文件
mappers文件夹存放映射文件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> <!--设置连接数据库的环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/MyBatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--引入mappers文件夹的映射文件--> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
创建接口类DAO,UserMapper
package com.atguigu.mybatis.mapper; public interface UserMapper { /** * 添加用户信息 */ int insertUser(); int deleteUser(); }
创建DAO接口类的映射配置文件 UserMapper.xml
<?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"> <!--指定为哪个DAO接口类配置--> <mapper namespace="com.atguigu.mybatis.mapper.UserMapper"> <!--int insertUser();id值为指定接口的方法--> <insert id="insertUser"> <!--编写sql insert 语句--> insert into t_user values(null,'张三','123',23,'女') </insert> <!--删除、更新、选择语句的标签--> <delete></delete> <updata></updata> <select></select> </mapper>
service层调用DAO接口,进行数据操作
public void testInsertUser() throws IOException { //读取MyBatis的核心配置文件 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); //获取SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); //获取sqlSession,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务 //SqlSession sqlSession = sqlSessionFactory.openSession(); //创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交 SqlSession sqlSession = sqlSessionFactory.openSession(true); //通过代理模式创建UserMapper接口的代理实现类对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句 int result = userMapper.insertUser(); }
userMapper.insertUser()调用DAO类UserMapper的接口方法,然后根据UserMapper.xml配置的sql语句,底层进行对数据库的操作
2. 日志依赖
和Spring一样,MyBatis有自己专属的日志功能
3. 简单的增删查改
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
<delete id="deleteUser">
delete from t_user where id = 6
</delete>
<update id="updateUser">
update t_user set username = '张三' where id = 5
</update>
<select id="getUserById" resultType="com.atguigu.mybatis.bean.User">
select * from t_user where id = 2
</select>
查询返回实体的设定:属性
resultType:自动映射,属性名和表中字段名必须一致的情况
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
4. sql语句获取参数
获取若干个参数:
接口方法:使用
@Param
注解,为每个形参建立键值对,username
-uname
,password
-pwd
User CheckLoginByParam(@Param("username") String uname, @Param("password") String pwd);
sql语句:使用
#{键}
,获取键值<select id="CheckLoginByParam" resultType="User"> select * from t_user where username = #{username} and password = #{password} </select>
使用:
mapper.CheckLoginByParam("admin","123456");
执行原理:@Param为形参创建键值对,则 sql 中使用语法糖来调用键值对
获取实体类参数:
#{username}
会自动获取形参实体的属性值,需参数和属性名相同
<insert id="insertUser">
insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>
User user = new User(null,"Tom","123456",12,"男","123@321.com");
mapper.insertUser(user);
5. 各种查询功能
查询一个实体类:
User getUserById(@Param("id") int id);
查询多个实体类:
List<User> getUserList();
查询一个实体类(键值对返回):会将实体类的属性转换为键值对形式
Map<String, Object> getUserToMap(@Param("id") int id); //结果:{password=123456, sex=男, id=1, age=23, username=admin}
查询多个实体类(键值对返回):
List<Map<String, Object>> getAllUserToMap();
6. 特殊sql
模糊查询: 用"%"#{}"%"
拼接方式方式实现
<select id="getUserByLike" resultType="User">
select * from t_user where username like "%"#{mohu}"%"
</select>
批量删除:只能使用${}
,不能使用#{}
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
按表查询: 只能使用${}
,不能使用#{}
<select id="getUserByTable" resultType="User">
select * from ${tableName}
</select>
获取添加的数据的自增主键:
在mapper.xml中设置两个属性
useGeneratedKeys:设置使用自增的主键
keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>
7. 自定义resultMap
:解决数据库表的字段名和实体名不一致问题
基本使用:
<!--resultMap中
empResultMap:自定义名,给select的resultMap调用
Emp:实体类
eid:主键,Map属性,唯一设定
empName:实体类中的属性
emp_name:数据库中的表的字段
-->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="empResultMap">
select * from t_emp
</select>
8. 多对一映射处理
:多个员工对应一个部门
:查询的实体类的属性有另一个实体的问题,如下
public class Emp {
private Integer eid;
private Dept dept; //实体类
}
方式一:级联association
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<!--
dept:实体类的dept属性
Dept:dept的实体类型
里面设定Dept的属性名映射
-->
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
方法二:分步查询
:通过调用另一个查询方法来组合查询
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid); //根据员工id查询返回Emp类
Dept getDeptAndEmpByStepOne(@Param("did") Integer did); //根据部门id查询返回Dept
Emp中的xml设定:
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<!--association里的表示:Emp的dept属性调用select值的查询方法进行查询,传参为did-->
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"></association>
</resultMap>
Dept中的xml设定不用改变
9. 一对多映射处理
:一个部门实体类对应多个员工实体类
public class Dept {
private Integer did;
private List<Emp> emps;
}
分步查询:collection标签
<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"></collection>
</resultMap>
10. 延迟加载
例子:
实体类:
public class Dept {
private Integer did;
private List<Emp> emps;
}
分步查询的xml:
<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"></id>
<collection property="emps"
select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"></collection>
</resultMap>
使用查询接口:
Dept dept = mapper.DeptAndEmpByStepOneResultMap(1);
输出属性:
System.out.println(dept.getDeptDid());
开启延迟加载:如果只使用查询结果的dept.did
属性,不使用emp
的属性,则不会再执行collection
里的分步查询select
,如果使用dept.emp[0].id
emp的属性,才会执行绑定的分步查询
这叫做延迟加载
分步查询可以实现延迟加载
<settings>
<!--全局配置,开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
局部控制延迟加载:fetchType = "lazy(延迟加载)|eager(立即加载)"
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy">
</association>
11. 动态sql
if: 如果true则拼接,否则不拼接
test属性:判断语句
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName != null and empName !=''">
and emp_name = #{empName}
</if>
<if test="age != null and age !=''">
and age = #{age}
</if>
</select>
where:与 if 结合使用,如果没有 if 为true,则不添加where,否则添加where
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName !=''">
emp_name = #{empName}
</if>
<if test="age != null and age !=''">
and age = #{age}
</if>
</where>
</select>
trim:最灵活的标签
prefix:设置在trim标签前添加的内容
suffix:在trim标签中的内容的后面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
abc
<trim prefix="where"
suffix="#"
prefixOverrides="@"
suffixOverrides="%"
> @@jni%%</trim>
abc
得到的sql语句:abc #@joi% abc
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null and empName !=''">
emp_name = #{empName} and
</if>
<if test="age != null and age !=''">
age = #{age} and
</if>
</trim>
</select>
choose、when、otherwise:
相当于:if .... else if ..... else
foreach:
:实现批量操作
collection:设置@Param的参数数组
item:表示数组的每一个值
separator:设置每一个内容的分隔符
open、close:设置foreach标签的前后的符号
<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<!--对数组eids的每个值进行删除操作-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
得到的sql语句:delete from t_emp where eid in (1,2,3,4,5)
sql片段:
:设置一公共sql片段,在其他地方插入
<!--使用sql标签-->
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
<!--使用include标签,在sql语句中插入-->
<select id="getEmpByCondition" resultType="Emp">
select <include refid="empColumns"></include> from t_emp
</select>
12. MyBatis的缓存
public void testInsertUser() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int result = userMapper.insertUser();
}
一级缓存:
SqlSession级别:通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
Emp result1 = userMapper1.FindEmpById(1);
UserMapper userMapper2 = sqlSession1.getMapper(UserMapper.class);
Emp result1 = userMapper2.FindEmpById(1);
userMapper1.FindEmpById(1)查询后的结果被缓存到sqlSession1空间,userMapper2.FindEmpById(1)会直接从缓存中读取数据,非从数据库
同一个SqlSession两次查询期间执行了任何一次增删改操作会清空缓存
二级缓存:
SqlSessionFactory级别:比SqlSession更大,需手动开启,每一个SqlSessionFactory可创建多个SqlSession,每个SqlSession关闭或提交后,才将其缓存转到SqlSessionFactory中,才能被其他SqlSession使用
SqlSessionFactoryd的配置属性:
eviction:缓存的回收策略
flushInterval:刷新间隔
size:缓存大小
readOnly:设置只读或读写
查询语句的查询顺序:
先查询二级缓存,再查询一级缓存,最后查询数据库
缓存命中率: 指查询的结果在一级缓存中能查找到的概率
整合第三方缓存EHCache:了解
13. MyBatis的逆向工程
:逆向工程:先创建数据库表,由框架负责根据数据库表,自动生成对应的POJO、mapper类