框架應用:Mybatis - 開發詳述

ORM框架

  在實際開發中,工程中本質的任務是從數據庫中獲取數據,而後對數據進行操做,又或者寫入數據.開發時語言是大可能是面向對象的工程語言,這個時候就必須進行工程語言和數據庫鏈接語言的轉換,也就是所謂的ORM對象關係映射.html

  那麼須要使用到鏈接數據庫的技術,如Java的JDBC,或者其餘語言的ODBC等.然而,這種技術雖然能很好地與數據庫進行互動,可是有個問題是每一次訪問動做都必須保證鏈接時間較短(避免佔用資源),本身進行鏈接的開閉,SQL語句的拼寫.....java

  實在是太繁瑣了,嚴重影響工程開發進度以及編碼者的心情,尤爲是第二點,形成很惡劣的影響.因此,有人就編寫出了ORM框架來幫你進行兩種語言半自動轉換,代價是你須要寫一些配置文件.mysql

  ORM框架基本思想類似:git

  1.從配置文件中獲得sessionFactory;github

  2.由sessionFactory產生session;redis

  3.在session中完成CRUD和事務提交等操做;spring

  4.關閉session;sql

Mybatis是什麼?數據庫

  mybatis是一個持久層框架,apache基金會熱門項目之一.apache

  Mybatis是java這種工程語言用到的一種ORM框架,它能作的事本質上就是幫你處理兩種語言之間的轉換.

  mybatis能夠將向prepareStatement中的輸入參數自動進行輸入映射,將查詢結果靈活映射成java對象(輸出映射).

  充當以前jdbc鏈接的持久層(dao)的功能.

標準dao編程回顧

  1.建立數據庫

/*
SQLyog v10.2 
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `items` */

CREATE TABLE `items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '商品名稱',
  `price` float(10,1) NOT NULL COMMENT '商品訂價',
  `detail` text COMMENT '商品描述',
  `pic` varchar(64) DEFAULT NULL COMMENT '商品圖片',
  `createtime` datetime NOT NULL COMMENT '生產日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Table structure for table `orderdetail` */

CREATE TABLE `orderdetail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `orders_id` int(11) NOT NULL COMMENT '訂單id',
  `items_id` int(11) NOT NULL COMMENT '商品id',
  `items_num` int(11) DEFAULT NULL COMMENT '商品購買數量',
  PRIMARY KEY (`id`),
  KEY `FK_orderdetail_1` (`orders_id`),
  KEY `FK_orderdetail_2` (`items_id`),
  CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Table structure for table `orders` */

CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '下單用戶id',
  `number` varchar(32) NOT NULL COMMENT '訂單號',
  `createtime` datetime NOT NULL COMMENT '建立訂單時間',
  `note` varchar(100) DEFAULT NULL COMMENT '備註',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*Table structure for table `user` */

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

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

/*
SQLyog v10.2 
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Data for the table `items` */

insert  into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'臺式機',3000.0,'該電腦質量很是好!!!!',NULL,'2015-02-03 13:22:53'),(2,'筆記本',6000.0,'筆記本性能好,質量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'揹包',200.0,'名牌揹包,容量大質量好!!!!',NULL,'2015-02-06 13:23:02');

/*Data for the table `orderdetail` */

insert  into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);

/*Data for the table `orders` */

insert  into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);

/*Data for the table `user` */

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'張三','2014-07-10','1','北京市'),(16,'張小明',NULL,'1','河南鄭州'),(22,'陳小明',NULL,'1','河南鄭州'),(24,'張三丰',NULL,'1','河南鄭州'),(25,'陳小明',NULL,'1','河南鄭州'),(26,'王五',NULL,NULL,NULL);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
sql

  2.建立maven工程,pox.xml下引入mybatis,log4j,mysql,junit等依賴.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/jdbc/spring-cache.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.harry</groupId>
  <artifactId>harry-mybatis</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>harry-mybatis Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
  <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

  <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.3.11.RELEASE</version>
</dependency>

    <!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.40</version>
</dependency>

  </dependencies>
  <build>
    <finalName>harry-mybatis</finalName>
  </build>
</project>
pom.xml

  3.在resource中配置log4j.properties;

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.properties

  4.建立User.java(POJO)

package com.harry.entity;

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


@SuppressWarnings("serial")
public class User implements Serializable {
    
    //屬性名和數據庫表的字段對應
    private int id;
    private String username;// 用戶姓名
    private String sex;// 性別
    private Date birthday;// 生日
    private String address;// 地址
    
