Mybatis(3、延迟加载、查询缓存、与ehcache整合、逆向工程、与spring整合)

版权声明:本文为博主原创文章,未经博主允许不得转载。    https://blog.csdn.net/www1056481167/article/details/70597788
延迟加载
延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

使用association实现延迟加载
需要先定义连个mapper的方法对应的statements。
1、只查询订单信息

在查询订单的statement中使用association去延迟加载(执行)下边的statement(关联查询用户信息)

<!-- 查询订单关联用户,用户信息需要延迟加载 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
    SELECT * FROM orders
</select>
2、关联查询用户信息

      通过上边查询到的订单信息中user_id去关联查询用户信息

         使用UserMapper.xml中的findUserById
<select id="findUserById" parameterType="int" resultType="user">
        SELECT * FROM USER WHERE id=#{value}
</select>

上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候先去查询findUserBuyId,通过resultMap的定义将延迟加载执行配置起来。

延迟加载resultMap
使用association中的select指定延迟加载去执行的statement中的id

        <!-- 延迟加载的resultMap -->
    <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserLazyLoadingResultMap">
            <!--对订单信息进行映射配置  -->
            <id column="id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
            <!-- 实现对用户信息进行延迟加载
            select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
            要使用userMapper.xml中findUserById完成根据用户id(user_id)用户信息的查询,
            如果findUserById不在本mapper中需要前边加namespace
            column:订单信息中关联用户信息查询的列,是user_id
            关联查询的sql理解为:
            SELECT
                orders.*, (
                    SELECT
                        username
                    FROM
                        USER
                    WHERE
                        orders.user_id = USER .id
                ) username,
                (
                    SELECT
                        sex
                    FROM
                        USER
                    WHERE
                        orders.user_id = USER .id
                ) sex
            FROM
                orders
             -->
            <association property="user"  javaType="cn.itcast.mybatis.po.User"
             select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
            <!-- 实现对用户信息进行延迟加载 -->
            </association>
    </resultMap>
Mapper.java
// 查询订单关联查询用户,用户信息是延迟加载
public List<Orders> findOrdersUserLazyLoading() throws Exception;
测试
1、执行上边mapper方法(findOrdersUserLazyLoading),内部去调用cn.itcast.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查询orders信息(单表)。

2、在程序中去遍历上一步骤查询出的List<Orders>,当我们调用Orders中的getUser方法时,开始进行延迟加载。

3、延迟加载,去调用UserMapper.xml中findUserbyId这个方法获取用户信息。
延迟加载的配置
mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。

在mybatis核心配置文件中配置:

lazyLoadingEnabled、aggressiveLazyLoading

mybatis.xml的配置

<settings>
        <!-- 打开延迟加载 的开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 将积极加载改为消极加载即按需要加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

查询缓存
一级缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

mybaits提供一级缓存,和二级缓存。


一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 

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

为什么要用缓存?

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
工作原理:

一级缓存的测试
@Test
    public void testCache1() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        
        //下边查询使用一个SqlSession
        //第一次发起请求,查询id为1的用户
        User user1 = userMapper.findUserById(1);
        System.out.println(user1);        
//        如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。        
        //更新user1的信息
        user1.setUsername("测试用户22");
        userMapper.updateUser(user1);
        //执行commit操作去清空缓存
        sqlSession.commit();        
        //第二次发起请求,查询id为1的用户
        User user2 = userMapper.findUserById(1);
        System.out.println(user2);
        
        sqlSession.close();
        
    }
应用:

正式开发,是将mybatis和spring进行整合开发,事务控制在service中。

一个service方法中包括很多mapper方法调用。

service{

      //开始执行时,开启事务,创建SqlSession对象

      //第一次调用mapper的方法findUserById(1)

      //第二次调用mapper的方法findUserById(1),从一级缓存中取数据

      //方法结束,sqlSession关闭

}

如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。


二级缓存
原理:

首先开启mybatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

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

                <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
2、在UserMapper.xml中开启二级缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(hashMap)

<mapper namespace="test">
     <!—开启本namespace的二级缓存-->
     <cache/>
调用pojo实现序列化接口

