Spring之Mybatis一、二级缓存 延迟加载 逆向工程

首先,新建项目:
在这里插入图片描述
在这里插入图片描述
在pom.xml中导入相关依赖:mybatis、mysql、Junit、log4j、
当我们使用web项目,需要访问http://localhost:8080/xx/xx,通过部署到Tomcat容器中,放在webapp中,需要通过classpath(这里的classpath就等于webapp)的db.properties访问。而我们现在是非web项目,只有resources,所以需要在pom.xml中写入<build><resources>......

<build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                        <include>**/*.properties</include>
                    </includes>

                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.xml</include>
                        <include>**/*.properties</include>
                    </includes>

                </resource>
            </resources>
    </build>

这里的**.*.xml中的“**”表示所有的目录,无论多少级目录,(最多1024级)都可以找到以".xml"结尾的文件。
在java包中新建mapper包(遵循7项规则)和po包:
仍是以最简单的select * from user为例。
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="com.bit.mapper.UserMapper">

    <resultMap id="userResultMap" type="com.bit.po.User">
        <result column="username" property="userName"/>
        <result column="id" property="userId"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="userSex"/>
        <result column="address" property="userAddress"/>

    </resultMap>


    <select id="queryUser" parameterType="int" resultMap="userResultMap">
            select * from user where id=#{value};
    </select>

</mapper>

UserMapper接口如下所示:

package com.bit.mapper;

import com.bit.po.User;

import java.util.List;

public interface UserMapper {
    public User queryUser(int myId)throw Exception; 
}

在po包中写User类:(toString、getter、setter方法省略)

package com.bit.po;

import java.util.Date;

public class User
{
    public User()
    {}
private int userId
    private String userName;
private String userSex;
private Date birthday;
private String userAddress;
}

在resources中新建sqlMapConfig.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>
    <properties resource="db.properties"/>
<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
                    <dataSource type="POOLED">
                        <property name="driver" value="${jdbc.driver}"/>
                           <property name="url" value="${jdbc.url}"/>
                        <property name="username" value="${jdbc.username}"/>
                        <property name="password" value="${jdbc.password}"/>
                    </dataSource>
        </environment>
</environments>

<mappers>
    <package name="com.bit.mapper"></package>
</mappers>

</configuration>

因为要使用单元测试,需要日志打印信息,在resources中新增log4j.properties:

log4j.rootLogger=DEBUG, Console
#Mybatis log4j
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

进行单元测试:
(使用IO的时候一定要try catch)

import com.bit.mapper.UserMapper;
import com.bit.po.User;
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 org.junit.Before;
import org.junit.Test;


import java.io.InputStream;

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void testInit()
    {String file="sqlMapConfig.xml";
        try{
            InputStream inputStream=null;
                   inputStream=Resources.getResourceAsStream(file);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
        }
        catch (Exception e)
        {e.printStackTrace();}
        }

    @Test
    public void testQueryUserById() throws Exception {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User result=userMapper.queryUser(30);
System.out.println(result);
sqlSession.close();
    }
}

运行结果为:User{userId=30, userName=‘Arvin’, userSex=‘2’, birthday=null, userAddress=‘null’}

缓存是这样的,我们是使用sqlSession执行操作的,当访问数据库时,通过sqlsession发出sql语句,当第二次使用sqlsession,想从数据库执行查询时,它先不发出sql语句,而是从sqlsession里面去查,看在sqlsession中有无相关缓存,如果没有,再去数据库中查,如果有,就不去数据库,就不发出sql语句了——这说明,sqlsession具有缓存数据的能力,在Mybatis框架中,sqlsession就是一级缓存。
一级缓存的特点:1.直接使用,无须配置。 2。不得不用,无法剔除。
3.无法管理一级缓存。

 SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //第一次查询,原则是:先到sqlsession中看有无需要的记录,如果没有,去DB中查,发出sql'语句
        User result=userMapper.queryUser(30);
//当执行queryUser(int myid)方法时,就会写入一级缓存
        //sqlsession就是一级缓存,就是个HashMap
        System.out.println(result);
        //第二次查询,先到sqlsession去看有无需要的记录,如果有,就不去DB中查,不发出sql语句,
        // 直接从缓存中取数据
       result=userMapper.queryUser(30);


System.out.println(result);
sqlSession.close();

当执行update、delete、insert操作时,就会清空一级缓存,之后就不能再从一级缓存中拿了,就会调用sql语句,去DB中查了。
在UserMapper.xml中写:

