简介

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html

持久化

** 持久化是将程序数据在持久状态和瞬时状态间转换的机制。**

  • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等
  • JDBC就是一种持久化机制。文件IO也是一种持久化机制
  • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是

为什么需要持久化服务呢?那是由于内存本身的缺陷引起的

  • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
  • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。

第一个程序

Mybatis依赖

<!--mybatis pom依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
<!--mysql 链接驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<!--junit 测试工具-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.1</version>
  <scope>test</scope>
</dependency>
<!--日志传输-->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.10</version>
</dependency>
<!--解决maven文件过滤-->
<build>
  <resources>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
      <filtering>true</filtering>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>

XML配置(mybatis-config.xml)

<!--config配置-->
<?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="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <!--xml引用-->
    <mapper resource="xxxx/xxx.xml"/>
  </mappers>
</configuration>

代码

package com.mybatis.dao;
import com.mybatis.pojo.User;
import java.util.List;
 
/**
 * @ProjectName: ssm-framework
 * @Package: com.mybatis.Service
 * @ClassName: UserDto
 * @Author: zd
 * @Description:
 * @Date: 2021/11/2 11:24
 * @Version: 1.0
 */
public interface UserMapper {
    List<User> selectUser();
}
 
package com.mybatis.pojo;
/**
 * @ProjectName: ssm-framework
 * @Package: com.mybatis.pojo
 * @ClassName: User
 * @Author: zd
 * @Description:
 * @Date: 2021/11/2 11:17
 * @Version: 1.0
 */
public class User {
    private int id;
    private String name;
    private String pwd;
}
 
 
/**
* mybayis工具类
*/
package com.mybatis.uils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
 * @ProjectName: ssm-framework
 * @Package: com.mybatis.uils
 * @ClassName: MybatisUtils
 * @Author: zd
 * @Description:
 * @Date: 2021/11/2 11:27
 * @Version: 1.0
 */
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource ="mybatis-config.xml";
            InputStream inputStream=Resources.getResourceAsStream(resource);
            sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
 
    //获取SqlSession链接
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }
}
 
 
/**
* 测试类
*/
@Test
public void connTest(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    List<User> list=mapper.selectUser();
    list.forEach(user -> System.out.println(user));
}
<!--接口对应的XML配置-->
<?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">
<mapper namespace="com.mybatis.dao.UserMapper">
    <select id="selectUser" resultType="com.mybatis.pojo.User">
        select * from user
    </select>
</mapper>
<mappers>
  <!--xml在mybatis-config.xml中配置引用-->
  <mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>

CRUD操作及配置解析

namespace

接口xml中的配置参数,namespace属性要指定接口的全限定类名, 与接口的完整包名,必须一致! 。

<mapper namespace="com.mybatis.dao.UserMapper">

select

  • select语句有很多属性可以详细配置每一条SQL语句
    • SQL语句返回值类型【完整的类名或者别名】
    • 传入SQL语句的参数类型 【万能的Map,可以多尝试使用】
    • 命名空间中唯一的标识符
    • 接口中的方法名与映射文件中的SQL语句ID 一一对应
    • id 对应的namespace中的方法名, parameterType 参数类型, resultType sql语句执行的返回值。
