Mybatis

快速开始

public interface UserDao {

    List<User> findAll();
}
<?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.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/user.xml"/>
    </mappers>
</configuration>
<?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="wang.ismy.mybatis.dao.UserDao">

    <select id="findAll" resultType="wang.ismy.mybatis.entity.User">
    SELECT * FROM user
  </select>
</mapper>

使用xml

@Test
public void findAll() throws IOException {

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(
            Resources.getResourceAsStream("config.xml"));
    SqlSession sqlSession = factory.openSession();

    List<User> list = sqlSession.getMapper(UserDao.class).findAll();
    assertEquals(6,list.size());
}

根据条件查询

<select id="findById" resultType="wang.ismy.mybatis.entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>

细节:

更新

<insert id="save" parameterType="wang.ismy.mybatis.entity.User">
    INSERT INTO user(username,address,sex,birthday) 
    VALUES(#{username},#{address},#{sex},#{birthday})
</insert>
int save(User user);

需要注意的是,mybatis的SqlSession关闭了事务的默认提交,当进行完更新操作后,需要手动调用sqlSession.commit();

模糊查询字符串拼接问题

SELECT * FROM user WHERE username LIKE '%' #{name} '%'

插入数据后返回ID

<insert id="save" parameterType="wang.ismy.mybatis.entity.User">

    <selectKey keyColumn="id" keyProperty="id" resultType="int">
    select last_insert_id();
    </selectKey>
    INSERT INTO user(username,address,sex,birthday) VALUES(#{username},#{address},#{sex},#{birthday})
</insert>

使用resultMap

<resultMap id="userMap" type="wang.ismy.mybatis.entity.User">
    <!--主键-->
    <id column="id" property="id"/>
    <!--非主键-->
    <result column="username" property="username"/>
</resultMap>

<select id="findAll" resultMap="userMap">
    select * from user
</select>

Properties标签

<properties resource="jdbc.cfg">

    </properties>

typeAliases标签

<typeAliases>
    <!--指定别名,不区分大小写-->
    <typeAlias type="wang.ismy.mybatis.entity.User" alias="user"/>

    <!--指定该包下的所有类为别名,不区分大小写-->
    <package name="wang.ismy.mybatis.entity"/>
</typeAliases>

mapper

<mappers>
    <package name="wang.ismy.mybatis.dao">
</mappers>

使用注解

public interface UserDao {

    @Select("SELECT * FROM user")
    List<User> findAll();
}
注解 作用
@Insert 实现新增
@Update 实现更新
@Delete 实现删除
@Select 实现查询
@Result 实现结果集封装
@Results 可以与@Result 一起使用,封装多个结果集
@ResultMap 实现引用@Results 定义的封装
@One 实现一对一结果集封装
@Many 实现一对多结果集封装
@SelectProvider 实现动态 SQL 映射
@CacheNamespace 实现注解二级缓存的使用

Result注解使用

@Select("SELECT * FROM user")
@Results({
        @Result(id=true,column = "id",property ="id"),
        @Result(column = "username",property ="username"),
        @Result(column = "sex",property ="sex"),
        @Result(column = "address",property ="address"),
        @Result(column = "birthday",property ="birthday")
})
List<User> find();
一对一查询
@Select("SELECT * FROM account")
@Results({
        @Result(id=true,column = "id",property = "id"),
        @Result(column = "uid",property = "uid"),
        @Result(column = "money",property = "money"),
        @Result(column = "uid",property = "user",one = @One(select = "wang.ismy.mybatis.dao.UserDao.findById",fetchType = FetchType.LAZY))
})
List<Account> findAll();

一对多查询

@Select("SELECT * FROM user")
@Results({
        @Result(id=true,column = "id",property ="id"),
        @Result(column = "username",property ="username"),
        @Result(column = "sex",property ="sex"),
        @Result(column = "address",property ="address"),
        @Result(column = "birthday",property ="birthday"),
        @Result(column = "id",property = "account",
                many = @Many(select = "wang.ismy.mybatis.dao.AccountDao.findById",fetchType = FetchType.LAZY))
})
List<User> find();

开启二级缓存

@CacheNamespace
public interface UserDao {}
<!--如果使用注解的话,则指定class属性-->
<mappers>
    <mapper class="wang.ismy.mybatis.dao.UserDao"/>
</mappers>

原理及源码分析

自定义Mybatis分析

Mapper 注册中心 -> 执行器 -> StatementHadnler -> ResultsetHandler

核心接口:

XML相关:

重点:

MapperProxy:

MapperMethod

ParamNameResolver

MappedStatement

Executor

StatementHandler

MetaObject:拦截器元数据

缓存机制

延迟加载

就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING" />
    <!--延迟加载相关-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
<resultMap id="userMap" type="user">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result property="address" column="address"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <collection property="account" ofType="Account" select="wang.ismy.mybatis.dao.AccountDao.findById" column="id">
        <id column="account_id" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
    </collection>
</resultMap>

使用的动态代理实现的延迟加载

缓存

批注 2020-05-18 103307

一级缓存

一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

命中原则:

生命周期:

缓存销毁:

设计理念:

二级缓存

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

二级缓存中存放的是数据而不是对象

开启
<!--默认为true,可以省略-->
<setting name="cacheEnabled" value="true"/>
<cache/>
<select id="findAll" resultMap="userMap" useCache="true">
    SELECT * FROM user
</select>
命中原则
生命周期

创建:

销毁:

缓存清除策略

批注 2020-05-18 105408

多表查询

一对一

<resultMap id="accountMap" type="Account">
    <id column="account_id" property="id"/>
    <result column="UID" property="uid"/>
    <result column="MONEY" property="money"/>
    <association property="user" javaType="User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result property="address" column="address"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
    </association>
</resultMap>
<select id="findAll" resultMap="accountMap">
     SELECT account.ID AS account_id,
     account.UID,
     account.MONEY,
     user.*
     FROM account,user
     WHERE account.UID = user.id
</select>
@Data
public class Account {

    private Integer id;

    private Integer uid;

    private Double money;

    private User user;
}

一对多

<resultMap id="userMap" type="user">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result property="address" column="address"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <collection property="account" ofType="Account">
        <id column="account_id" property="id"/>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
    </collection>
</resultMap>
<select id="findAll" resultMap="userMap">
   SELECT user.*,
    account.ID as account_id,
    account.uid,
    account.money
    FROM user LEFT OUTER JOIN account ON user.id = account.UID
</select>
@Data
public class User {

    private Integer id;

    private String username;

    private LocalDate birthday;

    private String sex;

    private String address;

    private List<Account> account;
}

左外连接的使用

多对多

多对多的映射关系,可以拆分成两个一对多的关系

动态SQL

if

<if test="username != null">
    #{username} 'abc'
</if>

where 标签

<where>
    <if test="...">
        ...
    </if>
</where>

这样就不用写where 1=1前缀

foreach标签

<foreach collection="ids" open="id in ( " close=")" item="uid"  separator=",">
      #{uid}     
</foreach>

SQL重用

<sql id="sql">
    SELECT * FROM user WHERE id = #{id}
</sql>
<select id="findById" resultType="wang.ismy.mybatis.entity.User">
    <include refid="sql"/>
</select>

连接池与事务

连接池:

事务:

/*提交事务*/
sqlSession.commit();
/*回滚事务*/
sqlSession.rollback();

分页插件

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
</dependency>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <props>
                        <prop key="helperDialect">oracle</prop>
                        <prop key="reasonable">true</prop>
                        
                    </props>
                </property>
            </bean>
        </array>
    </property>
</bean>
@Override
public List<Order> findAll() {
    PageHelper.startPage(1,5);
    return orderDao.findAll();
}