<update id="modifyUserById" parameterType="com.bit.po.User">
     update user set username=#{userName} where id = #{userId}
 </update>

接口中要有对应的方法:public void modifyUserById(User user) throws Exception;
在单元测试中写:

 //清空一级缓存
            result.setUserId(1);
            result.setUserName("TTTTT");
            result.setUserAddr("ttttt");
            userMapper.modifyUserById(result);//发出update语句
            sqlSession.commit();//清空一级缓存

二级缓存是mapper级别的缓存,跨sqlsession的。每个mapper都有一个namespace,mapper之间可以共享缓存,二级缓存是需要配置的。分两步1.在sqlMapConfig.xml中配置总开关。2.在UserMapper中配置子开关。
二级缓存的特点:1.需要配置。2.可以管理(开启,关闭,清空,使用)
二级缓存的适用场景:1.经常被查询的数据
2.相对来说不重要的数据
3.对实时性要求不高的数据
4.在多表关联中,最好是存放单表的数据在缓存中

1.在sqlMapConfig.xml中配置总开关:

 <settings>
        <!--开启二级缓存总开关-->
        <setting name="cacheEnabled" value="true"/>
        </settings>

2.在UserMapper中配置子开关。

 <!--该标签表示在某个mapper中开启二级缓存,子属性取默认值-->
<cache/>

Mybatis中的二级缓存没有指定一定要存在于内存当中,(如:可以存在于硬盘中)所以需要类实现序列化接口,为了能够被反序列化。因此po包的User类需要:public class User implements Serializable
对二级缓存进行单元测试:

 //测试二级缓存
    @Test
    public void test2()
    {
        SqlSession sqlSession1=sqlSessionFactory.openSession();
        SqlSession sqlSession2=sqlSessionFactory.openSession();
        SqlSession sqlSession3=sqlSessionFactory.openSession();

        try
        {UserMapper userMapper=sqlSession1.getMapper(UserMapper.class);
        User user1=userMapper.queryUser(30);
        System.out.println(user1);
           
            //一级缓存关闭
            sqlSession1.close();
        
        UserMapper userMapper1=sqlSession2.getMapper(UserMapper.class);
        User user2=userMapper1.queryUser(30);
        System.out.println(user2);

        }


        catch (Exception ex){ex.printStackTrace();}
    finally {
            sqlSession2.close();
        }
    }
    

既然把一级缓存关闭了,那么如果不发出sql语句,就说明是二级缓存在起作用。
同样的,二级缓存也是通过update,delete,insert来进行清空。sqlSession3就是用来清空缓存的。先把它(Id=30的元组)查询出来,再用它进行更新。

  //通过update,delete,insert清空二级缓存
UserMapper userMapper2=sqlSession3.getMapper(UserMapper.class);
User user3=userMapper2.queryUser(30);
userMapper2.modifyUserById(user3);
sqlSession3.commit();

延迟加载

在这里插入图片描述

select orders.*,user.username,user.sex,user.address 
from orders,user 
where orders.user_id=user.id;

如果只是想查询某个订单号是哪个用户下的,需要使用延迟加载,先:
select * from orders;查出订单,比如说看到了编号为“1000014”的订单,想知道这个订单是哪个用户下的,这时再写:

 select username from user where orders.user_id=user,id and orders.number=1000014;

第二条语句是按需执行的。
我们把两条语句写成一条:

 select orders.*,
 (select username from user where orders.user_id = user.id)
  as MyUserNanme,
 (select sex from user where orders.user_id = user.id) 
 as MySex
   from orders;

在命令行中执行结果如下: 在这里插入图片描述
先在mapper包下新建OrderMapper接口和OrderMapper.xml:
OrderMapper.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="bit.mapper.OrdersMapper">

        <!--实现延迟加载-->
        <resultMap id="orderuserlazyRM" type="com.bit.po.Orders">
            <!--配置订单信息-->
            <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文件中的queryUserById

            column: 订单信息中关联用户信息的列名 user_id
            关联SQL如下:
            select orders.*,
                (select username from user where orders.user_id = user.id) as MyUserNanme,
                (select sex from user where orders.user_id = user.id) as MySex
                from orders;
               -->
           
           !--订单到用户是1:1的关系-->
            <association property="user" javaType="com.bit.po.User"
                         select="com.bit.mapper.UserMapper.queryUser" column="user_id"/>

        </resultMap>
        <!--实现延迟加载-->
        <select id="findorderuserlazyload" resultMap="orderuserlazyRM">
        select * from orders;
    </select>
