mybatis学习笔记

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();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值