//接口
public interface UserMapper {
    //返回集合
    List<User> selectUser();
    //返回对象
    User selById(int id);
    //多条件查询
    User selByConditions(@Param("name") String name,@Param("pwd") String pwd);
    //map集合查询
    User selByMap(Map<String, String> map);
}
 
 
//测试类
public class DemoTest {
    @Test
    public void connTest(){
        SqlSession session=MybatisUtils.getSession();
        UserMapper mapper=session.getMapper(UserMapper.class);
        List<User> list=mapper.selectUser();
        list.forEach(user -> System.out.println(user));
    }
    @Test
    public void connTest1(){
        SqlSession session=MybatisUtils.getSession();
        UserMapper mapper=session.getMapper(UserMapper.class);
        User user=mapper.selById(1);
        System.out.println(user);
    }
    @Test
    public void connTest2(){
        SqlSession session=MybatisUtils.getSession();
        UserMapper mapper=session.getMapper(UserMapper.class);
        User user=mapper.selByConditions("测试","123456");
        System.out.println(user);
    }
    @Test
    public void connTest3(){
        SqlSession session=MybatisUtils.getSession();
        UserMapper mapper=session.getMapper(UserMapper.class);
        Map<String, String> map = new HashMap<>();
        map.put("pwd","123456");
        map.put("name","测试");
        User user=mapper.selByMap(map);
        System.out.println(user);
    }
}
<?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">
<mapper namespace="com.mybatis.dao.UserMapper">
    <resultMap id="BaseResultMap" type="com.mybatis.pojo.User">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="pwd" jdbcType="VARCHAR" property="pwd"/>
    </resultMap>
 
    <sql id="Base_Column_List">
        id, name, pwd
    </sql>
    <select id="selectUser" resultType="com.mybatis.pojo.User">
        select * from user
    </select>
    <select id="selById" resultType="com.mybatis.pojo.User">
        select * from user where id = #{id}
    </select>
    <select id="selByConditions" resultType="com.mybatis.pojo.User">
        select
        <include refid="Base_Column_List"></include>
        from user where name = #{name,jdbcType=VARCHAR} AND pwd = #{pwd,jdbcType=VARCHAR}
    </select>
    <select id="selByMap" parameterType="map" resultType="com.mybatis.pojo.User">
        select
        <include refid="Base_Column_List"></include>
        from user where name = #{name,jdbcType=VARCHAR} AND pwd = #{pwd,jdbcType=VARCHAR}
    </select>
</mapper>

多条件查询需要在接口的参数上添加@Param(“xxx”)参数;

insert

使用新增,修改,删除时,必须使用session.commit()提交事务,否则数据库不会插入数据。

/**
   * insert方法
*/
int addUser(User user);
@Test
public void connTest4(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    User user = new User(4,"新增","Aa123123");
    int i=mapper.addUser(user);
    System.out.println(i);
    //提交事务,必须提交否则数据库不会插入数据
    session.commit();
    session.close();
}
<!--新增-->
<insert id="addUser" parameterType="com.mybatis.pojo.User">
  insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

update

/**
* update方法
*/
int updateUser(User user);
@Test
public void connTest5(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    User user = new User(4,"修改","Aa1111");
    int i=mapper.updateUser(user);
    System.out.println(i);
    //提交事务,必须提交否则数据库不会插入数据
    session.commit();
    session.close();
}
<!--修改-->
<update id="updateUser" parameterType="com.mybatis.pojo.User">
  update user set name = #{name},pwd = #{pwd} where id = #{id}
</update>

delete

/**
  * delete删除
*/
int deleteUser(User user);
 
@Test
public void connTest6(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    User user = new User(4,"修改","Aa1111");
    int i=mapper.deleteUser(user);
    System.out.println(i);
    //提交事务,必须提交否则数据库不会插入数据
    session.commit();
    session.close();
}
<!--删除-->
<delete id="deleteUser" parameterType="com.mybatis.pojo.User">
  delete from user where id = #{id}
</delete>

Map传参的sql使用

使用map传参可以对sql传入多个参数,切不按照表中名称或实体类中名称进行引用,若不使用map传多个参数需要使用@Param注解修饰。

@Test
public void connTest3(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    Map<String, String> map = new HashMap<>();
    map.put("pwd","123456");
    map.put("name","测试");
    User user=mapper.selByMap(map);
    System.out.println(user);
    session.close();
}
<select id="selByMap" parameterType="map" resultType="com.mybatis.pojo.User">
        select
        <include refid="Base_Column_List"/>
        from ssm_framework.user where name = #{name,jdbcType=VARCHAR} AND pwd = #{pwd,jdbcType=VARCHAR}
    </select>

模糊搜索

第1种:在Java代码中添加sql通配符

String str = "%测试%";
<select id="selByString" resultType="com.mybatis.pojo.User">
  select
  <include refid="Base_Column_List"/>
  from user where name like #{str}
</select>

第2种:在sql语句中拼接通配符,会引起sql注入**(不建议使用)**

