1、什么是mybatis
-
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
2、使用mybatis
-
在pom.xml中引入jar
<dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.48</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
-
创建mybatis的核心配置文件: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是每个environment的标识,可在上面的default中引用 <environment id="development"> //事务管理器 <transactionManager type="JDBC"/> //配置数据源 <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> </configuration>
-
创建SqlSession工具类
public class MybatisUtil { private static SqlSessionFactory factory; static { try { //读取配置文件,获取工厂 String resource="mybatis-config.xml"; InputStream inputStream= Resources.getResourceAsStream(resource); factory=new SqlSessionFactoryBuilder().build(inputStream);//构建工厂 } catch (IOException e) { e.printStackTrace(); } } //获取sqlsession public static SqlSession getSqlSession(){ return factory.openSession();//使用工厂获取SqlSession } }
-
创建接口类
public interface UserMapper { //获取所有用户 List<User> getUserList(); //根据id查询用户 User getById(int id); //增加一个用户 void insertUser(User user); //修改用户 void updateUser(User user); //删除用户 void deleteUser(int id); }
-
创建mybatis映射文件
<?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"> <!--namespace:用于绑定dao接口--> <mapper namespace="com.com.hzp.dao.UserMapper"> <!--getUserList:查询所有用户--> <!--id:方法名 resultType:方法返回的类型--> <select id="getUserList" resultType="com.hzp.pojo.User"> select * from mybatis.user </select> <!--insertUser--> <insert id="insertUser" parameterType="com.hzp.pojo.User"> insert into mybatis.user values (#{id},#{name},#{upwd});//id name upwd是User实体类的属性名,使用它们来取出User中的属性值 </insert> <!--updateUser--> <update id="updateUser" parameterType="com.hzp.pojo.User"> update mybatis.user set name=#{name},pwd=#{upwd} where id=#{id}; </update> <!--deleteUser--> <delete id="deleteUser" parameterType="com.hzp.pojo.User"> delete from mybatis.user where id=#{id}; </delete> </mapper>
-
在mybatis-config.xml注册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> .... <mappers> //第一种注册方式:指定mapper映射文件 <mapper resource="com/hzp/dao/UserMapper.xml"/> //第二种方式:指定映射文件所绑定的接口类 <mapper class="com.hzp.dao.UserMapper"/> //第三种方式:指定包名 <package name="com.hzp.dao"/> </mappers> </configuration>
注意点:第二种方式和第三种方式需满足两个条件:
1、接口类和映射文件必须同名
2、接口类和映射文件必须在同一个包下
-
测试
@Test public void test(){ //使用工具类获取SqlSession SqlSession sqlSession = MybatisUtil.getSqlSession(); //获得代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); //执行方法 List<User> userList = mapper.getUserList(); //输出 for (User user : userList) { System.out.println(user); } //关闭session sqlSession.close(); } //增删改必须提交事务 @Test public void insertUser(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.insertUser(new User(4, "王舞", "765623")); sqlSession.commit();//提交事务 sqlSession.close();//关闭session }
3、解析mybatis执行过程
//SqlSessionFactoryBuilder.java
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
//创建工厂
SqlSessionFactory factory;
//通过inputStream创建解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析器进行解析并将解析结果configuration返回
factory = this.build(parser.parse());
......
return factory;
}
//XMLConfigBuilder.java
//XMLConfigBuilder构造方法
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
//通过inputStream创建XPathParser
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
//XPathParser对象被封装在XMLConfigBuilder中
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
//this.configuration.setVariables(props);
this.parsed = false;
//this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
......
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));//<configuration>是mybatis-config.xml根节点
return this.configuration;
}
private void parseConfiguration(XNode root) {
try {
//下面就是解析<configuration>节点下的子节点了
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
//这里是解析properties节点的,其它的都差不多
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = this.configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
this.parser.setVariables(defaults);
//将解析出来的Properties放入configuration
this.configuration.setVariables(defaults);
}
}
//DefaultSqlSessionFactory.java
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
public SqlSession openSession(boolean autoCommit) {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession sqlSession;
//从configuration中获取环境信息,也就是mybatis-config.xml内<environment>中的
Environment environment = this.configuration.getEnvironment();
//通过environment获取TransactionFactory 事务工厂对象
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//使用事务工厂获取Transaction对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//通过事务对象获取Executor对象 执行器
Executor executor = this.configuration.newExecutor(tx, execType);
//将configuration和executor放入到SqlSession对象中
sqlSession = new DefaultSqlSession(this.configuration, executor, autoCommit);
。。。。。
return sqlSession;
}
总的来说,
SqlSessionFactoryBuilder
内的build
方法通过inputStream
(通过mybatis-config.xml
获取的流)创建了一个XMLConfigBuilder
对象,该对象内封装了一个XPathParser
对象(解析器对象),解析器对象会解析inputStream中的数据,然后将解析结果放入configuration
对象,该对象又会被作为参数传入DefaultSqlSessionFactory
构造,获得工厂对象,接着通过工厂获取SqlSession,获取前,会先得到configuration对象内的Environment
对象,通过它来创建TransactionFactory
事务工厂对象,有了工厂对象,便可以从里面获取Transaction
事务对象,接着通过事务对象获取Executor
执行器(执行增删查改),最后将执行器对象和configuration对象放入SqlSession
内并返回。
//大致看下Transaction接口和Executor接口
public interface Transaction {
Connection getConnection() throws SQLException;//获取连接
void commit() throws SQLException;//提交事务
void rollback() throws SQLException;//回滚事务
void close() throws SQLException;//关闭连接
Integer getTimeout() throws SQLException;//获取连接超时时间
}
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;//结果处理器
int update(MappedStatement ms, Object parameter) throws SQLException;//增、删、改都属于修改范畴
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;//查询
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
void clearLocalCache();//清理缓存
Transaction getTransaction();
void close(boolean forceRollback);
}
4、XML配置
1、properties(属性)
引入外部配置文件,动态替换数据源中的数据
<!--引入外部配置文件-->
<properties resource="db.properties">
<!--也可以在这里额外添加其它属性,如果和配置文件中的冲突,以配置文件优先-->
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--通过${属性名}进行引用-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
2、settings(设置)
cacheEnabled
:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存,默认值为true,所以其实可以不写。
<!--mybatis-config.xml-->
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
<!--UserMapper.xml-->
<cache/> //加上这个标签当前mapper会在SqlSession关闭时将一级缓存的数据放入到二级缓存
lazyLoadingEnabled
:延迟加载(懒加载)的全局开关。当开启时,所有关联对象都会延迟加载,也就是如果不使用关联对象的话是不会去查询的。默认值false。也可以通过fetchType单独设置懒加载。
//核心配置文件
<setting name="lazyLoadingEnabled" value="true"/>
//映射文件
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" select="getTeacherById" javaType="Teacher"/>
<!--
<association property="teacher" column="tid" select="getTeacherById" javaType="Teacher" fetchType="lazy"/>
-->
</resultMap>
<!--getStuList-->
<select id="getStuList" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<select id="getTeacherById" resultType="Teacher">
select * from mybatis.teacher where id=#{id}
</select>
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> stuList = mapper.getStuList();
System.out.println("懒加载。。。。");
for (Student student : stuList) {
System.out.println(student);
}
sqlSession.close();
}
//执行结果
DEBUG main org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
DEBUG main org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 274773041.
DEBUG main com.hzp.dao.StudentMapper.getStuList - ==> Preparing: select * from mybatis.student
DEBUG main com.hzp.dao.StudentMapper.getStuList - ==> Parameters:
DEBUG main com.hzp.dao.StudentMapper.getStuList - <== Total: 4
懒加载。。。。
DEBUG main com.hzp.dao.StudentMapper.getTeacherById - ==> Preparing: select * from mybatis.teacher where id=?
DEBUG main com.hzp.dao.StudentMapper.getTeacherById - ==> Parameters: 1(Integer)
DEBUG main com.hzp.dao.StudentMapper.getTeacherById - <== Total: 1
Student(id=1, name=张三, teacher=Teacher(id=1, name=李存华))
Student(id=2, name=刘翔, teacher=Teacher(id=1, name=李存华))
Student(id=3, name=如烟, teacher=Teacher(id=1, name=李存华))
Student(id=4, name=萱儿, teacher=Teacher(id=1, name=李存华))
//如果不进行输出的话是这样的
DEBUG main org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
DEBUG main org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 274773041.
DEBUG main com.hzp.dao.StudentMapper.getStuList - ==> Preparing: select * from mybatis.student
DEBUG main com.hzp.dao.StudentMapper.getStuList - ==> Parameters:
DEBUG main com.hzp.dao.StudentMapper.getStuList - <== Total: 4
懒加载。。。。
mapUnderscoreToCamelCase
:是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
<setting name="mapUnderscoreToCamelCase" value="true"/>
//BlogMapper.xml
//这个是单独抽取的sql片段后面讲
<sql id="if-author-title">
<if test="author!=null">
author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-author-title"></include>
</where>
</select>
//Blog.java
public class Blog implements Serializable {
private String id;//博客id
private String title;//博客标题
private String author;//博客作者
private Date createTime;//创建时间 数据库中是create_time
private int views;//浏览量
}
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String,Object> map=new HashMap<String, Object>();
map.put("author", "小灵儿");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
测试结果:
Blog(id=2, title=我去无情, author=小灵儿, createTime=Tue Aug 18 00:00:00 CST 2020, views=30)
Blog(id=3, title=炉石传说, author=小灵儿, createTime=Tue Aug 18 00:00:00 CST 2020, views=60)
logImpl
:指定 MyBatis 所用日志的具体实现,可选值:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,这里使用LOG4J 。
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<setting name="logImpl" value="LOG4J"/>
#log4j.properties
log4j.rootLogger = debug,A,B,C
# 输出到控制台
log4j.appender.A = org.apache.log4j.ConsoleAppender
log4j.appender.A.Target = System.out
log4j.appender.A.layout = org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern = %p %t %c - %m%n
# 输出到日志文件
log4j.appender.B = org.apache.log4j.DailyRollingFileAppender
log4j.appender.B.File = logs/log.log
log4j.appender.B.Append = true
log4j.appender.B.Threshold = DEBUG # 输出EBUG级别以上的日志
log4j.appender.B.layout = org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern = %p %t %c - %m%n
# 保存异常信息到单独文件
log4j.appender.C = org.apache.log4j.DailyRollingFileAppender
log4j.appender.C.File = logs/error.log # 异常日志文件名
log4j.appender.C.Append = true
log4j.appender.C.Threshold = ERROR #只输出ERROR级别以上的日志
log4j.appender.C.layout = org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern = %p %t %c - %m%n
3、typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。一般是给pojo类设置。
<typeAliases>
<!--单独给一个pojo类设置别名-->
<typeAlias alias="blog" type="com.hzp.pojo.Blog"></typeAlias>
<!--也可以直接指定一个包的别名,别名默认是类名首字母小写,不过好像大写也阔以-->
<package name="com.hzp.pojo"/>
</typeAliases>
<select id="queryBlogIF" parameterType="map" resultType="Blog">//这里resultType可以不写全限定类名
select * from mybatis.blog
<where>
<include refid="if-author-title"></include>
</where>
</select>
4、environments(环境配置)
environments内可配置多套环境,不过只有一套可以生效,也就是default中引用的id。
environment内有两部分组成:
1、transactionManager事务管理器,type有两种:JDBC|MANAGED
2、数据源,type有三种:UNPOOLED|POOLED|JNDI,一般使用POOLED,其内部的property就是设置一些连接数据库的参数
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
5、mappers(映射器)
注册映射器,告诉mybatis去哪找我们的映射文件。
注意点:第二种方式和第三种方式必须满足两个条件:
1、接口名字和映射文件名必须一致
2、接口和映射文件必须在同一个包下
<mappers>
//第一种注册方式:指定mapper映射文件
<mapper resource="com/hzp/dao/UserMapper.xml"/>
//第二种方式:指定映射文件所绑定的接口类
<mapper class="com.hzp.dao.UserMapper"/>
//第三种方式:指定包名
<package name="com.hzp.dao"/>
</mappers>
5、XML映射
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。
1、select
<!--查询所有
id:方法名
resultType:查询结果集映射成pojo的类型,需要
注意,字段名需要和pojo类的属性名一样才能成功映射(除了满足驼峰命名自动映射的)
-->
<select id="selectBlogList" resultType="Blog">
select * from blog;
</select>
<!--
查询所有用户
如果有字段名和属性名不一致的话,则需要使用resultMap手动配置映射
id:在select中使用resultMap进行引用
type:pojo类型
property:属性名
column:字段名
-->
<select id="selectUserList" resultMap="UserMap">
select * from user;
</select>
<resultMap id="UserMap" type="user">
<!--字段名和属性名一样可以不用配-->
<!--<id property="id" column="id"/>-->
<!--<result property="name" column="name"/>-->
<result property="upwd" column="pwd"/>
</resultMap>
<!--
分页查询
-->
<select id="selectPageList" parameterType="map" resultType="Blog">
select * from blog
where title=#{title}
limit #{startIndex},#{pageSize}
</select>
2、insert
<!--
插入数据
parameterType:指定参数的类型
#{}:用于取出参数传过来的数据,如果是基本数据类型,名字可以任意,如果是引用类型,
取用名和属性名必须一致才能取,如果是map,则通过key来取
-->
<insert id="insertBlog" parameterType="Blog">
insert into Blog values(#{id},#{title},#{author})
</insert>
3、update
<update id="updateBlog" parameterType="map">
update blog set title=#{title},author=#{author} where id=#{id}
</update>
4、delete
<delete id="deleteBlog" parameterType="int">
delete from blog where id=#{id}
</delete>
6、多表查询
1、多对一
public class Student {
private int id;
private String name;
private Teacher teacher;
}
public class Teacher {
private int id;
private String name;
}
<!--按照查询嵌套处理-->
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--
因为Student中包含复杂属性teacher,无法直接映射,
需要关联其它查询。
property:复杂属性名
column:关联另一张表的字段
select:被关联的查询,column的值将会作为它的查询条件
javaType:属性类型
-->
<association property="teacher" column="tid" select="getTeacherById" javaType="Teacher"/>
</resultMap>
<!--getStuList-->
<select id="getStuList" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<select id="getTeacherById" resultType="Teacher" parameterType="int">
select * from mybatis.teacher where id=#{id}
</select>
-------------------------------------------------------------------
<!--按照结果嵌套处理-->
<select id="getStuList2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname from mybatis.student s,mybatis.teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<!--查询已关联,可以直接映射-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
2、一对多
public class Student {
private int id;
private String name;
}
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
<!--结果嵌套查询-->
<select id="getTeacherById" resultMap="TeacherStudent">
select t.id tid,t.name tname,s.id sid,s.name sname from teacher t,student s
where t.id=s.tid;
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--collection用于处理集合属性,ofType是集合内存储元素的pojo类型-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!--查询嵌套-->
<select id="getTeacherById2" resultMap="TeacherStudent2">
select * from mybatis.teacher;
</select>
<!--
javaType:实体类属性的类型
ofType:映射到集合比如List里的pojo类型
-->
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" select="getStudent" column="id" ofType="Student"/>
</resultMap>
<select id="getStudent" resultType="Student">
select * from mybatis.student where tid=#{tid}
</select>
7、动态sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
1、if
<!--test是条件表达式,如果为true,则拼接里面的语句
<where>标签是智能的,它会根据情况确定是否去掉‘and’,
因为第一个表达式为false的话会多出来一个and
-->
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="author!=null">
author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</where>
</select>
2、choose…when…otherwise
<!--
类似于switch,只会有一个生效
-->
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="author!=null">
author=#{author}
</when>
<when test="title!=null">
title=#{title}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
3、set
set会智能的删除多余的逗号
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="author!=null">
author=#{author}
</if>
<if test="title!=null">
,title=#{title}
</if>
</set>
where id=#{id}
</update>
4、foreach
foreach用于遍历集合,collection表示该集合,item表示集合内元素,open内的值将会作为左边界,close作为右边界,separator表示分隔符
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
5、sql
sql标签的作用是抽取可复用的sql片段,并可通过include进行引用。
<sql id="if-author-title">
<if test="author!=null">
author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-author-title"></include>
</where>
</select>
8、缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以将我们查询的数据暂时放在缓存内,如果下次来了相同的查询,会直接从缓存中取而不去数据库,默认情况下,只启用了本地的会话缓存,它仅仅对一个会话(SqlSession)中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
查询顺序:
1、先查看二级缓存
2、再查看一级缓存
3、最后才去数据库
<cache/>
//不清理缓存,第二次查询日志不会产生sql语句
@Test
public void queryBlogForeach(){
//一级缓存:SqlSession级别的缓存,默认开启
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String,Object> map=new HashMap<String, Object>();
ArrayList<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids", ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
// sqlSession.clearCache();//清理缓存
System.out.println("=======================");
List<Blog> blogs1 = mapper.queryBlogForeach(map);
for (Blog blog : blogs1) {
System.out.println(blog);
}
sqlSession.close();
}
//增删改会刷新缓存
@Test
public void testCache01(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
//先查询
HashMap<String,Object> map1=new HashMap<String, Object>();
ArrayList<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map1.put("ids", ids);
List<Blog> blogs = mapper.queryBlogForeach(map1);
System.out.println("=========");
//再修改
HashMap<String,Object> map2=new HashMap<String, Object>();
map2.put("author", "小九九");
map2.put("id", "7c30f47fe65e4a96991adc1d1d9d3dbc");
mapper.updateBlog(map2);
//再次查询
List<Blog> blogs2 = mapper.queryBlogForeach(map1);
sqlSession.close();
}
@Test
public void testCache02(){
//不同的查询不会刷新缓存
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
//查询1
HashMap<String,Object> map1=new HashMap<String, Object>();
ArrayList<Integer> ids=new ArrayList<Integer>();
ids.add(1);
map1.put("ids", ids);
List<Blog> blogs = mapper.queryBlogForeach(map1);
System.out.println("=========");
//查询2
HashMap<String,Object> map2=new HashMap<String, Object>();
ArrayList<Integer> ids2=new ArrayList<Integer>();
ids2.add(2);
ids2.add(3);
map2.put("ids", ids2);
List<Blog> blogs2 = mapper.queryBlogForeach(map2);
System.out.println("==================");
//查询1
List<Blog> blogs3 = mapper.queryBlogForeach(map1);
for (Blog blog : blogs3) {
System.out.println(blog);
}
sqlSession.close();
}
//测试二级缓存
@Test
public void testCache03(){
/*
Caused by: java.io.NotSerializableException: com.hzp.pojo.Blog
Blog实体类必须序列化才能使用二级缓存
所有的数据会先存放在一级缓存中,等到会话提交或关闭时,才会转交到
二级缓存
*/
//不同的查询不会刷新缓存
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession1.getMapper(BlogMapper.class);
//查询1
HashMap<String,Object> map1=new HashMap<String, Object>();
ArrayList<Integer> ids=new ArrayList<Integer>();
ids.add(1);
map1.put("ids", ids);
List<Blog> blogs = mapper.queryBlogForeach(map1);
sqlSession1.close();
System.out.println("=========");
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
BlogMapper mapper2 = sqlSession2.getMapper(BlogMapper.class);
//查询2
List<Blog> blogs2 = mapper2.queryBlogForeach(map1);
sqlSession2.close();
}