MyBatis高级应用

131 篇文章 1 订阅

内容

1.Mapper文件之Sql标签用法
2.Mapper文件之主键映射
3.Mapper文件之ResultMap标签用法
4.Mapper文件之关联查询
5.懒加载机制
6.缓存机制
7.Mapper文件之动态Sql

一.Sql标签用法

用来定义sql块,便于代码复用.
<?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="mapper.UserMapper">
    <!--定义sql块-->
    <sql id="baseSql">
        id,username,password
    </sql>
    <sql id="nameSql">
        username
    </sql>

    <select id="selectAll" resultType="User">
        select
        <!--引入sql块-->
         <include refid="baseSql"></include>
        from user
    </select>

</mapper>

二.主键映射

1.当数据插入操作不关心插入后数据的主键(唯一标识),那么建议使用 不返回自增主键值 的方式来配置插入
语句,这样可以避免额外的SQL开销.
2.当执行插入操作后需要立即获取插入的自增主键值,比如一次操作中保存一对多这种关系的数据,那么就要
使用 插入后获取自增主键值的方式配置.

1.方式一

 <!--useGeneratedKeys:使用主键自增机制
	keyProperty:把获取的主键自增值绑定到id属性上
	注意:适用于主键有自增机制的数据库,如mysql,sqlServer等
-->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into User (username,password) values(#{username},#{password})
  </insert>

2.方式二

  <!--通用方式-->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    <!--
	resultType:表示返回值类型
	keyColumn:查询主键值对应的列名
	keyProperty:把主键值绑定到相应对像的属性名
 	order:执行获取主键语句的顺序
 -->
        <selectKey keyColumn="ID" keyProperty="id" order="AFTER" resultType="User">
            SELECT LAST_INSERT_ID() as ID FROM DUAL
        </selectKey>

        insert into User (username,password) values(#{username},#{password})
    </insert>

三.ResultMap标签的用法

1.作用

1.自定义结果集映射:
	当对像的setter方法后缀和查询表中数据对应的列不相同,mybatis默认会封装数据失败.
2.多表关联查询的映射:
	后面会详细讲解
注意:select标签中的ResultMap属性和ResultType属性,两者不能同时使用

2.解决方式

1.通过给字段起别名方式
	让别名的模型对像的Setter方法后缀一致.
2.使用ResultMap标签重新定义字段和属性的映射关系

3.代码实现

 <!--自定义结果集映射-->
    <resultMap id="baseResultMap" type="User">
        <!--定义主键-->
        <id column="ID" property="id"></id>
        <result column="NAME" property="username"></result>
        <result column="PASSWORD" property="password"></result>
    </resultMap>
    
    <!--使用resultMap属性引入自定义结果集映射关系-->
    <select id="selectAll"  resultMap="baseResultMap">
        select
        <!--引入sql块-->
         <include refid="baseSql"></include>
        from user
    </select>

四.关联查询

1.多表关联关系

1.关联一方
	关联的单个POJO对像
2.关联多方
	关联的是集合对像

2.关联一方

方式一:通过JOIN查询方式进行多表关联操作
//User
public class User {

    private Integer id;
    private String username;
    private String password;
    
    //省略getter和setter
    
}
//Orders
public class Orders {

    private Integer id;
    private Double price;
    //关联一方
    private User user;
    
    //省略getter和setter
    
}
<!--OrdersMapper.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="mapper.OrdersMapper">

    <resultMap id="baseResultMap" type="Orders">
        <id property="id" column="ID"></id>
        <result property="price" column="PRICE"></result>
        <!--关联一方User-->
        <!--
        property:用来指定关联属性
        javaType:用来指定关联属性的具体类型
        -->
        <association property="user" javaType="User">
            <id property="id" column="ID"></id>
            <result property="username" column="NAME"></result>
            <result property="password" column="PASSWORD"></result>
        </association>
    </resultMap>

	<!--关联一方:方式一通过join查询方式-->
    <select id="selectById" parameterType="java.lang.Integer" resultMap="baseResultMap">
        select * from orders left join user on orders.uid=user.id where orders.id=#{id}
    </select>
</mapper>
方式二:通过select方式进行查询(Mybatis懒加载生效的前提)
<!--OrdersMapper.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="mapper.OrdersMapper">

    <resultMap id="baseResultMap2" type="Orders">
        <id property="id" column="ID"></id>
        <result property="price" column="PRICE"></result>
        <!--关联一方User-->
        <!--
        property:用来指定关联属性
        javaType:用来指定关联属性的具体类型
	    column:把当前结果集对像字段数据传递到另一个select语句中
        select:使用其他Mapper文件中的select语句进行查询.
        注意:其他mapper文件一般是namespace+statementID方式进行查询,
			如果当前mapper文件中的select语句,则namespace可省略
        -->
        <association property="user" javaType="User" column="UID" select="mapper.UserMapper.selectById"></association>
    </resultMap>


    <!--方式二:通过select语句方式-->
    <select id="selectById2" parameterType="java.lang.Integer" resultMap="baseResultMap2">
        select * from orders where id = #{id}
    </select>

</mapper>
<!--UserMapper.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="mapper.UserMapper">
    
     <resultMap id="baseResultMap" type="User">
        <id column="ID" property="id"></id>
        <result column="NAME" property="username"></result>
        <result column="PASSWORD" property="password"></result>
    </resultMap>
  
    <select id="selectById"  resultMap="baseResultMap">
        select * from user where id = #{id}
    </select>
</mapper>

练习

新建地址表Address 和User 是一对一关系,然后通join方式和select方式进行关联查询,以User为主动方关联Adress信息

3.关联多方

使用JOIN查询方式
//User
public class User {

    private Integer id;
    private String username;
    private String password;
    
    //关联多方
    private List<Orders> list;
    
    //省略getter和setter
    
}
//Orders
public class Orders {

    private Integer id;
    private Double price;
    //省略getter和setter
    
}
<!--UserMapper.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="mapper.UserMapper">
    
    <resultMap id="baseResultMap2" type="User">
        <!--定义主键-->
        <id column="ID" property="id"></id>
        <result column="NAME" property="username"></result>
        <result column="PASSWORD" property="password"></result>
        <!--关联多方-->
        <!--
        ofType:集合中元素的数据类型
        -->
        <collection property="list" ofType="Orders">
            <id column="OID" property="id"></id>
            <result column="PRICE" property="price"></result>
        </collection>
    </resultMap>

    <!--关联多方:方式一使用JOIN查询方式
	注意:当多张表的列名相同时,会出现数据封装错误问题,一般使用给列起别名方式处理-->
    <select id="selectById2" parameterType="java.lang.Integer" resultMap="baseResultMap2">
         SELECT user.*,orders.id as oid,orders.price FROM USER LEFT JOIN orders ON user.id = orders.uid WHERE user.id=#{id}
    </select>

</mapper>
方式二:使用select方式进行关联查询
<!--UserMapper.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="mapper.UserMapper">
   
    <!--自定义结果集映射-->
    <resultMap id="baseResultMap" type="User">
        <!--定义主键-->
        <id column="ID" property="id"></id>
        <result column="NAME" property="username"></result>
        <result column="PASSWORD" property="password"></result>
		<!--关联多方:方式二使用select方式-->
        <collection property="list" ofType="Orders"  column="ID" select="mapper.OrdersMapper.selectByUid"></collection>
    </resultMap>

    <select id="selectById"  resultMap="baseResultMap">
        select
        <include refid="baseSql"></include>
        from user where id = #{id}
    </select>

</mapper>
<!--OrdersMapper.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="mapper.OrdersMapper">
	
    <select id="selectByUid" resultType="Orders">
        select * from Orders where uid = #{uid}
    </select>

</mapper>

五.懒加载机制

Mybatis懒加载机制比hibernate来的简单.只对关联查询时,会使用懒加载.

1.懒加载前提

1.关联查询时,要使用select方式进行查询,不能使用join查询
2.默认情况,mybatis没有开启懒加载,所以要在Config文件配置全局设置

原理:
当需要加载关联表数据时,mybatis会为你查询关联表,否则,不去查询关联表,提交服务器的性能.

2.代码示例

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>
    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
   
    <!--省略其他配置-->
</configuration>
<!--OrdersMapper.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="mapper.OrdersMapper">

    <resultMap id="baseResultMap2" type="Orders">
        <id property="id" column="ID"></id>
        <result property="price" column="PRICE"></result>
        <association property="user" javaType="User" column="UID" select="mapper.UserMapper.selectById"></association>
    </resultMap>

    <!--方式二:通过select语句方式-->
    <select id="selectById2" parameterType="java.lang.Integer" resultMap="baseResultMap2">
        select * from orders where id = #{id}
    </select>

</mapper>
<!--UserMapper.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="mapper.UserMapper">
    
     <resultMap id="baseResultMap" type="User">
        <id column="ID" property="id"></id>
        <result column="NAME" property="username"></result>
        <result column="PASSWORD" property="password"></result>
    </resultMap>
  
    <select id="selectById"  resultMap="baseResultMap">
        select * from user where id = #{id}
    </select>
</mapper>
//OrdersMapper接口
public interface OrdersMapper {

    Orders selectById2(int id);

}

//OrdersService
public class OrdersService {

    public Orders findById2(int id){

        SqlSession session = DBUtil.openSqlSession();

        OrdersMapper mapper = session.getMapper(OrdersMapper.class);

        Orders orders = mapper.selectById2(id);

        session.close();

        return orders;
    }
}
//测试类
   @Test
    public void testFindById2(){
		//当没有主动使用关联数据时,会发现没有使用select语句去查询关联表,只有使用的时候才去查询
        Orders orders = service.findById2(1);
        
        System.out.println(orders.getUser());
     
    }

六.缓存机制

1.缓存分类

同hibernate一样,mybatis分为两级缓存:
1.一级缓存:
	属于SqlSession范围内的缓存,属性内存级别的缓存
	(hibernate一级缓存,是Session范围内的缓存)
2.二级缓存
	属于Mapper范围内的缓存,为同个mapper范围内的多个SqlSession共享的区域,但是Mybatis并没有对二级缓存提供实现,要依赖于第三方缓存厂商(EHCache,Redis....)
	(hibernate二级缓存,为多个session共享,是SessionFactory级别的缓存)

2.一级缓存

Mybatis的一级缓存是自动开启的,无需手动管理.

[外链图片转存失败(img-q0fpquIr-1564490171975)(1.bmp)]

一级缓存使用:
1.第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信
息。得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为
了让缓存中存储的是最新的信息,避免脏读。
2.第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户
信息。
<!--UserMapper.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="mapper.UserMapper">
  
    <!--一级缓存-->
    <select id="selectByCache1" resultType="User">
        select * from User where id = #{id}
    </select>

</mapper>
//接口
public interface UserMapper {

    User selectByCache1(int id);
}

//Service
  	/**
     * 一级缓存测试
     * @return
     */
    public User findByCache1(int id){

        SqlSession session = DBUtil.openSqlSession();

        UserMapper mapper = session.getMapper(UserMapper.class);

        //第一次取值(会从数据库中查找,并放入session缓存空间)
        User user = mapper.selectByCache1(id);
        //第二次取值(默认会从session取值,如果取不到,则再次请求数据库)
        //如果进行增删改数据后,执行commit()操作,则缓存失效
        //session.commit();

        User user2 = mapper.selectByCache1(id);

        System.out.println((user==user2)+"....................");

        session.close();

        return user;
    }
通过上述代码,可以验证一级缓存的存在.

3.二级缓存

1.Mybatis并没有提供对二缓存的实现
2.使用第三个缓存时,要在Config文件中手动开启二级缓存
3.多个SqlSession共享二级缓存
4.二级缓存可以手动提供缓存空间的管理策略

[外链图片转存失败(img-BWOq0Wal-1564490171975)(2.bmp)]

如上图示:
二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是二级缓存区
域。二级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。二级缓存中的value,就是查询出的
结果对象。

[外链图片转存失败(img-s8OB3zyZ-1564490171976)(3.bmp)]

如上图所示:
二级缓存是mapper级别的。
第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区域内。
第二次调用相同namespace下的mapper映射文件中相同的SQL去查询用户信息。会去对应的二级缓存内取结果。
如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该
namespace下的二级缓存。
二级缓存的实现步骤:
1.在config文件中开启二级缓存总开关,如下:
	<settings>
        <setting name="cacheEnabled" value="true"></setting>
    </settings>
2.在Mapper文件中使用二级缓存:
	<cache type="二级缓存的供应商"></cache>
	如果使用EhCache,则如下配置
	<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
3.缓存的对像必须实现序列化接口,如果类存在父类,那么父类也要实现序列化。(二级缓存数据有可能写入磁盘)
4.引数二级缓存供应商,这里以EhCache为例
       <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
5.配置该二级缓存的管理策略
	在resources目录下引入ehcache.xml,在文件中配置缓存管理策略

<!--ehcache.xml-->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

    <!--缓存对像写入磁盘-->
    <diskStore path="java.io.tmpdir"/>

    <!--
     1.maxElementsInMemory: 缓存空间大小
     2.eternal:缓存对像是否一直存在
     3.timeToIdleSeconds:缓存中空闲对像生存的时间
     4.timeToLiveSeconds:缓存中对像生存的最长时间
     5.maxElementsOnDisk:缓存到磁盘的最大空间
     6.memoryStoreEvictionPolicy:缓存对像剥离的算法
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            overflowToDisk="true"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
<!--UserMapper.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="mapper.UserMapper">
    <!--该Mapper范围内的查询都使用二级缓存-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
    
    <!--二级缓存-->
    <select id="selectByCache2" resultType="User">
        select * from User where id = #{id}
    </select>

</mapper>
//Mapper接口
public interface UserMapper {

    User selectByCache2(int id);
}
//service
  	/**
     * 二级缓存测试
     * @param id
     * @return
     */
    public void findByCache2(int id){
        //从第一个session对像关联数据
        SqlSession session = DBUtil.openSqlSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.selectByCache2(id);
        session.close();
        //从第二个session对像中关联数据
        SqlSession session2 = DBUtil.openSqlSession();
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        User user2 = mapper2.selectByCache2(id);
        session2.close();
        //从第三个session对像中关联数据
        SqlSession session3 = DBUtil.openSqlSession();
        UserMapper mapper3 = session3.getMapper(UserMapper.class);
        User user3 = mapper3.selectByCache2(id);
        session3.close();

        System.out.println(user);
        System.out.println(user2);
        System.out.println(user3);
    }
//测试结果
DEBUG [main] - Cache Hit Ratio [mapper.UserMapper]: 0.0  //第一次缓存命中率
DEBUG [main] - Opening JDBC Connection
EBUG [main] - Created connection 690521419.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2928854b]
DEBUG [main] - ==>  Preparing: select * from User where id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2928854b]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2928854b]
DEBUG [main] - Returned connection 690521419 to pool.
DEBUG [main] - Cache Hit Ratio [mapper.UserMapper]: 0.5//第二次缓存命中率
DEBUG [main] - Cache Hit Ratio [mapper.UserMapper]: 0.6666666666666666//第三次缓存命中率

练习

1.手动实现一级缓存和二级缓存,加深对缓存概念的理解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值