<select id="selByLike" resultType="com.mybatis.pojo.User">
  select
  <include refid="Base_Column_List"/>
  from user where name like "%"#{str}"%"
</select>

第3种:$符替换

<select id="selByLike" resultType="com.mybatis.pojo.User">
  select
  <include refid="Base_Column_List"/>
  from user where name like '%${str}%'
</select>

小结:

  • 所有的增删改操作都需要提交事务
  • 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
  • 有时候根据业务的需求,可以考虑使用map传递参数!
  • 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!

核心配置文件(mybatis-config.xml)

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

configuration(配置)

  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
    • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
  • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)

environments元素

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>
  • environments元素用来配置多个数据库环境,需要用default属性默认一个运行环境;
  • environment子元素
    • dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
    • 数据源是必须配置的,且包含三种内建数据源类型:
type="[UNPOOLED|POOLED|JNDI]")
UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
-  数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等....  
-  子元素节点:transactionManager - [ 事务管理器 ]  , 这两种事务管理器类型都不需要设置任何属性。  
<!-- 语法 -->
<transactionManager type="[ JDBC | MANAGED ]"/>

properties元素

可以在外部进行配置,并进行动态替换,可以在java属性文件中配置属性,也可在properties元素中配置;

properties配置中,如果properties文件与property属性存在相同名称的配置,则优先使用properties文件,最后使用property属性的配置。

<!--引入外部配置文件-->
<properties resource="db.properties">
  <property name="root" value="123"/>
  <property name="password" value="123456"/>
</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>
 
 
<!--或在属性中添加-->
<properties resource="db.properties">
  <property name="root" value="root"/>
  <property name="password" value="123456"/>
</properties>

settings元素

log日志

设置mybatis的行为,其中logImpl为设置mybatis日志;

Log4j输出:
<!--引入包-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,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
 
#日志输出级别
#不显示sql结果集
log4j.logger.org.mybatis=DEBUG
#显示sql结果集
log4j.logger.org.mybatis=TRACE
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
配置log4j为日志的实现
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
log4j在类中的使用
  1. 在要使用log4j的类中引入;
  2. 日志对象定义,参数为当前类的class文件
  3. 日志级别debug,info,error等;
static Logger logger = Logger.getLogger(DemoTest.class);
 
logger.info("info级别");
logger.debug("debug级别");
logger.error("error级别");

typeAliases元素(类型别名)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写 。

  1. 在config中配置类名别名
<typeAliases>
  <typeAlias type="com.mybatis.pojo.User" alias="User"></typeAlias>
</typeAliases>
 
<!--加别名后结果集可直接改为别名-->
<!--<select id="selByLike" resultType="com.mybatis.pojo.User">-->
<select id="selByLike" resultType="User">
  select
  <include refid="Base_Column_List"/>
  from user where name like "%"#{str}"%"
</select>
  1. 扫描包加别名

当实体类过多时,可以使用package属性进行起别名,默认会将该包名下的所有类名首字母大写改为小写。

例:Useruser;

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

不可DIY别名,如果要改可使用注解添加在类名上,注解别名优先级高于xml配置别名;

@Alias("user")
public class User {}
  1. 常用的数据类型,在命名上不区分大小写,命名规则如下:
别名映射类型别名映射类型
_bytebytedoubleDouble
_longlongfloatFloat
_shortshortbooleanBoolean
_intintdateDate
_integerintdecimalBigDecimal
_doubledoublebigdecimalBigDecimal
_floatfloatobjectObject
_booleanbooleanmapMap
stringStringhashmapHashMap
byteBytelistList
longLongarraylistArrayList
shortShortcollectionCollection
intIntegeriteratorIterator
integerInteger

mappers元素

映射器 : 定义映射SQL语句文件

可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
 
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///D:/mywork/ssm-framework/mybatis-framework/src/main/resources/mapper/UserMapper.xml"/>
</mappers>
 
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
 
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

生命周期和作用域

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
  • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
  • 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。
  • 所以 SqlSession 的最佳的作用域是请求或方法作用域。

ResultMap及分页

字段取别名

