- 1.从XML中构建SqlSessionFactory
- 2.生产sqlSession会话实例:他可以执行已映射的sql语句,由SQLSessionFactory生成,
- 3.mybatis的配置文件:configuration->settings typeAlias environment mapper
- 4.映射的mapper.xml文件
- 5.万能map
- 6.配置文件—环境:事物管理transactionManager+数据源dataSource
- 7.配置文件—类型别名typeAliases
- 8.setting
- 9.分页:可以直接在sql上用limit实现,也可以用RowBounds实现
- 10.mybatis执行原理
- 11.多对一查询
- 12.动态sql
- 13.缓存
- 14、一级缓存
- 15、二级缓存
- 16.小结:
1.从XML中构建SqlSessionFactory
每个基于MyBatis 的应用都是以一个SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder获得。而$qISessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实例(这个后面讲)构建出SqlSessionFactory的实例。
从xml文件中构建SQLSessionFactory实例:
mybatis有个Resources的工具类,可以获取文件内容转成字节流。
SQLSessionFactoryBuilder可以通过build(inputStream)生产成SQLSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2.生产sqlSession会话实例:他可以执行已映射的sql语句,由SQLSessionFactory生成,
SqlSession sqlSession = sqlSessionFactory.openSession()
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
// 这里如果不提交事物,数据不会插入,所以应该提交事物
bookMapper.addBook(new Book(8, "python", 20, "人生苦短,我选python",null));
sqlSession.commit();
3.mybatis的配置文件:configuration->settings typeAlias environment mapper
<?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>
<!-- 显示sql语句-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<typeAlias type="com.mybatis.model.Book" alias="Book"/>
<typeAlias type="com.mybatis.model.User" alias="User"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mybatis配置文件中好像不能写中文,而且下面这个 &好像不存在,需要用&转义出来-->
<property name="url" value="jdbc:mysql://localhost:3306/lizhendb?useUnicode=true&
characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.mybatis.dao.BookMapper"/>
</mappers>
</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
用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. 后端中:List
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
也可以引如外部配置文件
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)。
日志,实现就是打印日志,其中,如果是
也可以是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里有 List
<!-- 多对一:嵌套查询 子查询-->
<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.xmL中使用二级缓存—>
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.查询数据库