    //用戶建立的訂單列表
    private List<Orders> ordersList;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex
                + ", birthday=" + birthday + ", address=" + address + "]";
    }
    public List<Orders> getOrdersList() {
        return ordersList;
    }
    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }


}
User.java

  5.建立IDAO,IUserDAO繼承IDAO,UserDAOImpl實現IUSerDAO(數據庫鏈接開關應在service層)

  偷懶,此處參考軟件開發 : 軟件分層設計.

  這裏在mybatis中用以UserMapper.java和UserMapper.xml替代

  6.進行鏈接增刪改查測試.

Mybatis-Mapper規範

  Mybatis使用Mapper來取代DAO,其實二者做用是相同的,就是用來操做POJO.

  7. 建立UserMapper接口和UserMapper.xml(用戶輸入參數)

package com.harry.mapper;


import com.harry.entity.User;

public interface UserMapper {
    
    
        //根據id查詢用戶信息
    public User findUserById(int id) throws Exception;
    
    //根據用戶名列查詢用戶列表
    public List<User> findUserByName(String name)throws Exception;
    
    //插入用戶
    public void doInsertUser(User user)throws Exception;
    
    //刪除用戶
    public void doDeleteUser(int id)throws Exception;
    
    //更新用戶
        public void doUpdateUser(int id)throw Exception);
    

}
UserMapper.java
<?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.harry.mapper.UserMapper">

<select id="findUserById" parameterType="int" resultType="com.harry.entity.User">
        SELECT * FROM USER WHERE id=#{value}
    </select>

<select id="findUserByName" parameterType="java.lang.String" resultType="com.harry.entity.User">
        SELECT * FROM user WHERE username LIKE '%${value}%'
</select>

<insert id="insertUser" parameterType="com.harry,entiyUser">
        
                <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
        </selectKey>
        INSERT INTO user(id,username,birthday,sex,address) VALUE(#{id},#{username},#{birthday},#{sex},#{address}) 
</insert>        


<delete id="deleteUser" parameterType="java.lang.Integer">
        DELETE FROM user WHERE id=#{id}
</delete>

<update id="updateUser" parameterType="java.lang.Integer">
        UPDATE    user SET username = "Harry" WHERE id = #{id}
</update>
</mapper>
UserMapper.xml

  8.引入mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 加載屬性文件 -->
    <properties resource="db.properties">
        <!--properties中還能夠配置一些屬性名和屬性值  -->
        <!-- <property name="jdbc.driver" value=""/> -->
    </properties>
    <!-- 全局配置參數,須要時再設置 -->
    <!-- <settings>
    
    </settings> -->
    
    <!-- 別名定義 -->
    <typeAliases>
        
        <package name="com.harry.entity"/>
        
    </typeAliases>
    
    <!-- 和spring整合後 environments配置將廢除-->
    <environments default="development">
        <environment id="development">
        <!-- 使用jdbc事務管理,事務控制由mybatis-->
            <transactionManager type="JDBC" />
        <!-- 數據庫鏈接池,由mybatis管理-->
            <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.harry.mapper"/>

    </mappers>
    
</configuration>
sqlMapConfig.xml 

注意:配置中目錄結構以下和運行結果以下:

  

  

案例解析--標準DAO與Mybatis-Mapper

   

 

  1.用戶編寫sqlMapConfig.xml全局配置文件

  2.建立POJO

  3.建立獲取sqlSession

  4.建立Mapper接口與Mapper.xml配置文件

  5.定義SQL傳入參數

  6.使用Mapper接口

Mybatis作了什麼?

  1.數據庫鏈接建立、釋放形成系統資源浪費從而影響系統性能,若是使用鏈接池可解決這個問題.

   解決:在SqlMapConfig.xml中配置數據鏈接池,使用鏈接池管理數據庫連接.

   2.Sql語句硬編碼在代碼中形成代碼,sql一改變程序及必須從新編譯,實際應用sql變化的可能較大,sql變更須要改變java代碼.

   解決:Sql語句配置在XXXXmapper.xml文件中與java代碼分離.

  3.向sql語句傳參數麻煩,由於sql語句的where條件不必定,可能多也可能少,佔位符須要和參數一一對應.

   解決:Mybatis自動將java對象映射至sql語句,經過statement中的parameterType定義輸入參數的類型.

  4.對結果集解析麻煩,sql變化致使解析代碼變化,且解析前須要遍歷,若是能將數據庫記錄封裝成pojo對象解析比較方便.

   解決:Mybatis自動將sql執行結果映射至java對象,經過statement中的resultType定義輸出結果的類型.


MybatisUtil工具類 

  在實際開發中,咱們能夠編寫一個MybatisUtil輔助類來進行對進行操做,不過當有鏈接池和Spring對象注入,這個工具類就沒有必要.

1)在靜態初始化塊中加載mybatis配置文件和StudentMapper.xml文件一次