当实体类中的字段名与数据库中的字段名不一致时,查询结果会返回null;

@Alias("us")
public class User {
    private int id;
    private String name;
    private String passward;
}
<select id="selByLike" resultType="us">
  select * from user where name like "%"#{str}"%"
</select>

返会结果为:

User{id=1, name='测试', passward='null'}User{id=2, name='张测', passward='null'}

解决方案:

<!--为列名指定别名 , 别名和java实体类的属性名一致-->
<sql id="Base_Column_List">
  id, name, pwd as passward
</sql>
<select id="selByLike" resultType="us">
  select <include refid="Base_Column_List"/> from user where name like "%"#{str}"%"
</select>
 
<!--使用结果集映射->ResultMap 【推荐】-->
<resultMap id="BaseResultMap" type="com.mybatis.pojo.User">
  <id column="id" jdbcType="INTEGER" property="id"/>
  <result column="name" jdbcType="VARCHAR" property="name"/>
  <result column="pwd" jdbcType="VARCHAR" property="passward"/>
</resultMap>
<select id="selByLike" resultMap="BaseResultMap">
  select <include refid="Base_Column_List"/> from user where name like "%"#{str}"%"
</select>

ResultMap

  1. 自动映射

使用POJO类或数据类型的映射,不需要手动配置,直接使用resultType属性配置即可;

  1. 手动映射

当数据库中字段名和实体类中字段名不一致时,就需要手动映射将数据库字段与实体类字段一一映射对应,返回类型使用ResultMap配置引用。

<resultMap id="BaseResultMap" type="com.mybatis.pojo.User">
  <id column="id" jdbcType="INTEGER" property="id"/>
  <result column="name" jdbcType="VARCHAR" property="name"/>
  <result column="pwd" jdbcType="VARCHAR" property="pwd"/>
</resultMap>
<select id="selByLike" resultMap="BaseResultMap">
  select 
  <include refid="Base_Column_List"/>
  from user where name like "%"#{str}"%"
</select>

分页的几种方式

  1. 使用Limit实现分页
#语法
SELECT * FROM table LIMIT stratIndex,pageSize
#索引查询5-15行
SELECT * FROM table LIMIT 5,10;
#查询第n行之前的数据,如果只给定一个参数,它表示返回最大的记录行数目
SELECT * FROM table LIMIT 5;

在sql中,limit分页起始位置参数设置:

起始位置 =  (当前页面 - 1 ) * 页面大小
<select id="selByPage" resultMap="BaseResultMap">
  select * from mybatis.user limit #{startPage,javaType=INTEGER},#{pageSize,javaType=INTEGER}
</select>
 @Test
public void connTest9(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    int startPage = 1;
    int pageSize = 20;
    Map<String, Integer> map = new HashMap<>();
    map.put("startPage",(startPage-1)*pageSize);
    map.put("pageSize",pageSize);
    List<User> list=mapper.selByPage(map);
    list.forEach(user -> System.out.print(user.toString()));
    session.close();
}
 
/**
  * 分页查询
*/
List<User> selByPage(Map<String, Integer> map);
  1. RowBounds分页
//选择全部用户RowBounds实现分页
List<User> getUserByRowBounds();
 
@Test
public void testUserByRowBounds() {
    SqlSession session = MybatisUtils.getSession();
 
    int currentPage = 2;  //第几页
    int pageSize = 2;  //每页显示几个
    RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize);
 
    //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
    List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
 
    for (User user: users){
        System.out.println(user);
    }
    session.close();
}
<select id="getUserByRowBounds" resultType="user">
  select * from user
</select>
  1. PageHelper插件

官方文档:https://pagehelper.github.io/

<!--pom引入-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

Mybatis执行流程

画板

使用注解开发

注解开发

  • mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。最强大的 MyBatis 映射并不能用注解来构建。
  • sql类型主要成分:
    • @select()
    • @update()
    • @insert()
    • @delete()

注意:使用注解开发就不用mapper.xml映射文件

/**
 * 注解查询
*/
@Select("select * from mybatis.user")
List<User> getAllUser();
 
