mybatis


一、特性介绍

1、MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架

2、mybatis支持定制化sql、存储过程、以及高级映射

3、支持jdbcTemplate模板,减少模板代码和硬编码

4、MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录

二、MyBatis的缓存

默认打开一级缓存,二级没有打开

1、MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就
会从缓存中直接获取,不会从数据库重新访问

1.1、使一级缓存失效的四种情况:

不同的SqlSession对应不同的一级缓存

同一个SqlSession但是查询条件不同

同一个SqlSession两次查询期间执行了任何一次增删改操作

同一个SqlSession两次查询期间手动清空了缓存

2、MyBatis的二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被
缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

2.1二级缓存开启的条件:

a>在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
b>在mapper映射文件中设置标签
c>二级缓存必须在SqlSession关闭或提交之后有效
d>查询的数据所转换的实体类类型必须实现序列化的接口

2.2使二级缓存失效的情况:

两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

3、二级缓存的相关配置

在mapper配置文件中添加的cache标签可以设置一些属性:

<cache eviction="FIFO" flushInterval="60000" size="512"  readOnly="true"/>

3.1、eviction属性:缓存回收策略,默认的是 LRU。

LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

3.2、flushInterval属性:刷新间隔,单位毫秒

默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新(清空缓存的意识)

3.3、size属性:引用数目,正整数

代表缓存最多可以存储多少个对象,太大容易导致内存溢出

3.4 、readOnly属性:只读, true/false

true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。

false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

4、MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存

三、开发注意的点

1、传入参数到mapper.xml的sql中

1、@Param注解可以设置参数的可以名,方便在mapper中 #{key} 或 ${key} 取值可以以这个key名

Student selectByNameId(@Param("name") String name, @Param("id") String id);

2、传入的参数是实体类,直接可以#{属性} 、${属性}获取属性值,

3、如上多个参数传入映射SQL,写在映射SQL中都是以rg1, arg0, param1, param2为键的map存储

4、@MapKey注解设置map集合的键,值是每条数据所对应的map集合

@MapKey("id") //设置每条数据的id为键值为整条数据,这样可以多条放入map中,如下是mapper接口方法
Map<String, Object> getAllUserToMap();  

2、${} 和 #{} 的区别

1、${}的本质就是字符串拼接,#{}的本质就是占位符赋值