public class User implements Serializable {    
    //属性名和数据库表的字段对应
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

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

测试方法

// 二级缓存测试
    @Test
    public void testCache2() throws Exception {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        // 创建代理对象
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        // 第一次发起请求,查询id为1的用户
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1);        
        //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
        sqlSession1.close();        
        //使用sqlSession3执行commit()操作
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        User user  = userMapper3.findUserById(1);
        user.setUsername("张明明");
        userMapper3.updateUser(user);
        //执行提交,清空UserMapper下边的二级缓存
        sqlSession3.commit();
        sqlSession3.close();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        // 第二次发起请求,查询id为1的用户
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2);
        sqlSession2.close();
    }
useCache配置

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

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

刷新缓存

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

 设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

mybatis整合ehcache
依赖jar
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>
ehcache:是一个分布式的缓存框架

目的:为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)。

分布式方法
mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。

mybatis默认实现cache类


整合ehcache
配置mapper中cache中的type为ehcache对cache接口的实现类。
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 开启mapper的namespace下的二级缓存
type:指定cache接口的实现类的类型,mybatis默认使用PerpetualCache要和ehcache整合,
需要配置type为ehcache实现cache接口类型 
-->
 <cache type="cn.itcast.mybatis.util.PerpetualCache"/>
加入ehcache配置文件
<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>
spring和mybatis整合
在applicationContext.xml配置sqlSessionFactory和数据源。

<!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 数据源,使用dbcp -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="10" />
        <property name="maxIdle" value="5" />
    </bean>
    <!-- sqlSessinFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 加载mybatis的配置文件 -->
        <property name="configLocation" value="mybatis/SqlMapConfig.xml" />
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
原始dao开发
dao接口
public interface UserDao {
    // 根据id查询用户信息
    public User findUserById(int id) throws Exception;
}
实现类
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
    @Override
    public User findUserById(int id) throws Exception {
        //继承SqlSessionDaoSupport,通过this.getSqlSession()得到sqlSessoin
        SqlSession sqlSession = this.getSqlSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        return user;
    }
}
dao配置
<!-- 原始dao接口 -->    
    <bean id="userDao" class="cn.itcast.ssm.dao.UserDaoImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
测试代码
public class UserDaoImplTest {
    private ApplicationContext applicationContext;
    //在setUp这个方法得到spring容器
    @Before
    public void setUp() throws Exception {
        applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
    }
    @Test
    public void testFindUserById() throws Exception {
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        //调用userDao的方法
        User user = userDao.findUserById(1);
        System.out.println(user);
    }
}
mapper代理开发
mapper.xml和mapper.java

通过MapperScannerConfigurer进行扫描(建议使用)
<!-- mapper批量扫描,从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册 
    遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录 中
    自动扫描出来的mapper的bean的id为mapper类名(首字母小写)
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定扫描的包名 
        如果扫描多个包,每个包中间使用半角逗号分隔
        -->
        <property name="basePackage" value="cn.itcast.ssm.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>        
    </bean>    
测试代码
@Test
    public void testFindUserById() throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "classpath:spring/applicationContext.xml");
        UserMapper userMapper = (UserMapper) applicationContext
                .getBean("userMapper");
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
逆向工程
下载依赖
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

配置文件:
修改generatorConfig.xml的配置信息
主要修改的内容有:
1、jdbcConnection的数据库连接信息。
2、sqlMapGenerator的targetPackage,mapper.xml映射文件生成存放的位置
3、table将需要生成的表的表名都配置到配置文件中
<?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="mysql">
        </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.itcast.ssm.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 **需要配置**-->
        <sqlMapGenerator targetPackage="cn.itcast.ssm.mapper"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀-->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置**需要配置** -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.itcast.ssm.mapper" targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表**需要配置**-->
        <table tableName="items"></table>
    </context>
</generatorConfiguration>
执行生成的程序
@Test
    public void aa() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("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);
    }

生成后的代码

--------------------- 
作者:Koma-forever 
来源:CSDN 
原文:https://blog.csdn.net/www1056481167/article/details/70597788 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/xiao190128/article/details/83586540