@Test
public void connTest10(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    List<User> list=mapper.getAllUser();
    list.forEach(user -> System.out.println(user.toString()));
    session.close();
}

@Param注解

@Param注解用于给方法参数起名:

  • 在方法只接收一个基本类型参数时,可以不使用@Param;
  • 在方法接收多个参数时,一定要使用@Param注解给参数起名;
  • 如果参数是JavaBean,不能使用@Param;
User selByConditions(@Param("name") String name,@Param("pwd") String pwd);

${}符和#{}符的区别

  • #{}的作用是替换预编译语句中的占位符?(推荐使用)
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
  • ${}的作用是直接进行字符串替换
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');

Lombok

引入包且在编译器下载插件

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.16</version>
</dependency>

@Getter and @Setter

代替getset方法

@ToString

构建toSring方法

@EqualsAndHashCode

构建equals和hashCode方法

@AllArgsConstructor,@NoArgsConstructor

构建所有参数构造方法及无参构造方法

@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog

构建logFactory对象,日志输出使用。

@Data

构建get、set、无参构造方法,重写hashcode、equals方法。

@Builder

@Value

构建get方法

@Accessors(chain=true)

结合@Data注解使用可生成set链式编程,即set方法返回本对象;

复杂sql实现

create table teacher(
    id int(10) not null primary key auto_increment comment '主键id',
    name varchar(20) default null comment 'name'
)engine =INNODB DEFAULT CHARSET =utf8;
 
insert into teacher(`name`) values ('张三');
 
create table student(
    id int(10) not null primary key auto_increment comment '主键id',
    name varchar(20) default null comment '姓名',
    tid int(10) default null comment '老师id',
    key `fktid` (tid),
    constraint `fktid` foreign key (`tid`) references  `teacher` (id)
)engine =INNODB DEFAULT CHARSET =utf8;
 
insert into `student` (name, tid) VALUES ('小明',1);
insert into `student` (name, tid) VALUES ('小红',1);
insert into `student` (name, tid) VALUES ('小华',1);
insert into `student` (name, tid) VALUES ('小王',1);

一对多

当返回的一条数据中存在另一个类的数据,且为集合时,由于实体类字段和数据库字段不匹配需要使用resultMap映射,在映射中使用collection标签进行关联。collection中要将javaType替换为ofType。

按照结果嵌套处理

<mapper namespace="com.mybatis.dao.TeacherMapper">
    <select id="getTeacherAndStudents" resultMap="TeacherAndStudents">
        select t.id tid, t.name tname, s.id sid, s.name sname
        from student as s,
             teacher as t
        where t.id = s.tid
          and t.id = #{tid};
    </select>
    <!--按结果嵌套查询-->
    <resultMap id="TeacherAndStudents" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--实体类中参数为集合时,使用collection映射,使用ofType代替javaType-->
        <collection property="list" ofType="com.mybatis.pojo.Student">
            <result property="id" column="sid"/>
            <result property="name" column="tname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>

按照查询嵌套处理

<mapper namespace="com.mybatis.dao.TeacherMapper">
  <select id="getTeacherAndStudents2" resultMap="TeacherAndStudents2">
    select * from teacher where id = #{id};
  </select>
  <select id="getStudents" resultType="Student">
    select * from student where id = #{tid};
  </select>
  <!--按结果嵌套查询-->
  <resultMap id="TeacherAndStudents2" type="Teacher">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--实体类中参数为集合时,使用collection映射,使用ofType代替javaType-->
    <collection property="list" javaType="arraylist" ofType="Student" column="id" select="getStudents">
      <result property="id" column="id"/>
      <result property="name" column="name"/>
      <result property="tid" column="tid"/>
    </collection>
  </resultMap>
</mapper> 

多对一

查询所有学生及老师信息

select s.name,t.name from student as s ,teacher as t where s.tid = t.id;

按照查询嵌套处理

@Data
public class Student {
    private String id;
    private String name;
    //学生关联老师
    private Teacher teacher;
}
 
