1.从XML中构建SqlSessionFactory

每个基于MyBatis 的应用都是以一个SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder获得。而$qISessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实例(这个后面讲)构建出SqlSessionFactory的实例。
从xml文件中构建SQLSessionFactory实例:
mybatis有个Resources的工具类,可以获取文件内容转成字节流。
SQLSessionFactoryBuilder可以通过build(inputStream)生产成SQLSessionFactory

  1. String resource = "mybatis-config.xml";
  2. InputStream inputStream = Resources.getResourceAsStream(resource);
  3. sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);


2.生产sqlSession会话实例:他可以执行已映射的sql语句,由SQLSessionFactory生成,

  1. SqlSession sqlSession = sqlSessionFactory.openSession()
  2. BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
  3. // 这里如果不提交事物,数据不会插入,所以应该提交事物
  4. bookMapper.addBook(new Book(8, "python", 20, "人生苦短,我选python",null));
  5. sqlSession.commit();

3.mybatis的配置文件:configuration->settings typeAlias environment mapper

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4. <configuration>
  5. <!-- 显示sql语句-->
  6. <settings>
  7. <setting name="logImpl" value="LOG4J"/>
  8. </settings>
  9. <typeAliases>
  10. <typeAlias type="com.mybatis.model.Book" alias="Book"/>
  11. <typeAlias type="com.mybatis.model.User" alias="User"/>
  12. </typeAliases>
  13. <environments default="development">
  14. <environment id="development">
  15. <transactionManager type="JDBC"></transactionManager>
  16. <dataSource type="POOLED">
  17. <property name="driver" value="com.mysql.jdbc.Driver"/>
  18. <!--mybatis配置文件中好像不能写中文,而且下面这个 &好像不存在,需要用&amp;转义出来-->
  19. <property name="url" value="jdbc:mysql://localhost:3306/lizhendb?useUnicode=true&amp;
  20. characterEncoding=UTF-8&amp;useSSL=false&amp;serverTimezone=GMT%2b8"/>
  21. <property name="username" value="root"/>
  22. <property name="password" value="root"/>
  23. </dataSource>
  24. </environment>
  25. </environments>
  26. <mappers>
  27. <mapper class="com.mybatis.dao.BookMapper"/>
  28. </mappers>
  29. </configuration>

4.映射的mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
// 这个命名空间是需要的,来配置他映射接口的原文件(接口类)
<mapper namespace="com.mybatis.dao.BookMapper">
    <insert id="addBook" parameterType="Book">
        insert into books(bookID,bookName,bookCounts,detail)
       values(#{bookID},#{bookName},#{bookCounts},#{detail})
</insert>
</mapper>

CONCAT(‘%’,#{bookName},’%’),concat是拼接字符串
gmt_created <![CDATA[<=]]> #{endTime} 开头<![cdata[ 结尾]]> 中间写要转义的字符。
parameterType是参数类型(八大数据类型可以默认不写),
resultType是返回结果类型;resultMap是确定返回结果集
parameterType是参数类型;(Map写成map,List写成list,或者直接java.util.Map)
注意,即使这queryAll的返回结果是List,resultType也是Book

用map的主要功能是,构造pojo实例的时候可以少写点
比如,构造Book book = new Book(I,j,k,l,m),像这样,如果构造函数特别多,实例化一个book会很累,这样可以直接创建一个map,只写几个属性就行了,
但是,其实有些鸡肋,毕竟,book可以通过set赋值,和map.put也不多什么。

5.万能map

<select id="queryMapById" parameterType="map" resultType="Book">
       select * from books where bookID = #{mapID}
</select>
SqlSession sqlSession = MybatisUtils.getSqlSession();
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
Map map = new HashMap();
map.put("mapID",2);
Book book = bookMapper.queryMapById(map);

模糊查询:两种方法
1. 后端中:Listlist = mapper.getUser(“%李%”)
2. sql中:select * from user where name like “%”#{value}“%”
3. CONCAT(‘%’,#{bookName},’%’)

6.配置文件—环境:事物管理transactionManager+数据源dataSource

环境有两种,通过default来选择。(看前面的配置文件加以理解)
事物管理器有两种jdbc和managed,但是第二种几乎不用,了解即可

数据源(dataSource):
数据源类型有三种 dbcp c3p0 druid,目前只看c3p0就行
type:UNPOOLED | POOLED | JNDI :三种类型,没有连接池 | 有池子| 正常连接
属性:driver url username password 还有下面几个:
driver:jdbc驱动的java类的完全限定名
defaultTransactionIsolationLevel:默认的连接事物隔离级别
defaultNetworkTimeout:网络连接超时
也可以传递属性给数据库驱动:driver.encoding=UTF8
也可以引如外部配置文件
image.png

7.配置文件—类型别名typeAliases

1) 类型别名是为Java类型设置一个短的名字。
2) 存在的意义仅在于用来减少类完全限定名的冗余。
有两种存在方式:第一种type;第二种package

<typeAliases>
        <typeAlias type="com.mybatis.model.Book" alias="Book"/>
        <typeAlias type="com.mybatis.model.User" alias="User"/>
</typeAliases>

这是给具体的类加上别名

<typeAliases>
        <package name="com.mybatis.model"/>
</typeAliases>

用package,会直接扫描此文件下的所有类,然后首字母小写就是他的默认别名,如果想改的话,只能通过注解来改

@Alias("book1")
public class Book { }

8.setting

名称 默认 描述
是否开启缓存cacheEnabled: true 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
懒加载lazyLoadingEnable false 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
主键自动生成useGenerateKeys false 允许JDBC支持自动生成主键,需要驱动支持。如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如Derby)。
image.pngimage.png
日志,实现就是打印日志,其中,如果是
<setting name=”logImpl” value=” STDOUT_LOGGING“/>
也可以是LOG4J:让LOG4J来接管日志输出
那么怎么用LOG4J呢?
首先,创建LOG4J的配置文件,限定一些属性

