框架-Mybatis

第一天

第一天基础知识:
	1、对原生态JDBC程序(单独使用jdbc开发)问题总结
	2、mybatis框架原理
	3、mybatis入门程序:用于的增、删、改、查
	4、mybatis开发dao两种方法
		原始dao开发方法(程序需要编写dao接口和dao实现类)
		mybatis的mapper接口(相当于dao接口)代理开发方法
	5、mybatis配置文件SqlMapConfig.xml
	6、	mybatis输入映射
		mybatis输出映射
	7、mybatis的动态sql

1、对原生态JDBC程序(单独使用jdbc开发)问题总结

1.1、使用原生态的JDBC操作数据库

  • 数据库表:

    /*
    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 */;
    
    
  • 导连接数据的jar包

  • 通过原生态的jdbc编程连接数据库

    package com.zwd.mybatis.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    
    /**
     * 原生态JDBC编程中的问题
     * @author Administrator
     *
     */
    public class JdbcTest {
    	public static void main(String[] args) throws ClassNotFoundException, SQLException {
    		//1.注册驱动
    		Class.forName("com.mysql.jdbc.Driver");
    		//2.获取连接
    		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis","root","root");
    		//3.获取预处理对象
    		String sql = "select * from user where username = ?";
    		PreparedStatement ps = conn.prepareStatement(sql);
    		//4.操作数据库并获取结果集
    		ps.setString(1, "陈小明");
    		ResultSet rs = ps.executeQuery();
    		//5.处理结果集
    		while(rs.next()){
    			System.out.println(rs.getString("id") + " " + rs.getString("username"));
    		}
    		//6.关闭资源
    		rs.close();
    		ps.close();
    		conn.close();
    	}
    }
    

1.2、问题总结

1、数据库连接,使用时就创建,不适用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库的性能。
2、将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3、向preparedStatement中设置参数,对占位符号的位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句以及占位符号和参数全部配置在xml中。
4、从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。

2、Mybatis

2.1、什么是mybatis

1、mybatis是一个持久层框架,是apache下的顶级项目。mybatis托管到goolecode下,再后来托管到github下(https://github.com/mybatis/mybatis-3/releases)
2、mybatis让程序员将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要的sql语句
3、mybatis可以将向preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)

2.2、mybatis框架

在这里插入图片描述

2.3、mybatis环境配置

  • mybatis的jar下载地址

    https://github.com/mybatis/mybatis-3/releases
    
  • 导包:核心包 + lib下面的包 + 数据库驱动

  • 在classpath下创建log4j.properties 文件:用来记录程序运行的日志

    # Global logging configuration
    # 在开发环境下使用DEBUG;在生产环境下使用info或者error
    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
    
  • 在classpath下创建SqlMapConfig.xml文件

  • 创建sqlMap包,用于之后存放mapper.xml文件
    在这里插入图片描述

3、mybatis入门程序

3.1、查询单条记录-根据id查询用户(user)

  • 配置SqlMapConfig.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    	<!-- 和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="root" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    </configuration>
    
  • 创建po(persistent object 持久对象)类:User.java

    package com.zwd.mybatis.po;
    
    import java.util.Date;
    
    /*******************************************************************************
     * javaBeans
     * user --> User 
     * <table explanation>
     * @author 2019-09-09 11:14:42
     * 
     */	
    public class User implements java.io.Serializable {
    	//field
    	/**  **/
    	private int id;
    	/** 用户名称 **/
    	private String username;
    	/** 生日 **/
    	private Date birthday;
    	/** 性别 **/
    	private String sex;
    	/** 地址 **/
    	private String address;
    	//method
    	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 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 toString Method 
    	public String toString() {
    		StringBuffer sb=new StringBuffer();
    		sb.append("{");
    		sb.append("'id':'"+this.getId()+"',");
    		sb.append("'username':'"+this.getUsername()+"',");
    		sb.append("'birthday':'"+this.getBirthday()+"',");
    		sb.append("'sex':'"+this.getSex()+"',");
    		sb.append("'address':'"+this.getAddress()+"'");
    		sb.append("}");
    		return sb.toString();
    	}
    }
    
  • 在sqlMap包下创建映射文件:User.xml

    映射文件命名:
    User.xml(原始ibatis命名),mapper代理开发映射文件名称叫XXXMapper.xml,比如:UserMapper.xml、ItemsMapper.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">
    
    <!-- namespace命名空间:作用就是对sql进行分类化管理,理解sql隔离
    注意:使用mapper代理开发,namespace有特殊重要的作用。 -->
    
    <mapper namespace="test">
    	
    	<!-- 在映射文件配置很多sql语句 -->
    
    	<!-- 通过id查询用户的信息:
    			select标签:
    				id:标识映射文件中的sql
    				parameterType:指定输入参数的类型
    				resultType:指定输出结果将会映射的对象类型。
    							select指定的resultType表示将单条记录映射成的java对象类型
    				sql查询语句:
    					#{}:表示一个占位符
    					#{id}:其中的id表示接受的输入参数,参数名称为id。
    							如果输入参数为简单类型,参数名称可以任意。可以是value或其他值-->
    	<select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
    		select * from user where id = #{id}
    	</select>
    </mapper>
    
  • SqlMapConfig.xml中加载映射文件

    <!-- 加载映射文件 -->
    	<mappers>
    		<mapper resource="sqlMap/User.xml"/>
    	</mappers>
    
    <?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>
    	<!-- 和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="root" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<!-- 加载映射文件 -->
    	<mappers>
    		<mapper resource="sqlMap/User.xml"/>
    	</mappers>
    	
    </configuration>
    
  • 编写程序:

    package com.zwd.mybatis.test;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;
    
    import com.zwd.mybatis.po.User;
    /**
     * 
     * @author Administrator
     *
     */
    public class MybatisDemo1 {
    	/**
    	 * 根据id查询用户:
    	 *  <select id="findUserById" parameterType="int" 			   								resultType="com.zwd.mybatis.po.User">
    			select * from user where id = #{id}
    	 *  </select>
    	 * @throws IOException
    	 */
    	@Test
    	public void findUserById() throws IOException {
    		//1、获取会话工厂
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    		//2、根据会话工厂获取SqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		//3、用SqlSession操作数据库
    		//selectOne(statement, parameter):
    		//第一个参数:映射文件中statement的id,值为:namespace+"."+statement的id
    		//第二个参数:指定和映射文件所匹配的parameterType的resultType类型的对象
    		User user = sqlSession.selectOne("test.findUserById", 1);
    		System.out.println(user);
    		//4、关闭资源
    		sqlSession.close();
    	}
    }
    
    

3.2、 查询多条记录-根据用户名称模糊查询用户记录

User.xml

<!-- 根据用户名称模糊查询用户列表:可能会返回多条记录
			resultType:指定的就是单条记录所映射的java对象类型
			${}:表示拼接sql串,将接收到的参数内容不加任何修饰拼接在sql中。
				使用${}拼接sql,会引起sql注入问题
				${value}:接收输入参数的内容,如果是简单类型,${}中只能使用value
			-->
	<select id="findUserByName" parameterType="java.lang.String" resultType="com.zwd.mybatis.po.User">
		select * from user where username like '%${value}%'
	</select>
	/**
	 * 根据用户名称模糊查询用户列表
	 * @throws IOException 
	 */
	@Test
	public void findUserByName() throws IOException{
		//1、获取会话工厂
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//2、根据会话工厂获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//3、使用sqlSession操作数据库
		
//		//select * from user where username like '%小明%'
//		List<User> users = sqlSession.selectList("test.findUserByName");
//		for (User user : users) {
//			System.out.println(user);
//		}
		
		//select * from user where username like '%${value}%'
		List<User> users = sqlSession.selectList("test.findUserByName", "小明");
		for (User user : users) {
			System.out.println(user);
		}
	}

3.3、添加用户

  • 映射文件:
	<!-- 插入用户:
			parameterType:输入参数类型是pojo:User
							#{}:中指定pojo的属性名,接收到pojo对象的属性值,mybatis通过OGNL获取对象的属性值-->
	<select id="insertUser" parameterType="com.zwd.mybatis.po.User">
		insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
	</select>
  • 编写程序:

    	/**
    	 * 插入用户
    	 * @throws IOException
    	 */
    	@Test
    	public void insertUser() throws IOException{
    		//1.获取会话工厂
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    		//2.根据会话工厂获取sqlSession
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		//3.使用SqlSession操作数据库
    		User user = new User();
    		user.setUsername("赵文迪");
    		user.setAddress("贵州遵义");
    		user.setBirthday(new Date());
    		user.setSex("女");
    		sqlSession.insert("test.insertUser", user);
    		sqlSession.commit();
    		sqlSession.close();
    	}
    
添加用户-自增主键返回