2)使用ThreadLocal對象讓當前線程與SqlSession對象綁定在一塊兒

3)獲取當前線程中的SqlSession對象,若是沒有的話,從SqlSessionFactory對象中獲取SqlSession對象 

4)獲取當前線程中的SqlSession對象,再將其關閉,釋放其佔用的資源

/**
 * MyBatis工具類
 * @author AdminTC
 */
public class MyBatisUtil {
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    private MyBatisUtil(){}
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }
    public static void closeSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession != null){
            sqlSession.close();
            threadLocal.remove();
        }
    }
    public static void main(String[] args) {
        Connection conn = MyBatisUtil.getSqlSession().getConnection();
        System.out.println(conn!=null?"鏈接成功":"鏈接失敗");
    }
}
MybatisUtil.java

動態SQL

   什麼是動態sql?當查詢參數數量未知,須要靠查詢者動態地添加查詢條件.

  知識點:

    1.當遇到跨表查詢的時候,你用什麼對象來承接對象?是否是須要一個表數據結合體對象來進行承接?

    2.對查詢條件進行判斷,若是輸入參數不爲空才進行查詢條件拼接. 

    3.使用<where>替代WHERE.

  使用查詢視圖對象來包裝跨表查詢的狀況,建立UserQueryVO.java

package com.harry.entity;

import java.util.List;

public class UserQueryVO {
    
    //傳入多個id
    private List<Integer> ids;
    
    
    //在這裏包裝所須要的查詢條件
    
    //用戶查詢條件
    private User user;

    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;
    }
    
    //能夠包裝其它的查詢條件,訂單、商品
    //....
    

}
UserQueryVO.java

 

<mapper namespace="com.harry.mapper.UserMapper">

    <!-- 定義sql片斷
    id:sql片斷的惟 一標識
    
    經驗:是基於單表來定義sql片斷,這樣話這個sql片斷可重用性才高
    在sql片斷中不要包括 where
     -->
    <sql id="query_user_where">
        <if test="user!=null">
            <if test="user.sex!=null and user.sex!=''">
                and user.sex = #{user.sex}
            </if>
            <if test="user.username!=null and user.username!=''">
                and user.username LIKE '%${user.username}%'
            </if>
            <if test="ids!=null">
            <!-- 使用 foreach遍歷傳入ids
            collection:指定輸入 對象中集合屬性
            item:每一個遍歷生成對象中
            open:開始遍歷時拼接的串
            close:結束遍歷時拼接的串
            separator:遍歷的兩個對象中須要拼接的串
             -->
             <!-- 使用實現下邊的sql拼接:
              AND (id=1 OR id=10 OR id=16) 
              -->
            <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
                <!-- 每一個遍歷須要拼接的串 -->
                id=#{user_id}
            </foreach>
            
            <!-- 實現  「 and id IN(1,10,16)」拼接 -->
            <!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
                每一個遍歷須要拼接的串
                #{user_id}
            </foreach> -->
            
            </if>
        </if>
    </sql>

<select id="findUserList" parameterType="com.harry.entity.UserQueryVO" 
            resultType="com.harry.entity.UserCustom">
    SELECT * FROM USER
    <!-- 
    where能夠自動去掉條件中的第一個and
     -->
    <where>
        <!-- 引用sql片斷 的id,若是refid指定的id不在本mapper文件中,須要前邊加namespace -->
        <include refid="query_user_where"></include>
        <!-- 在這裏還要引用其它的sql片斷  -->
    </where>
UserMapper.xml

配置文件

  sqlMapperConfig.xml

db.properties

   將數據源鏈接信息存儲在.properties文件中,避免將信息硬編碼至java類中,這樣即便修改了數據源信息也不用再次編譯程序.

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=password
db.properties
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="db.properties">
        <!-- 在properties內部用property定義屬性 -->
        <!-- 若是外部配置文件有該屬性,則內部定義屬性被外部屬性覆蓋 -->
    </properties>
    
    <!-- 和spring整合後 environments配置將廢除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事務管理 -->
            <transactionManager type="JDBC" />
            <!-- 數據庫鏈接池 -->
            <dataSource type="POOLED">
                <!-- 
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="bendanren" /> 
                -->
                <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>
        <mapper resource="sqlmap/User.xml"/>
    </mappers>
</configuration>
sqlMapConfig.xml

typeAliases

  批量進行別名掃描,自動定義別名,別名就是類名

<typeAliases>
    <package name="com.harry.mybatis"/>
</typeAliases>
sqlMapConfig.xml