</mapper>

因为从订单到用户是一对一的关系,所以使用<association>
接口中有相应方法:

package com.bit.mapper;
import com.bit.po.Orders;
import java.util.List;

public interface OrderMapper {
    public List<Orders> findorderuserlazyload() throws Exception;
}

在po包下需要Orders类(省略getter、setter方法):

package com.bit.po;
import java.util.Date;
public class Orders {
public Orders()
    {}
    private Integer id;
    private Integer userid;
    private String number;
    private Date createtime;
    private String note;
    //1:1
    private User user;

延时加载也是需要开关的,在sqlMapConfig.xml中写:

 <!--延时加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>

在单元测试中写:

    //测试延迟加载
    @Test
    public void testLazyLoading()
    {
        SqlSession sqlSession=sqlSessionFactory.openSession();
        try{
            OrderMapper orderMapper=sqlSession.getMapper(com.bit.mapper.OrderMapper.class);
            //此时发出sql语句:select * from orders
            List<Orders> result=orderMapper.findorderuserlazyload();

            //此时需求是根据orders查询user
            //所以延迟发出第二条sql语句,即延迟加载
            for(Orders orders:result)
            {System.out.println("username"+orders.getUser().getUserName());}
System.out.println(result);

        }
        catch (Exception ex){ex.printStackTrace();}

        finally {
            sqlSession.close();
        }
    }

运行结果为:
在这里插入图片描述
延迟加载的适用场景:
1.当数据特别多的时候
2.当关联查询特别频繁的时候
3.当需要追求效率的时候

Mybatis的逆向工程
*是一个工具类,类似于我们使用的log4j
*反向帮我们生产po类,mapper接口,xxMapper.xml文件
步骤:
1.在pom.xml中添加逆向工程的plugin:

<plugins>
            <!-- mybatis-generator自动生成代码插件 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
            </plugin>
        </plugins>

2.需要generatorConfig.xml:(见XiaoMing的mybatis_generator)

<?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>

<!--

    <classPathEntry location="D:\mvn_local_repository\mysql\mysql-connector-java\5.1.38\mysql-connector-java-5.1.38.jar" />

-->

    <properties resource="db.properties"/>

    <context id="mysql" targetRuntime="MyBatis3">
        <!-- 生成的pojo,将implements Serializable-->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <!-- 是否去除自动生成的注释 true:是 : false:-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!-- 数据库链接URL、用户名、密码 -->
       <!-- <jdbcConnection
                driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://127.0.0.1:3306/webtestdb"
                userId="root"
                password="root">
        </jdbcConnection>-->

        <jdbcConnection
                driverClass="${jdbc.driver}"
                connectionURL="${jdbc.url}"
                userId="${jdbc.username}"
                password="${jdbc.password}">
        </jdbcConnection>

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



        <!--
        生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径,./src/main/java,
        也可以使用“MAVEN”来自动生成,这样生成的代码会在target/generatord-source目录下
        -->
        <!--<javaModelGenerator targetPackage="com.joey.mybaties.test.pojo" targetProject="MAVEN">-->
        <javaModelGenerator targetPackage="com.arvin.po" targetProject="./src/main/java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
            <property name="enableSubPackages" value="false"/>
            <!-- 从数据库返回的值被清理前后的空格  -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>


        <!--对应的mapper.xml文件  -->
        <sqlMapGenerator targetPackage="com.arvin.mapper" targetProject="./src/main/java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>


        <!-- 对应的Mapper接口类文件 -->
        <javaClientGenerator targetPackage="com.arvin.mapper" type="XMLMAPPER" targetProject="./src/main/java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 列出要生成代码的所有表,这里配置的是不生成Example文件 -->
        <!--指定数据库表-->
        <table tableName="items" domainObjectName="Items"/>
        <table tableName="orders" domainObjectName="Orders"/>
        <table tableName="orderdetail" domainObjectName="Orderdetail"/>
        <table tableName="user" domainObjectName="User"/>

    </context>

</generatorConfiguration>

2.在Maven项目的resource文件目录中引
3.配置generatorConfig,xml文件
4.编写客户端调用generatorConfig.xml文件

发布了47 篇原创文章 · 获赞 1 · 访问量 1278

猜你喜欢

转载自blog.csdn.net/weixin_41750142/article/details/102446626