映射文件

	<!--将插入数据的主键返回:
			select LAST_INSERT_ID():得到刚insert进去的记录的主键值,只适用与自增主键。
			keyProperty:将查询到的主键值设置到parameterType指定的对象的哪个属性中。
			order:指定select LAST_INSERT_ID()语句相对于insert语句的执行顺序
			resultType:指定select LAST_INSERT_ID()的结果类型
		 -->
	<insert id="insertUser1" parameterType="com.zwd.mybatis.po.User">
		<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
			select LAST_INSERT_ID()
		</selectKey>
		insert into
		user(username,birthday,sex,address)
		values(#{username},#{birthday},#{sex},#{address})
	</insert>

程序代码:

	/**
	 * 插入用户
	 * @throws IOException
	 */
	@Test
	public void insertUser1() throws IOException{
		//1.获取会话工厂
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//2.根据会话工厂获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//3.使用SqlSession操作数据库
		User user = new User();
		user.setUsername("赵文迪");
		user.setAddress("贵州遵义");
		user.setBirthday(new Date());
		user.setSex("女");
		sqlSession.insert("test.insertUser1", user);
		
		System.out.println(user.getId());
		sqlSession.commit();
		sqlSession.close();
	}
添加用户-非自增主键返回
使用mysql的uuid()函数生成主键,需要修改表中id字段类型为string,长度设置成35位。

执行思路:
先通过uuid()查询到主键,将主键输入 到sql语句中。

执行uuid()语句顺序相对于insert语句之前执行。
删除用户和更新用户

映射文件:

	<!-- 4、删除用户 -->
	<delete id="deleteUserById" parameterType="java.lang.Integer">
		delete from user where
		id = #{id}
	</delete>

	<!-- 5、更新用户: 注意:id必须存在 -->
	<update id="updateUser" parameterType="com.zwd.mybatis.po.User">
		update user set
		username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
		where id = #{id}
	</update>

程序代码:

	/**
	 * 删除用户
	 * @throws IOException
	 */
	@Test
	public void deleteUserById() throws IOException{
		//1、获取会话工厂
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//2、根据会话工厂获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//3、通过sqlSession操作数据库
		sqlSession.delete("test.deleteUserById", 26);
		//4、提交事务
		sqlSession.commit();
		//5、关闭会话
		sqlSession.close();
	}
	
	/**
	 * 更新用户
	 * @throws IOException
	 */
	@Test
	public void updateUser() throws IOException{
		//1、获取会话工厂
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//2、根据会话工厂获取sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//3、通过sqlSession操作数据库
		User user = new User();
		user.setId(25);
		user.setUsername("赵文迪");
		user.setAddress("贵州遵义");
		user.setBirthday(new Date());
		user.setSex("女");
		sqlSession.update("test.updateUser", user);
		//4、提交事务
		sqlSession.commit();
		//5、关闭会话
		sqlSession.close();
	}

3.4、总结

parameterType

在映射文件中通过parameterType指定输入 参数的类型。

ResultType

在映射文件中通过resultType指定输出结果的类型。

#{}和${}
#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,#{}中可以写成value或其它名称。
#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

${}表示一个拼接符号,会引用sql注入,所以不建议使用${}。
${}接收输入参数,类型可以是简单类型,pojo、hashmap。
如果接收简单类型,${}中只能写成value。
${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。
selectOne和selectList
selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。

selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。

3.5 mybatis和hibernate本质区别和应用场景

hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。
对sql语句进行优化、修改比较困难的。
应用场景:
	适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。

mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全 的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。
应用场景:
	适用与需求变化较多的项目,比如:互联网项目。

企业进行技术选型,以低成本 高回报作为技术选型的原则,根据项目组的技术力量进行选择。

4、mybatis开发dao的方法

4.1、SqlSession的使用范围

SqlSessionFactoryBuilder

通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory

将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。

在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。

SqlSessionFactory

通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。

将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory。

SqlSession

SqlSession是一个面向用户(程序员)的接口。

SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。

SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。

SqlSession最佳应用场合在方法体内,定义成局部变量使用。

4.2、原始dao的开发方法(程序员需要写dao接口和dao实现方法)

  • 使用上述的SqlMapConfig.xmluser.xml 的配置文件。

  • 编写dao接口:UserDao.java

    package com.zwd.mybatis.dao;
    
    import com.zwd.mybatis.po.User;
    
    /**
     * 用户持久层接口
     * @author Administrator
     *
     */
    public interface UserDao {
    
    	/**
    	 * 根据id查询用户
    	 * @param id
    	 * @return
    	 */
    	public User findUserById(Integer id);
    	
    	/**
    	 * 插入用户
    	 * @param user
    	 */
    	public void insertUser(User user);
    }
    
  • 编写dao的实现类:UserDaoImpl.java

    package com.zwd.mybatis.dao.impl;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    
    import com.zwd.mybatis.dao.UserDao;
    import com.zwd.mybatis.po.User;
    
    public class UserDaoImpl implements UserDao {
    
    	private SqlSessionFactory sqlSessionFactory;
    	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
    		this.sqlSessionFactory = sqlSessionFactory;
    	}
    
    	@Override
    	public User findUserById(Integer id) {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		User user = sqlSession.selectOne("test.findUserById", id);
    		return user;
    	}
    
    	@Override
    	public void insertUser(User user) {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		sqlSession.insert("test.insertUser", user);
    	}
    }
    
  • 测试方法:

    package com.zwd.mybatis.test;
    
    import static org.junit.Assert.fail;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Before;
    import org.junit.Test;
    
    import com.zwd.mybatis.dao.UserDao;
    import com.zwd.mybatis.dao.impl.UserDaoImpl;
    import com.zwd.mybatis.po.User;
    
    public class UserDaoImplTest {
    
    	private SqlSessionFactory sqlSessionFactory;
    
    	@Before
    	public void setUp() throws IOException {
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	}
    
    	@Test
    	public void testFindUserById() {
    		UserDao dao = new UserDaoImpl(sqlSessionFactory);
    		User user = dao.findUserById(1);
    		System.out.println(user);
    	}
    
    	@Test
    	public void testInsertUser() {
    		fail("Not yet implemented");
    	}
    }
    
  • 问题:

    1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
    
    2、调用sqlsession方法时将statement的id硬编码了
    
    3、调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
    

4.3、mapper代理方法

程序员需要写mapper接口,相当于dao接口;还需要编写mapper.xml配置文件。

程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。

开发规范:
1、在mapper.xml中namespace等于mapper接口的全类名
2、mapper.java接口中的方法名和mapper.xml中statement的id一致
3、mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
4、mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。
  • userMapper.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">
    
    <!-- namespace命名空间:作用就是对sql进行分类化管理,理解sql隔离 
    注意:使用mapper代理开发,namespace有特殊重要的作用,namespace等于mapper接口的全限定类名。 -->
    
    <mapper namespace="com.zwd.mybatis.mapper.UserMapper">
    
    	<!-- 1、通过id查询用户的信息-->
    	<select id="findUserById" parameterType="int"
    		resultType="com.zwd.mybatis.po.User">
    		select * from user where id = #{id}
    	</select>
    
    	<!--2、 插入用户 -->
    	<insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
    		insert into
    		user(username,birthday,sex,address)
    		values(#{username},#{birthday},#{sex},#{address})
    	</insert>
    
    </mapper> 
    
  • UserMapper.java :mapper接口(dao接口)

    package com.zwd.mybatis.mapper;
    
    import com.zwd.mybatis.po.User;
    
    /**
     * 用户持久层接口
     * @author Administrator
     *
     */
    public interface UserMapper {
    
    	/**
    	 * 根据id查询用户
    	 * @param id
    	 * @return
    	 * <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
    	 */
    	public User findUserById(Integer id);
    	
    	/**
    	 * 插入用户
    	 * @param user
    	 * <insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
    	 */
    	public void insertUser(User user);
    }
    
  • 在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>
    	<!-- 和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="root" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<!-- 加载映射文件 -->
    	<mappers>
    		<mapper resource="mapper/userMapper.xml"/>
    	</mappers>
    	
    </configuration>
    
  • 测试:

    //创建mapper对象,mybatis自动创建mapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.findUserById(1);
    
    	@Before
    	public void setUp() throws IOException {
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	}
    
    	@Test
    	public void testFindUserById() {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		User user = userMapper.findUserById(1);
    		System.out.println(user);
    	}
    	
    

5、SqlMapConfig.xml配置文件

mybatis的全局配置文件SqlMapConfig.xml,配置内容如下:

properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

5.1、properties属性

properties特性:

注意: MyBatis 将按照下面的顺序来加载属性:
在 properties 元素体内定义的属性首先被读取。 
然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。 
最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

建议:
不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX
需求:
将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。
在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。

将数据库连接参数只配置在db.properties中,原因:方便对参数进行统一管理,其它xml可以引用该db.properties。
  • 添加db.properties 数据库连接信息文件

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis
    jdbc.user=root
    jdbc.password=root
    
  • SqlMapConfig.xml中的配置

    	<!-- 加载数据库的配置文件 -->
    	<properties resource="db.properties">
    		<!-- <property name="jdbc.Driver" value=""/> -->
    	</properties>
    
        <dataSource type="POOLED">
    		<property name="driver" value="${jdbc.driver}" />
    		<property name="url" value="${jdbc.url}" />
    		<property name="username" value="${jdbc.user}" />
    		<property name="password" value="${jdbc.password}" />
    	</dataSource>
    
    <?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>
    	
    	<!-- 和spring整合后 environments配置将废除-->
    	<environments default="development">
    		<environment id="development">
    		<!-- 使用jdbc事务管理-->
    			<transactionManager type="JDBC" />
    		<!-- 数据库连接池-->
    			<dataSource type="POOLED">
    				<property name="driver" value="${jdbc.driver}" />
    				<property name="url" value="${jdbc.url}" />
    				<property name="username" value="${jdbc.user}" />
    				<property name="password" value="${jdbc.password}" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<!-- 加载映射文件 -->
    	<mappers>
    		<mapper resource="mapper/userMapper.xml"/>
    	</mappers>
    	
    </configuration>
    

5.2、settings(全局配置参数)

mybatis框架在运行时可以调整一些运行参数。
比如:开启二级缓存、开启延迟加载。。

全局参数将会影响mybatis的运行行为。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

5.3、typeAliases(类型别名)

  • 需求:
在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。

如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
mybatis默认支持的别名:
别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
自定义别名(对于自己定义的类型需要自己定义别名)
  • 配置:
	<!-- 别名定义: -->
	<typeAliases>
		<!-- 单个别名定义:
			type:类的全限定类名 
			alias:别名-->
		<typeAlias type="com.zwd.mybatis.po.User" alias="user"/>
		
		<!-- 批量定义:
				 指定包名,mybatis自动扫描包中的类,自定义类的别名,别名就是类名(首字母大写或小写都可以)-->
		<package name="com.zwd.mybatis.po"/>
	</typeAliases>
<?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">
		<!-- <property name="jdbc.Driver" value=""/> -->
	</properties>
	
	<!-- 别名定义: -->
	<typeAliases>
		<!-- 单个别名定义:
			type:类的全限定类名 
			alias:别名-->
		<typeAlias type="com.zwd.mybatis.po.User" alias="user"/>
		
		<!-- 批量定义:
				 指定包名,mybatis自动扫描包中的类,自定义类的别名,别名就是类名(首字母大写或小写都可以)-->
		<package name="com.zwd.mybatis.po"/>
	</typeAliases>
	
	<!-- 和spring整合后 environments配置将废除-->
	<environments default="development">
		<environment id="development">
		<!-- 使用jdbc事务管理-->
			<transactionManager type="JDBC" />
		<!-- 数据库连接池-->
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.user}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	
	<!-- 加载映射文件 -->
	<mappers>
		<mapper resource="mapper/userMapper.xml"/>
	</mappers>
	
</configuration>

5.4、typeHandlers(类型处理器)

mybatis中通过typeHandlers完成jdbc类型和java类型的转换。

通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义.

mybatis支持类型处理器:

类型处理器Java类型JDBC类型
BooleanTypeHandlerBoolean,boolean任何兼容的布尔值
ByteTypeHandlerByte,byte任何兼容的数字或字节类型
ShortTypeHandlerShort,short任何兼容的数字或短整型
IntegerTypeHandlerInteger,int任何兼容的数字和整型
LongTypeHandlerLong,long任何兼容的数字或长整型
FloatTypeHandlerFloat,float任何兼容的数字或单精度浮点型
DoubleTypeHandlerDouble,double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerBigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerStringCHAR和VARCHAR类型
ClobTypeHandlerStringCLOB和LONGVARCHAR类型
NStringTypeHandlerStringNVARCHAR和NCHAR类型
NClobTypeHandlerStringNCLOB类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB和LONGVARBINARY类型
DateTypeHandlerDate(java.util)TIMESTAMP类型
DateOnlyTypeHandlerDate(java.util)DATE类型
TimeOnlyTypeHandlerDate(java.util)TIME类型
SqlTimestampTypeHandlerTimestamp(java.sql)TIMESTAMP类型
SqlDateTypeHandlerDate(java.sql)DATE类型
SqlTimeTypeHandlerTime(java.sql)TIME类型
ObjectTypeHandler任意其他或未指定类型
EnumTypeHandlerEnumeration类型VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。

5.5、mappers(映射配置)

加载单个的映射文件(mapper代理方式的dao)
  • 方式一:

    	<!-- 方式一:通过的映射文件来加载单个映射文件 -->
    	<mapper resource="mapper/userMapper.xml"/>
    
  • 方式二:

    	<!-- 方式二:通过mapper接口来加载单个映射文件
    			需要遵循的规范:需要把mapper接口的类名和mapper.xml的映射文件名保持一致,
    						并且再一个目录中,上边规范的前提是:使用mapper代理方法。-->
    		<mapper class="com.zwd.mybatis.mapper.UserMapper"/>
    

在这里插入图片描述

批量加载(mapper代理方式的dao)-推荐使用
	<!-- 方式三:批量加载mapper映射文件
					指定mapper接口的包名,mybatis自动扫描包下所有mapper接口进行加载。
					需要遵循的规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,
									且在一个目录中。
					规范的前提:使用的是mapper代理方法。 -->
		<package name="com.zwd.mybatis.mapper"/>

在这里插入图片描述

6、输入映射(parameterType)

通过parameterType指定输入参数的类型,类型可以使简单类型,hashMap,简单的pojo对象、pojo的包装类型

6.1、传递简单类型

	<!-- 1、通过id查询用户的信息-->
	<select id="findUserById" parameterType="int"
		resultType="com.zwd.mybatis.po.User">
		select * from user where id = #{id}
	</select>

6.2、传递pojo对象

<!--2、 插入用户 -->
	<insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
		insert into
		user(username,birthday,sex,address)
		values(#{username},#{birthday},#{sex},#{address})
	</insert>

6.3、传递pojo的包装类型

需求
完成客户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息,其他信息,比如商品、订单等。)
定义包装类型pojo
package com.zwd.mybatis.po;
/**
 * 用户的包装类型
 * @author Administrator
 *
 */
public class UserQueryVo {
	
	//这里包装所需要的查询条件
	
	//用户查询条件
	private UserCustomer user;

	public UserCustomer getUser() {
		return user;
	}

	public void setUser(UserCustomer user) {
		this.user = user;
	}
	//可以包装其他的查询条件,订单、商品
}


package com.zwd.mybatis.po;
/**
 * 用户的扩展类
 * @author Administrator
 *
 */
public class UserCustomer extends User{

	//可以扩展用户的信息
}

mapper.xml
	<!-- 3、用户的综合查询 -->
	<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user where sex=#{user.sex} and username like'${user.username}'
	</select>
<?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进行分类化管理,理解sql隔离 
注意:使用mapper代理开发,namespace有特殊重要的作用,namespace等于mapper接口的全限定类名。 -->

<mapper namespace="com.zwd.mybatis.mapper.UserMapper">

	<!-- 1、通过id查询用户的信息-->
	<select id="findUserById" parameterType="int"
		resultType="com.zwd.mybatis.po.User">
		select * from user where id = #{id}
	</select>

	<!--2、 插入用户 -->
	<insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
		insert into
		user(username,birthday,sex,address)
		values(#{username},#{birthday},#{sex},#{address})
	</insert>
	
	<!-- 3、用户的综合查询 -->
	<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user where sex=#{user.sex} and username like'${user.username}'
	</select>

</mapper> 
mapper.java
/**
	 * 综合查询用户
	 * <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user where sex=#{user.sex} and username like'${user.username}'
	*  </select>
	 */
	public List<UserCustomer> findUserList(UserQueryVo userVo); 
package com.zwd.mybatis.mapper;

import java.util.List;

import com.zwd.mybatis.po.User;
import com.zwd.mybatis.po.UserCustomer;
import com.zwd.mybatis.po.UserQueryVo;

/**
 * 用户持久层接口
 * @author Administrator
 *
 */
public interface UserMapper {

	/**
	 * 根据id查询用户
	 * @param id
	 * @return
	 * <select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
	 */
	public User findUserById(Integer id);
	
	/**
	 * 插入用户
	 * @param user
	 * <insert id="insertUser" parameterType="com.zwd.mybatis.po.User">
	 */
	public void insertUser(User user);
	
	/**
	 * 综合查询用户
	 * <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user where sex=#{user.sex} and username like'${user.username}'
	*  </select>
	 */
	public List<UserCustomer> findUserList(UserQueryVo userVo); 
}

测试
	/**
	 * 综合查询
	 * @throws IOException 
	 */
	@Test
	public void findUserList() throws IOException{
//		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//创建UserMapper对象,mybatis自动生成mapper代理对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		//创建包装对象,设置查询条件
		UserQueryVo userVo = new UserQueryVo();
		UserCustomer userCustomer = new UserCustomer();
		userCustomer.setSex("女");
		userCustomer.setUsername("赵文迪");
		userVo.setUser(userCustomer);
		
		//调用userMapper的方法
		List<UserCustomer> userList = userMapper.findUserList(userVo);
		for (UserCustomer user : userList) {
			System.out.println(user);
		}
	}

6.4、传递HashMap(和传递简单的pojo对象一样)

通过键来取值

mapper.xml

<!-- 4、传递hashMap -->
	<select id="findUserListByHashMap" parameterType="java.util.HashMap" 
			resultType="com.zwd.mybatis.po.User">
		select * from user where sex=#{sex} and username like '${username}'
	</select>

mapper.java

	/**
	 * 输入参数为HashMap
	 * <!-- 4、传递hashMap -->
		<select id="findUserListByHashMap" parameterType="java.util.HashMap" 
				resultType="com.zwd.mybatis.po.User">
			select * from user where sex=#{sex} and username like '${username}'
		</select>
	 * @return
	 */
	public List<User> findUserListByHashMap(HashMap map);

测试

	@Test
	public void findByHashMap(){
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		HashMap map = new HashMap<>();
		map.put("sex", "女");
		map.put("username", "赵文迪");
		
		List<User> list = userMapper.findUserListByHashMap(map);
		for (User user : list) {
			System.out.println(user);
		}
	}

7、输出映射

7.1、resultType

使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功

​ 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。

​ 如果查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

输出简单类型
mapper.xml
<!-- 5、输出简单类型:用户信息的综合查询总数 -->
	<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
			resultType="int">
		select count(*) from user where sex=#{user.sex} and username like'${user.username}'
	</select>
mapper.java
	/**
	 * 	<!-- 5、输出简单类型:用户信息的综合查询总数 -->
	<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
			resultType="int">
		select count(*) from user where sex=#{user.sex} and username like'${user.username}'
	</select>

	 * @param userVo
	 * @return
	 */
	public int findUserCount(UserQueryVo userVo);
测试
	/**
	 * 输出类型为简单类型
	 */
	@Test
	public void findUserCount(){
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//创建UserMapper对象,mybatis自动生成mapper代理对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		//创建包装对象,设置查询条件
		UserQueryVo userVo = new UserQueryVo();
		UserCustomer userCustomer = new UserCustomer();
		userCustomer.setSex("女");
		userCustomer.setUsername("赵文迪");
		userVo.setUser(userCustomer);
		
		//调用userMapper的方法
		int userCount = userMapper.findUserCount(userVo);
		System.out.println(userCount);
	}
输出pojo对象和pojo对象的列表

​ 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。在mapper.java指定的方法返回值类型不一样。

<!--返回单个pojo对象-->
<select id="findUserById" parameterType="int"
		resultType="com.zwd.mybatis.po.User">
	select * from user where id = #{id}
</select>
<!--返回一个pojo列表-->
<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
		resultType="com.zwd.mybatis.po.UserCustomer">
	select * from user where sex=#{user.sex} and username like'${user.username}'
</select>

​ 1、输出当个pojo对象,方法返回值是单个对象类型。

public User findUserById(Integer id);

​ 2、输出pojo对象list,方法返回值是List

	public List<UserCustomer> findUserList(UserQueryVo userVo); 

​ 生成的动态代理对象中是根据mapper方法的返回值类型确定确定是调用selectOne()还是selectList()

输出HashMap

​ 输出pojo对象可以改用hashmap输出类型,将输出的字段名称作为map的key,value为字段值。

7.2、resultMap

mybatis中使用resultMap完成高级输出结果映射

resultMap使用方法

​ 如果查询出来的列名和pojo的属性名不一致,通过定义个resultMap对列名和pojo属性名之间作一个映射关系。

​ 1、定义resultMap

​ 2、使用resultMap作为statement的输出映射类型

对下列sql语句使用pojo类User作为输出映射
select id id_ , username username_ from user where id = #{id}

​ 由于查询出来的列名和User中的属性名不一致,所以不能使用resultType输出类型。需要使用resultMap

mapper.xml
<!-- 6、resultMap:通过id查询用户的信息-->
	
	<!-- 6.1、定义resultMap
			将select id id_ , username username_ from user和User类中的属性作一个映射关系。
			type:resultType最终映射的java对象类型,可以使用别名。
			id:对resultType的唯一标识 -->
	<resultMap type="com.zwd.mybatis.po.User" id="userResultMap">
	
	 <!-- id表示查询结果集中的唯一标识:column:查询出来的列名;property:type指定的pojo类型中的属性名 -->
		<id column="id_" property="id"/>
	 <!-- result表示查询结果集中的普通列名:column:查询出来的列名;property:type指定的pojo类型中的属性名 -->	
		<result column="username_" property="username"/>
	
	</resultMap>
	
	<!-- 6.2、使用resultMap作为输出类型 -->
	<select id="findUserByIdResultMap" parameterType="int"
		resultMap="userResultMap">
		select id id_ , username username_ from user where id = #{id}
	</select>
	<!-- 6、resultMap:通过id查询用户的信息-->
	
	<!-- 6.1、定义resultMap -->
	<resultMap type="com.zwd.mybatis.po.User" id="userResultMap">
		<id column="id_" property="id"/>
		<result column="username_" property="username"/>
	</resultMap>
	
	<!-- 6.2、使用resultMap作为输出类型:
			resultMap:指定定义的resultMap的id,如果这个resultMap在其他的mapper文件中,前边需要加namespace -->
	<select id="findUserByIdResultMap" parameterType="int"
		resultMap="userResultMap">
		select id id_ , username username_ from user where id = #{id}
	</select>
mapper.java
	/**
	 * 	<!-- 6.2、使用resultMap作为输出类型 -->
	<select id="findUserByIdResultMap" parameterType="int"
		resultMap="userResultMap">
		select id id_ , username username_ from user where id = #{id}
	</select>

	 * @param id
	 * @return
	 */
	public User findUserByIdResultMap(int id);
测试
	
	@Test
	/**
	 * resultMap
	 */
	public void findUserByIdResultMap() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		User user = userMapper.findUserByIdResultMap(25);
		System.out.println(user);
		
	}

8、动态sql

8.1、需求

​ 用户信息综合查询列表和用户信息查询列表总数这两个statement的定义使用动态sql。

​ 对查询条件进行判断,如果输入参数不为空才进行查询条件拼接。(如果条件给的值为null,拼接后执行报错)

8.2、mapper.xml
<!-- 3、用户的综合查询 -->
	<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user
		<where><!-- 自动去掉条件中的第一个and -->
			<if test="user!=null">
				<if test="user.sex!=null and user.sex!=''">
					sex=#{user.sex}
				</if>
				<if test="user.username!=null and user.username!=''">
					and username like'${user.username}'
				</if>
			</if>
		</where>
	</select>

	<!-- 5、输出简单类型:用户信息的综合查询总数 -->
	<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
			resultType="int">
		select count(*) from user
		<where><!-- 自动去掉条件中的第一个and -->
			<if test="user!=null">
				<if test="user.sex!=null and user.sex!=''">
					sex=#{user.sex}
				</if>
				<if test="user.username!=null and user.username!=''">
					and username like'${user.username}'
				</if>
			</if>
		</where>
	</select>
8.3、mapper.java
/**
	 * 综合查询用户
	 * <select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user where sex=#{user.sex} and username like'${user.username}'
	*  </select>
	 */
	public List<UserCustomer> findUserList(UserQueryVo userVo); 

	
	/**
	 * 	<!-- 5、输出简单类型:用户信息的综合查询总数 -->
	<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
			resultType="int">
		select count(*) from user where sex=#{user.sex} and username like'${user.username}'
	</select>

	 * @param userVo
	 * @return
	 */
	public int findUserCount(UserQueryVo userVo);
8.5、测试

​ 在测试时可以使sql的条件参数值为null。

	/**
	 * 综合查询
	 * @throws IOException 
	 */
	@Test
	public void findUserList() throws IOException{
//		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//创建UserMapper对象,mybatis自动生成mapper代理对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		//创建包装对象,设置查询条件
		UserQueryVo userVo = new UserQueryVo();
		UserCustomer userCustomer = new UserCustomer();
		userCustomer.setSex("女");
//		userCustomer.setUsername("赵文迪");
		userVo.setUser(userCustomer);
		
		//调用userMapper的方法
		List<UserCustomer> userList = userMapper.findUserList(userVo);
		for (UserCustomer user : userList) {
			System.out.println(user);
		}
	}
8.6、sql片段
需求

​ 将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其他的statement中就可以引用该sql片段。

定义sql片段
	<!-- 定义sql片段
			经验:是基于单表来定义sql片段,这样的话sql片段重用性才高;
				 在sql片段汇总不要包括where -->
	<sql id="query_user_where">
		<if test="user!=null">
				<if test="user.sex!=null and user.sex!=''">
					sex=#{user.sex}
				</if>
				<if test="user.username!=null and user.username!=''">
					and username like'${user.username}'
				</if>
			</if>
	</sql>
引用sql片段
<!-- 3、用户的综合查询 -->
	<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user
		<where>
			
		<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
			<include refid="query_user_where"></include>
			<!-- 还可以引用多个sql片段 -->
		<!-- 	<include refid="query_user_where1"></include> -->
		
		</where>
	</select>

	<!-- 5、输出简单类型:用户信息的综合查询总数 -->
	<select id="findUserCount" parameterType="com.zwd.mybatis.po.UserQueryVo"
			resultType="int">
		select count(*) from user
		<where>
			<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
			<include refid="query_user_where"></include>
		</where>
	</select>
8.7、foreach
需求
	在用户查询列表和查询总数的statement中增加多个id输入查询。
	sql语句如下:
		(1)select * from user where id = 25 or id = 1 or id = 27
    	(2)select * from user where id in(1,25,27)

修改输入参数类型

​ 在输入参数类型中添加一个list集合,用于存储多个id。UserQueryVo.java

package com.zwd.mybatis.po;

import java.util.List;

/**
 * 用户的包装类型
 * @author Administrator
 *
 */
public class UserQueryVo {
	
	//这里包装所需要的查询条件
	
	//添加list集合,用于存储多个id
	private List<Integer> ids;
	
	//用户查询条件
	private UserCustomer user;

	public UserCustomer getUser() {
		return user;
	}

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

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

	public void setIds(List<Integer> ids) {
		this.ids = ids;
	}
	
	//可以包装其他的查询条件,订单、商品
	
}
mapper.xml
<!-- 定义sql片段
			经验:是基于单表来定义sql片段,这样的话sql片段重用性才高;
				 在sql片段汇总不要包括where -->
	<sql id="query_user_where">
		<if test="user!=null">
				<if test="user.sex!=null and user.sex!=''">
					sex=#{user.sex}
				</if>
				<if test="user.username!=null and user.username!=''">
					and username like'${user.username}'
				</if>
				
				<!-- select * from user where 1=1 and (id = 1 or id=25 or id = 27) -->
				<if test="ids!=null">
					<!-- 使用foreach遍历传入的ids
						 collection:指定输入对象中的集合属性名称
						 item:每个遍历生产的对象名称
						 open:开始遍历时拼接的串
						 close:结束遍历时拼接的串
						 separator:遍历的两个对象中需要拼接的串
						 -->
		<!-- 使用下列语句拼接:and (id = 1 or id=25 or id = 27) -->
		<foreach collection="ids" item="user_id" open="and (" close=")" separator="or">
				id=#{user_id}
		</foreach>
        
        <!--使用下列语句拼接:and id in(1,25,27) -->
	<foreach collection="ids" item="user_id" open="and id in(" close=")" separator=",">
		 		#{user_id}
	</foreach>
				</if>
			</if>
	</sql>

<!-- 3、用户的综合查询 -->
	<select id="findUserList" parameterType="com.zwd.mybatis.po.UserQueryVo" 
			resultType="com.zwd.mybatis.po.UserCustomer">
		select * from user
		<where>
		<!-- 引用sql片段的id:如果refid指定的id不在本mapper文件中,需要前边加namespace -->
			<include refid="query_user_where"></include>
			<!-- 还可以引用多个sql片段 -->
		<!-- 	<include refid="query_user_where1"></include> -->
		</where>
	</select>
测试
/**
	 * 综合查询
	 * @throws IOException 
	 */
	@Test
	public void findUserList() throws IOException{
//		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//创建UserMapper对象,mybatis自动生成mapper代理对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		//创建包装对象,设置查询条件
		UserQueryVo userVo = new UserQueryVo();
		UserCustomer userCustomer = new UserCustomer();
		userCustomer.setSex("女");
		userCustomer.setUsername("赵文迪");
		userVo.setUser(userCustomer);
		
		List<Integer> ids = new ArrayList<>();
		ids.add(1);
		ids.add(25);
		ids.add(27);
		
		userVo.setIds(ids);
		
		//调用userMapper的方法
		List<UserCustomer> userList = userMapper.findUserList(userVo);
		for (UserCustomer user : userList) {
			System.out.println(user);
		}
	}

第二天

订单商品数据模型分析
高级结果集映射(一对一、一对多、多对多)
mybatis延迟加载
mybatis查询缓存(一级缓存、二级缓存)
mybatis和spring进行整合
mybatis的逆向工程

1、第一天复习

Mybatis框架执行过程:

1、配置mybatis的配置文件:SqlMapConfig.xml(名称不固定)
2、通过配置文件,加载mybatis运行环境,创建SqlSessionFactory会话工厂。
	SqlSessionFactory在实际使用时按单例方式。
3、通过SqlSessionFactory创建SqlSession
	SqlSession是一个面向用户的接口(提供操作数据库的方法),实例对象是线程不安全的,建议sqlSession应用场合在方法体内。
4、调用sqlSession的方法去操作数据库。
5、释放资源,关闭SqlSession。

mybatis开发dao的方法:

1、原始dao的方法(user.xml;userDao.java;userDaoImpl.java)
	需要编写dao接口和dao实现类
	需要在dao实现类中注入一个SqlSessionFactory会话工厂
2、mapper代理开发方法(建议使用;userMapper.xml;userMapper.java)
	需要编写dao接口和mapper映射文件。
	编写mapper.xml和mapper.java需要遵循一个开发规范:
		(1)mapper.xml中的namespace就是mapper.java的类全路径名。
		(2)mapper.xml中statement的id和mapper.java中方法名一致。
		(3)mapper.xml中parameterType指定输入参数的类型和mapper.java的方法输入参数类型一致。
		(4)mapper.xml中statement的resultType/resultMap指定输出结果的类型和mapper.java的方法			返回值类型一致。

输入映射:

	parameterType:指定输入参数类型可以简单类型、pojo、hashMap。
对于综合查询,建议parameterType使用包装的pojo,有利于系统扩展。

输出映射:

	resultType:
		查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。
	resultMap:
		可以通过resultMap完成一些高级映射。
		如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。
		高级映射:
			将关联查询的列映射到一个pojo属性中。(一对一)
			将关联查询的列映射到一个List<pojo>中。(一对多)

**动态sql:**if判断、where、foreach、sql片段。

2、课程安排

1、对订单商品数据模型进行分析
2、高级映射:
	实现一对一、一对多、多对多查询。
	延迟加载。
3、查询缓存:
	一级缓存
	二级缓存
4、mybatis和spring整合。
5、逆向工程。

3、对订单商品数据模型进行分析

数据库表:

/*
Navicat MySQL Data Transfer

Source Server         : ZWD
Source Server Version : 50711
Source Host           : localhost:3306
Source Database       : mybatis

Target Server Type    : MYSQL
Target Server Version : 50711
File Encoding         : 65001

Date: 2019-11-20 10:46:15
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `items`
-- ----------------------------
DROP TABLE IF EXISTS `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;

-- ----------------------------
-- Records of items
-- ----------------------------
INSERT INTO `items` VALUES ('1', '台式机', '3000.0', '该电脑质量非常好!!!!', null, '2015-02-03 13:22:53');
INSERT INTO `items` VALUES ('2', '笔记本', '6000.0', '笔记本性能好,质量好!!!!!', null, '2015-02-09 13:22:57');
INSERT INTO `items` VALUES ('3', '背包', '200.0', '名牌背包,容量大质量好!!!!', null, '2015-02-06 13:23:02');

-- ----------------------------
-- Table structure for `orderdetail`
-- ----------------------------
DROP TABLE IF EXISTS `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 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orderdetail
-- ----------------------------

-- ----------------------------
-- Table structure for `orders`
-- ----------------------------
DROP TABLE IF EXISTS `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=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '10', '11', '2019-09-18 09:41:28', '33');

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `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=30 DEFAULT CHARSET=utf8;

在这里插入图片描述

用户表user和订单表orders:

​ user–>orders:一个用户可以创建多个订单;一对多。

​ orders–>user:一个订单只由一个用户创建;一对一。

订单表orders和订单明细表orderdetail:

​ orders–>ordertail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail中记录;一对多。

​ ordertail–>orders:一个订单明细只能包括在一个订单中;一对一

订单明细orderdetail–>商品表items:

​ ordertail–>items:一个订单明细只对应一个商品信息;一对一。

​ items–>ordertail:一个商品可以包括在多个订单明细中;一对多。

4、一对一查询

4.1、需求

​ 查询订单信息,关联查询创建订单的用户信息。

select orders.*,`user`.username,user.sex,`user`.address 
FROM user,orders
where `user`.id = orders.user_id

4.2、方法一:resultType

添加一个pojo类:继承Orders类,添加用户的部分属性。

根据返回值的结果,po类Orders.java中的属性已经不满足结果字段,需要封装一个pojo类OrdersCustom.java,用于接收返回值的字段。

package com.zwd.mybatis.po;
/**
 * 订单的扩展类
 * @author Administrator
 *
 */
//通过此类映射订单和用户查询的结果,让此类继承包括字段较多的pojo类
public class OrdersCustom extends Orders {
	//添加用户的属性
	private String username;
	private String sex;
	private String address;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
}
mapper.xml

​ 记住在SqlMapConfig.xml中配置映射文件。

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

<mapper namespace="com.zwd.mybatis.mapper.OrdersMapper">
	<select id="findOrders" parameterType="int" resultType="com.zwd.mybatis.po.OrdersCustom">
		select orders.*,`user`.username,user.sex,`user`.address 
		from user,orders
		where `user`.id = #{id}
	</select>
</mapper>
mapper.java
package com.zwd.mybatis.mapper;

import java.util.List;

import com.zwd.mybatis.po.OrdersCustom;

public interface OrdersMapper {
	public List<OrdersCustom> findOrders(int orderId);
}

测试
	@Test
	public void findOrders() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
		List<OrdersCustom> orders = ordersMapper.findOrders(10);
		
		System.out.println(orders);
	}