typeHandlers(類型處理器)

  Mybatis中經過類型處理器完成對jdbc和java類型轉換

  typeHandlers,Mybatis提供的typeHandler能夠知足大部分開發要求

Mappers(映射配置,在mybatis總配置文件中配置)

  經過resource加載mapper文件  <mapper resource="sqlmap/UserMapper.xml"/>

  經過class來加載mapper文件   <mapper class="com.harry.mybatis.UserMapper"/>

  經過package批量加載mapper文件(推薦使用)  <mapper package="com.harry.mybatis"/>

  Mapper.xml  

  輸入映射

    經過parameterType指定輸入參數的類型,類型能夠是簡單類型,hashmap,pojo的包裝類型

  輸出映射

    resultType(selectOne)

    使用resultType進行輸出映射,只有查詢出來的列名和pojo的屬性名一致,該列才能夠映射成功.

    若是查詢出來的列名與pojo屬性名全不一致,沒有建立pojo對象.

    resultMap(SelectList)

    使用resultMap進行輸出映射,對進行多個pojo進行映射.  


高級映射 

  高級映射指的是表與表之間的關係映射(一對一,一對多,多對多),分析關係映射沒有思路的話是很可貴出正確結果,因此下面講述分析思路.

  數據模型的分析思路

     

/*
SQLyog v10.2 
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Table structure for table `items` */

CREATE TABLE `items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '商品名稱',
  `price` float(10,1) NOT NULL COMMENT '商品訂價',
  `detail` text COMMENT '商品描述',
  `pic` varchar(64) DEFAULT NULL COMMENT '商品圖片',
  `createtime` datetime NOT NULL COMMENT '生產日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Table structure for table `orderdetail` */

CREATE TABLE `orderdetail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `orders_id` int(11) NOT NULL COMMENT '訂單id',
  `items_id` int(11) NOT NULL COMMENT '商品id',
  `items_num` int(11) DEFAULT NULL COMMENT '商品購買數量',
  PRIMARY KEY (`id`),
  KEY `FK_orderdetail_1` (`orders_id`),
  KEY `FK_orderdetail_2` (`items_id`),
  CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Table structure for table `orders` */

CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '下單用戶id',
  `number` varchar(32) NOT NULL COMMENT '訂單號',
  `createtime` datetime NOT NULL COMMENT '建立訂單時間',
  `note` varchar(100) DEFAULT NULL COMMENT '備註',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*Table structure for table `user` */

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

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

/*
SQLyog v10.2 
MySQL - 5.1.72-community : Database - mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*Data for the table `items` */

insert  into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'臺式機',3000.0,'該電腦質量很是好!!!!',NULL,'2015-02-03 13:22:53'),(2,'筆記本',6000.0,'筆記本性能好,質量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'揹包',200.0,'名牌揹包,容量大質量好!!!!',NULL,'2015-02-06 13:23:02');

/*Data for the table `orderdetail` */

insert  into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);

/*Data for the table `orders` */

insert  into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);

/*Data for the table `user` */

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'張三','2014-07-10','1','北京市'),(16,'張小明',NULL,'1','河南鄭州'),(22,'陳小明',NULL,'1','河南鄭州'),(24,'張三丰',NULL,'1','河南鄭州'),(25,'陳小明',NULL,'1','河南鄭州'),(26,'王五',NULL,NULL,NULL);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
演示.sql

  

  推導思路 

      • 用戶表user:記錄了購買商品的用戶信息
      • 訂單表orders:記錄了用戶所建立的訂單(購買商品的訂單)
      • 訂單明細表orderdetail:記錄了訂單的詳細信息即購買商品的信息
      • 商品表items:記錄了商品信息

表與表之間的業務關係:

在分析表與表之間的業務關係時須要創建在某個業務意義基礎上去分析.先分析數據級別之間有關係的表之間的業務關係:

    • usre和orders:

user—>orders:一個用戶能夠建立多個訂單,一對多 
orders—>user:一個訂單隻由一個用戶建立,一對一

    • orders和orderdetail:

orders—>orderdetail:一個訂單能夠包括多個訂單明細,由於一個訂單能夠購買多個商品,每一個商品的購買信息在orderdetail記錄,一對多關係

orderdetail—> orders:一個訂單明細只能包括在一個訂單中,一對一

    • orderdetail和itesm:

orderdetail—>itesms:一個訂單明細只對應一個商品信息,一對一

items—> orderdetail:一個商品能夠包括在多個訂單明細 ,一對多

再分析數據庫級別沒有關係的表之間是否有業務關係:

    • orders和items:

orders和items之間能夠經過orderdetail表創建關係.

