1.全局配置
   <properties></properties>引入外部*.ptoperties文件,可以通过${jdbc.ulr}区出properties中的值
    <settings>
       <setting name="" value=""/>定义mybatis的一些全局性设置,在官网可以查看具体设置哪些
       <setting name="mapUnderscoreToCamelCase" value="false"/>设置是否开启下划线命名法转驼峰命名法
    </settings> 
    <typeAliases></typeAliases> 为一些类定义别名
    <typeHandlers></typeHandlers> 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系
    <objectFactory type=""></objectFactory> 对象工厂
    <plugins>  插件:mybatis的插件,插件可以修改mybatis的内部运行规则
       <plugin interceptor=""></plugin>
    </plugins> 
2.mapper映射文件
 
  1. 增删改查
  2. <mapper namespace="com.njupt.mapper.StudentMapper"> <!-- 接口全类名 -->
            <!-- resultType 可以省略 -->
            <select id="getStudentById"  resultType="com.njupt.entities.Student">
                   select * from student where id=#{id}
            </select>
             <!-- resultType 可以省略 -->
            <insert id="addStudent"  parameterType="com.njupt.entities.Student">
                   insert into student(name,age) values(#{name},#{age})
            </insert>
            <!-- resultType 可以省略 -->
            <update id="updateStudent"  parameterType="com.njupt.entities.Student">
                   update student set name=#{name}, age=#{age} where id =  #{id}
            </update>
             <!-- resultType 可以省略 -->
            <delete id="deleteStudent">
                   delete from student where id=#{id}
            </delete>
    </mapper>
  3. 使用方法
  4.     public void getStudentById() throws IOException{
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //1.获取sqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //2.获取SqlSession对象那个
            SqlSession session = sqlSessionFactory.openSession();
            //会为接口自动创建一个代理对象,代理对象执行对应的方法
            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            Student student = studentMapper.getStudentById(1);
            System.out.println(student);
            int tmp = studentMapper.addStudent(new Student("aaa",18));
            session.commit();
        }
  5. 增删改的注意事项
    1. sqlSessionFactory.openSession()这种方式不会自动提交修改,sqlSessionFactory.openSession(true)这样会自动提交
    2. 增删改标签没有resultType标签,但是会返回操作的数据行数。可以自动转为int,long,boolean类型
  6. 添加并获取自增主键
  7.      useGeneratedKeys表示使用自增主键,keyProperty表示将使用jDBC操作获取的id赋值给参数的哪个属性
      <insert id="addStudent" parameterType="com.njupt.entities.Student" useGeneratedKeys="true" keyProperty="id">
          insert into student(name,age) values(#{name},#{age})
      </insert>
       String resource = "mybatis-config.xml";
       InputStream inputStream = Resources.getResourceAsStream(resource);
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
       SqlSession session = sqlSessionFactory.openSession();
       StudentMapper studentMapper = session.getMapper(StudentMapper.class);
       Student student = new Student("bbb",18);//此时student的id是没有值的
       int tmp = studentMapper.addStudent(student);
       System.out.println(student.getId()); //输出3
       session.commit();
       System.out.println(student.getId());//输出3
3.Mybatis参数处理
  1. 只有一个参数时,Mybatis不做处理,随便取,都可以取出来
    1. public Student getStudentById(Integer id);
    2. <select id="getStudentById" resultType="com.njupt.entities.Student">
    3.     select * from student where id=#{id}//这里的#{}可以随便写,#{a},#{b}
    4. </select>
  2. 当有多个参数时,Mybati会将多个参数封装成map
    1. key为param1,param2.......paramN; value为参数值
    2. 或者采用#{0},#{1}这种根据索引取值
    3. 使用@param注解指定key
      1. public Student getStudentByIdAndName(@Param("id")int id, @Param("name")String name);
      2. <select id="getStudentByIdAndName" resultType="com.njupt.entities.Student">
      3.     select * from student where id=#{id} and name=#{name}
      4. </select>
    4. 直接使用POJO对象
      1. public int addStudent(Student student);
    5. 如果多个参数不是实体类,但是又经常使用,那么就自定义一个参数实体类,专门用来传参
    6. 将多个参数封装成map,直接传入自定义的map
  3. 当传入的参数是Collection(List,Set)的对象时,也特殊处理
    1. Collection=====>#{collection}
    2. List=====>#{list[0]},List的默认键为list
    3. Set======>#{set}
      1. public Student getStudentByIds(List<Integer> ids);
      2. <select id="getStudentByIds" resultType="com.njupt.entities.Student">
      3.     select * from student where id=#{list[0]}
      4. </select>
  4. 示例,考虑以下场景如何取值
    1. public Student getStudentByIdAndName(@Param("id")int id,String name);
      1. id==>#{id/param1}   name==>#{param2}
    2. public Student getStudentByIdAndName(@Param("id")int id,Student student);
      1. id==>#{id/param1}, name==>#{param2.name}
  5. #{}与${}都可以取出参数的值,他们有什么区别
    1. #{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement; 可以防止sql注入
    2. ${}:取出的值直接拼接在sql语句中
    3. 什么情况下使用#{}与${}
      1. 只要支持PreparedStatement的,都采用#{}
      2. 不支持PreparedStatement都采用${},例如,表名,排序等
        1. select * from 2016_table/2017_table/2018_table   -->select *from table ${year}_table
        2. select * from table order by ${f_name} ${order}
  6. #{}:还可以规定参数的一些规则
    1. javaType, jdbcType, mode(存储过程),numericScale(保留小数位数),resultMap, typeHandler,jdbcTypeName,expression
    2. javaType通常在某些特定条件下需要被设置
      1. 假设添加email字段为null,java类型:null----映射---->jdbc类型:OTHER。而oracle不支持OTHER类型就会报错
      2. 解决方案一:修改全局配置
        1. <setting name="jdbcTypeForNull" value="NULL"/>
      3. 解决方案二:
      1. #{email,jdbcType=NULL}
4.查询语句的细节
  1. 查询语句返回一个List那么ResultType应该放的是List中元素的类型
    1. public List<Student> getStudentsGreaterThanAge(@Param("age") int age);  
    2. <select id="getStudentsGreaterThanAge" resultType="com.njupt.entities.Student">
    3.     select * from student where age>=#{age}
    4. </select>
  2. 返回map
    1. 返回一个map,键为数据库的列名,值为数据库的值
      1. public Map<String,Object> getMapById(@Param("age") int id);
      2. <select id="getMapById" resultType="map">
      3.      select * from student where id=#{id}
      4. </select>
    2. 返回一个map,键为指定的键,值为POJO对象
      1. @MapKey("id")
      2. public Map<Integer, Student> getStudentsByAge(@Param("age") int age);
      3. <select id="getStudentsByAge" resultType="com.njupt.entities.Student">  
      4.     select * from student where age>=#{age}
      5. </select>
    3. 如果数据库列名与javaBean属性字段不匹配,返回结果无法封装成javaBean,该怎么办
      1. 给列名起别名
      2. 满足驼峰命名法,我们在全局配置文件中开启驼峰命名法
      3. 使用resultMap自定义映射
        1. <!-- type表示封装的javaBean类型,id为这个映射规则返回的Map类型的别名 -->
        2. <resultMap type="com.njupt.entities.Student" id="stu">
        3.     <!-- 元素id表示主键映射,coulumn表示数据库的哪一列,property表示映射为javaBean的哪个属性 -->
        4.     <id column="id" property="id"/>
        5.     <!-- 元素result表示普通列的映射,coulumn表示数据库的哪一列,property表示映射为javaBean的哪个属性 -->
        6.     <result column="name" property="name"/>
        7. </resultMap>
        8. <select id="getStudentsByAge" resultMap="stu">
        9.     select * from student where age>=#{age}
        10. </select>
  3. resultMap的高级用法
    1. 一对一,如果javaBean对象内的属性也有一个对象该怎么做呢,例如学生信息里有学校
      1. 联合查询
        1. 级联属性
          1. <resultMap type="com.njupt.entities.Student" id="stuPlus">
          2.     <id column="id" property="id"/>
          3.     <result column="name" property="name"/>
          4.     <result column="scId" property="school.id"/>
          5.     <result column="scName" property="school.name"/>
          6. </resultMap>
          7. <select id="getStudentById" resultMap="stuPlus">
          8.     select st.id id,st.name name, st.sc_id schoolId, sc.id scId, sc.name scName
          9.     from student st
          10.     inner join school sc
          11.     on st.sc_id=sc.id
          12.     where id=#{id}
          13. </select>
        2. 利用association标签实现属性对象的封装
          1. <resultMap type="com.njupt.entities.Student" id="stuPlus">
          2.     <id column="id" property="id"/>
          3.     <result column="name" property="name"/>
          4.     <association property="school" javaType="com.njupt.entities.School">
          5.          <id column="scId" property="id"/>
          6.          <result column="scName" property="name"/>
          7.     </association>
          8. </resultMap>
      2. 分步查询association
        1. 学生内有一个属性时学校,我们可以先查出学生信息,然后根据查出的学生信息的scId,再去查学校信息
          1. Mybatis简单实用
    2. 一对多,如果一个学校含有很多学生,该怎么查呢 collection
      1. 联合查询
        1. <resultMap type="com.njupt.entities.School" id="sch">
        2.     <id column="id" property="id"/>
        3.     <result column="name" property="name"/>
        4.     <collection property="stuList" ofType="com.njupt.entities.Student">
        5.             <id column="stId" property="id"/>
        6.             <result column="stName" property="name"/>
        7.     </collection>
        8. </resultMap>
        9. <select id="getAllStudentsInSchool" resultMap="sch">
        10.      select sc.id id,sc.name name, st.id stId, st.name stName
        11.      from school sc
        12.      inner join  student st
        13.      on sc.id=st.sc_id
        14.      where id=#{id}
        15. </select>
      2. 分步查询
        1. <resultMap type="com.njupt.entities.School" id="sch">
        2.     <id column="id" property="id"/>
        3.     <result column="name" property="name"/>
        4.     <collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="id">
        5.     </collection>
        6. </resultMap>
        7. <select id="getAllStudentsInSchool" resultMap="sch">
        8.      select id, name
        9.      from school
        10.      where id=#{id}
        11. </select>
    3. 扩展
      1. 在上面的示例中,第二步查询只传递了一个参数,如果有多个参数怎么办column="{id=id,name=name}"
        1. <collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="{id=id,name=name}" >
  4. 延迟加载,考虑到association每次都会发送两条sql,当我们调用某个字段时,才去发送对应的sql,我们需要开启延迟加载
    1. 全局配置
      1. <setting name="lazyLoadingEnabled" value="true"/>开启延迟加载
      2. <setting name="aggressivelazyLoadingEnabled" value="false"/>关闭非延迟加载
    2. 分步查询的标签有个字段fetchType也可设置延迟加载,且其优先级高于全局配置
      1. <collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="id" fetchType="lazy">
5.动态sql
    1. Mybatis简单实用
  1. if标签
    1. 要求根据传过来的参数,字段非空,来设置sql语句
      1. <select id="getStudentByIf" resultType="com.njupt.entities.Student">
      2.     select * from student where
      3.     <if test="id!=null">id=#{id}</if>
      4.     <if test="name!=null and name!='' ">and name=#{name}</if>
      5. </select>
      6. OGNL虽然支持&& 等逻辑判断,但是由于在xml中&属于特殊字符,需要使用html的实体字符来替代
  2. where标签,用于查询时去除头部多余的and
    1. 对于上面的sql拼装,我们发现如果id为空,那么sql语句就变为了。select * from student where and name=#{name}这就错了
    2. where标签可以去除头部的and或者or等
      1. <select id="getStudentByIf" resultType="com.njupt.entities.Student">
      2.     select * from student
      3.     <where>
      4.         <if test="id!=null">id=#{id}</if>
      5.         <if test="name!=null and name!='' ">and name=#{name}</if>
      6.     </where>
      7. </select>
  3. trim标签
    1. prefix:给trim标签体最终返回的结果,加一个前缀
    2. prefixOverrides:给trim标签体最终返回的结果,删除指定前缀
    3. suffix:给trim标签体最终返回的结果,加一个后缀
    4. suffixOverrides:给trim标签体最终返回的结果,删除指定后缀
    5. <select id="getStudentByIf" resultType="com.njupt.entities.Student">
    6.         select * from student        
    7.         <trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="and">
    8.             <if test="id!=null">id=#{id} and</if>
    9.             <if test="name!=null and name!='' "> name=#{name}</if>
    10.         </trim>
    11.     </select>
  4. choose标签,分支选择,相当于带了break的switch-case语句
    1. 进行一个查询,带了id字段就用id查询,带了name字段就用name查询
    2. <select id="getStudentByIf" resultType="com.njupt.entities.Student">
    3.     select * from student        
    4.     <where>
    5.         <choose>
    6.             <when test="id!=null"> id=#{id}</when>
    7.             <when test="name!=null">name=#{name}</when>
    8.             <otherwise>1=1</otherwise>
    9.         </choose>
    10.     </where>
    11. </select>
  5. set标签,用于去除更新操作时,多余的逗号","
    1. <update id="updateStudent">
    2.     update student
    3.     <set>
    4.         <if test="name!=null">name=#{name},</if>
    5.         <if test="age!=null">age=#{age}</if>
    6.     </set>
    7.     <where>
    8.         id=#{id}
    9.     </where>
    10. </update>
  6. foreach标签,用于批量操作
    1. 批量删除,方法1,
      1.  public int deleteMoreStu(String ids);   
      2. <delete id="deleteMoreStu">
      3.     delete from student where id in (${ids})
      4. </delete>
      5. studentMapper.deleteMoreStu("1,2,3,4,5");
    2. 利用foreach批量处理集合,
      1. collection:集合名
      2. item:集合内的每个元素的临时名称
      3. open:在循环结束后得到的结果前面,添加的字符串
      4. close:在循环结束后得到的结果后面,添加的字符串
      5. index:如果Collection为List,则index为下标。如果为Map,则index为键
      6. separator:分隔符
      7. public int deleteMoreStu(@Param("ids")List<Integer> ids);
      8. <delete id="deleteMoreStu">
      9.     delete from student where id in 
      10.     <foreach collection="ids" item="id" open="(" close=")" separator=",">//如果没有用@Param修饰,collection=“list”
      11.         #{id}
      12.     </foreach>
      13. </delete>
    3. 批量操作
      1. delete
        1. delete from student where id in ()
        2. delete from student where id=1 or id=2
      2. select
        1. select * from student where id in ()
        2. select * from student where id=1 or id=2
      3. update:
        1. 批量修改
      4. insert
        1. insert into student(name,age) values ("www",24),("yh",25),("xiaowang",18)
        2. public int insertMoreStudents(@Param("stuList")List<Student> stuList);
        3. <insert id="insertMoreStudents" parameterType="com.njupt.entities.Student">
        4.     insert into student(name,age) values
        5.     <foreach collection="stuList" item="stu" separator=",">
        6.         (#{stu.name}, #{stu.age})
        7.     </foreach>
        8. </insert>
  7. sql标签用于抽取可重用sql语句片段,支持判断语句
    1. <sql id="insertCols">name,age</sql>
    2. <insert id="insertMoreStudents" parameterType="com.njupt.entities.Student">
    3.     insert into student(<include refid="insertCols"></include>) values
    4.     <foreach collection="stuList" item="stu" separator=",">
    5.         (#{stu.name}, #{stu.age})
    6.     </foreach>
    7. </insert>
  8. 内置参数
    1. _parameter:代表整个参数
      1. 单个参数:_parameter就是这个参数
      2. 多个参数:参数会被封装为一个map,_parameter就是这个map
    2. _databaseId:如果配置了databaseIdProvider标签
      1. _databaseId:就代表当前数据库的别名
6.缓存
  1. 一级缓存(本地缓存):其实就是sqlSession对象的一个Map. sqlSession级别的缓存,一级缓存是一直开启的,无法关闭
    1. 与数据库同一次会话期间查询到的数据会放到本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
    2. 一级缓存失效情况(即执行同样的sql会访问数据库)
      1. sqlSession不同
      2. sqlSession相同,查询条件不同
      3. sqlSession相同,查询条件相同,但是两次查询期间,执行了增删改操作
      4. sqlSession相同,手动清除了缓存,session.clearCache()
  2. 二级缓存(全局缓存):基于namespace的缓存,即一个Mapper.XML文件,对应一个二级缓存
    1. 工作机制
      1. 一个会话,查询一条数据,这个数据会被放在当前sqlSession的一级缓存中
      2. 如果会话关闭或者提交,当前缓存会被保存到二级缓存中,新的会话查询信息,可以参照二级缓存。
      3. 不同的namespace查出的数据会放在自己对应的缓存(map)中
    2. 使用
      1. 全局配置文件中开启全局二级缓存配置
        1. <setting name="cacheEnabled" value="true"/>开启二级缓存
      2. 对要开启二级缓存的Mapper.XML中,启动二级缓存
        1. eviction:缓存回收策略
          1. LRU-最近最少使用,移除最长时间不被使用的对象,默认
          2. FIFO-先进先出,按对象进入缓存的顺序移除他们
          3. SOFT-软引用,移除基于垃圾回收器状态和引用规则的对象
          4. WEAK-弱引用,更积极地移除基于垃圾回收器状态和引用规则的对象
        2. flushInterval:缓存刷新间隔,缓存多长时间清空一次,默认不清空,单位毫秒
        3. readOnly:缓存是否只读,默认false
          1. true:只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改原数据,因此,maybatis直接返回数据的引用,效率较高。
          2. false:非只读。获取的数据可能会被用户修改,那么mybatis就不会将原数据的引用交给用户,而是利用序列化技术,返回给用户一个副本。安全,速度慢
        4. size:默认缓存内存大小
        5. type:自定义缓存的全类名,实现Cache接口
        6. <mapper namespace="com.njupt.mapper.StudentMapper"> <!-- 接口全类名 -->
        7.     <cache eviction="FIFO" flushInterval="1000" readOnly="false" ></cache>
      3. POJO对象实现序列化接口
        1. 由于二级缓存使用序列化技术,因此POJO对象需要实现Serializable接口
  3. 和缓存有关的设置
    1. <setting name="cacheEnabled" value="false"/>关闭二级缓存,一级缓存一直开着
    2.  <select id="" useCache="true"></select>,UseCahe标签默认为true,设置是否使用二级缓存,
    3. <delete id="deleteMoreStu" flushCache="true"> flushCache默认为true,即每次增删改都会清空一二级缓存
    4. sqlSession.clearCache()只会清空一级缓存
    5. <setting name="localCacheScope" value="false"/>一级缓存的作用域
      1. SESSION:缓存作用域为Session
      2. STATEMENT:缓存作用域为Statement,相当于禁用了缓存
  4. 缓存的使用顺序,二级缓存-->一级缓存-->数据库
  5. 缓存的实现原理
    1. 我们知道缓存就是一个Map,那么其接口为
      1. Mybatis简单实用
      2.  
    2. Cache接口的实现类
      1. Mybatis简单实用
 
 

相关文章: