二、mybatis进阶

1. 关联查询

1.1 多对一

PO类

public class Orders {

    private Integer id; 
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;

    //用户信息
    private User user;
    。。。
 }

映射文件

    <resultMap type="cn.com.model.Orders" id="userordermap">
        <id property="id" column="id" />
        <result property="userId" column="user_id" />
        <result property="number" column="number" />
        <result property="createtime" column="createtime" />
        <result property="note" column="note" />

        <association property="user" javaType="cn.com.model.User">
            <id property="id" column="user_id" />
            <result property="username" column="username" />
            <result property="address" column="address" /> 
        </association>
    </resultMap>

    <select id="findOrdersListResultMap" resultMap="userordermap">
        SELECT  orders.*,   user.username,  user.address
        FROM    orders, user
        WHERE orders.user_id = user.id 
    </select>

多对一中,使用association 映射一方

<association property="user" javaType="cn.com.model.User">
            <id property="id" column="user_id" />
            <result property="username" column="username" />
            <result property="address" column="address" /> 
</association>

“id” 会使用orders中的user_id 对id进行赋值
property : 多方中对应的一方的属性名
javaType:类型

1.2 一对多

PO类

public class Orders {

    private Integer id; 
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;

    //用户信息
    private User user;

    //订单明细
    private List<Orderdetail> orderdetails;
。。。。
}

映射文件

<resultMap type="cn.com.model.Orders" id="userorderdetailmap">
    <id property="id" column="id" />
    <result property="userId" column="user_id" />
    <result property="number" column="number" />
    <result property="createtime" column="createtime" />
    <result property="note" column="note" />
    <association property="user" javaType="cn.com.model.User">
        <id property="id" column="user_id" />
        <result property="username" column="username" />
        <result property="address" column="address" />
    </association>
    <collection property="orderdetails" ofType="cn.com.model.Orderdetail">
        <id property="id" column="orderdetail_id" />
        <result property="itemsId" column="items_id" />
        <result property="itemsNum" column="items_num" />
    </collection>
</resultMap>  

    <select id="findOrdersDetailList" resultMap="userorderdetailmap">
        SELECT  orders.*,user.username,user.address,
                orderdetail.id orderdetail_id,orderdetail.items_id, orderdetail.items_num
        FROM orders,user,orderdetail
        WHERE orders.user_id = user.id
        AND orders.id = orderdetail.orders_id
    </select>

一对多中,一方中会定义多方的一个集合,在映射文件中使用collection映射。

<collection property="orderdetails" ofType="cn.com.model.Orderdetail">
        <id property="id" column="orderdetail_id" />
        <result property="itemsId" column="items_id" />
        <result property="itemsNum" column="items_num" />
</collection>

property:一方中的多方集合属性名称。
ofType:指定关联查询的结果集中的对象类型即List中的对象类型。

1.3 resultMap继承

上面的resultMap userorderdetailmap 可以继承userordermap ,复用重复部分

<resultMap type="cn.com.model.Orders" id="userorderdetailmap" extends="userordermap">

    <collection property="orderdetails" ofType="cn.com.model.Orderdetail">
        <id property="id" column="orderdetail_id" />
        <result property="itemsId" column="items_id" />
        <result property="itemsNum" column="items_num" />
    </collection>
</resultMap> 

resultMap 中用 extends=”父resultMap” 可复用已有的resultMap中的映射配置

1.4 多对多

需要关联查询映射的信息是:用户、订单、订单明细、商品信息
订单:一个用户对应多个订单,使用collection映射到用户对象的订单列表属性中
订单明细:一个订单对应多个明细,使用collection映射到订单对象中的明细属性中
商品信息:一个订单明细对应一个商品,使用association映射到订单明细对象的商品属性中

PO类

public class User { 
    private int id;
    private String username; 
    private String sex; 
    private Date birthday; 
    private String address; 

    //一个用户对应多个订单
    private List<Orders> orders = new ArrayList<Orders>();
}
public class Orders {

    private Integer id; 
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;

    //用户信息
    private User user;

    //订单明细
    private List<Orderdetail> orderdetails;
    。。
}
public class Orderdetail {

    private Integer id;
    private Integer ordersId;
    private Integer itemsId;
    private Integer itemsNum;

    private Items items;
 。。。。   
}

映射文件

<resultMap type="cn.com.model.User" id="userOrderListResultMap">
        <id column="user_id" property="id"/>
        <result column="username" property="username"/>
        <collection property="orders" ofType="cn.com.model.Orders">
          <id  column="id" property="id"/>
          <result property="number" column="number"/>
            <association property="user" javaType="cn.com.model.User">
                <id property="id" column="user_id" />
                <result property="username" column="username" />
                <result property="address" column="address" /> 
            </association>
            <collection property="orderdetails" ofType="cn.com.model.Orderdetail">
                <id  column="orderdetail_id" property="id"/>
                <result property="ordersId" column="id"/>
                <result property="itemsId" column="items_id"/>
                <result property="itemsNum" column="items_num"/>
                <association property="items" javaType="cn.com.model.Items">
                     <id column="items_id" property="id"/>
                     <result column="items_name" property="name"/>
                     <result column="items_detail" property="detail"/>
                </association>
            </collection>
       </collection>
  </resultMap>
  <select id="findAll" resultMap="userOrderListResultMap">
    SELECT  orders.*,   USER .username, USER .address,
            orderdetail.id   orderdetail_id,
            orderdetail.items_id,
            orderdetail.items_num,
            items.name items_name,
            items.detail items_detail
    FROM    orders, USER,orderdetail,items
    WHERE   orders.user_id = USER .id
            AND orders.id = orderdetail.orders_id
            AND orderdetail.items_id = items.id
  </select> 

2. 延迟加载

需要查询关联信息时,使用mybatis延迟加载特性可有效的减少数据库压力,首次查询只查询主要信息,关联信息(如订单用户关系,可先查询订单,需要用户信息)获取时再加载。

2.1 打开延迟加载开关

在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading

设置项描述允许值默认值
lazyLoadingEnabled全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载true,falsefalse
aggressiveLazyLoading当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载true ,falsetrue
<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

2.2 多对一延迟加载

PO类
一个用户可以有多个订单,多个订单归属一个用户
订单与用户是多对一关系,在订单实体类中增加User的属性

public class Orders {

    private Integer id; 
    private Integer userId;
    private String number;
    private Date createtime;
    private String note; 
    //用户信息
    private User user;
    。。。。。
}

映射文件

  • Orders映射文件

在Orders的映射文件中,通过association 配置关联属性,此处不在配置User的各个属性映射
增加select 和 colum属性配置

select=”cn.com.model.UserDao.findUserById”
在association 中配置select属性,指定懒加载关联查询使用的statement,findUserById在user.xml中,所以此处加上命名空间
column=”user_id”
在association 中配置column属性,指定懒加载关联查询使用的statement中需要传入的值

<?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="cn.com.dao.OrdersDao"> 
    <resultMap type="cn.com.model.Orders" id="orderLazyUser">
        <id property="id" column="id" />
        <result property="userId" column="user_id" />
        <result property="number" column="number" />
        <result property="createtime" column="createtime" />
        <result property="note" column="note" />

        <association property="user" javaType="cn.com.model.User"
            select="cn.com.model.UserDao.findUserById" column="user_id">

        </association>
    </resultMap>
    <select id="findOrdersLazyLoadUser" resultMap="orderLazyUser">
        SELECT id,user_id,number,createtime,note  FROM  orders
    </select> 

</mapper>
  • User映射文件

此处 的findUserById 在orders映射文件中使用到

<mapper namespace="cn.com.model.UserDao">
    <select id="findUserById" parameterType="int" resultType="cn.com.model.User">
        SELECT * FROM USER WHERE id=#{value}
    </select>
</mapper> 

测试类

@Test
    public void findOrdersLazyLoadUser() throws Exception{ 
        String resource = "sqlmapconfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrdersDao ordersDao = sqlSession.getMapper(OrdersDao.class);
        List<Orders> list = ordersDao.findOrdersLazyLoadUser();
        for(Orders orders:list){
            System.out.println(orders.getUser().getUsername());
        }
        sqlSession.close();
    }

此处在System.out.println(orders.getUser().getUsername());才会查询数据库,加载User的值

3. 查询缓存

Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。

Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。

3.1 一级缓存

一级缓存区域是根据SqlSession为单位划分的。
同一个SqlSession每次查询会先从缓存区域找,如果找不到再从数据库查询,查询到数据将数据写入缓存。下一次再次执行相同的查询时,则会从缓存中取出,不再查询数据库。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。
一级缓存是SQLSession级别的缓存。默认是开启的。

- 测试一级缓存

    SqlSession session = sqlSessionFactory.openSession();
    //获限mapper接口实例
    UserMapper userMapper = session.getMapper(UserMapper.class);
    //第一次查询
    User user1 = userMapper.findUserById(1);
    System.out.println(user1);
    //第二次查询,由于是同一个session则不再向数据发出语句直接从缓存取出
    User user2 = userMapper.findUserById(1);
    System.out.println(user2);
    //关闭session
    session.close();
    SqlSession session = sqlSessionFactory.openSession();
    //获限mapper接口实例
    UserMapper userMapper = session.getMapper(UserMapper.class);
    //第一次查询
    User user1 = userMapper.findUserById(1);
    System.out.println(user1);
    //在同一个session执行更新
    user1 .setUsername("xxx");
    userMapper.updateUser(user1 );
    session.commit();
    //第二次查询,虽然是同一个session但是由于执行了更新操作session的缓存被清空,这里重新发出sql操作
    User user2 = userMapper.findUserById(1);
    System.out.println(user2);

3.2 二级缓存

二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域。
每一个namespace会有一个自己的二级缓存,不同的 namespace用不同的二级缓存。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存
在SqlMapConfig.xml设置二级缓存的总开关默认是开启的,但是建议还是配置开启。

3.2.1 开启二级缓存

在核心配置文件SqlMapConfig.xml中加入

<setting name="cacheEnabled" value="true"/>
配置项描述允许值默认值
cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置true,falsetrue

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
在UserMapper.xml中开启二缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。

<mapper namespace="cn.com.dao.OrdersDao"> 
    <!-- 开启本 mapper的 namespace下的二级缓存-->
    <cache type=""/>

type指定实现mybatis cache 接口的实现类 类名,默认是org.apache.ibatis.cache.impl.PerpetualCache

这里写图片描述

3.2.2 pojo类实现序列化接口

为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样在内存。

public class Orders implements Serializable{
    private Integer id; 
    private Integer userId;
    private String number;
    private Date createtime;
    private String note; 
    //用户信息
    private User user;
    。。
}
3.2.3 测试1
String resource = "sqlmapconfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        OrdersDao ordersDao1 = sqlSession1.getMapper(OrdersDao.class);
        //第一次查询,缓存中没数据,请求数据库查询
        Orders  orders1 = ordersDao1.findById(3);
        //sqlsession关闭
        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        OrdersDao ordersDao2 = sqlSession2.getMapper(OrdersDao.class);
        //第二次查询,缓存中有数据,从缓存中取出数据,不再请求数据库查询
        Orders  orders2 = ordersDao2.findById(3);
        sqlSession2.close();