從需求來實現高級映射

  需求1. 查詢訂單信息,關聯查詢建立訂單的用戶信息(一對一查詢)

    1.1. sql

      肯定查詢的主表:  訂單表  

          得出SELECT * FROM orders

      而後肯定關聯表:  用戶表  

        使用內鏈接仍是外鏈接?

          因爲orders表有一個外鍵(user_id),經過外鍵關聯查詢用戶只能查出一條記錄(由於是一對一的關係),可使用內連接.                    

      SELECT * FROM orders.*,

         user.username,

        user.sex,

        user.address

        FROM orders,user

        WHERE orders.user_id = user.id;            

    1.2 建立pojo

package com.harry.entity;

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


@SuppressWarnings("serial")
public class User implements Serializable {
    
    //屬性名和數據庫表的字段對應
    private int id;
    private String username;// 用戶姓名
    private String sex;// 性別
    private Date birthday;// 生日
    private String address;// 地址
    
    //用戶建立的訂單列表
    private List<Orders> ordersList;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex
                + ", birthday=" + birthday + ", address=" + address + "]";
    }
    public List<Orders> getOrdersList() {
        return ordersList;
    }
    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }


}
User.java
package com.harry.entity;

import java.util.Date;
import java.util.List;

public class Orders {
    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

    private String note;
    
    //用戶信息
    private User user;
    
    //訂單明細
    private List<Orderdetail> orderdetails;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }

    public User getUser() {
        return user;
    }

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

    public List<Orderdetail> getOrderdetails() {
        return orderdetails;
    }

    public void setOrderdetails(List<Orderdetail> orderdetails) {
        this.orderdetails = orderdetails;
    }

    
    
    
}
Orders.java

    1.3 mapper.xml與mapper對象

    由於這是聯合查詢,映射對象分佈在兩個對象,因此須要鏈接兩個對象,在Mapper配置文件中用到<association>.

package com.harry.mapper;

import java.util.List;

import com.harry.entity.Orders;

public interface OrdersMapperCustom {
    public List<Orders> findOrdersUser()throws Exception;
}
OrdersMapperCustom
<?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">
<!-- namespace:命名空間,用於隔離sql,還有一個很重要的做用,後面會講 -->
<mapper namespace="com.harry.mapper.OrdersMapperCustom">


<!-- 訂單查詢關聯用戶的resultMap
    將整個查詢的結果映射到com.harry.entity.Orders中
     -->
    <resultMap type="com.harry.entity.Orders" id="OrdersUserResultMap">
        <!-- 配置映射的訂單信息 -->
        <!-- id:指定查詢列中的惟 一標識,訂單信息的中的惟 一標識,若是有多個列組成惟一標識,配置多個id
            column:訂單信息的惟 一標識 列
            property:訂單信息的惟 一標識 列所映射到Orders中哪一個屬性
          -->
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        
        <!-- 配置映射的關聯的用戶信息 -->
        <!-- association:用於映射關聯查詢單個對象的信息
        property:要將關聯查詢的用戶信息映射到Orders中哪一個屬性
         -->
        <association property="user"  javaType="com.harry.entity.User">
            <!-- id:關聯查詢用戶的惟 一標識
            column:指定惟 一標識用戶信息的列
            javaType:映射到user的哪一個屬性
             -->
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        
        </association>
    </resultMap>
    
    
<!-- id:statement的id 或者叫作sql的id-->
    <!-- parameterType:聲明輸入參數的類型 -->
    <!-- resultType:聲明輸出結果的類型,應該填寫pojo的全路徑 -->
    <!-- #{}:輸入參數的佔位符,至關於jdbc的? -->
    <select id="findOrdersUser" resultMap="OrdersUserResultMap">
        SELECT orders.*, user.username, user.sex, user.address FROM user, orders WHERE orders.user_id  = user.id
    </select>
</mapper>
OrderMapperCustom.xml

    1.4 調用OrdersMapperCustom中的方法

@Test
    public void testQueryOrdersCustomById() throws Exception {
        // 4. 建立SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 5. 執行SqlSession對象執行查詢,獲取結果User
        // 第一個參數是User.xml的statement的id,第二個參數是執行sql須要的參數;
        List<Orders> orders = sqlSession.selectList("findOrdersUser");
        System.out.println(orders.size());
        // 6. 打印結果
        Iterator i = orders.iterator();
        while(i.hasNext()) {
            System.out.println(i.next());
        }

        // 7. 釋放資源
        sqlSession.close();
    }
test

    1.5 一對一查詢結果.

總結:也可使用resultType更簡單實現該需求,而後用變量數對應的自定義類進行承接,resultMap能夠實現跨對象的嵌套.

  resultMap能夠實現延遲加載,但resultType沒法實現.

  需求2 . 查詢訂單及訂單明細的信息(一對多)

    2.1sql

    肯定查詢的主表:訂單表

    而後肯定關聯表:訂單明細表

    採用resultType方式映射,替訂單表加一些訂單明細表的映射元素.

