mybatis講義

第一天:mybatis入門

1.1 mybatis的概述

? mybatis 是一個(gè)優(yōu)秀的基于 java 的持久層框架,它內(nèi)部封裝了 jdbc,使開發(fā)者只需要關(guān)注 sql 語(yǔ)句本身,而不需要花費(fèi)精力去處理加載驅(qū)動(dòng)盒粮、創(chuàng)建連接柠偶、創(chuàng)建 statement 等繁雜的過(guò)程赘那。
mybatis 通過(guò) xml 或注解的方式將要執(zhí)行的各種 statement 配置起來(lái),并通過(guò) java 對(duì)象和 statement 中sql 的動(dòng)態(tài)參數(shù)進(jìn)行映射生成最終執(zhí)行的 sql 語(yǔ)句,最后由 mybatis 框架執(zhí)行 sql 并將結(jié)果映射為 java 對(duì)象并返回壁畸。
采用 ORM 思想解決了實(shí)體和數(shù)據(jù)庫(kù)映射的問題,對(duì) jdbc 進(jìn)行了封裝,屏蔽了 jdbc api 底層訪問細(xì)節(jié)使我們不用與 jdbc api 打交道,就可以完成對(duì)數(shù)據(jù)庫(kù)的持久化操作摆霉。

1.1.1jdbc 程序的回顧

public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
            Class.forName("com.mysql.jdbc.Driver");
            //通過(guò)驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)鏈接
            connection =
                    DriverManager
                            .getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");
                                    //定義 sql 語(yǔ)句 ?表示占位符
                                    String sql = "select * from user where username = ?";
            //獲取預(yù)處理 statement
            preparedStatement = connection.prepareStatement(sql);
            //設(shè)置參數(shù),第一個(gè)參數(shù)為 sql 語(yǔ)句中參數(shù)的序號(hào)(從 1 開始),第二個(gè)參數(shù)為設(shè)置的參數(shù)值
            preparedStatement.setString(1, "王五");
            //向數(shù)據(jù)庫(kù)發(fā)出 sql 執(zhí)行查詢,查詢出結(jié)果集
            resultSet = preparedStatement.executeQuery();
                //遍歷查詢結(jié)果集
            while(resultSet.next()){
                System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            if(resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
上邊使用 jdbc 的原始方法(未經(jīng)封裝)實(shí)現(xiàn)了查詢數(shù)據(jù)庫(kù)表記錄的操作椒拗。

1.1.2 jdbc 問題分析

  1. 數(shù)據(jù)庫(kù)鏈接創(chuàng)建淡溯、釋放頻繁造成系統(tǒng)資源浪費(fèi)從而影響系統(tǒng)性能,如果使用數(shù)據(jù)庫(kù)鏈接池可解決此問題兵琳。
  2. Sql 語(yǔ)句在代碼中硬編碼,造成代碼不易維護(hù),實(shí)際應(yīng)用 sql 變化的可能較大,sql 變動(dòng)需要改變 java
  3. 代碼勃蜘。
  4. 使用 preparedStatement 向占有位符號(hào)傳參數(shù)存在硬編碼,因?yàn)?sql 語(yǔ)句的 where 條件不一定,可能多也可能少,修改 sql 還要修改代碼,系統(tǒng)不易維護(hù)硕噩。
  5. 對(duì)結(jié)果集解析存在硬編碼(查詢列名),sql 變化導(dǎo)致解析代碼變化,系統(tǒng)不易維護(hù),如果能將數(shù)據(jù)庫(kù)記錄封裝成 pojo 對(duì)象解析比較方便。

1.2 mybatis的環(huán)境搭建及入門案例

環(huán)境搭建的注意事項(xiàng):
第一個(gè):創(chuàng)建UserDao.xml 和 UserDao.java時(shí)名稱是為了和我們之前的知識(shí)保持一致元旬。
在Mybatis中它把持久層的操作接口名稱和映射文件也叫做:Mapper
所以:IUserDao 和 IUserMapper是一樣的
第二個(gè):在idea中創(chuàng)建目錄的時(shí)候榴徐,它和包是不一樣的
包在創(chuàng)建時(shí):com.itheima.dao它是三級(jí)結(jié)構(gòu)
目錄在創(chuàng)建時(shí):com.itheima.dao是一級(jí)目錄
第三個(gè):mybatis的映射配置文件位置必須和dao接口的包結(jié)構(gòu)相同
第四個(gè):映射配置文件的mapper標(biāo)簽namespace屬性的取值必須是dao接口的全限定類名
第五個(gè):映射配置文件的操作配置(select),id屬性的取值必須是dao接口的方法名

    當(dāng)我們遵從了第三匀归,四坑资,五點(diǎn)之后,我們?cè)陂_發(fā)中就無(wú)須再寫dao的實(shí)現(xiàn)類穆端。

1.2.1 創(chuàng)建 maven 工程

創(chuàng)建mybatis-study工程,工程信息如下

<groupId>org.example</groupId>
<artifactId>mybatis-study</artifactId>
<version>1.0-SNAPSHOT</version>

1.2.2 添加 Mybatis3.4.5 的坐標(biāo)

在 pom.xml 文件中添加 Mybatis3.4.5 的坐標(biāo),如下:

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>

1.2.3 添加 數(shù)據(jù)庫(kù)表

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用戶名稱',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性別',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龍'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龍'),(45,'傳智播客','2018-03-04 12:04:06','男','北京金燕龍'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小馬寶莉','2018-03-08 11:44:00','女','北京修正');



DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '編號(hào)',
  `UID` int(11) default NULL COMMENT '用戶編號(hào)',
  `MONEY` double default NULL COMMENT '金額',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



insert  into `account`(`ID`,`UID`,`MONEY`) values (1,41,1000),(2,45,1000),(3,41,2000);



DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `ID` int(11) NOT NULL COMMENT '編號(hào)',
  `ROLE_NAME` varchar(30) default NULL COMMENT '角色名稱',
  `ROLE_DESC` varchar(60) default NULL COMMENT '角色描述',
  PRIMARY KEY  (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `role`(`ID`,`ROLE_NAME`,`ROLE_DESC`) values (1,'院長(zhǎng)','管理整個(gè)學(xué)院'),(2,'總裁','管理整個(gè)公司'),(3,'校長(zhǎng)','管理整個(gè)學(xué)校');



DROP TABLE IF EXISTS `user_role`;

CREATE TABLE `user_role` (
  `UID` int(11) NOT NULL COMMENT '用戶編號(hào)',
  `RID` int(11) NOT NULL COMMENT '角色編號(hào)',
  PRIMARY KEY  (`UID`,`RID`),
  KEY `FK_Reference_10` (`RID`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user_role`(`UID`,`RID`) values (41,1),(45,1),(41,2);

1.2.4 編寫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">
<!--mybatis的主配置文件-->
<configuration>
    <!--配置環(huán)境-->
    <environments default="mysql">
        <!--配置mysql的環(huán)境-->
        <environment id="mysql">
            <!-- 配置事務(wù)類型-->
            <transactionManager type="JDBC"/>
            <!-- 配置數(shù)據(jù)源(連接池)-->
            <dataSource type="POOLED">
                <!-- 配置連接數(shù)據(jù)庫(kù)的4個(gè)基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!-- 指定字符編碼為UTF-8 防止中文亂碼-->
                <property name="url" value="jdbc:mysql://localhost:3306/easy?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定映射配置文件的位置,映射配置文件指的是每個(gè)dao獨(dú)立的配置文件 -->
   <mappers>
        <!-- 使用相對(duì)于類路徑的資源引用 -->
         <mapper resource="org/example/dao/UserDao.xml"/>

        <!-- 使用完全限定資源定位符(URL) -->
        <!--<mapper url="file:///var/mappers/AuthorMapper.xml"/>-->

        <!-- 使用映射器接口實(shí)現(xiàn)類的完全限定類名 -->
        <!-- <mapper class="org.mybatis.builder.AuthorMapper"/> -->

        <!-- 將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊(cè)為映射器 -->
        <!--<package name="org.example.mapper"/>-->
    </mappers>
</configuration>

1.2.5編寫 User 實(shí)體類

package org.example.domain;


import java.io.Serializable;
import java.util.Date;

/**
 * 用戶的實(shí)體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String 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 address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

1.2.6 編寫持久層接口 UserDao

package org.example.dao;

import org.example.domain.User;
import java.util.List;

/**
 * UserDao 接口就是我們的持久層接口
 */
public interface UserDao {

    /**
     * 查詢所有用戶操作
     * @return
     */
    List<User> findAll();
}

1.2.7 編寫持久層接口的映射文件 UserDao.xml

要求:
創(chuàng)建位置:必須和持久層接口在相同的包中袱贮。
名稱:必須以持久層接口名稱命名文件名,擴(kuò)展名是.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="org.example.dao.UserDao">
    <!-- 配置查詢所有操作 -->
    <select id="findAll" resultType="org.example.domain.User">
        select * from user
    </select>
</mapper>

1.2.8 編寫測(cè)試類

package org.example.test;


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.example.dao.UserDao;
import org.example.domain.User;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 *  mybatis 入門案例
 */
public class MybatisTest {

    /**
     * 入門案例
     * @param args
     */
    public static void main(String[] args) throws IOException {
        //1.讀取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

        //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
        SqlSessionFactory factory = builder.build(in);

        //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
        SqlSession session = factory.openSession();

        //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
        UserDao userDao = session.getMapper(UserDao.class);
        //6.使用代理對(duì)象執(zhí)行查詢所有方法
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println(user);
        }
        //7.釋放資源
        session.close();
        in.close();
    }
}

第二天:mybatis基本使用

使用要求:
1、持久層接口和持久層接口的映射配置必須在相同的包下
2体啰、持久層映射配置中 mapper 標(biāo)簽的 namespace 屬性取值必須是持久層接口的全限定類名
3攒巍、SQL 語(yǔ)句的配置標(biāo)簽<select>,<insert>,<delete>,<update>的 id 屬性必須和持久層接口的
方法名相同嗽仪。

2.1 基于代理 Dao 實(shí)現(xiàn) CRUD 操作

2.1.1根據(jù) ID 查詢

在持久層接口中添加 findById 方法

/**
 * 根據(jù)Id查詢用戶
 */
User findById(Integer id);
<!-- 根據(jù)Id查詢用戶-->
<select id="findById" parameterType="java.lang.Integer" resultType="org.example.domain.User">
select * from  user where id = #{id}
</select>
在用戶的映射配置文件中配置細(xì)節(jié):
resultType 屬性:
    用于指定結(jié)果集的類型。
parameterType 屬性:
    用于指定傳入?yún)?shù)的類型柒莉。
sql 語(yǔ)句中使用#{}字符:
    它代表占位符,相當(dāng)于原來(lái) jdbc 部分所學(xué)的?,都是用于執(zhí)行語(yǔ)句時(shí)替換實(shí)際的數(shù)據(jù)闻坚。
    具體的數(shù)據(jù)是由#{}里面的內(nèi)容決定的。
#{}中內(nèi)容的寫法:
    由于數(shù)據(jù)類型是基本類型,所以此處可以隨意寫兢孝。

在測(cè)試類添加測(cè)試

public class MybatisTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private  UserDao userDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
            userDao = sqlSession.getMapper(UserDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    
    @Test
    public void testFindAll() throws IOException {
        //6.使用代理對(duì)象執(zhí)行查詢所有方法
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println(user);
        }
    }
    
     /**
     * 測(cè)試根據(jù)id查詢用戶
     */
    @Test
    public void testDFindById(){
        User user = userDao.findById(49);
        System.out.println(user);
    }
}

2.1.2 保存操作

在持久層接口中添加新增方法

/**
 * 保存用戶
 * @param user
 * @return 影響數(shù)據(jù)庫(kù)記錄的行數(shù)
 */
int saveUser(User user);

在用戶的映射配置文件中配置

<!-- 保存用戶-->
<insert id="saveUser">
    新增用戶后,同時(shí)還要返回當(dāng)前新增用戶的 id 值,因?yàn)?id 是由數(shù)據(jù)庫(kù)的自動(dòng)增長(zhǎng)來(lái)實(shí)現(xiàn)的,所以就相
當(dāng)于我們要在新增后將自動(dòng)增長(zhǎng) auto_increment 的值返回窿凤。
<!-- 配置插入操作后,獲取出入數(shù)據(jù)的id-->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        select last_insert_id();
    </selectKey>
    INSERT
    INTO
        user
        (username, address, sex, birthday)
    VALUES
        (#{username}, #{address}, #{sex}, #{birthday});
</insert>
細(xì)節(jié):
parameterType 屬性:
    代表參數(shù)的類型,因?yàn)槲覀円獋魅氲氖且粋€(gè)類的對(duì)象,所以類型就寫類的全名稱。
sql 語(yǔ)句中使用#{}字符:
    它代表占位符,相當(dāng)于原來(lái) jdbc 部分所學(xué)的?,都是用于執(zhí)行語(yǔ)句時(shí)替換實(shí)際的數(shù)據(jù)跨蟹。
    具體的數(shù)據(jù)是由#{}里面的內(nèi)容決定的雳殊。
#{}中內(nèi)容的寫法:
    由于我們保存方法的參數(shù)是 一個(gè) User 對(duì)象,此處要寫 User 對(duì)象中的屬性名稱。
    它用的是 ognl 表達(dá)式窗轩。
ognl 表達(dá)式:
    它是 apache 提供的一種表達(dá)式語(yǔ)言,全稱是:
Object Graphic Navigation Language
    對(duì)象圖導(dǎo)航語(yǔ)言
    它是按照一定的語(yǔ)法格式來(lái)獲取數(shù)據(jù)的夯秃。
    語(yǔ)法格式就是使用 #{對(duì)象.對(duì)象}的方式
        #{user.username}它會(huì)先去找 user 對(duì)象,然后在 user 對(duì)象中找到 username 屬性,并調(diào)用getUsername()方法把值取出來(lái)。但是我們?cè)?parameterType 屬性上指定了實(shí)體類名稱,所以可以省略 user.而直接寫 username痢艺。

添加測(cè)試類中的測(cè)試方法

@Test
public void testSave() throws IOException {
    User user = new User();
    user.setUsername("mybatis saveuser2");
    user.setAddress("天涯海角");
    user.setSex("男");
    user.setBirthday(new Date());
    System.out.println("保存之前:" + user);
    // 執(zhí)行保存
    userDao.saveUser(user);
    System.out.println("保存之后:" + user);
}
    打開 Mysql 數(shù)據(jù)庫(kù)發(fā)現(xiàn)并沒有添加任何記錄,原因是什么?
    這一點(diǎn)和 jdbc 是一樣的,我們?cè)趯?shí)現(xiàn)增刪改時(shí)一定要去控制事務(wù)的提交,那么在 mybatis 中如何控制事務(wù)提交呢?
    可以使用:session.commit();來(lái)實(shí)現(xiàn)事務(wù)提交仓洼。加入事務(wù)提交后的代碼如下:
    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

2.1.3 用戶更新

在持久層接口中添加更新方法

/**
 * 更新用戶
 * @param user
 * @return 影響數(shù)據(jù)庫(kù)記錄的行數(shù)
 */
int updateUser(User user);

在用戶的映射配置文件中配置

<!--更新用戶-->
<update id="updateUser" parameterType="org.example.domain.User">
    UPDATE user
    SET username = #{username},
        address = #{address},
        sex = #{sex},
        birthday = #{birthday}
    WHERE id = #{id}

加入更新的測(cè)試方法

/**
 * 更新測(cè)試
 */
@Test
public void testUpdate(){
    User user = new User();
    user.setId(49);
    user.setUsername("mybatis 49");
    user.setAddress("天涯海角");
    user.setSex("男");
    user.setBirthday(new Date());
    // 執(zhí)行保存
    userDao.updateUser(user);
}

2.1.4 用戶刪除

在持久層接口中添加刪除方法

/**
 * 根據(jù)id刪除用戶
 * @param id
 * @return 影響數(shù)據(jù)庫(kù)記錄的行數(shù)
 */
int deleteUser(Integer id);

在用戶的映射配置文件中配置

<!-- 根據(jù)id刪除用戶-->
<delete id="deleteUser" parameterType="java.lang.Integer">
    delete from user where id = #{id}
</delete>

加入刪除的測(cè)試方法

/**
 * 測(cè)試刪除
 */
@Test
public void testDelete(){
    userDao.deleteUser(50);
}

2.1.5 用戶模糊查詢

在持久層接口中添加模糊查詢方法

/**
 * 根據(jù)名稱模糊查詢用戶信息
 */
List<User> findByName(String username);

在用戶的映射配置文件中配置

<!-- 根據(jù)名稱模糊查詢用戶信息 -->
<select id="findByName" parameterType="java.lang.String"  resultType="org.example.domain.User">
    select * from user where username like concat('%',#{username},'%')
</select>

加入模糊查詢的測(cè)試方法

/**
 * 測(cè)試迷糊查詢
 */
@Test
public void testFindByName(){
    List<User> list = userDao.findByName("小二");
    for (User user: list) {
        System.out.println(user);
    }
}

2.1.6 查詢使用聚合函數(shù)

在持久層接口中添加模糊查詢方法

/**
 * 查詢總用戶數(shù)
 * @return
 */
int findTotal();

在用戶的映射配置文件中配置

<!--查詢總的用戶數(shù)-->
<select id="findTotal" resultType="int">
    select count(*) from user
</select>

加入聚合查詢的測(cè)試方法

/**
 *  測(cè)試查詢總的用戶數(shù)
 */
@Test
public void testFindTotal(){
    int total = userDao.findTotal();
    System.out.println("總用戶數(shù): " + total);
}

2.1.7#{}${}的區(qū)別

#{}表示一個(gè)占位符號(hào)
通過(guò)#{}可以實(shí)現(xiàn) preparedStatement 向占位符中設(shè)置值,自動(dòng)進(jìn)行 java 類型和 jdbc 類型轉(zhuǎn)換,#{}可以有效防止 sql 注入期吓。 #{}可以接收簡(jiǎn)單類型值或 pojo 屬性值惦界。 如果 parameterType 傳輸單個(gè)簡(jiǎn)單類型值,#{}括號(hào)中可以是 value 或其它名稱。
**{}表示拼接 sql 串** 通過(guò){}可以將 parameterType 傳入的內(nèi)容拼接在 sql 中且不進(jìn)行 jdbc 類型轉(zhuǎn)換, {}可以接收簡(jiǎn)單類型值或 pojo 屬性值,如果 parameterType 傳輸單個(gè)簡(jiǎn)單類型值,{}括號(hào)中只能是 value隆圆。

2.2 mybatis的參數(shù)和返回值

2.2.1 parameterType 配置參數(shù)

? 我們?cè)谏弦徽鹿?jié)中已經(jīng)介紹了 SQL 語(yǔ)句傳參,使用標(biāo)簽的 parameterType 屬性來(lái)設(shè)定植酥。該屬性的取值可以是基本類型,引用類型(例如:String 類型),還可以是實(shí)體類類型(POJO 類)镀岛。同時(shí)也可以使用實(shí)體類的包裝類,本章節(jié)將介紹如何使用實(shí)體類的包裝類作為參數(shù)傳遞。

? 基 本 類 型 和 String 我 們 可 以 直 接 寫 類 型 名 稱 , 也 可 以 使 用 包 名 . 類 名 的 方 式 , 例 如 :java.lang.String友驮。

? 實(shí)體類類型,目前我們只能使用全限定類名漂羊。
究其原因,是 mybaits 在加載時(shí)已經(jīng)把常用的數(shù)據(jù)類型注冊(cè)了別名,從而我們?cè)谑褂脮r(shí)可以不寫包名,而我們的是實(shí)體類并沒有注冊(cè)別名,所以必須寫全限定類名。在今天課程的最后一個(gè)章節(jié)中將講解如何注冊(cè)實(shí)體類
的別名卸留。

別名 映射的類型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

2.2.2 傳遞 pojo 包裝對(duì)象

? 開發(fā)中通過(guò) pojo 傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括用戶查詢條件還包括其它的查詢條件(比如將用戶購(gòu)買商品信息也作為查詢條件),這時(shí)可以使用包裝對(duì)象傳遞輸入?yún)?shù)走越。
Pojo 類中包含 pojo。
需求:根據(jù)用戶名查詢用戶信息,查詢條件放到 QueryVo 的 user 屬性中耻瑟。

  1. 編寫 QueryVo
/**
 * 查詢條件對(duì)象
 */
public class QueryVo implements Serializable {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
  1. 編寫持久層接口
/**
 * 根據(jù)QueryVo中的條件查詢用戶
 * @param vo
 * @return
 */
List<User> findByVo(QueryVo vo); 
  1. 持久層接口的映射文件

    <!--根據(jù)QueryVo中的條件查詢用戶-->
    <select id="findByVo" parameterType="org.example.domain.QueryVo" resultType="org.example.domain.User">
        select * 
        from user 
        where username like concat('%',#{user.username},'%')
    </select>
    
    1. 測(cè)試包裝類作為參數(shù)
    /**
     * 測(cè)試根據(jù)QueryVo中的條件查詢用戶
     */
    @Test
    public void testFindByVo(){
        QueryVo vo = new QueryVo();
        User user = new User();
        user.setUsername("王");
        vo.setUser(user);
        List<User> list = userDao.findByVo(vo);
        for (User u: list) {
            System.out.println(u);
        }
    }
    

2.2.3 resultType 配置結(jié)果類型

? resultType 屬性可以指定結(jié)果集的類型,它支持基本類型和實(shí)體類類型旨指。
我們?cè)谇懊娴?CRUD 案例中已經(jīng)對(duì)此屬性進(jìn)行過(guò)應(yīng)用了。
需要注意的是,它和 parameterType 一樣,如果注冊(cè)過(guò)類型別名的,可以直接使用別名喳整。沒有注冊(cè)過(guò)的必須
使用全限定類名谆构。例如:我們的實(shí)體類此時(shí)必須是全限定類名(今天最后一個(gè)章節(jié)會(huì)講解如何配置實(shí)體類的別名)
同時(shí),當(dāng)是實(shí)體類名稱是,還有一個(gè)要求,實(shí)體類中的屬性名稱必須和查詢語(yǔ)句中的列名保持一致,否則無(wú)法實(shí)現(xiàn)封裝。

2.2.3.1 基本類型示例

  1. Dao 接口
// 查詢總記錄條數(shù)
int findTotal();
  1. 映射配置

  2. <!-- 查詢總記錄條數(shù) -->
    <select id="findTotal" resultType="int">
      select count(*) from user;
    </select>
    

2.2.3.2 實(shí)體類類型示例

  1. Dao 接口
/**
 * 查詢所有用戶操作
 * @return
 */
List<User> findAll();
  1. 映射配置
<!-- 配置查詢所有操作 -->
<select id="findAll" resultType="org.example.domain.User">
    select * from user
</select>

2.2.4 resultMap 結(jié)果類型

? resultMap 標(biāo)簽可以建立查詢的列名和實(shí)體類的屬性名稱不一致時(shí)建立對(duì)應(yīng)關(guān)系框都。從而實(shí)現(xiàn)封裝搬素。
select 標(biāo)簽中使用 resultMap 屬性指定引用即可。同時(shí) resultMap可以實(shí)現(xiàn)將查詢結(jié)果映射為復(fù)雜類型的pojo,比如在查詢結(jié)果映射對(duì)象中包括 pojolist 實(shí)現(xiàn)一對(duì)一查詢和一對(duì)多查詢。

1.定義 resultMap

<!-- 建立 User 實(shí)體和數(shù)據(jù)庫(kù)表的對(duì)應(yīng)關(guān)系
    type 屬性:指定實(shí)體類的全限定類名
    id 屬性:給定一個(gè)唯一標(biāo)識(shí),是給查詢 select 標(biāo)簽引用用的熬尺。
-->
<resultMap type="org.example.domain.User" id="userMap">
    <id column="id" property="userId"/>
    <result column="username" property="userName"/>
    <result column="sex" property="userSex"/>
    <result column="address" property="userAddress"/>
    <result column="birthday" property="userBirthday"/>
</resultMap>
id 標(biāo)簽:用于指定主鍵字段
result 標(biāo)簽:用于指定非主鍵字段
column 屬性:用于指定數(shù)據(jù)庫(kù)列名
property 屬性:用于指定實(shí)體類屬性名稱 
  1. 映射配置

    <select id="findAll" resultMap="userMap">
     select * from user
    </select>    
    

2.3 SqlMapConfig.xml配置文件

2.3.1 配置內(nèi)容

-properties(屬性)
    --property
-settings(全局配置參數(shù))
    --setting
-typeAliases(類型別名)
    --typeAliase
    --package
-typeHandlers(類型處理器)
-objectFactory(對(duì)象工廠)
-plugins(插件)
-environments(環(huán)境集合屬性對(duì)象)
    --environment(環(huán)境子屬性對(duì)象)
        ---transactionManager(事務(wù)管理)
        ---dataSource(數(shù)據(jù)源)
-mappers(映射器)
    --mapper
    --package

2.3.2 properties(屬性)

? 在使用 properties 標(biāo)簽配置時(shí),我們可以采用兩種方式指定屬性配置摸屠。

第一種

<dataSource type="POOLED">
    <!-- 配置連接數(shù)據(jù)庫(kù)的4個(gè)基本信息-->
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/easy?useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</dataSource>

第二種

  1. classpath 下定義 db.properties文件

    properties 標(biāo)簽設(shè)置 resource="jdbcConfig.properties" 可以直接使用

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=1234

<properties resource="jdbcConfig.properties">
</properties>

<environments default="mysql">
        <!--配置mysql的環(huán)境-->
        <environment id="mysql">
            <!-- 配置事務(wù)類型-->
            <transactionManager type="JDBC"/>
            <!-- 配置數(shù)據(jù)源(連接池)-->
            <dataSource type="POOLED">
                <!-- 配置連接數(shù)據(jù)庫(kù)的4個(gè)基本信息-->
                <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>

  1. properties標(biāo)簽配置
<!-- 配置連接數(shù)據(jù)庫(kù)的信息
resource 屬性:用于指定 properties 配置文件的位置,要求配置文件必須在類路徑下
resource="jdbcConfig.properties"
url 屬性:
    URL: Uniform Resource Locator 統(tǒng)一資源定位符
    http://localhost:8080/mystroe/CategoryServlet

URI
    URI:Uniform Resource Identifier 統(tǒng)一資源標(biāo)識(shí)符
    /mystroe/CategoryServlet
    它是可以在 web 應(yīng)用中唯一定位一個(gè)資源的路徑
-->

<properties url=file:///D:/IdeaProjects/day02_eesy_01mybatisCRUD/src/main/resources/jdbcConfig.properties">
</properties>
  1. 此時(shí)我們的 dataSource 標(biāo)簽就變成了引用上面的配置

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

2.3.3 mappers(映射器)

  1. <mapper resource=" " />
使用相對(duì)于類路徑的資源
如:<mapper resource="com/itheima/dao/IUserDao.xml" />
  1. <mapper class=" " />
使用 mapper 接口類路徑
如:<mapper class="com.itheima.dao.UserDao"/>
注意:此種方法要求 mapper 接口名稱和 mapper 映射文件名稱相同,且放在同一個(gè)目錄中。
  1. <package name=""/>
注冊(cè)指定包下的所有 mapper 接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此種方法要求 mapper 接口名稱和 mapper 映射文件名稱相同,且放在同一個(gè)目錄中粱哼。

第三天:mybatis的深入和多表

3.1 Mybatis 連接池與事務(wù)深入

3.1.1 Mybatis 的連接池技術(shù)

? 我們?cè)谇懊娴?WEB 課程中也學(xué)習(xí)過(guò)類似的連接池技術(shù),而在 Mybatis 中也有連接池技術(shù),但是它采用的是自己的連接池技術(shù)季二。在 Mybatis 的 SqlMapConfig.xml 配置文件中,通過(guò)<dataSource type=”pooled”>來(lái)實(shí)現(xiàn) Mybatis 中連接池的配置。
Mybatis 連接池的分類

3.1.1.1 Mybatis 連接池的分類

在 Mybatis 中我們將它的數(shù)據(jù)源 dataSource 分為以下幾類:

UNPOOLED 不使用連接池的數(shù)據(jù)源
POOLED 使用連接池的數(shù)據(jù)源
JNDI 使用 JNDI 實(shí)現(xiàn)的數(shù)據(jù)源

? 相應(yīng)地,MyBatis 內(nèi)部分別定義了實(shí)現(xiàn)了 java.sql.DataSource 接口的 UnpooledDataSource,
PooledDataSource 類來(lái)表示 UNPOOLED皂吮、POOLED 類型的數(shù)據(jù)源戒傻。

? 在這三種數(shù)據(jù)源中,我們一般采用的是 POOLED 數(shù)據(jù)源(很多時(shí)候我們所說(shuō)的數(shù)據(jù)源就是為了更好的管理數(shù)據(jù)庫(kù)連接,也就是我們所說(shuō)的連接池技術(shù))。

Mybatis 中數(shù)據(jù)源的配置

3.1.1.2 Mybatis 中數(shù)據(jù)源的配置

? 我們的數(shù)據(jù)源配置就是在 SqlMapConfig.xml文件中,具體配置如下:


<!-- 配置數(shù)據(jù)源(連接池)信息 -->
<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>

? MyBatis 在初始化時(shí),根據(jù)<dataSource>的 type 屬性來(lái)創(chuàng)建相應(yīng)類型的的數(shù)據(jù)源 DataSource,即:
type="POOLED": MyBatis 會(huì)創(chuàng)建 PooledDataSource 實(shí)例
type="UNPOOLED" : MyBatis 會(huì)創(chuàng)建 UnpooledDataSource 實(shí)例

type="JNDI": MyBatis 會(huì)從 JNDI 服務(wù)上查找 DataSource 實(shí)例,然后返回使用

3.1.1.3 Mybatis 中 DataSource的存取

? MyBatis 是 通 過(guò) 工 廠 模 式 來(lái) 創(chuàng) 建 數(shù) 據(jù) 源 DataSource 對(duì) 象 的 , MyBatis 定 義 了 抽 象 的 工 廠 接口 :org.apache.ibatis.datasource.DataSourceFactory,通過(guò)其 getDataSource()方法返回?cái)?shù)據(jù)源DataSource蜂筹。
下面是 DataSourceFactory 源碼,具體如下:

package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
    /**
     * @author Clinton Begin
     */
    public interface DataSourceFactory {
        void setProperties(Properties props);
        DataSource getDataSource();
    }

? MyBatis 創(chuàng)建了 DataSource 實(shí)例后,會(huì)將其放到 Configuration 對(duì)象內(nèi)的 Environment對(duì)象中, 供以后使用。

具體分析過(guò)程如下:

1.先進(jìn)入 XMLConfigBuilder類中,可以找到如下代碼:

image.png

2.分析 configuration對(duì)象的 environment屬性,結(jié)果如下:

image.png

3.1.1.4 Mybatis 中連接的獲取過(guò)程分析

? 當(dāng)我們需要?jiǎng)?chuàng)建 SqlSession 對(duì)象并需要執(zhí)行 SQL 語(yǔ)句時(shí),這時(shí)候 MyBatis 才會(huì)去調(diào)用 dataSource 對(duì)象來(lái)創(chuàng)建 java.sql.Connection對(duì)象芦倒。也就是說(shuō),java.sql.Connection對(duì)象的創(chuàng)建一直延遲到執(zhí)行 SQL 語(yǔ)句的時(shí)候艺挪。

@Test
public void testSql() throws Exception {
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession   = factory.openSession();
    List<User> list = sqlSession.selectList("findUserById",41);
    System.out.println(list.size());
}

? 只有當(dāng)?shù)?4 句 sqlSession.selectList("findUserById"),才會(huì)觸發(fā) MyBatis 在底層執(zhí)行下面這個(gè)方
法來(lái)創(chuàng)建 java.sql.Connection對(duì)象。
如何證明它的加載過(guò)程呢?
我們可以通過(guò)斷點(diǎn)調(diào)試,在 PooledDataSource 中找到如下 popConnection()方法,如下所示:

image.png

分析源代碼,得出 PooledDataSource 工作原理如下:

image.png

下面是連接獲取的源代碼:

image.png

? 最后我們可以發(fā)現(xiàn),真正連接打開的時(shí)間點(diǎn),只是在我們執(zhí)行 SQL 語(yǔ)句時(shí),才會(huì)進(jìn)行兵扬。其實(shí)這樣做我們也可以進(jìn)一步發(fā)現(xiàn),數(shù)據(jù)庫(kù)連接是我們最為寶貴的資源,只有在要用到的時(shí)候,才去獲取并打開連接,當(dāng)我們用完了就再立即將數(shù)據(jù)庫(kù)連接歸還到連接池中麻裳。

3.1.2 Mybatis 的事務(wù)控制

3.1.2.1 Mybatis 中手動(dòng)提交事務(wù)

? Mybatis 中事務(wù)的提交方式,本質(zhì)上就是調(diào)用 JDBC 的 setAutoCommit()來(lái)實(shí)現(xiàn)事務(wù)控制。
我們運(yùn)行之前所寫的代碼:

@Test
public void testSaveUser() throws Exception {
    User user = new User();
    user.setUsername("mybatis user09");
    //6.執(zhí)行操作
    int res = userDao.saveUser(user);
    System.out.println(res);
    System.out.println(user.getId());
}
@Before//在測(cè)試方法執(zhí)行之前執(zhí)行
public void init()throws Exception {
    //1.讀取配置文件
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.創(chuàng)建構(gòu)建者對(duì)象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //3.創(chuàng)建 SqlSession 工廠對(duì)象
    factory = builder.build(in);
    //4.創(chuàng)建 SqlSession 對(duì)象
    session = factory.openSession();
    //5.創(chuàng)建 Dao 的代理對(duì)象
    userDao = session.getMapper(IUserDao.class);
}
@After//在測(cè)試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
    //7.提交事務(wù)
    session.commit();
    //8.釋放資源
    session.close();
    in.close();
}

? 這是我們的Connection 的整個(gè)變化過(guò)程,通過(guò)分析我們能夠發(fā)現(xiàn)之前的 CUD 操作過(guò)程中,我們都要手動(dòng)進(jìn)行事務(wù)的提交,原因是 setAutoCommit()方法,在執(zhí)行時(shí)它的值被設(shè)置為 false 了,所以我們?cè)?CUD 操作中,必須通過(guò) sqlSession.commit()方法來(lái)執(zhí)行提交操作

3.1.2.2 Mybatis 自動(dòng)提交事務(wù)的設(shè)置

? 通過(guò)上面的研究和分析,現(xiàn)在我們一起思考,為什么 CUD 過(guò)程中必須使用 sqlSession.commit()提交事務(wù)?主要原因就是在連接池中取出的連接,都會(huì)將調(diào)用 connection.setAutoCommit(false)方法,這樣我們就必須使用 sqlSession.commit()方法,相當(dāng)于使用了 JDBC 中的 connection.commit()方法實(shí)現(xiàn)事務(wù)提交器钟。
明白這一點(diǎn)后,我們現(xiàn)在一起嘗試不進(jìn)行手動(dòng)提交,一樣實(shí)現(xiàn) CUD 操作津坑。

@Before//在測(cè)試方法執(zhí)行之前執(zhí)行
public void init()throws Exception {
    //1.讀取配置文件
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.創(chuàng)建構(gòu)建者對(duì)象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //3.創(chuàng)建 SqlSession 工廠對(duì)象
    factory = builder.build(in);
    //4.創(chuàng)建 SqlSession 對(duì)象
    session = factory.openSession(true);
    //5.創(chuàng)建 Dao 的代理對(duì)象
    userDao = session.getMapper(IUserDao.class);
}
@After//在測(cè)試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
    //7.釋放資源
    session.close();
    in.close();
}

? 我們發(fā)現(xiàn),此時(shí)事務(wù)就設(shè)置為自動(dòng)提交了,同樣可以實(shí)現(xiàn) CUD 操作時(shí)記錄的保存。雖然這也是一種方式,但就編程而言,設(shè)置為自動(dòng)提交方式為 false 再根據(jù)情況決定是否進(jìn)行提交,這種方式更常用傲霸。因?yàn)槲覀兛梢愿鶕?jù)業(yè)務(wù)情況來(lái)決定提交是否進(jìn)行提交疆瑰。

3.2 Mybatis 的動(dòng)態(tài) SQL 語(yǔ)句

3.2.1 動(dòng)態(tài) SQL 之<if>標(biāo)簽

? 我們根據(jù)實(shí)體類的不同取值,使用不同的 SQL 語(yǔ)句來(lái)進(jìn)行查詢。比如在 id 如果不為空時(shí)可以根據(jù) id 查詢,如果 username 不同空時(shí)還要加入用戶名作為條件昙啄。這種情況在我們的多條件組合查詢中經(jīng)常會(huì)碰到穆役。

  1. 持久層 Dao 接口
/**
 * 根據(jù)傳入?yún)?shù)條件
 * @param user 查詢的條件,有可能有用戶名,有可能有性別,也有可能有地址,還有可能是都有
 * @return
 */
List<User> findUserByCondition(User user);
  1. 持久層 Dao 映射配置
<!--根據(jù)條件查詢-->
<select id="findUserByCondition" resultType="org.example.domain.User" parameterType="org.example.domain.User">
    select * from user
    where 1=1
    <if test="username != null and username != '' ">
        and username like concat('%',#{username},'%')
    </if>
    <if test="address != null and address != '' ">
        and address like concat('%',#{address},'%')
    </if>
</select>

? 注意:<if>標(biāo)簽的 test 屬性中寫的是對(duì)象的屬性名,如果是包裝類的對(duì)象要使用 OGNL 表達(dá)式的寫法。
另外要注意 where 1=1 的作用~!

  1. 測(cè)試

    @Test
    public void  testFindUserByCondition(){
        User user = new User();
        user.setUsername("王");
        List<User> list = userDao.findUserByCondition(user);
        for (User u: list) {
            System.out.println(u);
        }
    }
    

3.2.2 動(dòng)態(tài) SQL 之<where>標(biāo)簽

為了簡(jiǎn)化上面 where 1=1 的條件拼裝,我們可以采用<where>標(biāo)簽來(lái)簡(jiǎn)化開發(fā)梳凛。

持久層 Dao 映射配置

<!--根據(jù)條件查詢-->
<select id="findUserByCondition" resultType="org.example.domain.User" parameterType="org.example.domain.User">
    select * from user
    <where>
        <if test="username != null and username != '' ">
            and username like concat('%',#{username},'%')
        </if>
        <if test="address != null and address != '' ">
            and address like concat('%',#{address},'%')
        </if>
    </where>
</select>

3.2.3 動(dòng)態(tài)標(biāo)簽之<foreach>標(biāo)簽

? 傳入多個(gè) id 查詢用戶信息,用下邊兩個(gè) sql 實(shí)現(xiàn):
SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%張%' AND id IN (10,89,16)

? 這樣我們?cè)谶M(jìn)行范圍查詢時(shí),就要將一個(gè)集合中的值,作為參數(shù)動(dòng)態(tài)添加進(jìn)來(lái)耿币。
這樣我們將如何進(jìn)行參數(shù)的傳遞?

  1. 在 QueryVo 中加入一個(gè) List 集合用于封裝參數(shù)
public class QueryVo implements Serializable {

    private User user;

    private List<Integer> ids;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}
  1. 持久層 Dao 接口

    /**
     * 根據(jù)queryVo提供的id集合,查詢用戶信息
     * @param vo
     * @return
     */
    List<User> findUserInIds(QueryVo vo);
    
  2. 持久層 Dao 映射配置

<!--根據(jù)queryVo提供的id集合,查詢用戶信息-->
<select id="findUserInIds" resultType="org.example.domain.User" parameterType="org.example.domain.QueryVo">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>

</select>

SQL 語(yǔ)句:
select 字段 from user where id in (?)
<foreach> 標(biāo)簽用于遍歷集合,它的屬性:
collection: 代表要遍歷的集合元素,注意編寫時(shí)不要寫#{}
open: 代表語(yǔ)句的開始部分
close: 代表結(jié)束部分
item="id" item: 代表遍歷集合的每個(gè)元素,生成的變量名
sperator: 代表分隔符

  1. 編寫測(cè)試方法

    /**
     * foreach 標(biāo)簽的使用
     */
    @Test
    public void  testFindUserInIds(){
        QueryVo vo = new QueryVo();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(41);
        ids.add(42);
        ids.add(51);
        vo.setIds(ids);
    
        List<User> list = userDao.findUserInIds(vo);
        for (User u: list) {
            System.out.println(u);
        }
    }
    

3.2.4 Mybatis 中簡(jiǎn)化編寫的 SQL 片段

? Sql 中可將重復(fù)的 sql提取出來(lái),使用時(shí)用 include引用即可,最終達(dá)到 sql 重用的目的。

  1. 定義代碼片段

    ?

    <sql id="defaultUser">
        select * from user
    </sql>
    
  1. 引用代碼片段

        <!-- 配置查詢所有操作 -->
        <select id="findAll" resultType="org.example.domain.User">
            <include refid="defaultUser"></include>
    <!--        select * from user-->
        </select>
    

3.3 Mybatis 多表查詢之一對(duì)多

3.3.1 一對(duì)一查詢(多對(duì)一)

? 使用 resultMap,定義專門的 resultMap 用于映射一對(duì)一查詢結(jié)果韧拒。
通過(guò)面向?qū)ο蟮?has a)關(guān)系可以得知,我們可以在 Account 類中加入一個(gè) User 類的對(duì)象來(lái)代表這個(gè)賬戶是哪個(gè)用戶的淹接。

1. 定義賬戶信息的實(shí)體類

/**
 * 賬號(hào)實(shí)體類
 */
public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //從表實(shí)體應(yīng)該包含一個(gè)主體實(shí)體的對(duì)象引用
    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 +
                ", user=" + user +
                '}';
    }
}

2. 編寫 Sql 語(yǔ)句

select b.*,a.id as aid,a.uid,a.money from  account a, user b where a.uid = b.id;

3. 定義賬戶的持久層 Dao 接口

/**
 * 查詢所有賬號(hào),同時(shí)還要獲取到當(dāng)前賬號(hào)的所屬用戶信息
 * @return
 */
List<Account> findAll();

4. 定義 AccountDao.xml 文件中的查詢配置信息

<!-- 定義account和user的resultMap-->
    <resultMap id="accountUserMap" type="org.example.domain.Account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!-- 一對(duì)一的關(guān)系映射: 配置封裝user的內(nèi)容 -->
        <association property="user" column="uid" javaType="org.example.domain.User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>

    <!-- 查詢所有賬號(hào)-->
    <select id="findAll" resultMap="accountUserMap">
         select b.*,a.id as aid,a.uid,a.money from  account a, user b where a.uid = b.id;

    </select>

5. 測(cè)試方法

/**
 * 查詢所有賬號(hào)
 */
@Test
public void testFindAll()  {
    //6.使用代理對(duì)象執(zhí)行查詢所有方法
    List<Account> accounts = accountDao.findAll();
    for(Account account : accounts) {
        System.out.println(account);
        //System.out.println(account.getUser());
    }
}

3.3.2 一對(duì)多查詢

? 需求:
查詢所有用戶信息及用戶關(guān)聯(lián)的賬戶信息。
分析:
用戶信息和他的賬戶信息為一對(duì)多關(guān)系,并且查詢過(guò)程中如果用戶沒有賬戶信息,此時(shí)也要將用戶信息
查詢出來(lái),我們想到了左外連接查詢比較合適叛溢。

1 . 編寫 SQL 語(yǔ)句

SELECT
 u.*,
 a.ID as aid,
 a.MONEY
FROM
 user u
LEFT JOIN account a on
 u.id = a.UID

2 . User 類加入 List<Account>

/**
 * 用戶的實(shí)體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> 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;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

3. 用戶持久層 Dao 接口中加入查詢方法

/**
 * 查詢所有用戶操作
 * @return
 */
List<User> findAll();

4. 用戶持久層 Dao 映射文件配置

<!-- 定義User的resultMap -->
    <resultMap id="userAccountMap" type="org.example.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <result property="address" column="address"></result>
        <!--配置user對(duì)象中國(guó)accounts集合映射-->
        <collection property="accounts" ofType="org.example.domain.Account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>

    </resultMap>

    <!-- 配置查詢所有操作 -->
    <select id="findAll" resultMap="userAccountMap">
        SELECT
            u.*,
            a.ID as aid,
            a.MONEY
        FROM
            user u
        LEFT JOIN account a on
            u.id = a.UID    
    </select>

5. 測(cè)試方法

@Test
    public void testFindAll()  {
        //6.使用代理對(duì)象執(zhí)行查詢所有方法
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println("---------------------------------");
            System.out.println(user);
            for (Account account : user.getAccounts()){
                System.out.println(account);
            }
        }
    }

3.4 Mybatis 多表查詢之多對(duì)多

3.4.1 實(shí)現(xiàn) Role 到 User 多對(duì)多

通過(guò)前面的學(xué)習(xí),我們使用 Mybatis 實(shí)現(xiàn)一對(duì)多關(guān)系的維護(hù)塑悼。多對(duì)多關(guān)系其實(shí)我們看成是雙向的一對(duì)多關(guān)系。

1 業(yè)務(wù)要求及實(shí)現(xiàn) SQL需求:

實(shí)現(xiàn)查詢所有對(duì)象并且加載它所分配的用戶信息雇初。
    分析:
    查詢角色我們需要用到 Role 表,但角色分配的用戶的信息我們并不能直接找到用戶信息,而是要通過(guò)中間表(USER_ROLE 表)才能關(guān)聯(lián)到用戶信息拢肆。
   下面是實(shí)現(xiàn)的 SQL 語(yǔ)句:
SELECT
 r.id as rid,
 r.role_name,
 r.role_desc,
 u.id ,
 u.username,
 u.birthday,
 u.sex,
 u.address
FROM
`role` r
LEFT JOIN user_role ur ON
r.ID = ur.RID
LEFT JOIN `user` u ON u.id = ur.UID

2 編寫角色實(shí)體類

public class Role  implements Serializable {

    private Integer roleId;

    private String roleName;

    private String roleDesc;

    // 多對(duì)多的關(guān)系映射: 一個(gè)角色可以賦予多個(gè)用戶
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

3 編寫 Role 持久層接口

    /**
     * 查詢所有角色
     * @return
     */
    List<Role> findAll();
}

4 編寫映射文件

<!--定義Role表的ResultMap-->
<resultMap id="roleMap" type="org.example.domain.Role">
    <id property="roleId" column="rid"></id>
    <result property="roleName" column="ROLE_NAME"></result>
    <result property="roleDesc" column="ROLE_DESC"></result>
    <collection property="users" ofType="org.example.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

    </collection>
</resultMap>

<!-- 查詢所有角色-->
<select id="findAll" resultMap="roleMap">
    SELECT
        r.id as rid,
        r.role_name,
        r.role_desc,
        u.id ,
        u.username,
        u.birthday,
        u.sex,
        u.address
    FROM
    `role` r
    LEFT JOIN user_role ur ON
    r.ID = ur.RID
    LEFT JOIN `user` u ON u.id = ur.UID
</select>

5 編寫測(cè)試類

public class RoleTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private RoleDao roleDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
        roleDao = sqlSession.getMapper(RoleDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    /**
     * 查詢所有賬號(hào)
     */
    @Test
    public void testFindAll()  {
        //6.使用代理對(duì)象執(zhí)行查詢所有方法
        List<Role> roles = roleDao.findAll();
        for(Role role : roles) {
            System.out.println(role);
            System.out.println(role.getUsers());
        }
    }
}

12

3.4.2 實(shí)現(xiàn) User 到 Role 的多對(duì)多

? 從 User 出發(fā),我們也可以發(fā)現(xiàn)一個(gè)用戶可以具有多個(gè)角色,這樣用戶到角色的關(guān)系也還是一對(duì)多關(guān)系。這樣我們就可以認(rèn)為 User 與 Role 的多對(duì)多關(guān)系,可以被拆解成兩個(gè)一對(duì)多關(guān)系來(lái)實(shí)現(xiàn)。

1 業(yè)務(wù)要求及實(shí)現(xiàn) SQL需求:

SELECT
    u.id ,
    u.username,
    u.birthday,
    u.sex,
    u.address,
    r.id as rid,
    r.role_name,
    r.role_desc 
FROM
`user` u
LEFT JOIN user_role ur ON
u.ID = ur.UID
LEFT JOIN `role` r ON r.id = ur.RID

2 編寫角色實(shí)體類

/**
 * 用戶的實(shí)體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> accounts;
    // 多對(duì)多的關(guān)系映射: 一個(gè)用戶可以具有多個(gè)角色
    private List<Role> roles;

    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;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

3 編寫 Role 持久層接口

/**
 * 查詢所有用戶及權(quán)限信息
 * @return
 */
List<User> findAllRole();

4 編寫映射文件

<!-- 定義User權(quán)限的resultMap -->
<resultMap id="userRoleMap" type="org.example.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <result property="address" column="address"></result>
    <!--配置user對(duì)象中國(guó)accounts集合映射-->
    <collection property="roles" ofType="org.example.domain.Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
    </collection>

</resultMap>

<select id="findAllRole" resultMap="userRoleMap">
        SELECT
            u.id ,
            u.username,
            u.birthday,
            u.sex,
            u.address,
            r.id as rid,
            r.role_name,
            r.role_desc
        FROM
        `user` u
        LEFT JOIN user_role ur ON
        u.ID = ur.UID
        LEFT JOIN `role` r ON r.id = ur.RID
    </select>

5 編寫測(cè)試類

@Test
public void testFindAllRole(){
    List<User> users = userDao.findAllRole();
    for (User user : users){
        System.out.println(user);
        System.out.println(user.getRoles());
    }
}

第四天:mybatis的緩存和注解開發(fā)

4.1 Mybatis 延遲加載策略(查詢的時(shí)機(jī))

? 通過(guò)前面的學(xué)習(xí),我們已經(jīng)掌握了 Mybatis 中一對(duì)一,一對(duì)多,多對(duì)多關(guān)系的配置及實(shí)現(xiàn),可以實(shí)現(xiàn)對(duì)象的關(guān)聯(lián)查詢郭怪。實(shí)際開發(fā)過(guò)程中很多時(shí)候我們并不需要總是在加載用戶信息時(shí)就一定要加載他的賬戶信息支示。此時(shí)就是我們所說(shuō)的延遲加載。

4.1.1 為什么要用延遲加載策略

延遲加載:
就是在需要用到數(shù)據(jù)時(shí)才進(jìn)行加載,不需要用到數(shù)據(jù)時(shí)就不加載數(shù)據(jù)鄙才。延遲加載也稱懶加載.
好處:

? 先從單表查詢,需要時(shí)再?gòu)年P(guān)聯(lián)表去關(guān)聯(lián)查詢,大大提高數(shù)據(jù)庫(kù)性能,因?yàn)椴樵儐伪硪汝P(guān)聯(lián)查詢多張表速度要快颂鸿。
壞處:
因?yàn)橹挥挟?dāng)需要用到數(shù)據(jù)時(shí),才會(huì)進(jìn)行數(shù)據(jù)庫(kù)查詢,這樣在大批量數(shù)據(jù)查詢時(shí),因?yàn)椴樵児ぷ饕惨臅r(shí)間,所以可能造成用戶等待時(shí)間變長(zhǎng),造成用戶體驗(yàn)下降。

實(shí)現(xiàn)需求

需求:
查詢賬戶(Account)信息并且關(guān)聯(lián)查詢用戶(User)信息攒庵。如果先查詢賬戶(Account)信息即可滿足要求,當(dāng)我們需要查詢用戶(User)信息時(shí)再查詢用戶(User)信息嘴纺。把對(duì)用戶(User)信息的按需去查詢就是延遲加載。
mybatis 第三天實(shí)現(xiàn)多表操作時(shí),我們使用了 resultMap 來(lái)實(shí)現(xiàn)一對(duì)一,一對(duì)多,多對(duì)多關(guān)系的操作浓冒。主要是通過(guò) association栽渴、collection 實(shí)現(xiàn)一對(duì)一及一對(duì)多映射。association稳懒、collection 具備延遲加載功能闲擦。

實(shí)際使用場(chǎng)景

在對(duì)應(yīng)的四種表關(guān)系中:一對(duì)多,一對(duì)一场梆,多對(duì)多
    一對(duì)多墅冷,多對(duì)多:通常情況下我們都是采用延遲加載。
    一對(duì)一:通常情況下我們都是采用立即加載或油。

4.1.2 使用 assocation 實(shí)現(xiàn)延遲加載(一對(duì)一)

? 需求:
查詢賬戶信息同時(shí)查詢用戶信息寞忿。

1. 編寫賬戶實(shí)體類

/**
 * 賬號(hào)實(shí)體類
 */
public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //從表實(shí)體應(yīng)該包含一個(gè)主體實(shí)體的對(duì)象引用
    private User user;

// getter + setter 省略

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

2.賬戶的持久層 DAO 接口

public interface AccountDao {

    /**
     * 查詢所有賬號(hào),同時(shí)還要獲取到當(dāng)前賬號(hào)的所屬用戶信息
     * @return
     */
    List<Account> findAll();
}

3. 賬戶的持久層映射文件

    <!-- 定義account和user的resultMap-->
    <resultMap id="accountUserMap" type="org.example.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一對(duì)一的關(guān)系映射: 配置封裝user的內(nèi)容  配置延遲加載-->
        <association property="user" column="uid" javaType="org.example.domain.User" select="org.example.dao.UserDao.findById">
        </association>

    </resultMap>

    <!-- 查詢所有賬號(hào)-->
    <select id="findAll" resultMap="accountUserMap">
            select * from account
    </select>

select: 填寫我們要調(diào)用的 select 映射的 id
column : 填寫我們要傳遞給 select 映射的參數(shù)

4. 用戶的持久層接口和映射文件

/**
 * 根據(jù)Id查詢用戶
 */
User findById(Integer id);

    <!-- 根據(jù)Id查詢用戶-->
    <select id="findById" parameterType="java.lang.Integer" resultType="org.example.domain.User">
    select * from  user where id = #{id}
    </select>

5. 編寫測(cè)試只查賬戶信息不查用戶信息。

public class AccountTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private AccountDao accountDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
            accountDao = sqlSession.getMapper(AccountDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    /**
     * 查詢所有賬號(hào)
     */
    @Test
    public void testFindAll()  {
        List<Account> accounts = accountDao.findAll();
        for(Account account : accounts) {
           // System.out.println(account);
        }
    }
 
}

6 測(cè)試結(jié)果

DEBUG example.dao.AccountDao.findAll  - ==>  Preparing: select * from account 
DEBUG example.dao.AccountDao.findAll  - ==> Parameters: 
DEBUG example.dao.AccountDao.findAll  - <==      Total: 3
DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG source.pooled.PooledDataSource  - Returned connection 1629911510 to pool.

? 我們發(fā)現(xiàn),因?yàn)楸敬沃皇菍?Account 對(duì)象查詢出來(lái)放入 List 集合中,并沒有涉及到 User 對(duì)象,所以就沒有發(fā)出 SQL 語(yǔ)句查詢賬戶所關(guān)聯(lián)的 User 對(duì)象的查詢顶岸。

4.1.3 使用 Collection 實(shí)現(xiàn)延遲加載(一對(duì)多)

同樣我們也可以在一對(duì)多關(guān)系配置的<collection>結(jié)點(diǎn)中配置延遲加載策略腔彰。
<collection>結(jié)點(diǎn)中也有 select 屬性,column 屬性。
需求:
完成加載用戶對(duì)象時(shí),查詢?cè)撚脩羲鶕碛械馁~戶信息

1. 在 User 實(shí)體類中加入List<Account>屬性

/**
 * 用戶的實(shí)體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> accounts;
    // 多對(duì)多的關(guān)系映射: 一個(gè)用戶可以具有多個(gè)角色
    private List<Role> roles;

// getter setter 省略

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

2. 編寫用戶和賬戶持久層接口的方法

/**
 * 查詢所有用戶操作
 * @return
 */
List<User> findAll();

/**
 *查詢所有賬戶 根據(jù)用戶id 
 * @return
 */
List<Account> findAllByUid(Integer uid);

3. 編寫用戶持久層映射配置

<!-- 定義User賬號(hào)的resultMap -->
<resultMap id="userMap" type="org.example.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <result property="address" column="address"></result>
   
   <!--配置user對(duì)象中國(guó)accounts集合映射  延遲加載-->
    <collection property="accounts" column="id" ofType="org.example.domain.Account" select="org.example.dao.AccountDao.findAllByUid">

    </collection>

</resultMap>

<!-- 配置查詢所有操作 -->
<select id="findAll" resultMap="userMap">
    select * from user
</select>

<collection>標(biāo)簽:
        主要用于加載關(guān)聯(lián)的集合對(duì)象
select 屬性:
        用于指定查詢 account 列表的 sql 語(yǔ)句,所以填寫的是該 sql 映射的 id
column 屬性:
        用于指定 select 屬性的 sql 語(yǔ)句的參數(shù)來(lái)源,上面的參數(shù)來(lái)自于 user 的 id 列,所以就寫成 id 這一個(gè)字段名了

4. 編寫賬戶持久層映射配置

<select id="findAllByUid" resultType="org.example.domain.Account">
    select * from account where uid= #{uid}
</select>

5. 測(cè)試只加載用戶信息

 package org.example.test;


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.example.dao.UserDao;
import org.example.domain.Account;
import org.example.domain.QueryVo;
import org.example.domain.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import sun.nio.ch.Net;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 *  mybatis 入門案例
 */
public class UserTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private  UserDao userDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
            userDao = sqlSession.getMapper(UserDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }


    @Test
    public void testFindAll()  {
        //6.使用代理對(duì)象執(zhí)行查詢所有方法
        List<User> users = userDao.findAll();
    }

}

6 測(cè)試結(jié)果:

測(cè)試結(jié)果如下:

DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
DEBUG source.pooled.PooledDataSource  - Created connection 1629911510.
DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG rg.example.dao.UserDao.findAll  - ==>  Preparing: select * from user 
DEBUG rg.example.dao.UserDao.findAll  - ==> Parameters: 
DEBUG rg.example.dao.UserDao.findAll  - <==      Total: 10
DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG source.pooled.PooledDataSource  - Returned connection 1629911510 to pool.

我們發(fā)現(xiàn)并沒有加載 Account 賬戶信息蜕琴。

4.2 mybatis中的一級(jí)緩存和二級(jí)緩存

4.2.1 Mybatis 一級(jí)緩存

4.2.1.1證明一級(jí)緩存的存在

? 一級(jí)緩存是 SqlSession級(jí)別的緩存,只要 SqlSession 沒有 flushclose,它就存在萍桌。

1. 編寫用戶持久層 Dao 接口
public interface UserDao {
    /**
     * 根據(jù) id 查詢
     * @param userId
     * @return
     */
    User findById(Integer userId);
}
2. 編寫用戶持久層映射文件
<?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.itheima.dao.IUserDao">
    <!-- 根據(jù) id 查詢 -->
    <select id="findById" resultType="UsEr" parameterType="int" useCache="true">
        select * from user where id = #{uid}
    </select>
</mapper>
3. 編寫測(cè)試方法
@Test
public void testFindById() {
    User user = userDao.findById(41);
    System.out.println("第一次查詢的用戶:"+user);
    User user2 = userDao.findById(41);
    System.out.println("第二次查詢用戶:"+user2);
    System.out.println(user == user2);
}
4. 測(cè)試結(jié)果
DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e720b71]
DEBUG g.example.dao.UserDao.findById  - ==>  Preparing: select * from user where id = ? 
DEBUG g.example.dao.UserDao.findById  - ==> Parameters: 49(Integer)
DEBUG g.example.dao.UserDao.findById  - <==      Total: 1
第一次查詢的用戶:org.example.domain.User@17d0685f
第二次查詢的用戶:org.example.domain.User@17d0685f
true

    我們可以發(fā)現(xiàn),雖然在上面的代碼中我們查詢了兩次,但最后只執(zhí)行了一次數(shù)據(jù)庫(kù)操作,這就是 Mybatis 提供給我們的一級(jí)緩存在起作用了。因?yàn)橐患?jí)緩存的存在,導(dǎo)致第二次查詢 id 為 41 的記錄時(shí),并沒有發(fā)出 sql 語(yǔ)句從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),而是從一級(jí)緩存中查詢凌简。

4.2.1.2 一級(jí)緩存的分析

? 一級(jí)緩存是 SqlSession 范圍的緩存,當(dāng)調(diào)用 SqlSession 的修改,添加,刪除,commit(),close()等方法時(shí),就會(huì)清空一級(jí)緩存上炎。
第一次發(fā)起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息,如果沒有,從數(shù)據(jù)庫(kù)查
詢用戶信息。
得到用戶信息,將用戶信息存儲(chǔ)到一級(jí)緩存中雏搂。
如果 sqlSession去執(zhí)行 commit 操作(執(zhí)行插入藕施、更新、刪除),清空 SqlSession 中的一級(jí)緩存,這樣做的目的為了讓緩存中存儲(chǔ)的是最新的信息,避免臟讀凸郑。
第二次發(fā)起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息,緩存中有,直接從緩存中獲取用戶信息裳食。

4.2.2 Mybatis 二級(jí)緩存

? 二級(jí)緩存是 mapper 映射級(jí)別的緩存,多個(gè) SqlSession去操作同一個(gè) Mapper 映射的 sql 語(yǔ)句,多個(gè)SqlSession 可以共用二級(jí)緩存,二級(jí)緩存是跨 SqlSession的。

? 首先開啟 mybatis 的二級(jí)緩存芙沥。
sqlSession1 去查詢用戶信息,查詢到用戶信息會(huì)將查詢數(shù)據(jù)存儲(chǔ)到二級(jí)緩存中诲祸。
如果 SqlSession3 去執(zhí)行相同 mapper 映射下 sql,執(zhí)行 commit 提交,將會(huì)清空該 mapper 映射下的二級(jí)緩存區(qū)域的數(shù)據(jù)浊吏。
sqlSession2 去查詢與 sqlSession1 相同的用戶信息,首先會(huì)去緩存中找是否存在數(shù)據(jù),如果存在直接從緩存中取出數(shù)據(jù)。

4.2.2.1 二級(jí)緩存的開啟與關(guān)閉

第一步:在 SqlMapConfig.xml文件開啟二級(jí)緩存
<settings>
    <!-- 開啟二級(jí)緩存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>
因?yàn)?cacheEnabled 的取值默認(rèn)就為 true,所以這一步可以省略不配置救氯。為 true 代表開啟二級(jí)緩存;為
false 代表不開啟二級(jí)緩存找田。
第二步:配置相關(guān)的Mapper 映射文件
<cache>標(biāo)簽表示當(dāng)前這個(gè) mapper 映射將使用二級(jí)緩存,區(qū)分的標(biāo)準(zhǔn)就看 mapper 的 namespace 值。
<?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.itheima.dao.UserDao">
    <!--開啟user支持二級(jí)緩存-->
    <cache/>
</mapper>
第三步:配置statement上面的useCache 屬性
<!-- 根據(jù) id 查詢 -->
<select id="findById" resultType="org.example.domain.User" parameterType="int" useCache="true">
    select * from user where id = #{uid}
</select>
將 UserDao.xml 映射文件中的<select>標(biāo)簽中設(shè)置 useCache=”true”代表當(dāng)前這個(gè) statement 要使用二級(jí)緩存,如果不使用二級(jí)緩存可以設(shè)置為 false着憨。
注意:針對(duì)每次查詢都需要最新的數(shù)據(jù) sql,要設(shè)置成 useCache=false,禁用二級(jí)緩存墩衙。

4.2.2.2 二級(jí)緩存開啟的注意事項(xiàng)

? 當(dāng)我們?cè)谑褂枚?jí)緩存時(shí),所緩存的類一定要實(shí)現(xiàn) java.io.Serializable 接口,這種就可以使用序列化方式來(lái)保存對(duì)象。

4.3 mybatis的注解開發(fā)

? 這幾年來(lái)注解開發(fā)越來(lái)越流行,Mybatis 也可以使用注解開發(fā)方式,這樣我們就可以減少編寫 Mapper 映射文件了甲抖。本次我們先圍繞一些基本的 CRUD 來(lái)學(xué)習(xí),再學(xué)習(xí)復(fù)雜映射關(guān)系及延遲加載漆改。

4.3.1 mybatis 的常用注解說(shuō)明

@Insert:實(shí)現(xiàn)新增
@Update: 實(shí)現(xiàn)更新
@Delete: 實(shí)現(xiàn)刪除
@Select: 實(shí)現(xiàn)查詢
@Result: 實(shí)現(xiàn)結(jié)果集封裝
@Results: 可以與@Result 一起使用,封裝多個(gè)結(jié)果集
@ResultMap: 實(shí)現(xiàn)引用@Results 定義的封裝
@One: 實(shí)現(xiàn)一對(duì)一結(jié)果集封裝
@Many: 實(shí)現(xiàn)一對(duì)多結(jié)果集封裝
@SelectProvider: 實(shí)現(xiàn)動(dòng)態(tài) SQL 映射
@CacheNamespace: 實(shí)現(xiàn)注解二級(jí)緩存的使用

4.3.2 使用 Mybatis 注解實(shí)現(xiàn)基本 CRUD

? 單表的 CRUD 操作是最基本的操作,前面我們的學(xué)習(xí)都是基于 Mybaits 的映射文件來(lái)實(shí)現(xiàn)的。

1. 編寫實(shí)體類

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> accounts;
    // 多對(duì)多的關(guān)系映射: 一個(gè)用戶可以具有多個(gè)角色
    private List<Role> roles;

    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;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

2. 使用注解方式開發(fā)持久層接口

public interface IUserDao {

    /**
     * 查詢所有用戶
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address")
    })
    List<User> findAll();

    /**
     * 保存用戶信息
     * @param user
     * @return
     */
    @Insert("insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})")
    int saveUer(User user);

    /**
     * 修改用戶信息
     * @param user
     * @return
     */
    @Update("update user set username = #{username},sex = #{sex},birthday=#{birthday},address = #{address} where id = #{id}")
    int updateUser(User user);

    /**
     * 根據(jù)ID刪除用戶
     * @param userId
     * @return
     */
    @Delete("delete from user where id = #{userId} ")
    int deleteUser(Integer userId);

    /**
     * 根據(jù)ID查詢用戶
     * @param userId
     * @return
     */
    @ResultMap("userMap")
    @Select("select * from user where id = #{userId} ")
    User findById(Integer userId);

    /**
     * 根據(jù)名稱模糊查詢用戶
     * @param username
     * @return
     */
    @ResultMap("userMap")
    @Select("select * from user where username like concat('%',#{username},'%')")
    List<User> findUserByName(String username);

    /**
     * 查詢總用戶數(shù)
     * @return
     */
    @Select("select count(*) from user")
    int findTotalUser();

}
通過(guò)注解方式,我們就不需要再去編寫 UserDao.xml 映射文件了准谚。

3. 編寫測(cè)試方法

public class IUserTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
        //1.讀取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
        SqlSessionFactory factory = builder.build(in);

        //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
        sqlSession = factory.openSession();

        //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
        userDao = sqlSession.getMapper(IUserDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    /**
     * 查詢所有賬號(hào)
     */
    @Test
    public void testFindAll()  {
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }

    }

    @Test
    public void testSaveUser(){
        User user = new User();
        user.setUsername("唐老鴨");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("唐寧街10號(hào)");
        int i = userDao.saveUer(user);
        System.out.println("受影響行數(shù)-> " + i);
    }

    @Test
    public void testUpdateUser(){
        User user = new User();
        user.setId(54);
        user.setUsername("唐老鴨AAA");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("唐寧街10號(hào)");
        int i = userDao.updateUser(user);
        System.out.println("受影響行數(shù)-> " + i);
    }

    @Test
    public void testDeleteUser(){
        int i = userDao.deleteUser(56);
        System.out.println("受影響行數(shù)-> " + i);
    }

    @Test
    public  void  testFindById(){
        User user = userDao.findById(57);
        System.out.println(user);
    }

    @Test
    public void testFindByName(){
        List<User> users = userDao.findUserByName("唐");
        for(User user: users){
            System.out.println(user);
        }
    }

    @Test
    public void testFindTotla(){
        int total = userDao.findTotalUser();
        System.out.println("總用戶數(shù): " + total);
    }

}

4.3.3 使用注解實(shí)現(xiàn)復(fù)雜關(guān)系映射開發(fā)

? 實(shí)現(xiàn)復(fù)雜關(guān)系映射之前我們可以在映射文件中通過(guò)配置<resultMap>來(lái)實(shí)現(xiàn),在使用注解開發(fā)時(shí)我們需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解挫剑。

@Results 注解
代替的是標(biāo)簽 <resultMap>
該注解中可以使用單個(gè)@Result注解,也可以使用@Result集合 @Results({@Result(),@Result()})或@Results(@Result())
@Resutl 注解
代替了 <id>標(biāo)簽和<result>標(biāo)簽
@Result 中 屬性介紹:
**id** 是否是主鍵字段
**column** 數(shù)據(jù)庫(kù)的列名
**property** 需要裝配的屬性名
**one** 需要使用的@One 注解(@Result(one=@One()))

? **many** 需要使用的@Many 注解(@Result(many=@many()))
@One 注解(一對(duì)一)
代替了<assocation>標(biāo)簽,是多表查詢的關(guān)鍵,在注解中用來(lái)指定子查詢返回單一對(duì)象。
@One 注解屬性介紹:
**select**
指定用來(lái)多表查詢的 sqlmapper
**fetchType** 會(huì)覆蓋全局的配置參數(shù) lazyLoadingEnabled
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many 注解(多對(duì)一)
代替了<Collection>標(biāo)簽,是是多表查詢的關(guān)鍵,在注解中用來(lái)指定子查詢返回對(duì)象集合氛魁。
注意:聚集元素用來(lái)處理“一對(duì)多”的關(guān)系暮顺。需要指定映射的 Java 實(shí)體類的屬性,屬性的 javaType(一般為 ArrayList)但是注解中可以不定義;
使用格式:
@Result(property="",column="",many=@Many(select=""))

4.3.3.1使用注解實(shí)現(xiàn)一對(duì)一復(fù)雜關(guān)系映射及延遲加載

1 添加 User 實(shí)體類及 Account 實(shí)體類
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> accounts;


    // 省略getter,setter 和 toString 方法
    
    }
    
    public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //從表實(shí)體應(yīng)該包含一個(gè)主體實(shí)體的對(duì)象引用
    private User user;
    
    // 省略getter,setter 和 toString 方法
}
    
2. 添加賬戶的持久層接口并使用注解配置
public interface IAccountDao {

    /**
     * 查詢所有賬號(hào),并且獲取每個(gè)賬號(hào)的用戶信息
     * @return
     */
    @Select("select * from account")
    @Results(id = "accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "money",column = "money"),
            @Result(property = "user",column = "uid",one = @One(select = "org.example.dao.IUserDao.findById",fetchType = FetchType.LAZY))
    })
    List<Account> findAll();
}
@One:
相當(dāng)于<assocation>的配置
    select 屬性:代表將要執(zhí)行的 sql 語(yǔ)句
    fetchType 屬性:代表加載方式,一般如果要延遲加載都設(shè)置為 LAZY 的值
3 添加用戶的持久層接口并使用注解配置
public interface IUserDao {

    /**
     * 查詢所有用戶
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "accounts",column = "id",many = @Many(select = "org.example.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
    
        /**
     * 根據(jù)ID查詢用戶
     * @param userId
     * @return
     */
    @Select("select * from user where id = #{userId} ")
    User findById(Integer userId);
}
@Many:
相當(dāng)于<collection>的配置
    select 屬性:代表將要執(zhí)行的 sql 語(yǔ)句
    fetchType 屬性:代表加載方式,一般如果要延遲加載都設(shè)置為 LAZY 的值

4 測(cè)試一對(duì)一關(guān)聯(lián)及延遲加載
public class IAccountTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
            accountDao = sqlSession.getMapper(IAccountDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<Account> accounts = accountDao.findAll();
        for(Account account : accounts){
            System.out.println("--------每個(gè)賬號(hào)的信息--------");
            System.out.println(account);
            System.out.println("--------每個(gè)賬號(hào)所屬人員的信息--------");
            System.out.println(account.getUser());
        }
    }
}

4.3.3.2 使用注解實(shí)現(xiàn)一對(duì)多復(fù)雜關(guān)系映射

1 User 實(shí)體類加入List<Account>
/**
  * 用戶實(shí)體類
  */
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對(duì)多關(guān)系映射; 主表實(shí)體應(yīng)該包含從表實(shí)體的集合引用
    private List<Account> accounts;


    // 省略getter,setter 和 toString 方法
    
    }
    
/**
  * 賬戶實(shí)體類
  */
    public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //從表實(shí)體應(yīng)該包含一個(gè)主體實(shí)體的對(duì)象引用
    private User user;
    
    // 省略getter,setter 和 toString 方法
}
2 編寫用戶的持久層接口并使用注解配置
public interface IUserDao {

    /**
     * 查詢所有用戶
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "accounts",column = "id",many = @Many(select = "org.example.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}
@Many:
相當(dāng)于<collection>的配置
    select 屬性:代表將要執(zhí)行的 sql 語(yǔ)句
    fetchType 屬性:代表加載方式,一般如果要延遲加載都設(shè)置為 LAZY 的值
3 編寫賬戶的持久層接口并使用注解配置
public interface IAccountDao {
    @Select("select * from account where uid = #{userId}")
    List<Account> findAccountByUid(Integer userId);
}
4 添加測(cè)試方法
public class IUserTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IUserDao userDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
        //1.讀取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對(duì)象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.使用構(gòu)建者創(chuàng)建工廠對(duì)象 SqlSessionFactory
        SqlSessionFactory factory = builder.build(in);

        //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對(duì)象
        sqlSession = factory.openSession();

        //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對(duì)象
        userDao = sqlSession.getMapper(IUserDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    /**
     * 查詢所有賬號(hào)
     */
    @Test
    public void testFindAll()  {
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
            for(Account account : user.getAccounts()){
                System.out.println(account);
            }
        }

    }
}

4.3.4 mybatis 基于注解的二級(jí)緩存

1 在 SqlMapConfig中開啟二級(jí)緩存支持

<!-- 配置二級(jí)緩存 -->
<settings>
<!-- 開啟二級(jí)緩存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>

2 在持久層接口中使用注解配置二級(jí)緩存

/**
 * 用戶的持久層接口
 */
@CacheNamespace(blocking=true)  //mybatis 基于注解方式實(shí)現(xiàn)配置二級(jí)緩存
public interface IUserDao {}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秀存,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌羽氮,老刑警劉巖或链,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異档押,居然都是意外死亡澳盐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門令宿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)叼耙,“玉大人,你說(shuō)我怎么就攤上這事粒没∩竿瘢” “怎么了?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵癞松,是天一觀的道長(zhǎng)爽撒。 經(jīng)常有香客問我,道長(zhǎng)响蓉,這世上最難降的妖魔是什么硕勿? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮枫甲,結(jié)果婚禮上源武,老公的妹妹穿的比我還像新娘扼褪。我一直安慰自己,他們只是感情好粱栖,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布话浇。 她就那樣靜靜地躺著,像睡著了一般查排。 火紅的嫁衣襯著肌膚如雪凳枝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天跋核,我揣著相機(jī)與錄音岖瑰,去河邊找鬼。 笑死砂代,一個(gè)胖子當(dāng)著我的面吹牛蹋订,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刻伊,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼露戒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捶箱?” 一聲冷哼從身側(cè)響起智什,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丁屎,沒想到半個(gè)月后荠锭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晨川,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年证九,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片共虑。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愧怜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妈拌,到底是詐尸還是另有隱情拥坛,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布供炎,位于F島的核電站渴逻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏音诫。R本人自食惡果不足惜惨奕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竭钝。 院中可真熱鬧梨撞,春花似錦雹洗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至港粱,卻和暖如春螃成,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背查坪。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工寸宏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人偿曙。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓氮凝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親望忆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罩阵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容