2.mybatis知识

Mybatis 基础
课程计划
第一部分:
1、Mybatis 的介绍
2、Mybatis 的入门
a) 使用 jdbc 操作数据库存在的问题
b) Mybatis 的架构
c) Mybatis 的入门程序
3、Dao 的开发方法
a) 原始 dao 的开发方法
b) 接口的动态代理方式
4、SqlMapConfig.xml 文件说明
第二部分:
1、输入映射和输出映射
a) 输入参数映射
b) 返回值映射
2、动态 sql
3、关联查询
a) 一对一关联
b) 一对多关联
4、Mybatis 整合 spring
【学习目标】
1,了解 Mybatis 框架的特点和架构
2,掌握 Mybatis 框架的编写
A.理解配置文件 SqlMapConfig.xml 的作用
B.理解 pojo 类和映射文件的作用
C.理解 SqlSessionFactoryBuilder 对象的作用
D.理解 SqlSessionFactory 对象的作用
E.理解 SqlSession 对象的作用
F.掌握#{}和${}的区别
G.掌握 parameterType 和 resultType 的区别
H.掌握 selectOne 和 selectList 的区别
3,能使用 Mapper 动态代理方式开发 DAO 层
A.了解动态代理模式
B.掌握映射文件和接口文件的编写方式
4.了解 SqlMapConfig.xml 的其他配置
A.了解 properties 属性配置
B.了解 typeAlises(类型别名)配置
C.了解 mappers(映射器)配置

  1. Mybatis 介绍
    MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache
    software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月
    迁移到 Github。
    MyBatis 是一个优秀的持久层框架,它对 jdbc 的操作数据库的过程进行封装,
    使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建
    connection、创建 statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代
    码。
    Mybatis 通过 xml 或注解的方式 将要执行的各种 statement(statement、
    preparedStatemnt、CallableStatement)配置起来,并通过 java 对象和 statement
    中的 sql 进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结
    果映射成 java 对象并返回。
  2. 使用 jdbc 编程问题总结
    2.1. 创建 mysql 数据库
    创建数据库,将资料中的 mybatis.sql 脚本导入到数据库中。
    导入后效果如下图:
    2.2. 创建工程
    开发环境:
    IDE:eclipse
    JDK:1.8
    1、创建一个 web 工程。
    按下图进行创建
    2、需要 mysql 的数据库驱动,在资料中找到 jar 包。
    2.3. jdbc 编程步骤:
    1、 加载数据库驱动
    2、 创建并获取数据库链接
    3、 创建 jdbc statement 对象
    4、 设置 sql 语句
    5、 设置 sql 语句中的参数(使用 preparedStatement)
    6、 通过 statement 执行 sql 并获取结果
    7、 对 sql 执行结果进行解析处理
    8、 释放资源(resultSet、preparedstatement、connection)
    2.4. jdbc 程序
    新建包 com.igeek
    创建测试类 MybatisTest
    public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
    // 加载数据库驱动
    Class.forName(“com.mysql.jdbc.Driver”);
    // 通过驱动管理类获取数据库链接
    connection =
    DriverManager.getConnection(“jdbc:mysql://localhost:3306/mybatis?cha
    racterEncoding=utf-8”, “root”, “root”);
    // 定义 sql 语句 ?表示占位符
    String sql = “select * from user where username = ?”;
    // 获取预处理 statement
    preparedStatement = connection.prepareStatement(sql);
    // 设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数
    为设置的参数值
    preparedStatement.setString(1, “王五”);
    // 向数据库发出 sql 执行查询,查询出结果集
    resultSet = preparedStatement.executeQuery();
    // 遍历查询结果集
    while (resultSet.next()) {
    System.out.println(resultSet.getString(“id”) + " " +
    resultSet.getString(“username”));
    }
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    // 释放资源
    if (resultSet != null) {
    try {
    resultSet.close();
    } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    if (preparedStatement != null) {
    try {
    preparedStatement.close();
    } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    if (connection != null) {
    try {
    connection.close();
    } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
    }
    上边使用 jdbc 的原始方法(未经封装)实现了查询数据库表记录的操作。
    2.5. jdbc 问题总结如下:
    JDBC 操作数据库的流程:
    A:准备数据库连接参数
    B:连接数据库
    C:编译 SQL
    D:设置 SQL 参数
    E:执行 SQL 获取结果
    F:遍历结果集,封装数据到 JavaBean
    G:关闭资源
    出现的问题:
    1、 数据库连接参数硬编码到 Java 代码中,如果数据库环境需要改变,需要
    重新编译,不好!
    a) 解决:把数据库连接参数写到外部配置文件,通过配置文件加载参
    数信息
    2、 SQL 语句硬编码到 Java 代码中,业务变更、SQL 优化都需要修改 SQL 语
    句,又需要重新编译,不好!
    a) 解决:把 SQL 放到外部配置文件,通过加载配置文件,解析 SQL 语

    3、 SQL 参数问题因为把 SQL 放到外部配置中,用代码加载解析,那么就引
    发以下新问题:。
    a) 需要自动判断参数类型
    b) 需要自动判断参数位置对应关系
    c) 解决方案:如何自动判断参数类型和位置呢,待定?
    4、 遍历结果集非常麻烦,问题:
    a) 需要自己判断结果字段类型
    b) 需要自己明确结果字段的名称
    c) 需要手动封装结果字段到 JavaBean 中
    d) 如果有集合还得自己封装对象到集合中
    e) 解决:希望可以字段把结果集封装到 JavaBean,如果有多行,可以
    字段封装到集合
    5、 频繁打开和创建数据库连接,比较浪费资源,效率低
    a) 解决:采用数据库连接池
    上面的这些问题,我们自己实现起来比较繁琐,现有的持久层框架中,Hibernate
    也无法解决所有问题,而且 Hibernate 的学习成本比较高,在面对复杂查询时,
    查询效率略低。
    我们需要新的技术来解决这些问题:Mybatis
  3. Mybatis 概述
    3.1. 简介:
    3.2. 官网
    官网:http://www.mybatis.org/mybatis-3/
    中文官方文档:http://www.mybatis.org/mybatis-3/zh/index.html
    3.3. 特点
    Mybatis:
    1) 支持自定义 SQL、存储过程、及高级映射
    2) 实现自动对 SQL 的参数设置
    3) 实现自动对结果集进行解析和封装
    4) 通过 XML 或者注解进行配置和映射
    5) 实现 Java 对象与数据库表的映射转换
    可以发现,MyBatis 是对 JDBC 进行了简单的封装,帮助用户进行 SQL 参数的自动
    化映射,以及结果集与 Java 对象的映射。与 Hibernate 相比,更加配置简单、灵
    活、执行效率高。但是正因为此,所以没有实现完全自动化,需要手写 SQL,这
    是优点也是缺点。
    因此,对性能要求较高的电商类项目,一般会使用 MyBatis,而对与业务逻辑复
    杂,不太在乎执行效率的传统行业,一般会使用 Hibernate
    3.4. 架构
    MyBatis 架构总结:
    1、 MyBatis 有两类配置文件:
    a) mybatis-condig.xml,是 MyBatis 的全局配置文件,包含全局配置信息,
    如数据库连接参数、插件等。整个框架中只需要一个即可。
    b) xxxMapper.xml,是映射文件,里面配置要执行的 SQL 语句,每个 SQL
    对应一个 Statement,可以有多个 Mapper.xml 文件
    2、 首先会通过 SqlSessionFactoryBuilder 来加载配置文件,生成一个
    SqlSessionFactory
    a) 会加载 mybatis-config.xml 和 mapper.xml
    b) 加载 mapper.xml 的时候,顺便会对 Sql 进行编译,形成 statement
    Mybatis配置
    MybatisConfig.xml
    Mapper.xml Mapper.xml „„
    SqlSessionFactory
    SqlSession
    Executor(执行器)
    数据库
    Mapped Statement
    SQL输入参数
    HashMap
    String、Integer
    POJO
    输入
    输出结果
    HashMap
    String、Integer
    POJO
    输出
    3、 通过 SqlSessionFactory 建立连接,获取 SqlSession 对象
    4、 MyBatis 获取要执行的 statement,进行自动参数设置
    5、 SqlSession 底层会通过 Executor(执行器)来执行编译好的 Statement,
    获取结果
    6、 SQL 的输入参数类型:
    a) POJO,普通 Java 对象
    b) HashMap,其实是 POJO 的 Map 形式, 键值对就是对象字段名和值
    c) 各种基本数据类型
    7、 查询结果的输出形式
    a) POJO,普通 Java 对象
    b) HashMap,其实是 POJO 的 Map 形式, 键值对就是对象字段名和值
    c) 各种基本数据类型
  4. Mybatis 入门程序
    4.1. mybatis 下载
    mybaits 的代码由 github.com 管理
    下载地址:https://github.com/mybatis/mybatis-3/releases
    课前资料提供的 mybatis 如下:
    mybatis-3.2.7.jar mybatis 的核心包
    lib 文件夹 mybatis 的依赖包所在
    mybatis-3.2.7.pdf mybatis 使用手册
    4.2. 业务需求
    使用 MyBatis 实现以下功能:
    根据用户 id 查询一个用户
    根据用户名称模糊查询用户列表
    添加用户
    更新用户
    删除用户
    4.3. 环境搭建
    4.3.1. 创建 web 项目
    mybatis_d01_c05
    创建包 com.igeek
    创建类 MybatisTest
    如下图
    4.3.2. 加入 jar 包
    加入 mybatis 核心包、依赖包、数据驱动包。
    mybatis 核心包
    mybatis 依赖包
    数据库驱动包
    效果:
    4.3.3. 加入配置文件
    如下图创建资源文件夹 config,加入 log4j.properties 和 SqlMapConfig.xml 配置文

    4.3.3.1. log4j.properties
    在 config 下创建 log4j.properties 如下:

Global logging configuration

log4j.rootLogger=DEBUG, stdout

Console output…

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis 默认使用 log4j 作为输出日志信息。
4.3.3.2. SqlMapConfig.xml
在 config 下创建 SqlMapConfig.xml,如下:

<?xml version="1.0" encoding="UTF-8" ?> SqlMapConfig.xml 是 mybatis 核心配置文件,配置文件内容为数据源、事务管理。 4.3.3.3. 效果 4.3.4. 创建 pojo pojo 类作为 mybatis 进行 sql 映射使用,po 类通常与数据库表对应, 数据库 user 表如下图: User.java 如下: public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 //get/set…… } 4.3.5. 第六步:sql 映射文件 在 config 下的 sqlmap 目录下创建 sql 映射文件 User.xml: <?xml version="1.0" encoding="UTF-8" ?> 4.3.6. 第七步:加载映射文件 mybatis 框架需要加载 Mapper.xml 映射文件 将 users.xml 添加在 SqlMapConfig.xml,如下: 4.4. 实现根据 id 查询用户 使用的 sql: SELECT * FROM `user` WHERE id = 1 4.4.1. 映射文件: 在 user.xml 中添加 select 标签,编写 sql: <?xml version="1.0" encoding="UTF-8" ?> SELECT * FROM `user` WHERE id = #{id} 4.4.2. 测试程序: 测试程序步骤: 1. 创建 SqlSessionFactoryBuilder 对象 2. 加载 SqlMapConfig.xml 配置文件 3. 创建 SqlSessionFactory 对象 4. 创建 SqlSession 对象 5. 执行 SqlSession 对象执行查询,获取结果 User 6. 打印结果 7. 释放资源 MybatisTest 编写测试程序如下: public class MybatisTest { private SqlSessionFactory sqlSessionFactory = null; @Before public void init() throws Exception { // 1. 创建 SqlSessionFactoryBuilder 对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 2. 加载 SqlMapConfig.xml 配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 3. 创建 SqlSessionFactory 对象 this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); } @Test public void testQueryUserById() throws Exception { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行查询,获取结果 User // 第一个参数是 User.xml 的 statement 的 id,第二个参数是执行 sql 需 要的参数; Object user = sqlSession.selectOne("queryUserById", 1); // 6. 打印结果 System.out.println(user); // 7. 释放资源 sqlSession.close(); } } 4.4.3. 效果 测试结果如下图 4.5. 实现根据用户名模糊查询用户 查询 sql: SELECT * FROM `user` WHERE username LIKE '%王%' 4.5.1. 方法一 4.5.1.1. 映射文件 在 User.xml 配置文件中添加如下内容: SELECT * FROM `user` WHERE username LIKE #{username} 4.5.1.2. 测试程序 MybatisTest 中添加测试方法如下: @Test public void testQueryUserByUsername1() throws Exception { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行查询,获取结果 User // 查询多条数据使用 selectList 方法 List list = sqlSession.selectList("queryUserByUsername1", "%王%"); // 6. 打印结果 for (Object user : list) { System.out.println(user); } // 7. 释放资源 sqlSession.close(); } 4.5.1.3. 效果 测试效果如下图: 4.5.2. 方法二 4.5.2.1. 映射文件: 在 User.xml 配置文件中添加如下内容: SELECT * FROM `user` WHERE username LIKE '%${value}%' 4.5.2.2. 测试程序: MybatisTest 中添加测试方法如下: @Test public void testQueryUserByUsername2() throws Exception { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行查询,获取结果 User // 查询多条数据使用 selectList 方法 List list = sqlSession.selectList("queryUserByUsername2", " 王"); // 6. 打印结果 for (Object user : list) { System.out.println(user); } // 7. 释放资源 sqlSession.close(); } 4.5.2.3. 效果 测试结果如下图: 4.6. 小结 4.6.1. #{}和${} #{}表示一个占位符号,通过#{}可以实现 preparedStatement 向占位符中设置值, 自动进行 java 类型和 jdbc 类型转换。#{}可以有效防止 sql 注入。 #{}可以接收简 单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中 可以是 value 或其它名称。 ${}表示拼接 sql 串,通过${}可以将 parameterType 传入的内容拼接在 sql 中且不 进行 jdbc 类型转换,${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。 4.6.2. parameterType 和 resultType parameterType:指定输入参数类型,mybatis 通过 ognl 从输入对象中获取参数值 拼接在 sql 中。 resultType:指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到 容器 List 中 4.6.3. selectOne 和 selectList selectOne 查询一条记录,如果使用 selectOne 查询多条记录则抛出异常: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne (DefaultSqlSession.java:70) selectList 可以查询一条或多条记录。 4.7. 实现添加用户 使用的 sql: INSERT INTO `user` (username,birthday,sex,address) VALUES ('黄忠','2016-07-26','1','三国') 4.7.1. 映射文件: 在 User.xml 配置文件中添加如下内容: INSERT INTO `user` (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) 4.7.2. 测试程序 MybatisTest 中添加测试方法如下: @Test public void testSaveUser() { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行保存 // 创建需要保存的 User User user = new User(); user.setUsername("张飞"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("蜀国"); sqlSession.insert("saveUser", user); System.out.println(user); // 需要进行事务提交 sqlSession.commit(); // 7. 释放资源 sqlSession.close(); } 4.7.3. 效果 如上所示,保存成功,但是 id=0,需要解决 id 返回不正常的问题。 4.7.4. mysql 自增主键返回 查询 id 的 sql SELECT LAST_INSERT_ID() 通过修改 User.xml 映射文件,可以将 mysql 自增主键返回: 如下添加 selectKey 标签 SELECT LAST_INSERT_ID() INSERT INTO `user` (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。 效果如下图所示: 返回的 id 为 48,能够正确的返回 id 了。 4.7.5. Mysql 使用 uuid 实现主键 需要增加通过 select uuid()得到 uuid 值 创建表 CREATE TABLE `user1` ( `id` int(60) NULL DEFAULT NULL , `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名称' , `birthday` date NULL DEFAULT NULL COMMENT '生日' , `sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别' , `address` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址' , `uuid` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , PRIMARY KEY (`uuid`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=COMPACT ; public class User { private int id; private String uuid; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 } SELECT uuid() INSERT INTO `user1` (username,birthday,sex,address,uuid) VALUES (#{username},#{birthday},#{sex},#{address},#{uuid}) 注意这里使用的 order 是“BEFORE” 4.8. 修改用户 根据用户 id 修改用户名 使用的 sql: UPDATE `user` SET username = '赵云' WHERE id = 26 4.8.1. 映射文件 在 User.xml 配置文件中添加如下内容: UPDATE `user` SET username = #{username} WHERE id = #{id} 4.8.2. 测试程序 MybatisTest 中添加测试方法如下: @Test public void testUpdateUserById() { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行更新 // 创建需要更新的 User User user = new User(); user.setId(26); user.setUsername("关羽"); user.setSex("1"); user.setBirthday(new Date()); user.setAddress("蜀国"); sqlSession.update("updateUserById", user); // 需要进行事务提交 sqlSession.commit(); // 7. 释放资源 sqlSession.close(); } 4.8.3. 效果 测试效果如下图: 4.9. 删除用户 根据用户 id 删除用户 使用的 sql DELETE FROM `user` WHERE id = 47 4.9.1. 映射文件: 在 User.xml 配置文件中添加如下内容: delete from user where id=#{id} 4.9.2. 测试程序: MybatisTest 中添加测试方法如下: @Test public void testDeleteUserById() { // 4. 创建 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行 SqlSession 对象执行删除 sqlSession.delete("deleteUserById", 48); // 需要进行事务提交 sqlSession.commit(); // 7. 释放资源 sqlSession.close(); } 4.9.3. 效果 测试效果如下图: 4.10. Mybatis 解决 jdbc 编程的问题 1、 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用 数据库连接池可解决此问题。 解决:在 SqlMapConfig.xml 中配置数据连接池,使用连接池管理数据库链接。 2、 Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。 解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。 3、 向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能 少,占位符需要和参数一一对应。 解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。 4、 对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果 能将数据库记录封装成 pojo 对象解析比较方便。 解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型。 5. Dao 开发方法 使用 MyBatis 开发 Dao,通常有两个方法,即原始 Dao 开发方法和 Mapper 动态代理开发方法。 5.1. 需求 使用 MyBatis 开发 DAO 实现以下的功能: 根据用户 id 查询一个用户信息 根据用户名称模糊查询用户信息列表 添加用户信息 5.2. SqlSession 的使用范围 SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。 SqlSession 通过 SqlSessionFactory 创建。 SqlSessionFactory 是通过 SqlSessionFactoryBuilder 进行创建。 5.2.1. SqlSessionFactoryBuilder SqlSessionFactoryBuilder 用于创建 SqlSessionFacoty,SqlSessionFacoty 一旦创 建 完 成 就 不 需 要 SqlSessionFactoryBuilder 了,因为 SqlSession 是通过 SqlSessionFactory 创建的。所以可以将 SqlSessionFactoryBuilder 当成一个工具类使 用,最佳使用范围是方法范围即方法体内局部变量。 5.2.2. SqlSessionFactory SqlSessionFactory 是一个接口,接口中定义了 openSession 的不同重载方法, SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦创建后可以重复使 用,通常以单例模式管理 SqlSessionFactory。 5.2.3. SqlSession SqlSession 是一个面向用户的接口,sqlSession 中定义了数据库操作方法。 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不能共享使 用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态字段或实例字段中。 打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下: SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); } 5.3. 原始 Dao 开发方式 原始 Dao 开发方法需要程序员编写 Dao 接口和 Dao 实现类。 5.3.1. 映射文件 编写映射文件如下:(也可以使用入门程序完成的映射文件) <?xml version="1.0" encoding="UTF-8" ?> select * from user where id = #{id} select * from user where username like '%${value}%' SELECT LAST_INSERT_ID() insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) 5.3.2. Dao 接口 先进行 DAO 的接口开发,编码如下: public interface UserDao { /** * 根据 id 查询用户 * * @param id * @return */ User queryUserById(int id); /** * 根据用户名模糊查询用户 * * @param username * @return */ List queryUserByUsername(String username); /** * 保存用户 * * @param user */ void saveUser(User user); } 5.3.3. Dao 实现类 编写的 Dao 实现类如下 public class UserDaoImpl implements UserDao { private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { super(); this.sqlSessionFactory = sqlSessionFactory; } @Override public User queryUserById(int id) { // 创建 SqlSession SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 执行查询逻辑 User user = sqlSession.selectOne("queryUserById", id); // 释放资源 sqlSession.close(); return user; } @Override public List queryUserByUsername(String username) { // 创建 SqlSession SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 执行查询逻辑 List list = sqlSession.selectList("queryUserByUsername", username); // 释放资源 sqlSession.close(); return list; } @Override public void saveUser(User user) { // 创建 SqlSession SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 执行保存逻辑 sqlSession.insert("saveUser", user); // 提交事务 sqlSession.commit(); // 释放资源 sqlSession.close(); } } 5.3.4. Dao 测试 创建一个 JUnit 的测试类,对 UserDao 进行测试,测试代码如下: public class UserDaoTest { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws Exception { // 创建 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 加载 SqlMapConfig.xml 配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 创建 SqlsessionFactory this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); } @Test public void testQueryUserById() { // 创建 DAO UserDao userDao = new UserDaoImpl(this.sqlSessionFactory); // 执行查询 User user = userDao.queryUserById(1); System.out.println(user); } @Test public void testQueryUserByUsername() { // 创建 DAO UserDao userDao = new UserDaoImpl(this.sqlSessionFactory); // 执行查询 List list = userDao.queryUserByUsername("张"); for (User user : list) { System.out.println(user); } } @Test public void testSaveUser() { // 创建 DAO UserDao userDao = new UserDaoImpl(this.sqlSessionFactory); // 创建保存对象 User user = new User(); user.setUsername("刘备"); user.setBirthday(new Date()); user.setSex("1"); user.setAddress("蜀国"); // 执行保存 userDao.saveUser(user); System.out.println(user); } } 5.3.5. 问题 原始 Dao 开发中存在以下问题:  Dao 方法体存在重复代码:通过 SqlSessionFactory 创建 SqlSession,调用 SqlSession 的数据库操作方法  调用 sqlSession 的数据库操作方法需要指定 statement 的 id,这里存在 硬编码,不得于开发维护。 5.4. Mapper 动态代理方式 5.4.1. 开发规范 Mapper 接口开发方法只需要程序员编写 Mapper 接口(相当于 Dao 接口), 由 Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上 边 Dao 接口实现类方法。 Mapper 接口开发需要遵循以下规范: 1、 Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同。 2、 Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同 3、 Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同 4、 Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同 5.4.2. Mapper.xml(映射文件) 定义 mapper 映射文件 UserMapper.xml 将 UserMapper.xml 放在 config 下 mapper 目录下,效果如下: UserMapper.xml 配置文件内容: <?xml version="1.0" encoding="UTF-8" ?> select * from user where id = #{id} select * from user where username like '%${value}%' select last_insert_id() insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address}); 5.4.3. UserMapper(接口文件) 创建 UserMapper 接口代码如下: public interface UserMapper { /** * 根据 id 查询 * * @param id * @return */ User queryUserById(int id); /** * 根据用户名查询用户 * * @param username * @return */ List queryUserByUsername(String username); /** * 保存用户 * * @param user */ void saveUser(User user); } 5.4.4. 加载 UserMapper.xml 文件 修改 SqlMapConfig.xml 文件,添加以下所示的内容: 5.4.5. 测试 编写的测试方法如下: public class UserMapperTest { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws Exception { // 创建 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 加载 SqlMapConfig.xml 配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 创建 SqlsessionFactory this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); } @Test public void testQueryUserById() { // 获取 sqlSession,和 spring 整合后由 spring 管理 SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 从 sqlSession 中获取 Mapper 接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 执行查询方法 User user = userMapper.queryUserById(1); System.out.println(user); // 和 spring 整合后由 spring 管理 sqlSession.close(); } @Test public void testQueryUserByUsername() { // 获取 sqlSession,和 spring 整合后由 spring 管理 SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 从 sqlSession 中获取 Mapper 接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 执行查询方法 List list = userMapper.queryUserByUsername("张"); for (User user : list) { System.out.println(user); } // 和 spring 整合后由 spring 管理 sqlSession.close(); } @Test public void testSaveUser() { // 获取 sqlSession,和 spring 整合后由 spring 管理 SqlSession sqlSession = this.sqlSessionFactory.openSession(); // 从 sqlSession 中获取 Mapper 接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 创建保存对象 User user = new User(); user.setUsername("刘备"); user.setBirthday(new Date()); user.setSex("1"); user.setAddress("蜀国"); // 执行查询方法 userMapper.saveUser(user); System.out.println(user); // 和 spring 整合后由 spring 管理 sqlSession.commit(); sqlSession.close(); } } 5.4.6. 小结  selectOne 和 selectList 动态代理对象调用 sqlSession.selectOne()和 sqlSession.selectList()是根据 mapper 接口方法的返回值决定,如果返回 list 则调用 selectList 方法,如果返回单个对象 则调用 selectOne 方法。  namespace mybatis 官方推荐使用 mapper 代理方法开发 mapper 接口,程序员不用编写 mapper 接口实现类,使用 mapper 代理方法时,输入参数可以使用 pojo 包装对 象或 map 对象,保证 dao 的通用性。 6. SqlMapConfig.xml 配置文件 6.1. 配置内容 SqlMapConfig.xml 中配置的内容和顺序如下: properties(属性) settings(全局配置参数) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境集合属性对象) environment(环境子属性对象) transactionManager(事务管理) dataSource(数据源) mappers(映射器) 6.2. properties(属性) SqlMapConfig.xml 可以引用 java 属性文件中的配置信息如下: 在 config 下定义 db.properties 文件,如下所示: db.properties 配置文件内容如下: jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding =utf-8 jdbc.username=root jdbc.password=root SqlMapConfig.xml 引用如下: <?xml version="1.0" encoding="UTF-8" ?> 注意: MyBatis 将按照下面的顺序来加载属性:  在 properties 元素体内定义的属性首先被读取。  然后会读取 properties 元素中 resource 或 url 加载的属性,它会覆盖已读取 的同名属性。 6.3. typeAliases(类型别名) 6.3.1. mybatis 支持别名: 别名 映射的类型 _byte byte _long long _short short _int int _integer int _double double _float float _boolean boolean string String byte Byte long Long short Short int Integer integer Integer double Double float Float boolean Boolean date Date decimal BigDecimal bigdecimal BigDecimal map Map 6.3.2. 自定义别名: 在 SqlMapConfig.xml 中配置如下: <?xml version="1.0" encoding="UTF-8" ?> 在 mapper.xml 配置文件中,就可以使用设置的别名了 别名大小写不敏感 6.4. mappers(映射器) Mapper 配置的几种方法: 6.4.1. 使用相对于类路径的资源(现在的使用方式) 如: 6.4.2. 使用 mapper 接口类路径 如: 注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同 一个目录中。

6.4.3.
注册指定包下的所有 mapper 接口
如:
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同
一个目录中。

猜你喜欢

转载自blog.csdn.net/weixin_42137448/article/details/87902299
今日推荐