SELECT 

  orders.*,

  user.username,

  user.sex,

  user.address,

  orderdetail.id orderdetail_id,

  orderdetail.items_id,

  orderdetail.items_num,

  orderdetail.orders_id

FROM

  orders,

  user,

  orderdetail

WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id

   2.2 映射思路

      查詢結果出現冗餘數據,違背3NF,解決方法是在order類中添加List<OrderDetail>orderDetail屬性.

        

    最終會將訂單信息映射到orders中,訂單所對應的訂單明細映射到orders中的orderDetails屬性中.

        

    2.3 resultMap定義,關鍵點在於resultMap將orderdetail多方對象封裝在list對象中.

 1 <resultMap type="com.harry.entity.Orders" id="OrdersAndDetailResultMap" extends="OrdersUserResultMap">
 2         <!-- 訂單信息 -->
 3         <!-- 用戶信息 -->
 4         <!-- 使用extends繼承,不用在中配置訂單信息和用戶信息的映射 -->
 5         
 6         <!-- 訂單明細信息
 7         一個訂單關聯查詢出了多條明細,要使用collection進行映射
 8         collection:對關聯查詢到多條記錄映射到集合對象中
 9         property:將關聯查詢到多條記錄映射到com.harry.entity.Orders哪一個屬性
10         ofType:指定映射到list集合屬性中pojo的類型
11          -->
12           <collection property="orderdetails" ofType="com.harry.entity.Orderdetail">
13              <!-- id:訂單明細惟 一標識
14              property:要將訂單明細的惟 一標識 映射到cn.itcast.mybatis.po.Orderdetail的哪一個屬性
15                -->
16              <id column="orderdetail_id" property="id"/>
17              <result column="items_id" property="itemsId"/>
18              <result column="items_num" property="itemsNum"/>
19              <result column="orders_id" property="ordersId"/>
20          </collection>
21 </resultMap>    
22 
23 <select id="findOrdersDetail" resultMap="OrdersAndDetailResultMap">
24         SELECT 
25           orders.*,
26           user.username,
27           user.sex,
28           user.address,
29           orderdetail.id orderdetail_id,
30           orderdetail.items_id,
31           orderdetail.items_num,
32           orderdetail.orders_id
33         FROM
34           orders,    
35           user,
36           orderdetail
37         WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id
38     </select>
Mapper.xml增長片斷

  需求3. 查詢用戶和用戶購買商品的信息,用戶名、用戶地址、購買商品名稱、購買商品時間、購買商品數量(多對多)

    3.1sql

    肯定查詢的主表:用戶表

    因爲用戶和商品沒有直接關聯,經過訂單和訂單明細進行關聯

    而後肯定關聯表:orders,orderdetail,items

SELECT 

  orders.*,

  user.username,

  user.sex,

  user.address,

  orderdetail.id orderdetail_id,

  orderdetail.items_id,

  orderdetail.items_num,

  orderdetail.orders_id,

  items.name items_name,  

  items.detail items_detail,

  items.price items_price

FROM

  orders,

  user,

  orderdetail,

  items

WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id

   3.2映射思路

將用戶信息映射到user中.

user類中添加訂單列表屬性List<Orders> orderslist,將用戶建立的訂單映射到orderslist

Orders中添加訂單明細列表屬性List<OrderDetail>orderdetials,將訂單的明細映射到orderdetials

OrderDetail中添加Items屬性,將訂單明細所對應的商品映射到Items

   3.3 ResultMap

<resultMap type="com.harry.entity.User" id="UserAndItemsResultMap">
    <!-- 用戶信息 -->
        <id column="user_id" property="id"/>
        <result column="username" property="username"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
        
        <!-- 訂單信息
        一個用戶對應多個訂單,使用collection映射
         -->
         <collection property="ordersList" ofType="com.harry.entity.Orders">
             <id column="id" property="id"/>
             <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
            
             <!-- 訂單明細
         一個訂單包括 多個明細
          -->
              <collection property="orderdetails" ofType="com.harry.entity.Orderdetail">
                      <id column="orderdetail_id" property="id"/>
                     <result column="items_id" property="itemsId"/>
                     <result column="items_num" property="itemsNum"/>
                     <result column="orders_id" property="ordersId"/>
                     
                     <!-- 商品信息
              一個訂單明細對應一個商品
               -->
                   <association property="items" javaType="com.harry.entity.Items">
                       <id column="items_id" property="id"/>
                       <result column="items_name" property="name"/>
                       <result column="items_detail" property="detail"/>
                       <result column="items_price" property="price"/>
                   </association>    
              </collection>
         </collection>