对于上诉例子的问题:数据库中orders表中的字段:user_id与po类Order.java中的属性:userId名称不一致,所以映射的时候该字段不能映射成功,还是得用resultMap来重新对应字段和属性之间的关系。

4.3、方法二:resultMap

不添加新的pojo类,将User的类对象添加到Orders类中,作为成员变量。

不添加pojo类,使用resultMap将查询结果中的订单信息映射到Orders对象中,需要在Orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。

Orders.java

package com.zwd.mybatis.po;

import java.sql.Timestamp;
import java.util.Date;

/*******************************************************************************
 * javaBeans
 * orders --> Orders 
 * <table explanation>
 * @author 2019-11-20 11:38:05
 * 
 */	
public class Orders implements java.io.Serializable {
	//field
	/**  **/
	private int id;
	/** 下单用户id **/
	private int userId;
	/** 订单号 **/
	private String number;
	/** 创建订单时间 **/
	private Timestamp createtime;
	/** 备注 **/
	private String note;
	
	//添加User对象
	private User user;
	
	
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	
	//method
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public Timestamp getCreatetime() {
		return createtime;
	}
	public void setCreatetime(Timestamp createtime) {
		this.createtime = createtime;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	@Override
	public String toString() {
		return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
				+ ", note=" + note + ", user=" + user + "]";
	}
}
mapper.xml
	<!-- 方式二: -->
	<resultMap type="com.zwd.mybatis.po.Orders" id="ordersResultMap">
		<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.zwd.mybatis.po.User">
			<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
			<id column="user_id" property="id"/>
			<result column="username" property="username"/>
			<result column="sex" property="sex"/>
			<result column="address" property="address"/>
		