#debug级别的日志,输出到console和file
log4j.rootLogger=debug,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG


然后

public class Mytest {             
    static Logger log = Logger.getLogger(Mytest.class);
    //在方法体里,就可以用了:
    log.info("info:");    
    log.debug("debug:");    
    log.error("error:");

9.分页:可以直接在sql上用limit实现,也可以用RowBounds实现

select * from books limit #{startIndex},#{total};
注意,第一个是开始索引,第二个是数据总长度。比如可以是1,1 表示从1开始截取一条数据。 当然分页也可以同RowBounds来实现,配合插件PageHelper一起用,用到再查。

10.mybatis执行原理

Resources获取加载全局配置文件-> 实例化SqlSessionFactoryBuilder构造器 -> 解析配置文件流XMLConfigBuilder ->
获取Configuration所有的配置信息 -> 创建sqlSessionFactory实例化 ->transactional事务管理 -> 创建executor执行器 ->
创建sqlSession -> 实现crud -> 看是否执行成功,不成功返回加粗步骤

11.多对一查询

select b.bookID,b.bookName,u.user_name from books b,user u where b.uid = u.id

book里有 User user User里有 Listbook

<!-- 多对一:嵌套查询  子查询-->
<select id="getBook" resultMap="bookUser">
    select * from books;
</select>
<resultMap id="bookUser" type="Book">
    <id property="bookID" column="bookID"></id>
    <result property="bookName" column="bookName"></result>
    <!--因为是对象,所以需要单独处理,这里用javaType来确定user的数据类型,然后给user赋值,通过getUser查出user,然后赋值,相当于是一个嵌套查询。合并了两次查询-->
    <association property="user" column="uid" javaType="User" select="getUserPart"/>
</resultMap>


<select id="getPart" resultType="User">
    <!--这里参数不管写什么,#{id},#{uid},#{did},都行,默认是上面的uid传过来的-->
    select user_name from user where id = #{id}
</select>


<!--多对一,按照结果嵌套-->
<select id="getBook2" resultMap="bookUser2">
    select b.bookID bid,b.bookName bname,u.user_name uname
    from books b,user u
    where b.uid = u.id;
</select>
<resultMap id="bookUser2" type="Book">
    <id property="bookID" column="bid"/>
    <result property="bookName" column="bname"/>
    <association property="user" javaType="User">
        <result property="user_name" column="uname" />
    </association>
</resultMap>


一对多 User getUser(@Param("uid")int uid);
<select id="getUser" resultMap="userBook">
    select * from user u,books b
    where b.uid = u.id and u.id = #{uid}
</select>
<resultMap id="userBook" type="user">
    <id property="id" column="id"></id>
    <result property="user_name" column="user_name"></result>
    <collection property="books" ofType="Book">
        <result property="bookName" column="bookName"></result>
    </collection>
</resultMap>


<select id="getUser22"  resultMap="userBook22">
    select * from user where id=#{uid}
</select>
<resultMap id="userBook22" type="user">
    <id property="id"  column="id"/>
    <result property="user_name" column="user_name"/>
    <collection property="books" column="id" javaType="List" ofType="Book" select="getBooksPart"></collection>
</resultMap>


<select id="getBooksPart" resultType="Book" >
    select * from books where uid =#{ uid}
</select>

1.关联- association【多对一】⒉集合- collection【一对多】
3. javaType & ofType
1.JavaType 用来指定实体类中属性的类型
2.ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
注意点: ·保证SQL的可读性,尽量保证通俗易懂 ·注意一对多和多对一中,属性名和字段的问题! ·如果问题不好排查错误,可以使用日志,建议使用Log4j

12.动态sql

where元素只会在至少有一个子元素的条件返回SQL子句的情况下才去插入”WHERE”子句。而且,若语句的开头为AND”或‘OR’,where元素也会将它们去除

<select id="list" parameterType="map" resultType="User">
    select * from user
    <where>
        <if test="">
            id = #{id}
        </if>
        <if test="">
            and name = #{name}
        </if>
    </where>
</select>


choose只会选择一个,如果第一个成立,第二个就不选,否则选第二个,否则选otherwise,如果第一个成立,选择第一个时候就退出了。会自动去掉and or等前缀,其实这些不写也行,因为只会选择一个

<select id="list" parameterType="map" resultType="User">
    select * from user
    <where>
        <choose>
            <when test="name!=null">
                name = #{name}
            </when>
            <when test="age!=null">
                and age=#{age}
            </when>
            <otherwise>
                and sex = #{sex}
            </otherwise>
        </choose>
    </where>
</select>


set会去掉多余的句号

<update id="update" parameterType="map">
    update user
    <set>
        <if test="name!=null">
            title=#{title},
        </if>
        <if test="age!=null">
            age=#{age}
        </if>
    </set>
    where id = #{id}
</update>


也可以单独抽出来sql片段
最好基于单表来定义SQL片段! 不要存在where标签

<sql id="if-name-age">
    <if test="name!=null">
        title=#{title},
    </if>
    <if test="age!=null">
        age=#{age}
    </if>
</sql>
<update id="update" parameterType="map">
    update user
    <set>
        <include refid="if-name-age"></include>
    </set>
    where id = #{id}
</update>

foreach:动态sql的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建in条件语句的时候,比如

<select id="select" resultType="User">
    select * from user u where id in
    <foreach collection="list" item="item" index="index" open="(" separator="," close=")">
        #{item}   <!--    (1,2,3,4,5)  -->
    </foreach>
</select>

13.缓存

1.什么是缓存[ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3.什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

Mybatis缓存

·MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
·MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)。二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

14、一级缓存

也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试步骤: 1.开启日志! 2.测试在一个Sesion中查询两次相同记录 3.查看日志输出
结果:只查一次数据库。如果两次查询之间有clearCache()

15、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存·基于namespace级别的缓存,一个名称空间,对应一个二级缓存;·
  • 工作机制:一级缓存的生命在sqlSession的start和end之中。

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

  • 新的会话查询信息,就可以从二级缓存中获取内容;
  • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

开启缓存:在configuration文件中,使用,然后在mapper映射文件的namespace下,
<! —在当前Mapper.xmL中使用二级缓存—>
flushInterval=”60000” 每隔60s刷新缓存
size=”512” 大小512个缓存
readonly=”true> 只读

清理缓存:clearCache()在后端这样。

BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
sqlSession.clearCache();
List<Book> lists = bookMapper.getBook();
sqlSession.clearCache();
List<Book> lists1 = bookMapper.getBook();


缓存失效的情况:
1.查询不同的东西 ⒉增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
3.查询不同的Mapper.xml 4.手动清理缓存!

16.小结:

1)只要开启了二级缓存,在同一个Mapper下就有
2)所有的数据都会先放在一级缓存中;
3)只有当会话提交,或者关闭的时候,才会提交到二级缓冲中!
缓存顺序:1.先看二级缓存中有没有 2.再看一级缓存中有没有 3.查询数据库
image.png