</resultMap>

<select id="findUserItems" resultMap="UserAndItemsResultMap">
    SELECT 
          orders.*,
          user.username,
          user.sex,
          user.address,
          orderdetail.id orderdetail_id,
          orderdetail.items_id,
          orderdetail.items_num,
          orderdetail.orders_id,
          items.name items_name,
          items.detail items_detail,
          items.price items_price
        FROM
          orders,    
          user,
          orderdetail,
          items
        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
    </select>
Mapper.xml片斷

ResultType和ResultMap總結

  這兩種配置方式均可以實現映射,但應用的場景存在差異

  ResultType更容易實現映射關係,僅僅只要有對應的變量進行存儲,只要擴展pojo便可(需求一)

  ResultMap須要定義特定的ResultMap,這種適用於pojo處理多對多映射關係,一對可能是多對多的一種特殊狀況(需求二)

需求一

查詢字段:用戶帳號、用戶名稱、用戶性別、商品名稱、商品價格(最多見)

企業開發中常見明細列表,用戶購買商品明細列表,

使用resultType將上邊查詢列映射到pojo輸出.

需求二

查詢字段:用戶帳號、用戶名稱、購買商品數量、商品明細(鼠標移上顯示明細)

使用resultMap將用戶購買的商品明細列表映射到user對象中. 

延遲加載(懶加載)

  resultMap能夠實現高級映射(使用associationcollection實現一對一及一對多映射),associationcollection具有延遲加載功能.

  延遲加載:先從單表查詢、須要時再從關聯表去關聯查詢,大大提升 數據庫性能,由於查詢單表要比關聯查詢多張錶速度要快.

  (舉例)使用association實現延遲加載

    1.需求:查詢訂單而且關聯查詢用戶信息

      主表 :  orders表

      關聯表 :  user表

    2.思路:定義兩個單獨的mapper對應的兩個表

<!-- 查詢訂單關聯查詢用戶,用戶信息須要延遲加載 -->
    <select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
        SELECT * FROM orders
    </select>
------------------------------------------------------------------------------------------------------
<select id="findUserById" parameterType="int" resultType="user">
        SELECT * FROM USER WHERE id=#{value}
    </select>
mapper.xml

    3.使用定義了延遲加載的resultMap

<!-- 延遲加載的resultMap -->
    <resultMap type="com.harry.entity.Orders" id="OrdersUserLazyLoadingResultMap">
            <!--對訂單信息進行映射配置  -->
            <id column="id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
            <!-- 實現對用戶信息進行延遲加載
            select:指定延遲加載須要執行的statement的id(是根據user_id查詢用戶信息的statement)
            要使用userMapper.xml中findUserById完成根據用戶id(user_id)用戶信息的查詢,若是findUserById不在本mapper中須要前邊加namespace
            column:訂單信息中關聯用戶信息查詢的列,是user_id
            關聯查詢的sql理解爲:
            SELECT orders.*,
    (SELECT username FROM user WHERE orders.user_id = user.id)username,
    (SELECT sex FROM user WHERE orders.user_id = user.id)sex
     FROM orders
             -->
            <association property="user"  javaType="com.harry.entityUser"
             select="com.harry.mapper.UserMapper.findUserById" column="user_id">
            <!-- 實現對用戶信息進行延遲加載 -->
        
            </association>
            
    </resultMap>
mapper.xml

    4.測試思路是分兩個mapper來進行調用,先調用getOrders(),而後再調用getUser();

    5.延遲加載配置

mybatis默認沒有開啓延遲加載,須要在SqlMapConfig.xmlsetting配置.

mybatis核心配置文件中配置:

lazyLoadingEnabled、aggressiveLazyLoading

<!-- 全局配置參數,須要時再設置 -->
    <settings>
        <!-- 打開延遲加載 的開關 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 將積極加載改成消極加載即按須要加載 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 開啓二級緩存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
sqlMapConfig.xml 

查詢緩存

  查詢緩存介紹

    mybatis提供查詢緩存,用於減輕數據壓力,提升數據庫性能.mybaits提供一級緩存,和二級緩存.

    

    由上圖能夠看清mybatis緩存機制

      1.一級緩存存在於每一個sqlSession之中,緩存自動啓動,語句commit爲緩存刷新

@Test
    public void testCache1() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();//建立代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        //下邊查詢使用一個SqlSession
        //第一次發起請求,查詢id爲1的用戶
        User user1 = userMapper.findUserById(1);
        System.out.println(user1);
        