@Data
public class Teacher {
    private String id;
    private String name;
}
<resultMap id="StudentAndTeacher" type="Student">
  <result property="id" column="id"/>
  <result property="name" column="name"/>
  <!--复杂类型的返回类型,返回对象使用:association  返回集合使用:collection  -->
  <association property="teacher" javaType="Teacher" column="tid" select="getTeacher"/>
</resultMap>
 
<select id="getTeacher" resultType="Teacher">
  select * from teacher where id = #{tid};
</select>
<select id="getAllStudentAndTeacher" resultMap="StudentAndTeacher">
  select * from student;
</select>

按照结果嵌套处理

<resultMap id="StudentAndTeacher2" type="Student">
  <result property="id" column="id"/>
  <result property="name" column="sname"/>
  <!--复杂类型的返回类型,返回对象使用:association  返回集合使用:collection  -->
  <association property="teacher" javaType="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
  </association>
 
</resultMap>
 
<select id="getAllStudentAndTeacher2" resultMap="StudentAndTeacher2">
  select s.id as id,s.name as sname,t.id as tid,t.name as tname from student as s ,teacher as t where s.tid = t.id;
</select>

javaType:用来指定类属性中的对象;

ofType:用来指定映射到集合中的pojo类型,泛型中的约束类型.

动态sql

根据不同的参数生成不同的sql,如多条件查询,不为null的属性进行查询。

if语句

如果使用多个条件查询时,当第一个参数为空时,第二个参数为where and xxx = #{xxx},所以使用where 1=1不合适,建议使用标签。

<select id="getTeacherAndStudents2" resultMap="TeacherAndStudents2">
  select id,name from ssm_framework.teacher where 1=1
  <if test ='id != null'>
    id = #{id}
  </if>
</select>

trim(where,set)语句

where标签在使用时,如果后缀条件满足时,会将条件语句前的and或or去掉,拼接sql。

set标签与where同理,去掉多余的逗号。

choose(when,otherwise)语句

<select id="getTeacherAndStudents2" resultMap="TeacherAndStudents2">
  select id,name,address from ssm_framework.teacher where 1=1
  <choose>
    <when test = 'address == '西安''>
      and name = #{name}
    </when>
    <when test = 'address == '北京''>
      and id = #{id}
    </when>
    <otherwise>
      and id = #{id} and name = #{name}
    </otherwise>
  </choose>
</select>

foreach语句

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

sql片段

多个sql存在公共语句时,会抽取出来当做sql片段,如:查询字段或查询条件修改条件。

<sql id="Base_map">
  id,name
</sql>
 
<select id="getTeacherAndStudents2" resultMap="TeacherAndStudents2">
  select <include refid="Base_map"/> from ssm_framework.teacher where id = #{id};
</select>

缓存

一级缓存

本地缓存,作用域为session级别,即一次连接内相同的查询会在缓存中取;

@Test
public void connTest2(){
    SqlSession session=MybatisUtils.getSession();
    UserMapper mapper=session.getMapper(UserMapper.class);
    User user=mapper.selByConditions("测试","123456");
    User user2=mapper.selByConditions("测试","123456");
    System.out.println(user);
    System.out.println(user2);
    System.out.println(user == user2);
    session.close();
}
//输出
[com.mybatis.dao.UserMapper.selByConditions]-==>  Preparing: select id, name, pwd from ssm_framework.user where name = ? AND pwd = ? 
[com.mybatis.dao.UserMapper.selByConditions]-==> Parameters: 测试(String), 123456(String)
[com.mybatis.dao.UserMapper.selByConditions]-<==      Total: 1
User{id=2, name='测试', pwd='123456'}
User{id=2, name='测试', pwd='123456'}
true

二级缓存

在mybatis-config.xml中,开启缓存;

<!--开启去全局二级缓存-->
<setting name="cacheEnabled" value="true"/>

mapper.xml开启二级缓存

<!--在当前mapper中开缓存-->
<cache/>

针对查询语句进行开启缓存

<select id="selById" parameterType="int" resultType="com.mybatis.pojo.User" useCache="true">
  select * from ssm_framework.user where id = #{id}
</select>

二级缓存生命周期是第一个session结束连接后,会将一级缓存的数据传递给二级缓存。二级缓存需要对实体类进行序列化。