		</association>
	</resultMap>
	
	
	<select id="findOrders1" parameterType="int" resultMap="ordersResultMap">
		select orders.*,`user`.username,user.sex,`user`.address 
		from user,orders
		where `user`.id = #{id}
	</select>
mapper.java
public List<Orders> findOrders1(int orderId);
测试
	@Test
	public void findOrders() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
		//List<OrdersCustom> orders = ordersMapper.findOrders(10);
		
		List<Orders> orders1 = ordersMapper.findOrders1(10);
		
		System.out.println(orders1);
	}

5、一对多

需求

​ 查询某订单的订单明细信息。(包括用户信息)

select orders.*,`user`.username,user.sex,`user`.address,orderdetail.id orderdetail_id,orderdetail.items_num
FROM user,orders,orderdetail
where `user`.id = orders.user_id and orderdetail.orders_id= orders.id

Orders.java类中添加一Orderdetail类的集合属性

//orders->orderdetail一对多关系映射:添加一个orderdetail集合
	private List<Orderdetail> orderdetail;
package com.zwd.mybatis.po;

import java.sql.Timestamp;
import java.util.List;

/*******************************************************************************
 * javaBeans
 * orders --> Orders 
 * <table explanation>
 * @author 2019-11-20 11:38:05
 * 
 */	