2、${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引
号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,
可以自动添加单引号。

3、#{}可以防止sql注入,${}不可以

4、如下方式即避免了${}的注入危险,也达到了拼接字符串的功能。

select * from t_user where username like "%"#{模糊}"%"

3、mapper接口 和 mapper.xml映射文件注意点

1、mapper接口的全类名和映射文件的命名空间(namespace)保持一致
2、mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

4、动态sql

1、where标签会将将if条件最前方多余的and去掉 ,不能去掉条件最后多余的and

2、choose、when、 otherwise相当于if…else if…else

3、trim用于去掉或添加标签中的内容
常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容

<select id="getEmpListByMoreTJ" resultType="Emp">
	select * from t_emp
	<trim prefix="where" suffixOverrides="and">
        <if test="ename != '' and ename != null">
        	ename = #{ename} and
        </if>
        <if test="age != '' and age != null">
        	age = #{age} and
        </if>
        <if test="sex != '' and sex != null">
        	sex = #{sex}
        </if>
    </trim>
</select>

4、foreach标签,separator分隔符,index 是当前迭代的序号

<foreach collection="emps" item="emp" open="(" separator="," close=")" index="index">
	#{emp.id}
</foreach>

四、相关运用讲解

1、核心配置文件 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>
    <!--引入properties文件-->
    <properties resource="jdbc.properties"/>
    
    <settings>
        <!--将表列名和属性名称使用驼峰命名法-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--lazyLoadingEnabled 延迟加载的全局开关,默认false-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。
         否则,每个延迟加载属性会按需加载,默认false-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    
    <!--设置包下的实体类别名,类名不区分大小写-->
    <typeAliases>
        <package name="entity.*"/>
    </typeAliases>
    
    <plugins>
        <!--分页插件-->
        <plugin interceptor="com.github.pagehelper.PageHelper"></plugin>
    </plugins>
    
    <!--配置数据源环境-->
    <environments default="development">
        <environment id="development">
            <!-- transactionManager:设置事务管理方式
                属性: type="JDBC|MANAGED"
                JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
                MANAGED:被管理,例如由Spring管理事务 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>


    <!--配置映射文件-->
    <mappers>
        <!--以包为单位引入映射文件
            要求:
            1、mapper接口所在的包要和映射文件所在的包一致
            2、mapper接口要和映射文件的名字一致-->
        <package name="mapper"/>
        <!--<mapper resource="mapper/StudentMapper.xml"/>-->
    </mappers>

</configuration>

2、查询结果集中含有实体对象 或 集合 或者 分步查询

2.1、DeptAndEmpMapper 接口

package mapper;

import entity.Dept;
import entity.Emp;
import org.apache.ibatis.annotations.Param;

public interface DeptAndEmpMapper {

    Dept getAllEmpByDept(@Param("deptId") Integer deptId);

    Emp getEmpDeptByEmpId(@Param("empId") Integer empId);

    Emp getEmpDeptByTowStep(@Param("empId") Integer empId);

    Dept getDeptById(@Param("deptId") Integer deptId);
}

2.1核心配置文件中的映射文件 mapper.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">

<mapper namespace="mapper.DeptAndEmpMapper">
    <!--如下是单个实体类列名对应属性名映射-->
    <resultMap id="empMap" type="entity.Emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
    </resultMap>
    <resultMap id="deptMap" type="entity.Dept">
        <id property="deptId" column="dept_id" />
        <result property="deptName" column="dept_name"/>
    </resultMap>


    <resultMap id="empDeptMap" type="entity.Emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="dept" resultMap="deptMap"/>
    </resultMap>
    <!-- 2、一对一关系,对象中含有对象-->
    <select id="getEmpDeptByEmpId" parameterType="java.lang.Integer" resultMap="empDeptMap">
        select * from emp e left join dept d on d.dept_id = e.dept_id where e. id = #{empId}
    </select>


    <resultMap id="empByDeptMap" type="entity.Dept">
        <id property="deptId" column="dept_id" />
        <result property="deptName" column="dept_name"/>
        <!--对象中含有集合 ofType:指定返回值类型 或者 resultMap-->
        <collection property="empList" resultMap="empMap" />
    </resultMap>
    <!-- 3、一对多关系,对象中含有集合-->
    <select id="getAllEmpByDept" parameterType="java.lang.Integer" resultMap="empByDeptMap">
        select * from dept d left join emp e on d.dept_id = e.dept_id where d.dept_id = #{deptId}
    </select>


    <resultMap id="empDeptByTowStepMap" type="entity.Emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <!-- ①select指定查询的唯一sql id , column指定查询入参,fetchType:eager立即加载 lazy懒加载-->
        <association property="dept" fetchType="lazy" select="mapper.DeptAndEmpMapper.getDeptById" column="dept_id"/>
    </resultMap>
    <!--1、分两次查询sql-->
    <select id="getEmpDeptByTowStep" parameterType="java.lang.Integer" resultMap="empDeptByTowStepMap">
        select * from emp e where e.id = #{empId}
    </select>
    <!--  ②第二次查询-->
    <select id="getDeptById" parameterType="java.lang.Integer" resultMap="deptMap">
        select * from dept e where e.dept_id = #{deptId}
    </select>

</mapper>

2.3、测试

public class DeptAndEmpTest {

    private SqlSession getSqlSession() throws IOException {
        //读取核心配置文件
        InputStream resource = Resources.getResourceAsStream("mybatis_config.xml");
        //创建SQLSession工厂类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resource);
        //创建会话,并设置自动提交事务 true
        return sqlSessionFactory.openSession(true);

    }

    @Test//3、一对多关系,对象中含有集合-
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSession();
        DeptAndEmpMapper mapper = sqlSession.getMapper(DeptAndEmpMapper.class);
        Dept allEmpByDept = mapper.getAllEmpByDept(1);
        System.out.println(allEmpByDept);

    }

    @Test//2、一对一关系,对象中含有对象
    public void test02() throws IOException {
        SqlSession sqlSession = getSqlSession();
        DeptAndEmpMapper mapper = sqlSession.getMapper(DeptAndEmpMapper.class);
        Emp emp = mapper.getEmpDeptByEmpId(1);
        System.out.println(emp);

    }

    @Test//1、分两次查询sql
    public void test03() throws IOException {
        SqlSession sqlSession = getSqlSession();
        DeptAndEmpMapper mapper = sqlSession.getMapper(DeptAndEmpMapper.class);
        Emp emp = mapper.getEmpDeptByTowStep(1);
        System.out.println(emp.getName());

    }

}

3、MyBatis获取参数值的方式

3.1、StudentMapper 接口

package mapper;

import entity.Student;
import org.apache.ibatis.annotations.Param;

public interface StudentMapper {

    Student selectStudent(String id);

    Student selectByName(String name);

