框架应用:Mybatis - 开发详述

ORM框架

  在实际开发中,工程中本质的任务是从数据库中获取数据,然后对数据进行操作,又或者写入数据.开发时语言是大多是面向对象的工程语言,这个时候就必须进行工程语言和数据库连接语言的转换,也就是所谓的ORM对象关系映射.

  那么需要使用到连接数据库的技术,如Java的JDBC,或者其他语言的ODBC等.然而,这种技术虽然能很好地与数据库进行互动,但是有个问题是每一次访问动作都必须保证连接时间较短(避免占用资源),自己进行连接的开闭,SQL语句的拼写.....

  实在是太繁琐了,严重影响工程开发进度以及编码者的心情,尤其是第二点,造成很恶劣的影响.所以,有人就编写出了ORM框架来帮你进行两种语言半自动转换,代价是你需要写一些配置文件.

  ORM框架基本思想相似:

  1.从配置文件中得到sessionFactory;

  2.由sessionFactory产生session;

  3.在session中完成CRUD和事务提交等操作;

  4.关闭session;

Mybatis是什么?

  mybatis是一个持久层框架,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.修改并执行下载程序

      

posted @ 2017-09-01 15:29  Rudolph_Browne  阅读(387)  评论(0编辑  收藏  举报