public class Orders implements java.io.Serializable {
	//field
	/**  **/
	private int id;
	/** 下单用户id **/
	private int userId;
	/** 订单号 **/
	private String number;
	/** 创建订单时间 **/
	private Timestamp createtime;
	/** 备注 **/
	private String note;
	
	//orders->user一对一关系映射:添加User对象
	private User user;
	
	//orders->orderdetail一对多关系映射:添加一个orderdetail集合
	private List<Orderdetail> orderdetail;
	
	
	public List<Orderdetail> getOrderdetail() {
		return orderdetail;
	}
	public void setOrderdetail(List<Orderdetail> orderdetail) {
		this.orderdetail = orderdetail;
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	
	//method
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public Timestamp getCreatetime() {
		return createtime;
	}
	public void setCreatetime(Timestamp createtime) {
		this.createtime = createtime;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	@Override
	public String toString() {
		return "Orders [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
				+ ", note=" + note + ", user=" + user + "]";
	}
}

mapper.xml

<!-- orders->orderdetail:一对多的关系映射 -->	
	
	<resultMap type="com.zwd.mybatis.po.Orders" id="ordersDetailsResultMap">
		<!-- 订单信息 -->
		<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="user" javaType="com.zwd.mybatis.po.User">
			<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
			<id column="user_id" property="id"/>
			<result column="username" property="username"/>
			<result column="sex" property="sex"/>
			<result column="address" property="address"/>
		</association>
		
		<!-- 订单明细信息 -->
		<!-- 一个订单关联查询出了多条订单明细,要使用collection进行映射。
			 collection:对关联查询到多条记录映射到集合对象中。
			 	property:将关联查询到的多条记录映射到Orders中的哪个属性。
			 	ofType:指定映射到哪个pojo类型的list集合-->
		<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
			<id column="orderdetail_id" property="id"/>
			<result column="items_num" property="itemsNum"/>
		</collection>
	
	</resultMap>
	
	<select id="findOrdersUserDetails"  resultMap="ordersDetailsResultMap">
		select orders.*,`user`.username,user.sex,`user`.address,orderdetail.id orderdetail_id,orderdetail.items_num
		FROM user,orders,orderdetail
		where `user`.id = orders.user_id and orderdetail.orders_id= orders.id
	</select>

改进:定义的resultMap 可以使用extends继承,所以这里可以继承一对一查询中定义的resultMap。extends="ordersResultMap"

<!-- orders->orderdetail:一对多的关系映射 -->	
	
	<resultMap type="com.zwd.mybatis.po.Orders" id="ordersDetailsResultMap" extends="ordersResultMap">
		
		<!-- 订单明细信息 -->
		<!-- 一个订单关联查询出了多条订单明细,要使用collection进行映射。
			 collection:对关联查询到多条记录映射到集合对象中。
			 	property:将关联查询到的多条记录映射到Orders中的哪个属性。
			 	ofType:指定映射到哪个pojo类型的list集合-->
		<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
			<id column="orderdetail_id" property="id"/>
			<result column="items_num" property="itemsNum"/>
		</collection>
	
	</resultMap>


<resultMap type="com.zwd.mybatis.po.Orders" id="ordersResultMap">
		<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.zwd.mybatis.po.User">
			<!-- id:关联查询用户的唯一标识:表orders中的外键对应user类中的哪个属性 -->
			<id column="user_id" property="id"/>
			<result column="username" property="username"/>
			<result column="sex" property="sex"/>
			<result column="address" property="address"/>
		
		</association>
	</resultMap>

mapper.java

	/**
	 * 一对多:查询订单以及订单的详细信息
	 * @return
	 */
	public List<Orders> findOrdersUserDetails();

测试

	@Test
	public void findOrdersUserDetails() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
		//List<OrdersCustom> orders = ordersMapper.findOrders(10);
		
		List<Orders> orders1 = ordersMapper.findOrdersUserDetails();
		
		System.out.println(orders1);
	}

6、多对多

需求

​ 查询用户(user)及用户购买的商品的信息(items)。

​ user和items的关系是多对多。

​ user–>orders:一对多;orders–>orderdetail:一对多;ordertail–>items:一对一。

在User类中添加一个Orders类的list;在Orders类中添加Orderdetail类的list;在Orderdetail中添加Items类的成员变量。

select 
	orders.*,
	`user`.username,user.sex,`user`.address,
	orderdetail.id orderdetail_id,orderdetail.items_num,
	items.id items_id,items.`name` items_name,items.detail items_detail,items.price items_price
FROM user,orders,orderdetail,items
where `user`.id = orders.user_id and orderdetail.orders_id = orders.id 
			and orderdetail.items_id = items.id

mapper.xml


<!-- user->items:多对多关系映射 -->	
	<resultMap type="com.zwd.mybatis.po.User" id="userItemsResultMap">
		<!-- 用户信息 -->
		<id column="user_id" property="id"></id>
		<result column="username" property="username"/>
		<result column="sex" property="sex"/>
		<result column="address" property="address"/>
		