//        若是sqlSession去執行commit操做(執行插入、更新、刪除),清空SqlSession中的一級緩存,這樣作的目的爲了讓緩存中存儲的是最新的信息,避免髒讀。
        
        //更新user1的信息
        user1.setUsername("測試用戶22");
        userMapper.updateUser(user1);
        //執行commit操做去清空緩存
        sqlSession.commit();
        
        //第二次發起請求,查詢id爲1的用戶
        User user2 = userMapper.findUserById(1);
        System.out.println(user2);
        
        sqlSession.close();
        
    }
一級緩存測試

      2.二級緩存存在於sqlSession之間,緩存需手動開啓,sqlSession一關閉緩存刷新,關閉二級緩存設置useCache

      開啓緩存:<setting name="cacheEnabled" value="true"/>

// 二級緩存測試
    @Test
    public void testCache2() throws Exception {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        // 建立代理對象
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        // 第一次發起請求,查詢id爲1的用戶
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1);
        
        //這裏執行關閉操做,將sqlsession中的數據寫到二級緩存區域
        sqlSession1.close();
        
        
        //使用sqlSession3執行commit()操做
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        User user  = userMapper3.findUserById(1);
        user.setUsername("張明明");
        userMapper3.updateUser(user);
        //執行提交,清空UserMapper下邊的二級緩存
        sqlSession3.commit();
        sqlSession3.close();
        
        

        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        // 第二次發起請求,查詢id爲1的用戶
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2);

        sqlSession2.close();
二級緩存測試

關閉特定Mapper緩存<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

源碼跟蹤

整合分佈式緩存

  分佈式緩存

    咱們系統爲了提升系統併發性能,通常對系統進行分佈式部署(集羣部署方式).

    假定這時某個用戶在一臺服務器登陸了系統,怎麼去通知服務器羣該用戶的訪問權限?

    

    二級緩存的應用場景

對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可採用mybatis二級緩存技術下降數據庫訪問量,提升訪問速度,業務場景好比:耗時較高的統計分析sql、電話帳單查詢sql等。

實現方法以下:經過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,好比設置爲30分鐘、60分鐘、24小時等,根據需求而定。

整合緩存框架(以redis爲例,待補齊)

    mybatis自身沒有實現分佈式緩存機制,但提供了接口去整合其餘分佈式緩存框架.

整合spring框架

    整合思路

      使用springi以單例模式管理sqlSessionFactory

      使用sqlSessionFactory建立sqlSession(spring和mybatis整合自動完成)

      持久層mapper使用spring容器進行管理

    整合準備

      maven引進mysql,spring core, aop, mybatis, mybatis-spring等jar

    sqlSessionFactory

      在applicationContext中配置sqlSessionFactory和數據源 

<!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />

    <!-- 數據源,使用dbcp -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="10" />
        <property name="maxIdle" value="5" />
    </bean>


    <!-- sqlSessinFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 加載mybatis的配置文件 -->
        <property name="configLocation" value="mybatis/SqlMapConfig.xml" />
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
applicationContext.xml

    mapper和mapper.xml

      生成特定的mapper和mapper.xml(略)

    經過MapperScannerConfigure進行mapper掃描

<!-- mapper批量掃描,從mapper包中掃描出mapper接口,自動建立代理對象而且在spring容器中註冊 
    遵循規範:將mapper.java和mapper.xml映射文件名稱保持一致,且在一個目錄 中
    自動掃描出來的mapper的bean的id爲mapper類名(首字母小寫)
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定掃描的包名 
        若是掃描多個包,每一個包中間使用半角逗號分隔
        -->
        <property name="basePackage" value="com.harry.ssm.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        
    </bean>
applicationContext.xml

逆向工程

    1.什麼是逆向工程?

      正向工程 : Entity --> Table

      逆向工程 : Table --> Entity

      逆向工程就是使用數據庫表來反向生成實體類和映射文件

    2.修改配置

      Mybatis官方提供了逆向工具,指定其代碼配置文件四個位置,entity,mapper,mapper.xml的所在位置,最後指定想生成的數據庫表.   

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自動生成的註釋 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--數據庫鏈接的信息:驅動類、鏈接地址、用戶名、密碼 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
            password="mysql">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

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

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="com.harry.ssm.entity"
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema做爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從數據庫返回的值被清理先後的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.harry.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema做爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="com.harry.ssm.mapper" 
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema做爲包的後綴 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定數據庫表 -->
        <table tableName="items"></table>
        <table tableName="orders"></table>
        <table tableName="orderdetail"></table>
        <table tableName="user"></table>

        
    </context>
</generatorConfiguration>
View Code

    3.修改並執行下載程序

      

相關文章
相關標籤/搜索