3.2.4 测试2
    String resource = "sqlmapconfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        OrdersDao ordersDao1 = sqlSession1.getMapper(OrdersDao.class);
        //第一次查询,缓存中没数据,请求数据库查询
        Orders  orders1 = ordersDao1.findById(3);
        //sqlsession关闭
        sqlSession1.close();

        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        OrdersDao ordersDao3 = sqlSession3.getMapper(OrdersDao.class);
        //第二次查询,缓存中有数据,从缓存中取出数据,不再请求数据库查询
        Orders  orders3 = ordersDao3.findById(3);
        orders3.setCreatetime(new Date());
        ordersDao3.updateOrder(orders3);
        sqlSession3.commit();
        //执行提交,清空OrderMapper下边的二级缓存,防止脏读
        sqlSession3.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        OrdersDao ordersDao2 = sqlSession2.getMapper(OrdersDao.class);
        //第三次查询,缓存中没有数据,请求数据库查询
        Orders  orders2 = ordersDao2.findById(3);
        sqlSession2.close();
3.2.5 特定查询禁用二级缓存(useCache配置)

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findById" useCache="false" parameterType="int" resultMap="orderLazyUser">
3.2.6 刷新缓存(清空缓存)

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache=”true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

3.3 mybatis整合ehcache

ehcache是一个分布式缓存框架。我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)
不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。
mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

3.3.1 整合原理

mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。
这里写图片描述

package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {
    String getId(); 
    void putObject(Object key, Object value); 
    Object getObject(Object key); 
    Object removeObject(Object key); 
    void clear(); 
    int getSize(); 
    ReadWriteLock getReadWriteLock(); 
}

mybatis默认实现cache类是:

package org.apache.ibatis.cache.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;

/**
 * @author Clinton Begin
 */
public class PerpetualCache implements Cache {

  private String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  public String getId() {
    return id;
  }

  public int getSize() {
    return cache.size();
  }

  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  public Object getObject(Object key) {
    return cache.get(key);
  }

  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  public void clear() {
    cache.clear();
  }

  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  public boolean equals(Object o) {
    if (getId() == null) throw new CacheException("Cache instances require an ID.");
    if (this == o) return true;
    if (!(o instanceof Cache)) return false;

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  public int hashCode() {
    if (getId() == null) throw new CacheException("Cache instances require an ID.");
    return getId().hashCode();
  } 
}
3.3.2 加入ehcache包

下载地址:http://download.csdn.net/detail/hxskmx/8802683
mybatis和ehcache整合包
ehcache实现mybatis cache接口 实现类
ehcache实现mybatis接口类

3.3.3 mybatis整合ehcache

配置mapper中cache中的type为ehcache对cache接口的实现类型。

<mapper namespace="cn.congxing.model.User"> 
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.3.4 加入ehcache的配置文件

在classpath下配置ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="F:\develop\ehcache" />
    <defaultCache 
        maxElementsInMemory="1000" 
        maxElementsOnDisk="10000000"
        eternal="false" 
        overflowToDisk="false" 
        timeToIdleSeconds="120"
        timeToLiveSeconds="120" 
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

4. generator代码生成

4.1 下载generator

http://download.csdn.net/detail/hxskmx/8802897

4.2 配置文件

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--数据库连接的信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"   password="123">
        </jdbcConnection>

        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl" userId="demo" password="demo">
        </jdbcConnection> -->

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 
            NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="cn.congxing.model"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="cn.congxing.model" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.congxing.dao" 
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="items"></table>
        <table tableName="orders"></table>
        <table tableName="orderdetail"></table>
        <table tableName="user"></table>
        <!-- <table schema="" tableName="sys_user"></table>
        <table schema="" tableName="sys_role"></table>
        <table schema="" tableName="sys_permission"></table>
        <table schema="" tableName="sys_user_role"></table>
        <table schema="" tableName="sys_role_permission"></table> -->

        <!-- 有些表的字段需要指定java类型
         <table schema="" tableName="">
            <columnOverride column="" javaType="" />
        </table> -->
    </context>
</generatorConfiguration>

4.3 生成代码

package cn.congxing.test; 

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {

    public void generator() throws Exception{

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //指定 逆向工程配置文件
        File configFile = new File("config/generatorConfig.xml"); 
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);

    } 
    public static void main(String[] args) throws Exception {
        try {
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值