		<!-- 订单信息 -->
		<collection property="ordersList" ofType="com.zwd.mybatis.po.Orders">
			<id column="id" property="id"/>
			<result column="number" property="number"/>
			<result column="createtime" property="createtime"/>
			<result column="note" property="note"/>
			
			<!-- 订单明细信息 -->
			<collection property="orderdetail" ofType="com.zwd.mybatis.po.Orderdetail">
				<id column="orderdetail_id" property="id"/>
				<result column="items_num" property="itemsNum"/>
				
				<!-- 商品信息 -->
				<association property="items" javaType="com.zwd.mybatis.po.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="userItemsResultMap">
		select 
			orders.*,
			`user`.username,user.sex,`user`.address,
			orderdetail.id orderdetail_id,orderdetail.items_num,
			items.id items_id,items.`name` items_name,items.detail items_detail,items.price items_price
		FROM user,orders,orderdetail,items
		where `user`.id = orders.user_id and orderdetail.orders_id = orders.id 
				and orderdetail.items_id = items.id
	</select>

mapper.java

	/**
	 * 多对多:查询用户及用户购买的商品的商品信息
	 * @return
	 */
	public List<User> findUserItems();

测试

@Test
	public void findUserItems() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		
		OrdersCustomMapper ordersMapper = sqlSession.getMapper(OrdersCustomMapper.class);
		//List<OrdersCustom> orders = ordersMapper.findOrders(10);
		
		List<User> user = ordersMapper.findUserItems();
		
		System.out.println(user);
	}

resultType和resultMap总结

7、延迟加载

resultMap可以实现高级映射(使用associationcollection 实现一对一、一对多映射)。

association、collection具备延迟加载功能。

​ 延迟加载:先从单表查询,需要时再从关联表去关联查询。大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

使用association实现延迟加载

​ 1、通过association标签的selectcolumn实现延迟加载。(collection实现方式一致)

<!-- 实现对用户信息的延迟加载
	select:指定延迟加载需要执行的statement的id,如果该statement没有在当前文件中,需要指定namepace
	column:订单信息中关联用户查询需要的关联字段;user_id -->
<association property="user" javaType="com.zwd.mybatis.po.User" 
					 select="com.zwd.mybatis.mapper.UserMapper.findUserById" 							 column="user_id">