    //封装参数以param值为key存入map
    Student selectByNameId(@Param("name") String name, @Param("id") String id);

    Student selectByStudent(Student student);

    int selectById(String id);

    int insertAreaDict(@Param("name") String name);
}

3.2、StudentMapper.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">
<!--
    mapper接口的全类名和映射文件的命名空间(namespace)保持一致
    mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致-->
<mapper namespace="mapper.StudentMapper">
    <resultMap id="studentMap" type="entity.Student">
        <id property="sId" column="s_id"/>
        <result property="sName" column="s_name"/>
        <result property="sBirth" column="s_birth"/>
        <result property="sSex" column="s_sex"/>
    </resultMap>
    <!-- ${}的本质就是字符串拼接,#{}的本质就是占位符赋值
    ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引
    号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,
    可以自动添加单引号-->
    <select id="selectStudent"  resultType="entity.Student" parameterType="java.lang.String">
      select * from student where s_id = #{id}
    </select>

    <select id="selectById"  resultType="java.lang.Integer">
      select count(*) from student where s_id = #{id}
    </select>

    <!--${} 3.5版本需要value或者-->
    <select id="selectByName"  resultMap="studentMap" >
      select * from student where s_name = '${value}'
    </select>

    <!--@Param设置参数键名 -->
    <select id="selectByNameId"  resultMap="studentMap" >
      select * from student where s_name = #{name} and s_id = #{id}
      <!--多个变量会默认放入map中以arg1, arg0, param1, param2为键-->
      <!--select * from student where s_name = #{param1} and s_id = #{param2} -->
    </select>

    <!--参数是实体类型可以直接取属性值-->
    <select id="selectByStudent"  resultMap="studentMap" parameterType="entity.Student">
      select * from student where s_name = #{SName} and s_id = #{sId}
    </select>

    <!-- useGeneratedKeys:设置使用自增的主键, keyProperty:将主键值设置在该属性上 -->
    <insert id="insertAreaDict" useGeneratedKeys="true" keyProperty="UUID">
      INSERT INTO mooc_area_dict_t (UUID,show_name) VALUES (null, #{name});
    </insert>

</mapper>

3.3 测试

public class MybatisStartTest {

    @Test//#{}占位符
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        int count = studentMapper.selectById("01");
        System.out.println(count);
        sqlSession.close();
    }

     @Test
    public void test02() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         Student student = studentMapper.selectStudent("01");
         System.out.println(student);
        sqlSession.close();
    }

     @Test//${} 3.5版本需要value或者
    public void test03() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         Student student = studentMapper.selectByName("王菊");
         System.out.println(student);
        sqlSession.close();
    }

     @Test//@Param设置参数键名
    public void test04() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         Student student = studentMapper.selectByNameId("王菊","08");
         System.out.println(student);
        sqlSession.close();
    }

     @Test//参数是实体类型可以直接取属性值
    public void test05() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         Student user = new Student();
         user.setSName("王菊");
         user.setSId("08");
         Student student = studentMapper.selectByStudent(user);
         System.out.println(student);
        sqlSession.close();
    }

     @Test//插入数据,自动生成主键id
    public void test06() throws IOException {
        SqlSession sqlSession = getSqlSession();
        //通过代理模式创建StudentMapper的代理实现类
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         studentMapper.insertAreaDict("上传");
        sqlSession.close();
    }

    private SqlSession getSqlSession() throws IOException {
        //读取核心配置文件
        InputStream resource = Resources.getResourceAsStream("mybatis_config.xml");
        //创建SQLSession工厂类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resource);
        //创建会话,并设置自动提交事务 true
        return sqlSessionFactory.openSession(true);

    }
}

五、分页插件的使用

①添加依赖

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.2.0</version>
</dependency>

②配置分页插件

<plugins>
	<!--设置分页插件-->
	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

1、在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能

pageNum:当前页的页码
pageSize:每页显示的条数

2、在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List list, int navigatePages)获取分页相关数据

list:分页之后的数据
navigatePages:导航分页的展示的页码数

@Test
public void testPage(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //查询功能之前开启分页功能
    Page<Object> page = PageHelper.startPage(5, 4);
    List<Emp> list = mapper.selectByExample(null);
    //查询功能之后可以获取分页相关的所有数据
    PageInfo<Emp> pageInfo = new PageInfo<>(list, 5);
    list.forEach(System.out::println);
    System.out.println(pageInfo);
}

3、分页相关数据

PageInfo{
	pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
	list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
	pages=8, reasonable=false, pageSizeZero=false},
	prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
	hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
	navigatepageNums=[4, 5, 6, 7, 8]
}

pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页展示的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值