MyBatis框架安排

4. 第四天:

1. MyBatis的延迟加载

  1. 引入问题:在一对多中,我们有一个用户,他有100个账户

    1. 问题1:在查询用户的时候,要不要把账户一起关联出来?
    2. 问题2:在查询账户时,要不要把用户一起关联出来?


    问题1:在查询用户时,应该是什么时候使用它的账户,就什么时候查。
    问题2:查询账户时,用户应该是随着账户一起被查询出来,这样才知道账户对应着谁。

什么是延迟加载:在真正使用数据时才发起查询,不用时不查询,又称按需查询(懒加载)
立即加载:不管用不用数据,只要一调用查询方法,就立即查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多中
通常情况下:
一对多、多对多采用的是延迟加载。
一对一、多对一采用的是立即加载。
简单记忆:右边多的采用延迟加载,否则采用立即加载。

<mark>演示1:账户和用户的关系,一对一(账户 | 用户)</mark>
账户和用户关系:一个账户只属于一个用户(特定时候一对一),但一个用户可以有多个账户(一对多)
需求:查询账户表信息,账户所属的用户信息不查询出来(延迟加载)

  1. 准备好两个实体类,Account,User

    package com.liuzeyu.domin;
    
    import java.io.Serializable;
    
    public class Account implements Serializable{
        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;
        }
    
    
        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", uid=" + uid +
                    ", money=" + money +
                    ", 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;
        }
    }
    
    
    package com.liuzeyu.domin;
    
    import java.util.Date;
    import java.util.List;
    
    public class User {
        private Integer id;
        private String address;
        private String username;
        private String sex;
        private Date birthday;
    
        private List<Account> accounts;
    
        public List<Account> getAccounts() {
            return accounts;
        }
    
        public void setAccounts(List<Account> accounts) {
            this.accounts = accounts;
        }
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", address='" + address + '\'' +
                    ", username='" + username + '\'' +
                    ", sex='" + sex + '\'' +
                    ", birthday=" + birthday +
                    '}';
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    }
    
    
    1. 两个映射配置文件IUserDao.xml,IAccountDao.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.liuzeyu.dao.IUserDao">
    
        <!-- 根据id查找一个 -->
        <select id="findById" resultType="user" parameterType="int">
            select * from user where id=#{uid}
        </select>
    </mapper>
    
    

    如果要配置延迟加载,association标签则不能封装User对象,如下

    <?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.liuzeyu.dao.IAccountDao">
        
        <resultMap id="rstAc" type="account">
            <id property="id" column="id"/>
            <result property="uid" column="uid"/>
            <result property="money" column="money"/>
            <!--javaType="user"映射后封装的结果集 外键:uid
            select :属性所指的内容就是查询用户的唯一标识
            column="uid",其中的uid就是select标签要查询的那个id ,不可以删掉
            -->
            <association property="user" column="uid" javaType="user" select="com.liuzeyu.dao.IUserDao.findById">
            </association>
        </resultMap>
    
        <!-- 打印user表数据 -->
        <select id="findAll" resultMap="rstAc">
          SELECT * from account
        </select>
    </mapper>
    
    
    1. 准备主配置文件
    <?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">
    <!-- mybatis主配置文件 -->
    <configuration>
    
        <properties resource="jdbcConfig.properties">
        </properties>
    
        <!--配置懒加载-->
        <!--<settings>-->
            <!--&lt;!&ndash;开启mybatis支持延迟加载&ndash;&gt;-->
            <!--<setting name="lazyLoadingEnabled" value="true"/>-->
            <!--&lt;!&ndash;关闭mybatis支持立即加载,默认true:mybatis < 3.4.1&ndash;&gt;-->
            <!--<setting name="aggressiveLazyLoading" value="false"/>-->
        <!--</settings>-->
        <!--自定义为配置文件取别名-->
        <typeAliases>
            <package name="com.liuzeyu.domin"/>
        </typeAliases>
        <!-- mysql配置文件 -->
        <environments default="mysql">
            <environment id="mysql">
                <!-- JDBC事物-->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 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.liuzeyu.dao"></package>
        </mappers>
    </configuration>
    
    
    1. 测试函数
    public class Test {
        private InputStream is = null;
        private IAccountDao accountDao = null;
        private  SqlSession session = null;
        /**
         * 初始化
         * @throws IOException
         */
        @Before  //测试开始前执行
        public void init() throws IOException {
            //1.解析xml配置文件
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.构建工厂
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            //3.工厂生产SqlSession对象
            session = factory.openSession();
            //session = factory.openSession(true);  //设置事物的自动提交,可以不用//session.commit();
            //4.创建代理Dao的对象
            accountDao = session.getMapper(IAccountDao.class);
        }
        @After //测试结束后执行
        public void destory(){
            //session.commit();
            //7.释放资源
            if( session != null){
                session.close();
            }
            if( is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        @org.junit.Test
        public void testSelect() throws IOException {
            //5.使用代理对象执行方法
            List<Account> accounts = accountDao.findAll();
        }
        
    }
    
    
    1. 测试结果
      1. 如果在主配置文件中不配置懒加载
      2. 如果在主配置文件中配置懒加载

        由以上日志信息可知,如果不配置懒加载,就会去根据id查询用户表的信息,配置了懒加载,则不会去查询用户表。

<mark>演示2:用户和账户的关系,一对多(用户 | 账户)</mark>
在演示1的基础上添加

  1. 映射配置IUserDao.xml文件添加

    <resultMap id="userMap" type="user">
        <id property="id" column="id"/>
        <result property="address" column="address"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <result property="username" column="username"/>
        <collection property="accounts"   column="id" select="com.liuzeyu.dao.IAccountDao.findByUid"/>
    </resultMap>
    
    <!-- 根据id查找一个 -->
    <select id="findAll" resultMap="userMap">
    	select * from user
    </select>
    
  2. 映射配置IAccountDao.xml文件添加

        <!-- 打印uid查询账户 -->
        <select id="findByUid" resultType="account" parameterType="int">
            SELECT * from account where uid=#{uid}
        </select>
    
  3. 测试函数

    public class UserTest {
        private InputStream is = null;
        private IUserDao userDao = null;
        private  SqlSession session = null;
        /**
         * 初始化
         * @throws IOException
         */
        @Before  //测试开始前执行
        public void init() throws IOException {
            //1.解析xml配置文件
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.构建工厂
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            //3.工厂生产SqlSession对象
            session = factory.openSession();
            //session = factory.openSession(true);  //设置事物的自动提交,可以不用//session.commit();
            //4.创建代理Dao的对象
            userDao = session.getMapper(IUserDao.class);
        }
        @After //测试结束后执行
        public void destory(){
            //session.commit();
            //7.释放资源
            if( session != null){
                session.close();
            }
            if( is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        @org.junit.Test
        public void testSelect() throws IOException {
            //5.使用代理对象执行方法
            List<User> users = userDao.findAll();
        }
    }
    
  4. 测试结果

    1. 在主配置文件中不设置懒加载(默认立即加载)
    2. 在主配置文件中设置懒加载

2. MyBatis的缓存

  1. 什么是缓存?
    答:存在于内存当中的临时数据。

  2. 为什么使用缓存?
    答:减少与数据库直接打交道,提高执行效率。

  3. 什么样的数据适用缓存,什么样的数据不适用?

    1. 适用缓存:
      • 不经常改变的数据
      • 并且缓存数据和数据库不同步时最终影响不大
    2. 不适用缓存:
      • 经常改变的数据
      • 并且缓存数据和数据库不同步时最终影响很大,如商品的库存,银行的汇率,股市的牌价等等
  4. MyBatis的一级缓存:一级缓存SqlSession对象范围的缓存,当我们执行查询之后,查询结果会同时存入SqlSession为我们提供的一块区域中,该区域的结构是Map,当我们再次查询到同样的数据时mybatis会先从sqlSession中获取。当调用SqlSession增删改,commit(),close()时就会清空一级缓存。
    示例:

    1. 准备User实体类
    package com.liuzeyu.domin;
    
    import java.util.Date;
    import java.util.List;
    
    public class User {
        private Integer id;
        private String address;
        private String username;
        private String sex;
        private Date birthday;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    }
    
    
    1. 准备映射配置文件
    <?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.liuzeyu.dao.IUserDao">
        <!-- 根据id查找一个 -->
        <select id="findById" resultType="user" parameterType="int">
            select * from user where id=#{uid}
        </select>
    
        <!-- 根据id查找一个 -->
        <select id="updateByUser" parameterType="user">
        update user set username=#{username} where id=#{id}
        </select>
    </mapper>
    
    
    1. dao层接口
    public interface IUserDao {
    
        //根据id查找一个
        public User findById(Integer id);
    
        //根据id查找一个
        public void  updateByUser(User user);
    }
    
    1. 测试函数
    public class UserTest {
        private InputStream is = null;
        private IUserDao userDao = null;
        private  SqlSession session = null;
        SqlSessionFactory factory = null;
        /**
         * 初始化
         * @throws IOException
         */
        @Before  //测试开始前执行
        public void init() throws IOException {
            //1.解析xml配置文件
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.构建工厂
            factory = new SqlSessionFactoryBuilder().build(is);
            //3.工厂生产SqlSession对象
            session = factory.openSession();
            //session = factory.openSession(true);  //设置事物的自动提交,可以不用//session.commit();
            //4.创建代理Dao的对象
            userDao = session.getMapper(IUserDao.class);
        }
        @After //测试结束后执行
        public void destory(){
            //session.commit();
            //7.释放资源
            if( session != null){
                session.close();
            }
            if( is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    //    测试一级缓存session.clearCache();
        @org.junit.Test
        public void testSelect() throws IOException {
            //5.使用代理对象执行方法
            User users1 = userDao.findById(41);
            System.out.println(users1);
            session.clearCache();
            User users2 = userDao.findById(41);
            System.out.println(users2);
            System.out.println(users1 == users2);
        }
    
        //测试一级缓存:sqlSession的更新操作
        @org.junit.Test
        public void testUpdate() throws IOException {
            //5.使用代理对象执行方法
            User users1 = userDao.findById(41);
            System.out.println(users1);
            users1.setUsername("SqlSession update");
    
            userDao.updateByUser(users1);
            User users2 = userDao.findById(41);
            System.out.println(users2);
            System.out.println(users1 == users2);  //false
        }
        //测试一级缓存:sqlSession.close();
        @org.junit.Test
        public void testClose() throws IOException {
            //5.使用代理对象执行方法
            User users1 = userDao.findById(41);
            System.out.println(users1);
            session.close();
            SqlSession session = factory.openSession();
            IUserDao userDao = session.getMapper(IUserDao.class);  //true
    
            userDao.updateByUser(users1);
            User users2 = userDao.findById(41);
            System.out.println(users2);
            System.out.println(users1 == users2);   //false
        }
    }
    
    

    <mark>留下疑问:为什么SqlSession的更新和删除操作,并没有清空缓存?</mark>

  5. Mybatis的二级缓存:它是指Mybatis的SqlSessionFactory对象缓存,在同一个SqlSessionFactory创建的SqlSession对象内共享缓存。

    二级缓存的使用步骤:

    1. 让MyBatis框架支持二级缓存(在SqlMapConfig.xml中配置)
        <settings>
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
    1. 让接口配置文件支持二级缓存,在IUserDao.xml中配置
        <!--开启配置文件二级缓存-->
        <cache/>
    
    1. 让当前操作系统支持二级缓存(在接口配置文件的select 标签中配置)
    
        <!-- useCache="true" -->
        <select id="findById" resultType="user" parameterType="int" useCache="true">
            select * from user where id=#{uid}
        </select>
    

    示例:
    在一级缓存的基础项目上,新建测试函数

package com.liuzeyu.test;

import com.liuzeyu.dao.IUserDao;
import com.liuzeyu.domin.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 java.io.IOException;
import java.io.InputStream;

public class UserTest2 {
    private InputStream is = null;
    SqlSessionFactory factory = null;
    /**
     * 初始化
     * @throws IOException
     */
    @Before  //测试开始前执行
    public void init() throws IOException {
        //1.解析xml配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.构建工厂
        factory = new SqlSessionFactoryBuilder().build(is);
    }
    @After //测试结束后执行
    public void destory(){
        if( is != null){
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
//    测试一级缓存session.clearCache();
    @org.junit.Test
    public void testSecondCash() throws IOException {
        SqlSession session1 = factory.openSession();
        IUserDao userDao1 = session1.getMapper(IUserDao.class);
        User user1 = userDao1.findById(41);
        System.out.println(user1);
        session1.close();

        SqlSession session2 = factory.openSession();
        IUserDao userDao2 = session2.getMapper(IUserDao.class);
        User user2 = userDao2.findById(41);
        System.out.println(user2);
        session2.close();

        System.out.println(user1 == user2);
    }

}

未配置二级缓存:

配置二级缓存:

可见两个不同的Sqlsession对象共享二级缓存区域,session1将查询的数据放到了二级缓存中,第二次查询就无需重新连接数据库查询了。

3. MyBatis注解开发

  1. 环境搭建

    1. 准备实体类
    package com.liuzeyu.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", adress='" + address + '\'' +
                    '}';
        }
    
        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 adress) {
            this.address = adress;
        }
    }
    
    
    1. 主配置文件
    <?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">
    <!-- mybatis主配置文件 -->
    <configuration>
        <!--加载配置文件-->
        <properties resource="jdbcConfig.properties"/>
        <!--取别名-->
        <typeAliases>
            <package name="com.liuzeyu.domain"/>
        </typeAliases>
        <!-- 配置环境 -->
        <environments default="mysql">
            <!-- 配置mysql环境 -->
            <environment id="mysql">
                <!-- 配置事物类型 -->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置数据库源(连接池)-->
                <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接口配置-->
        <mappers>
            <!--<mapper class="com.liuzeyu.dao.IUserDao"/>-->
            <package name="com.liuzeyu.dao"/>
        </mappers>
    </configuration>
    
    1. dao层接口
    public interface IUserDao {
    
        @Select("select * from user")
        public List<User> findAll();
    }
    
    1. 测试函数
    public class AnnotationTest {
    
        public static void main(String[] args) throws Exception{
            //1.加载配置文件
            InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessiionFactory
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
            //3.创建SqlSession对象
            SqlSession sqlSession = factory.openSession();
            //4.创建dao接口的代理dao
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            //5.代理dao执行
            List<User> users = userDao.findAll();
            //6.遍历
            for (User user : users) {
                System.out.println(user);
            }
            //7.释放资源
            sqlSession.close();
            is.close();
    
        }
    }
    
    
    1. 查询结果
    2. <mark>注意事项1:</mark>
      如果使用注解配置,只要是在resource存在,如下目录结构的映射文件

      无论是否在主配置文件中配置mapper映射文件的class属性,mybatis都会认为这是错误的,因为他不认识你是否要用注解开发

      <mark>注意事项2:</mark>
      注解开发与映射配置文件的区别,为什么可以使用注解开发?

      由此配置文件有的,注解也都能提供。
  2. 单表CRUD操作

    1. 在1的基础上操作,为dao层接口添加如下GRUD操作
    public interface IUserDao {
    
        @Select("select * from user")
        public List<User> findAll();
    //插入一个用户
        @Select("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})")
        public List<User> insertUser(User user);
    
    //根据id查找某一用户
        @Select("select * from user where id=#{id}")
        public User findById(Integer id);
    // 根据id删除某一用户
        @Delete("delete from user where id=#{id}")
        public void deleteById(Integer id);
    // 根据id更新某一用户的用户名
        @Update("update user set username=#{username} where id=#{id}")
        public void Update(User user);
    	// 根据name删除某一用户集合
        //@Select("select * from user where username like '%${username}%'")  不推荐使用
        @Select("select * from user where username like #{username}")
        public List<User> findByName(User user);
    }
    
    
    1. 测试函数
    public class AnnotationTestCRUD {
    
        private InputStream is = null;
        private SqlSession sqlSession = null;
        private SqlSessionFactory factory  = null;
        private IUserDao dao = null;
    
        @Before
        public void init() throws IOException {
            //1.加载配置文件
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建工厂
            factory = new SqlSessionFactoryBuilder().build(is);
            //3.创建SqlSession对象
            sqlSession = factory.openSession(true);
            //4.创建代理dao对象
             dao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destory() throws IOException {
            if(sqlSession != null){
                sqlSession.close();
            }
            if(is != null){
                is.close();
    
            }
        }
    
        /**
         * 插入一个用户
         */
        @Test
        public void testInsert(){
            User user = new User();
            user.setUsername("JAY");
            user.setAddress("莆田");
            user.setSex("男");
            user.setBirthday(new Date());
    
            dao.insertUser(user);
        }
    
        /**
         * 删除一个用户
         */
        @Test
        public void testDelete(){
            dao.deleteById(50);
        }
    
        /**
         * 根据id查找某一用户
         */
        @Test
        public void testFindById(){
            User user = dao.findById(41);
            System.out.println(user);
        }
    
        /**
         * 更新某一用户
         */
        @Test
        public void testUpdate(){
            User user = dao.findById(51);
            user.setUsername("Jack");
            dao.Update(user);
        }
    
        /**
         * 根据id查找某一用户
         */
        @Test
        public void testFindByNames(){
            User user = new User();
    		//user.setUsername("%王%");
            user.setUsername("王");
            List<User> users  = dao.findByName(user);
            for (User u : users) {
                System.out.println(u);
            }
        }
    }
    
    
  3. 解决实体类属性名和数据库字段名不一致问题

    1. 在1的基础上修改字段名,并生成对应的setXXX,getXXX方法
    
    public class User implements Serializable {
        private Integer userId;
        private String userName;
        private Date userBirthday;
        private String userSex;
        private String userAddress;
    
        @Override
        public String toString() {
            return "User{" +
                    "userId=" + userId +
                    ", userName='" + userName + '\'' +
                    ", userBirthday=" + userBirthday +
                    ", userSex='" + userSex + '\'' +
                    ", userAddress='" + userAddress + '\'' +
                    '}';
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Date getUserBirthday() {
            return userBirthday;
        }
    
        public void setUserBirthday(Date userBirthday) {
            this.userBirthday = userBirthday;
        }
    
        public String getUserSex() {
            return userSex;
        }
    
        public void setUserSex(String userSex) {
            this.userSex = userSex;
        }
    
        public String getUserAddress() {
            return userAddress;
        }
    
        public void setUserAddress(String userAddress) {
            this.userAddress = userAddress;
        }
    }
    
    
    1. 删除不需要的dao方法
    public interface IUserDao {
    
        @Select("select * from user")
        public List<User> findAll();
    
        @Select("insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})")
        public List<User> insertUser(User user);
    
        @Select("select * from user where id=#{id}")
        public User findById(Integer id);
    
    }
    
    1. 测试findAll方法,可见并没有被封装进去
    2. 解决实体类和数据库字段对应关系
      在dao层函数上添加,与接口配置文件的resultMap标签作用相同
        @Select("select * from user")
        @Results(id = "userMap",value = {
                @Result(id = true,property = "userId" ,column = "id"),
                @Result(property = "userName",column = "username"),
                @Result(property = "userBirthday",column = "birthday"),
                @Result(property = "userSex",column = "sex"),
                @Result(property = "userAddress",column = "address")
        })
        public List<User> findAll();
    
    1. 测试findAll
    2. 如果需要在其它地方引用则个Map,可以使用注解ResultMap的value字段引用即可
    
        //@ResultMap(value = {"userMap"}) 简写如下
        @ResultMap("userMap")
        @Select("select * from user where id=#{id}")
        public User findById(Integer id);
    
    1. 测试findById
  4. 多表查询操作

    1. 账户和用户的关系(账户 | 用户,多对一) mybatis称之为:一对一的关系

      1. 在4的基础上新增Account类
      public class Account implements Serializable{
          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;
          }
      
      
          @Override
          public String toString() {
              return "Account{" +
                      "id=" + id +
                      ", uid=" + uid +
                      ", money=" + money +
                      ", 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;
          }
      }
      
      
      1. 新增IAccountDao接口及方法
      import java.util.List;
      
      
      public interface IAccountDao {
      
          @Select("select * from account")
          @Results(id = "accountMap",value = {
                  @Result(id = true,property = "id" ,column = "id"),
                  @Result(property = "uid",column = "uid"),
                  @Result(property = "money",column = "money"),
                  @Result(property = "user",column = "uid",
                          //账户和用户对应关系是:一对一,使用@One
                          one = @One(select = "com.liuzeyu.dao.IUserDao.findById",fetchType = FetchType.LAZY))
          })
          public List<Account> findAll();
      }
      
      
      1. 测试方法
      public class AnnotationTestAccount {
      
          private InputStream is = null;
          private SqlSession sqlSession = null;
          private SqlSessionFactory factory  = null;
          private IAccountDao dao = null;
      
          @Before
          public void init() throws IOException {
              //1.加载配置文件
              is = Resources.getResourceAsStream("SqlMapConfig.xml");
              //2.创建工厂
              factory = new SqlSessionFactoryBuilder().build(is);
              //3.创建SqlSession对象
              sqlSession = factory.openSession(true);
              //4.创建代理dao对象
               dao = sqlSession.getMapper(IAccountDao.class);
          }
      
          @After
          public void destory() throws IOException {
              if(sqlSession != null){
                  sqlSession.close();
              }
              if(is != null){
                  is.close();
      
              }
          }
          /**
           * 查找所有
           */
          @Test
          public void testFindAll(){
              List<Account> accounts = dao.findAll();
              for (Account account : accounts) {
                  System.out.println(account);
              }
          }
      
      1. 测试结果
    2. 用户和账户的关系(用户 | 账户 ,一对多)

      1. User实体类添加属性Account集合
          private List<Account> accounts;
      
          public List<Account> getAccounts() {
              return accounts;
          }
      
          public void setAccounts(List<Account> accounts) {
              this.accounts = accounts;
          }
      
      
      1. 在5.1的基础上新增dao层IAccount接口方法
      userId  属性值
      <!-- 通过uid查询账户-->
      @Select("select * from account where uid=#{userId}")
      public Account findByUid(Integer id);
      
      1. 修改IUserDao的接口方法
      public interface IUserDao {
      
          @Select("select * from user")
          @Results(id = "userMap",value = {
                  @Result(id = true,property = "userId" ,column = "id"),
                  @Result(property = "userName",column = "username"),
                  @Result(property = "userBirthday",column = "birthday"),
                  @Result(property = "userSex",column = "sex"),
                  @Result(property = "userAddress",column = "address"),
                  @Result(property = "accounts",column = "id",
                          many = @Many(select = "com.liuzeyu.dao.IAccountDao.findByUid",fetchType= FetchType.LAZY))
          })
          public List<User> findAll();
      
      }
      
      
      1. 新增测试方法
      public class AnnotationTestCRUD {
      
          private InputStream is = null;
          private SqlSession sqlSession = null;
          private SqlSessionFactory factory  = null;
          private IUserDao dao = null;
      
          @Before
          public void init() throws IOException {
              //1.加载配置文件
              is = Resources.getResourceAsStream("SqlMapConfig.xml");
              //2.创建工厂
              factory = new SqlSessionFactoryBuilder().build(is);
              //3.创建SqlSession对象
              sqlSession = factory.openSession(true);
              //4.创建代理dao对象
               dao = sqlSession.getMapper(IUserDao.class);
          }
      
          @After
          public void destory() throws IOException {
              if(sqlSession != null){
                  sqlSession.close();
              }
              if(is != null){
                  is.close();
      
              }
          }
          /**
           * 查找所有
           */
          @Test
          public void testFindAll(){
              List<User> users = dao.findAll();
              for (User user : users) {
                  System.out.println(user);
              }
          }
      }
      
      
      1. 测试结果
  5. 缓存配置

    1. 一级缓存默认开启,共享SqlSession
          @Test
          public void testFindById(){
              User user1 = dao.findById(41);
              //sqlSession.clearCache();  开启后  user1 == user2为false
              User user2 = dao.findById(41);
              System.out.println(user1 == user2);  //true
          }
      
    2. 二级缓存的配置
      1. 新增类测试二级缓存
      public class AnnotationTestSecondLevel {
      
          private InputStream is = null;
          private SqlSessionFactory factory = null;
      
          @Before
          public void init() throws IOException {
              //1.加载配置文件
              is = Resources.getResourceAsStream("SqlMapConfig.xml");
              factory = new SqlSessionFactoryBuilder().build(is);
          }
      
          @After
          public void destory() throws IOException {
              if(is != null){
                  is.close();
      
              }
          }
      
          /**
           * 测试二级缓存
           */
          @Test
          public void testSecondLevel(){
              SqlSession sqlSession1 = factory.openSession(true);
              IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
              User user1 = dao1.findById(41);
              System.out.println(user1);
              sqlSession1.close();
      
      
              SqlSession sqlSession2 = factory.openSession(true);
              IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
              User user2 = dao2.findById(41);
              System.out.println(user2);
              sqlSession2.close();
          }
      
      }
      
      
      这里需注意的是必须是同一个工厂对象factory才可以共享二级缓存。
      1. 还未配置二级缓存前,测试结果
      2. 配置二级缓存
        1. 主配置文件开启二级缓存,注意要写到加载配置文件后
              <!--开启二级缓存-->
              <settings>
                  <setting name="cacheEnabled" value="true"/>
              </settings>
          
        2. dao层接口
        3. 重新运行测试函数,此时只打开一次数据库连接