</association>

​ 2、另外还需要修改全局配置文件SqlMapConfig.xml,添加设置。

设置项描述允许值默认值
lazyLoadingEnabled全局性设置懒加载。如果设为false,则所有相关联的都会被初始化加载true|falsefalse
aggressiveLazyLoading当设置为true的时候,懒加载的对象可能被任何懒属性全部加载。否则每个属性都按需加载。true|falsetrue
	<!-- 配置懒加载 -->
	<settings>
		<!-- 打开延迟加载的开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 将积极加载改为消极加载即按需加载 -->
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
需求

​ 查询订单并且关联查询用户信息。

​ 同样需要在Orders类中添加User的成员变量。

mapper.xml、mapper.java

​ 需要定义两个mapper的方法对应的statement。

​ 1、只查询订单信息:select * from orders

​ 2、关联查询用户信息:select * from user where user.id = #{orders.user_id}

通过上边查询到的订单信息中的user_id去关联查询用户信息。

2、关联查询用户信息:select * from user where user.id = #{orders.user_id}

userMapper.xml

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

<mapper namespace="com.zwd.mybatis.mapper.UserMapper">
	
	<select id="findUserById" parameterType="int" resultType="com.zwd.mybatis.po.User">
		select * 
		from user
		where user.id = #{id}
	</select>

</mapper>

userMapper.java

package com.zwd.mybatis.mapper;

import com.zwd.mybatis.po.User;

public interface UserMapper {
	/**
	 * 通过id查找用户
	 * @param id
	 * @return
	 */
	public User findUserById(int id);
}

1、只查询订单信息:select * from orders

OrdersCustomMapper.xml

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

<mapper namespace="com.zwd.mybatis.mapper.OrdersCustomMapper">
	
<!-- 延迟加载-->
	<!-- 延迟加载的resultMap -->
	<resultMap type="com.zwd.mybatis.po.Orders" id="ordersUserResultMap">
		<!-- 对订单信息进行映射配置 -->
		<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,如果该statement没有在当前文件中,需要指定namepace
	column:订单信息中关联用户查询需要的关联字段;user_id -->
		<association property="user" javaType="com.zwd.mybatis.po.User" 
					 select="com.zwd.mybatis.mapper.UserMapper.findUserById" 							 column="user_id">
		</association>
	</resultMap>
    
	<select id="findOrdersUser" resultMap="ordersUserResultMap">
		select *
		from orders
	</select>

</mapper>

OrdersCustomMapper.java

package com.zwd.mybatis.mapper;

import java.util.List;

import com.zwd.mybatis.po.Orders;
public interface OrdersCustomMapper {
	/**
	 * 使用延迟加载:查询订单及该订单的用户信息
	 * @return
	 */
	public List<Orders> findOrdersUser();
	
}
修改SqlMapConfig.xml配置文件
<!-- 配置懒加载 -->
	<settings>
		<!-- 打开延迟加载的开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 将积极加载改为消极加载即按需加载 -->
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
测试
@Test
	public void findOrdersUser() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		OrdersCustomMapper ordersCustomMapper = sqlSession.getMapper(OrdersCustomMapper.class);
		//查询订单列表
		List<Orders> ordersUser = ordersCustomMapper.findOrdersUser();
		//遍历订单表
		for (Orders order : ordersUser) {
			//执行getUser()去查询用户信息,这里实现按需加载(懒加载)
			User user = order.getUser();
			System.out.println("order:");
			System.out.println(order);
			System.out.println("user:");
			System.out.println(user);
		}
	}

8、查询缓存

什么是查询缓存:

​ mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

​ mybatis提供一级缓存和二级缓存。

在这里插入图片描述

​ 一级缓存是SqlSession级别的缓存。在操作数据库是需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

​ 二级缓存是mapper级别的缓存,多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。

为什么要用缓存:

​ 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

8.1、一级缓存

一级缓存的工作原理

在这里插入图片描述

​ 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

​ 如果sqlSession去执行commit操作(插入、更新、删除),mybatis就会清空sqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。

​ 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

一级缓存测试
/**
	 * 一级缓存测试
	 * @throws IOException
	 */
	@Test
	public void testCache1() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		
		//1、第一次查询id为1的用户
		User user = userMapper.findUserById(1);
		System.out.println(user);
		
		userMapper.findUserById(10);
		
		//2、修改用户
		user.setUsername("张明明");
		userMapper.updateUserById(user);
		//更新用户:执行commit去清空当前sqlSession的一级缓存
		sqlSession.commit();
		//3、第二次查询id为1的用户
		User user2 = userMapper.findUserById(1);
		System.out.println(user2);
		
		userMapper.findUserById(10);
		//关闭sqlSession
		sqlSession.close();
	}

8.2、二级缓存

二级缓存的工作原理

在这里插入图片描述

​ 1、首先开启mybatis的二级缓存

​ 2、sqlSession1去查询用户id为1的用户信息,查询到的用户信息会存储到userMapper的二级缓存中。(需要将sqlSession1关闭后sqlSession1.close()才会存储到二级缓存中)。

​ 3、sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存和一级缓存区别:二级缓存的范围更大,多个sqlSession可以共享一个userMapper的二级缓存区域。每一个namespace的mapper都有一个二级缓存区域。

开启二级缓存

​ Mybatis的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

SqlMapConfig.xml中设置二级缓存的总开关:

	<!-- 开启二级缓存总开关 -->
	<setting name="cacheEnabled" value="true"/>
标签描述允许值默认值
cacheEnabled对在此配置文件下的所有cache进行全局性开/关设置true|falsetrue

Usermapper.xml中添加配置:

	<!-- 在本mapper的namespace下开启二级缓存 -->
	<cache/>
pojo类实现序列化接口
二级缓存测试
	/**
	 * 二级缓存测试
	 * @throws IOException
	 */
	@Test
	public void testCache2() throws IOException{
		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		SqlSession sqlSession1 = sqlSessionFactory.openSession();
		SqlSession sqlSession2 = sqlSessionFactory.openSession();
		SqlSession sqlSession3 = sqlSessionFactory.openSession();
		
		//1、sqlSession1查询id为1的用户
		UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
		User user1 = userMapper1.findUserById(1);
		System.out.println(user1);
		//这里执行关闭操作,将sqlSession中的数据写入到二级缓存中。
		sqlSession1.close();
		
		//2、使用sqlSession3执行提交操作
		UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
		User user = userMapper3.findUserById(1);
		user.setUsername("张晓敏");
		
		userMapper3.updateUserById(user);
		//执行提交操作,清空userMapper的二级缓存
		sqlSession3.commit();
		sqlSession3.close();
		
		//3、sqlSession3查询id为1的用户
		UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
		User user2 = userMapper2.findUserById(1);
		System.out.println(user2);
		//这里执行关闭操作,将sqlSession中的数据写入到二级缓存中。
		sqlSession2.close();
	}
statement相关缓存参数
禁用二级缓存useCache

​ 在statement中设置useCache="false"可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

总结:针对每次查询都需要更新数据的sql,要设置成userCache=false,禁用二级缓存。

	<select id="findUserById" parameterType="int" 
	resultType="com.zwd.mybatis.po.User" useCache="false">
		select * 
		from user
		where user.id = #{id}
	</select>
刷新缓存flushCache(清空二级缓存)

​ 在mapper的同一个namespace中,如果有其他insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

​ 设置statement配置中的flushCache="true"属性,默认情况下为true。如果改成false则不会刷新,使用缓存是如果手动修改数据表中的查询数据会出现脏读。

<update id="updateUserById" parameterType="com.zwd.mybatis.po.User" flushCache="true">
		update user
		set user.username = #{username}
		where user.id = #{id}
	</update>

总结:一般执行完commit操作都需要刷新缓存,flushCache="true"表示刷新缓存,这样可以避免数据库脏读。

cache标签的属性

flushInterval:刷新间隔。可以被设置成任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,就是没有刷新间隔。缓存仅仅调用语句时刷新。

size:引用数目。可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

readOnly:只读。可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

8.3、mybatis和ehcache整合

​ EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

​ ehcache是一个分布式缓存框架。

分布式缓存

我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

在这里插入图片描述

​ 不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。

​ mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

整合方法

​ mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

mybatis提供二级缓存Cache接口,如下:

在这里插入图片描述

它的默认实现类:

在这里插入图片描述

通过实现Cache接口可以实现mybatis缓存数据通过其它缓存数据库整合,mybatis的特长是sql操作,缓存数据的管理不是mybatis的特长,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。

mybatis与ehcache整合步骤
加入ehcache包

在这里插入图片描述

添加缓存配置文件

classpath下添加:ehcache.xml

内容如下:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 	<diskStore path="F:\develop\ehcache" /> -->
   <defaultCache 
   	maxElementsInMemory="1000" 
   	maxElementsOnDisk="10000000"
   	eternal="false" 
   	overflowToDisk="false" 
   	timeToIdleSeconds="120"
   	timeToLiveSeconds="120" 
   	diskExpiryThreadIntervalSeconds="120"
   	memoryStoreEvictionPolicy="LRU">
   </defaultCache>
</ehcache>

属性说明:

diskStore:指定数据在磁盘中的存储位置。

defaultCache:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用defalutCache指定的的管理策略

