美文网首页
SSM框架mybatis的缓存与注解开发(三)

SSM框架mybatis的缓存与注解开发(三)

作者: Seapp | 来源:发表于2020-07-29 10:37 被阅读0次

    一、mybatis中的延迟加载

    问题引入:在一对多中,有一个用户对应100个账户
    • 在查询用户的时候,要不要把关联账户查询出来?
      (用户下的账户,什么时候使用,什么使用查询加载---延迟加载)
    • 在查询账户的时候,要不要把关联用户查询出来?
      (账户的所属用户信息应该是随着账户查询时一起查询出来---立即加载)

    1. 什么是延迟加载?

    在真正使用数据时才发起查询,不用时不查询。也叫做按需加载(懒加载)。
    一对多、多对多:通常情况下使用延迟加载。

    2. 什么是立即加载

    不管用户用,只要一调用方法,马上进行查询。
    多对一、一对一:通常情况下采用立即加载。

    3. 实战

    • 延迟加载的全局配置
     <!--配置参数-->
        <settings>
        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 
        特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。-->
            <setting name="lazyLoadingEnabled" value="true"/>
        <!--开启时,任一方法的调用都会加载该对象的所有延迟加载属性。
         否则,每个延迟加载属性会按需加载 -->
            <setting name="aggressiveLazyLoading" value="false"/>
            
        </settings>
    
    • 一对一延迟加载的实现:
    <mapper namespace="com.seapp.dao.IAccountDao">
        <!--实体类名与数据库列名不一致时通过resultMap来实现实体类名与列名的对应关系    -->
        <resultMap id="accountUserMap" type="account">
            <id column="aid" property="aId"/>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
            <!--asssociation属性来确定一对一实体类,
                column:由哪个字段类确定关联用户
                select: 配合延迟加载使用,指定IuserDao中的findById方法(根据用户id获取指定用户)        -->
            <association property="user" column="uid" select="com.seapp.dao.IUserDao.findById"/>
        </resultMap>
        <!--查询所有,包含账户所属用户-->
        <select id="findAllAccountUser" resultMap="accountUserMap">
            select a.id AS aid,a.uid,a.money from account a;
        </select>
    
    • 一对多延迟加载的实现
     <resultMap id="userMap" type="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
            <!-- 指定关联对象accounts集合的映射
                    ofType:指定集合中元素的类型  
                    column:指定根据什么参数获取账户信息
                    select:指定IAccountDao中根据uid查询账户的sql方法
                    -->
            <collection property="accounts" ofType="account" column="id" select="com.seapp.dao.IAccountDao.findByUserId"/>
        </resultMap>
     <!--查询所有用户及关联账户-->
        <select id="findAll" resultMap="userMap">
            SELECT
                *
            FROM
                USER ;
        </select>
    

    二、mybatis中的缓存

    1. 缓存简介

    • 什么是缓存
      存在于内存中的临时数据。
    • 为什么使用缓存
      减少和数据库的交互次数,提高执行效率。
    • 什么样的数据能使用缓存,什么样的数据不能使用缓存?
      适用于缓存的数据: 经常查询且不经常改动的数据、数据的正确与否对最终的结果影响不大的。
      不适用于缓存的: 经常改动的,数据的正确与否对最终结果影响很大的。

    2. mybatis中的一级缓存和二级缓存

    一级缓存:它指的是Mybatis中SqlSession对象的缓存

    当我们执行查询后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。该区域的结构是一个Map,当我们再次查询同样的数据时,mybatis会直接先从缓存区域中获取缓存数据。当SqlSession对象消失时,mybatis的一级缓存也就消失了。

    sqlSession.closeCache();//可清空当前缓存
    
    二级缓存:它指的是mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

    二级缓存的使用步骤:

    • 第一步:让mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)。
    <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存-->
            <setting name="cacheEnabled" value="true"/>
    
    • 第二步:让当前映射文件支持二级缓存(在IUserDao.xml中配置)。
    <!--  在映射文件中开启缓存  -->
        <cache/>
    
    • 第三步:让当前的操作支持二级缓存(在select标签中配置)。
     <!-- 查询所有用户 
          useCache:属性标签,开启缓存
          -->
     <select id="findById" parameterType="int" resultType="user" useCache="true">
            select * FROM user where id = #{id};
        </select>
    

    三、Mybatis中的注解开发

    注意: mybatis中若采用注解开发,则不可再用xml配置mapper文件开发。

    1. 环境搭建

    • mysql数据库中的user表为基础数据,实现对用户信息的维护
    • maven依赖库的引入
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.seapp</groupId>
        <artifactId>mybatis</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <dependencies>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.3</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.6</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
    
        </dependencies>
    
    </project>
    
    • 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="jdbcConfig.properties"></properties>
    
    
        <!--配置参数-->
        <settings>
            <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存-->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
    
        <!--typeAlises 配置别名,只能配置domain中类的别名    -->
        <typeAliases>
            <package name="com.seapp.mybatis.domain"/>
        </typeAliases>
    
        <!-- 配置环境   -->
        <environments default="mysql">
            <!--配置mysql环境-->
            <environment id="mysql">
                <!--配置事务类型 -->
                <transactionManager type="JDBC"></transactionManager>
                <!--配置数据源(连接池)
                    type:配置连接池
                        取值:
                            POOLED:采用传统的javax.sql.DataSource规范中的连接池,在mybatis中有针对该规范的实现。
                            UNPOOLED:采用传统的连接方式,虽然实现了javax.sql.DataSource规范,但并没有池的思想。
                            JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同服务器所能拿到的DataSource是
                                不同的,注意,如果不是web或maven的war工程,是不能使用的。
                -->
                <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>
    
        <!-- 指定映射文件的位置,映射配置文件指的是每个dao独立的配置文件-->
        <!-- 若使用注解配置的话,mapper配置属性应为class,值为被注解的dao的全限定类名-->
        <mappers>
            <package name="com.seapp.mybatis.dao"/>
        </mappers>
    </configuration>
    
    • 关联的dataSource数据源配置文件 jdbcConfig.properties
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF-8
    jdbc.username=root
    jdbc.password=root
    
    • log4j.properties配置文件
    ### 设置###
    log4j.rootLogger = debug,stdout,D,E
    
    ### 输出信息到控制抬 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
    
    ### 输出DEBUG 级别以上的日志到=D://logs/error.log ###
    log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.D.File = D://logs/log.log
    log4j.appender.D.Append = true
    log4j.appender.D.Threshold = DEBUG 
    log4j.appender.D.layout = org.apache.log4j.PatternLayout
    log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    
    ### 输出ERROR 级别以上的日志到=D://logs/error.log ###
    log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
    log4j.appender.E.File =D://logs/error.log 
    log4j.appender.E.Append = true
    log4j.appender.E.Threshold = ERROR 
    log4j.appender.E.layout = org.apache.log4j.PatternLayout
    log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
    

    2. 单表CRUD操作

    • 实体类User.java的实现(关联Account账户信息,为后续多表操作打基础)
    package com.seapp.mybatis.domain;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * user实体类的创建
     * @author seapp
     * @date 2020/7/28 23:08
     */
    public class User {
    
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        private List<Account> accounts;
    
        public List<Account> getAccounts() {
            return accounts;
        }
    
        public void setAccounts(List<Account> accounts) {
            this.accounts = accounts;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    
    • IUserDao接口实现
    package com.seapp.mybatis.dao;
    
    import com.seapp.mybatis.domain.User;
    import org.apache.ibatis.annotations.*;
    import org.apache.ibatis.mapping.FetchType;
    
    import java.util.List;
    
    /**
     * 注解实现对User实体类的CRUD.
     * @Select @Update @Delete @Insert
     * @author seapp
     * @date 2020/7/28 23:10
     */
    public interface IUserDao {
    
        /**
         * 查询所有用户信息
         * @return
         */
        @Select(value = "select * from user")
        @Results(id = "userMap",value = {@Result(id = true,column = "id",property = "id"),@Result(column = "username",property = "username"),
                @Result(column = "birthday",property = "birthday"),@Result(column = "sex",property = "sex"),
                @Result(column = "address",property = "address"),
                @Result(column = "id",property = "accounts",many = @Many(select = "com.seapp.mybatis.dao.IAccountDao.findByUserId",fetchType = FetchType.LAZY))
    
    
        })
        List<User> findAll();
    
        /**
         * 根据用户id获取用户信息
         * @param userId
         * @return
         */
        @Select(value = "select * from user where id = #{userId}")
        User findById(Integer userId);
    
    
        /**
         * 根据用户名称获取用户信息,支持模糊查询
         * @param userName
         * @return
         */
        //第一种方式
        @Select(value = "select * from user where username like #{username}")
        //第二种方式
    //    @Select(value = "select * from user where username like $%(value)%")
        List<User> findByUserName(String userName);
    
    
        /**
         * 更新用户信息
         * @param user
         */
        @Update(value = "update  user set username = #{username}," +
                "birthday = #{birthday},sex = #{sex},address = #{address} " +
                "where id = #{id}")
        void updateUser(User user);
    
    
        /**
         * 根据用户id,删除用户信息
         * @param userId
         */
        @Delete(value = "delete from user where id = #{uid}")
        void deleteUser(Integer userId);
    
    
        /**
         * 插入用户信息
         * @param user
         */
        @Insert("insert into user (username,birthday,sex,address) values " +
                "(#{username},#{birthday},#{sex},#{address});")
        void insertUser(User user);
    
        /**
         * 聚合查询
         * @return
         */
        @Select("select count(id) from user")
        Integer getCount();
    
    }
    
    
    • UserTest.java测试类的实现
    package com.seapp.mybatis;
    
    import com.seapp.mybatis.dao.IUserDao;
    import com.seapp.mybatis.domain.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.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author seapp
     * @date 2020/7/28 23:13
     */
    public class UserTest {
    
        InputStream in ;
        private SqlSession sqlSession;
        private IUserDao userDao;
    
        /**
         * 测试前的准备工作
         */
        @Before
        public void before() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession();
            userDao = sqlSession.getMapper(IUserDao.class);
        }
    
        /**
         * 测试完成后资源的释放
         */
        @After
        public void after(){
            sqlSession.commit();
            sqlSession.close();
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 测试查询所有用户信息
         */
        @Test
        public void findAllUserTest(){
    
            List<User> userList = userDao.findAll();
            for (User user :
                    userList) {
                System.out.println("user = " + user);
                System.out.println("user.getAccounts() = " + user.getAccounts());
            }
        }
    
        /**
         * 测试根据用户id获取用户信息
         */
        @Test
        public void findByUserIdTest(){
            User user = userDao.findById(3);
            System.out.println("user = " + user);
        }
    
    
        /**
         * 测试根据用户名称查询用户,并且支持模糊搜索。
         */
        @Test
        public void findByUserName(){
            //第一种方式
            List<User> userList = userDao.findByUserName("%p%");
            //第二种方式
    //        List<User> userList = userDao.findByUserName("p");
            for (User user :
                    userList) {
                System.out.println("user = " + user);
            }
        }
    
        /**
         * 更新用户信息
         */
        @Test
        public void updateUserTest(){
            //创建更新user对象
            User user = new User();
            user.setId(11);
            user.setUsername("王小2pp");
            user.setBirthday(new Date());
            user.setAddress("中华人民共和国");
            user.setSex("男");
            userDao.updateUser(user);
        }
    
        /**
         * 测试根据用户信息,删除指定用户
         */
        @Test
        public void deleteUserTest(){
            userDao.deleteUser(11);
        }
    
        /**
         * 插入用户信息
         */
        @Test
        public void insertUserTest(){
            //创建user对象
            User user = new User();
            user.setId(11);
            user.setUsername("李小p");
            user.setBirthday(new Date());
            user.setAddress("中华人民共和国");
            user.setSex("女");
            //执行插入语句
            userDao.insertUser(user);
        }
    
    
        /**
         * 测试获取用户数量
         */
        @Test
        public void getUserCountTest(){
            Integer count = userDao.getCount();
            System.out.println("count = " + count);
        }
    
    }
    
    

    3. 多表查询操作

    • 多表操作,增加Account.java实体类(User对象的引入,实现一对一关联查询)
    package com.seapp.mybatis.domain;
    
    /**
     * @author seapp
     * @date 2020/7/29 8:49
     */
    public class Account {
    
        private Integer id;
        private Integer uid;
        private Double money;
        private User user;
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getUid() {
            return uid;
        }
    
        public void setUid(Integer uid) {
            this.uid = uid;
        }
    
        public Double getMoney() {
            return money;
        }
    
        public void setMoney(Double money) {
            this.money = money;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", uid=" + uid +
                    ", money=" + money +
                    '}';
        }
    }
    
    
    • 一对一查询实现:
     /**
         * 一对一查询实现:@one中属性: 
         *          select:指定所关联用户的查询语句(根据用户id查询用户信息),
         *          用户id值由column指定的uid字段来确定
         *          fetchType:指定关联数据时为“立即加载”还是“懒加载”
         * 
         * @return
         */
        @Select("select * from account")
        @Results({@Result(column = "id",property = "id"),
                @Result(column = "uid",property = "uid"),
                @Result(column = "money",property = "money"),
                @Result(column = "uid",property = "user",
                        one =@One( select = "com.seapp.mybatis.dao.IUserDao.findById",fetchType = FetchType.EAGER))
        })
        List<Account> findAll();
    
    //@one中对应的select语句为:
     /**
         * 根据用户id获取用户信息
         * @param userId
         * @return
         */
        @Select(value = "select * from user where id = #{userId}")
        User findById(Integer userId);
    
    //测试实现类:
    package com.seapp.mybatis;
    
    import com.seapp.mybatis.dao.IAccountDao;
    import com.seapp.mybatis.domain.Account;
    import com.seapp.mybatis.domain.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.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author seapp
     * @date 2020/7/28 23:13
     */
    public class AccountTest {
    
        InputStream in ;
        private SqlSession sqlSession;
        private IAccountDao accountDao;
    
        /**
         * 测试前的准备工作
         */
        @Before
        public void before() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession();
            accountDao = sqlSession.getMapper(IAccountDao.class);
        }
    
        /**
         * 测试完成后资源的释放
         */
        @After
        public void after(){
            sqlSession.commit();
            sqlSession.close();
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 测试查询所有账户信息并关联其用户
         */
        @Test
        public void findAllAccountTest(){
            List<Account> accountList = accountDao.findAll();
            for (Account account : accountList) {
                System.out.println("account = " + account);
                System.out.println("account.getUser() = " + account.getUser());
            }
    
        }
    }
    
    • 一对多关联查询:
     /**
         * 查询所有用户信息
         * @return
         */
        @Select(value = "select * from user")
        @Results(id = "userMap",value = {@Result(id = true,column = "id",property = "id"),
                @Result(column = "username",property = "username"),
                @Result(column = "birthday",property = "birthday"),
                @Result(column = "sex",property = "sex"),
                @Result(column = "address",property = "address"),
                @Result(column = "id",property = "accounts",
                        many = @Many(select = "com.seapp.mybatis.dao.IAccountDao.findByUserId",
                                fetchType = FetchType.LAZY))
    
    
        })
        List<User> findAll();
    

    4.缓存的配置

    注解中二级缓存的开启:在接口实现类中,配置如下注解

    @CacheNamespace(blocking = true)//开启二级缓存
    public interface IUserDao {
    }
    

    相关文章

      网友评论

          本文标题:SSM框架mybatis的缓存与注解开发(三)

          本文链接:https://www.haomeiwen.com/subject/telfrktx.html