以下属性是必须的:

maxElementsInMemory - 在内存中缓存的element的最大数目

maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大

eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断

overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

以下属性是可选的:

timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大

timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大

diskSpoolBufferSizeMB- 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.

diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。

diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作

memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

开启ehcache缓存

EhcacheCache是ehcache对Cache接口的实现:

在这里插入图片描述

修改mapper.xml文件,在cache中指定EhcacheCache。

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

根据需求调整缓存参数:

<cache type="org.mybatis.caches.ehcache.EhcacheCache" > 
        <property name="timeToIdleSeconds" value="3600"/>
        <property name="timeToLiveSeconds" value="3600"/>
        <!-- 同ehcache参数maxElementsInMemory -->
		<property name="maxEntriesLocalHeap" value="1000"/>
		<!-- 同ehcache参数maxElementsOnDisk -->
        <property name="maxEntriesLocalDisk" value="10000000"/>
        <property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>

9、mybatis和spring整合

9.1、整合思路

​ 需要spring通过单例方式管理SqlSessionFactory。

​ spring和mybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession。(spring和mybatis整合自动完成)持久层的mapper都需要由spring进行管理。

9.2、整合环境准备

  • 创建java项目

  • 导入mybatis和spring的jar包:

在这里插入图片描述

  • 项目结构

在这里插入图片描述

  • 配置mybatis的总配置文件SqlMapConfig.xml(删除对数据源的配置,有spring来管理配置)

    <?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>
    	<!-- 配置懒加载 -->
    	<settings>
    		<!-- 打开延迟加载的开关 -->
    		<setting name="lazyLoadingEnabled" value="true"/>
    		<!-- 将积极加载改为消极加载即按需加载 -->
    		<setting name="aggressiveLazyLoading" value="false"/>
    		<!-- 开启二级缓存总开关 -->
    		<setting name="cacheEnabled" value="true"/>
    	</settings>
    
    	<!-- 和spring整合后 environments配置将废除-->
    	<environments default="development">
    		<environment id="development">
    		<!-- 使用jdbc事务管理-->
    			<transactionManager type="JDBC" />
    		<!-- 数据库连接池-->
    			<dataSource type="POOLED">
    				<property name="driver" value="${jdbc.driver}" />
    				<property name="url" value="${jdbc.url}" />
    				<property name="username" value="${jdbc.user}" />
    				<property name="password" value="${jdbc.password}" />
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<mappers>
    		<mapper resource="SqlMap/User.xml"/>
    		<mapper resource="com/zwd/mybatis/mapper/UserMapper.xml"/>
    		<mapper class="com.zwd.mybatis.mapper.OrdersCustomMapper"/>
    		<!-- <mapper resource="com/zwd/mybatis/mapper/OrdersCustomMapper.xml"/> -->
    	</mappers>
    	
    </configuration>
    

    更改后:

    <?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>
    
    	<mappers>
    		<mapper resource="SqlMap/User.xml"/>
    	</mappers>
    	
    </configuration>
    

9.3、ApplicationContext.xml

配置sqlSessionFactory
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 配置IOC -->
	<!-- 配置数据源 -->
	<context:property-placeholder location="db.properties"/>
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
		<property name="dataSource" ref="dataSource"></property>
	</bean>

</beans>

9.4、准备持久层-原始dao开发

  • 项目结构

在这里插入图片描述

  • 添加User.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="test">
    	<select id="findAllUsers" resultType="com.zwd.ssm.po.User">
    		select * from user
    	</select>
    </mapper>
    
  • 添加dao接口是实现类

    接口:

    package com.zwd.ssm.dao;
    
    import java.io.IOException;
    import java.util.List;
    
    import com.zwd.ssm.po.User;
    
    public interface UserDao {
    	public List<User> findAllUsers() throws IOException;
    }
    
    
注意:dao的实现类需要继承SqlSessionDaoSupport

实现类继承SqlSessionDaoSupport类。该类提供设置sqlSessionFactory的方法和获取sqlSession的方法。

package com.zwd.ssm.dao;

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

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.mybatis.spring.support.SqlSessionDaoSupport;

import com.zwd.ssm.po.User;

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {

//	private SqlSessionFactory sqlSessionFactory;//让dao类继承SqlSessionDaoSupport类
	@Override
	public List<User> findAllUsers() throws IOException {
		
//		SqlSession sqlSession = sqlSessionFactory.openSession();
		//继承SqlSessionDaoSupport,通过getSqlSession()获取sqlSession
		SqlSession sqlSession = getSqlSession();
		List<User> users = sqlSession.selectList("test.findAllUsers");
		
		return users;
	}
}
package com.zwd.ssm.test;

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

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.zwd.ssm.dao.UserDao;
import com.zwd.ssm.po.User;

public class UserDaoImplTest {

	@Test
	public void testFindAllUsers() throws IOException {
		ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
		UserDao bean = (UserDao) ac.getBean("userDao");
		List<User> users = bean.findAllUsers();
		System.out.println(users);
	}
}

9.5、准备持久层-mapper代理开发

  • 项目结构

在这里插入图片描述

  • mapper.xml和mapper.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.zwd.ssm.mapper.UserMapper">
    	<select id="findAllUsers" resultType="com.zwd.ssm.po.User">
    		select * from user
    	</select>
    </mapper>
    
    package com.zwd.ssm.mapper;
    
    import java.util.List;
    
    import com.zwd.ssm.po.User;
    
    public interface UserMapper {
    	public List<User> findAllUsers();
    }
    
  • 在SqlMapConfig.xml中配置mapper

    <?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>
    
    	<typeAliases>
    		<package name="com.zwd.ssm.po"/>
    	</typeAliases>
    	<mappers>
    		<mapper resource="com/zwd/ssm/mapper/UserMapper.xml"/>
    	</mappers>
    	
    </configuration>
    
  • spring配置:applicationContext.xml

注意:mapper的配置:使用MapperFactoryBean的创建代理对象

注意mapper的配置:使用MapperFactoryBean的创建代理对象

  <!-- 配置mapper:
  	MapperFactoryBean:根据mapper接口生产相应的代理对象 -->
  <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  	<property name="mapperInterface" value="com.zwd.ssm.mapper.UserMapper">   		</property>
  	<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
  </bean>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 配置IOC -->
	<!-- 配置数据源 -->
	<context:property-placeholder location="db.properties"/>
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="mybatis/SqlMapConfig.xml"></property>
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置mapper:
		MapperFactoryBean:根据mapper接口生产相应的代理对象 -->
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="com.zwd.ssm.mapper.UserMapper"></property>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>

</beans>
  • 测试:

    package com.zwd.ssm.test;
    
    import java.util.List;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zwd.ssm.mapper.UserMapper;
    import com.zwd.ssm.po.User;
    
    public class UserMapperTest {
    
    	@Test
    	public void test() {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
    		UserMapper mapper = (UserMapper) ac.getBean("userMapper");
    		List<User> users = mapper.findAllUsers();
    		System.out.println(users);
    	}
    }
    
改进

​ 对于多个mapper代理对象的生成需要一个一个写。可以使用org.mybatis.spring.mapper.MapperScannerConfigurer来批量生成mapper对象。

<!-- 统一扫描mapper创建代理对象:从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册。
		遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录中。
		自动扫描出来的mapper的bean的id为mapper类名首字母小写。
		basePackage:指定扫描的包名:如果扫描多个包,每个包中间使用逗号分隔
	 -->
	 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	 	<property name="basePackage" value="com.zwd.ssm.mapper"></property>
	 	<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
	 </bean>

​ 此处用MapperScannerConfigurer批量扫描配置后,SqlMapConfig.xml可以不用再配置映射文件。

10、mybatis的逆向工程

  • 创建java项目导包:

在这里插入图片描述

  • 创建generatorConfig.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="root">
    		</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="cn.zwd.ssm.po"
    			targetProject=".\src">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    			<!-- 从数据库返回的值被清理前后的空格 -->
    			<property name="trimStrings" value="true" />
    		</javaModelGenerator>
            <!-- targetProject:mapper映射文件生成的位置 -->
    		<sqlMapGenerator targetPackage="cn.zwd.ssm.mapper" 
    			targetProject=".\src">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    		</sqlMapGenerator>
    		<!-- targetPackage:mapper接口生成的位置 -->
    		<javaClientGenerator type="XMLMAPPER"
    			targetPackage="cn.zwd.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>
    		<!-- <table schema="" tableName="sys_user"></table>
    		<table schema="" tableName="sys_role"></table>
    		<table schema="" tableName="sys_permission"></table>
    		<table schema="" tableName="sys_user_role"></table>
    		<table schema="" tableName="sys_role_permission"></table> -->
    		
    		<!-- 有些表的字段需要指定java类型
    		 <table schema="" tableName="">
    			<columnOverride column="" javaType="" />
    		</table> -->
    	</context>
    </generatorConfiguration>
    
    
  • 创建GeneratorSqlmap.java执行文件并执行。


import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

public class GeneratorSqlmap {

	public void generator() throws Exception{

		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		//指定 逆向工程配置文件
		File configFile = new File("lib/generatorConfig.xml"); 
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
				callback, warnings);
		myBatisGenerator.generate(null);

	} 
	public static void main(String[] args) throws Exception {
		try {
			GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
			generatorSqlmap.generator();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值