MyBatis(一)

来源:互联网 发布:实名淘宝小号单个购买 编辑:程序博客网 时间:2024/05/19 19:32
  1. Mybatis的介绍
  2. Mybatis的入门
    1. 使用jdbc操作数据库存在的问题
    2. Mybatis的架构
    3. Mybatis的入门程序
  3. Dao的开发方法
    1. 原始dao的开发方法
    2. 接口的动态代理方式
  4. SqlMapConfig.xml文件说明
  5. 输入映射和输出映射
    1. 输入参数映射
    2. 返回值映射
  6. 动态sql
  7. 关联查询
    1. 一对一关联
    2. 一对多关联
  8. Mybatis整合spring

Mybatis介绍

MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis. 2013年11月迁移到Github.
3w
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码.

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

jdbc问题总结如下

  1. 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
  2. Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
  3. 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
  4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

MyBatis架构

这里写图片描述

  1. MyBatis配置: SqlMapConfig.xml, 此文件作为MyBatis的全局配置文件, 配置了MyBatis的运行环境等信息. mapper.xml文件即sql映射文件, 文件中配置了操作数据库的sql语句. 此文件需要在SqlMapConfig.xml中加载.
  2. 通过MyBatis环境等配置信息构造SqlSessionFactory会话工厂.
  3. 有会话工厂创建sqlSession会话, 操作数据库需要通过sqlSession进行.
  4. MyBatis底层自定义了Executor执行器接口操作数据库, Executor接口有两个实现, 一个是基本执行器, 一个是缓存执行器.
  5. Mapped Statement也是MyBatis一个底层封装对象, 它包含了MyBatis配置信息及sql映射信息等. mapper.xml文件中一个sql对应一个Mapped Statement对象, sql的id即是Mapped Statementid.
  6. Mapped Statement对sql执行输入参数进行定义, 包括HashMap, 基本类型, pojo, Executor通过Mapped Statement在执行sql前将将输入的java对象映射至sql中, 输入参数映射就是jdbc编程中对preparedStatement设置参数.
  7. Mapped Statement对sql执行输出结果进行定义, 包括HashMap, 基本类型, pojo, Executor通过Mapped Statememt在执行sql后将输出结果映射至java对象中, 输出结果映射过程相当于jdbc编程中对结果的解析处理过程.

MyBatis入门程序

MyBatis下载

下载地址(以3.2.7版本为例):https://github.com/mybatis/mybatis-3/releases

MyBatis目录结构:

这里写图片描述

  • mybatis-3.2.7.jar –> mybatis的核心包
  • lib –> mybatis的依赖包
  • mybatis-3.2.7 –> mybatis使用手册

业务需求(使用mybatis实现一些功能):

  1. 根据用户id查询一个用户
  2. 根须用户名称模糊查询用户列表
  3. 添加用户
  4. 更新用户
  5. 删除用户

环境搭建

1. 使用eclipse创建java工程(mybatis-first)

2. 在工程下新建lib文件夹,导入mybatis核心包, 依赖包, 数据库包

这里写图片描述

3. 创建Source Folder命名为config, 加入log4j.properties, SqlMapConfig.xml配置文件

这里写图片描述

log4j.properties 内容如下:

# Global logging configurationlog4j.rootLogger=DEBUG, stdout# Console output...log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

SqlMapConfig.xml 内容如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//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>

4. 创建pojo类

pojo类作为mybatis进行sql映射使用, po类通常与数据库表对应.

在src下创建包cn.itcast.mybatis.pojo,在pojo包下创建User.java类.

创建数据库及数据表:

CREATE DATABASE mybatis;USE mybatis;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;

导入数据

SET FOREIGN_KEY_CHECKS=0;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=6 DEFAULT CHARSET=utf8;INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);INSERT INTO `orders` VALUES ('5', '10', '1000012', '2015-02-12 16:13:23', null);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=27 DEFAULT CHARSET=utf8;INSERT INTO `user` VALUES ('1', '王五', null, '2', null);INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州');INSERT INTO `user` VALUES ('22', '陈小明', null, '1', '河南郑州');INSERT INTO `user` VALUES ('24', '张三丰', null, '1', '河南郑州');INSERT INTO `user` VALUES ('25', '陈小明', null, '1', '河南郑州');INSERT INTO `user` VALUES ('26', '王五', null, null, null);

User.java 如下:

public class User {    private int id;    private String username;// 用户姓名    private String sex;// 性别    private Date birthday;// 生日    private String address;// 地址    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public Date getBirthday() {        return birthday;    }    public void setBirthday(Date birthday) {        this.birthday = birthday;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    @Override    public String toString() {        return "User [id=" + id + ", username=" + username + ", sex=" + sex + "]";    }}

5. sql映射文件

在config下创建sqlmap包,在sqlmap包下创建sql映射文件User.xml. User.xml内容如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 --><mapper namespace="test"></mapper>

6. 加载映射文件

mybatis框架需要加载Mapper.xml映射文件, 将user.xml添加在sqlMapConfig.xml, 如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//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>

实现根据id查询用户

在User.xml中添加select标签, 编写sql:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 --><mapper namespace="test">    <!-- id:statement的id或者叫sql的id -->    <!-- parameterType: 声明输入参数的类型 -->    <!-- resultType: 声明输出结果的类型, 应该填写pojo的全路径 -->    <!-- #{}: 输入参数的占位符,相当于jdbc的? -->    <select id="queryUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">        SELECT * FROM `user` WHERE id = #{id}    </select></mapper>

测试程序步骤:

  1. 创建SqlSessionFactoryBulider对象
  2. 加载SqlMapConfig.xml配置文件
  3. 创建SqlSessionFactory对象
  4. 创建SqlSession对象
  5. 执行SqlSession对象执行查询, 获取User
  6. 打印结果
  7. 释放资源

测试类实现

在src下创建包cn.itcast.mybatis.test,在test包下创建MyBatisTest测试类

public class MyBatisTest {    private SqlSessionFactory sqlSessionFactory = null;    @Before    public void init() throws IOException {        // 1. 创建SqlsessionFactoryBulider        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        // 2. 加载SqlMapConfig.xml文件        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");        // 3. 创建SqlSessionFactory对象        this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);    }    @Test    public void testQueryUserById() {        // 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();    }}

执行结果如下图:

这里写图片描述

实现根据用户姓名模糊查询用户

方法一:

映射文件:在User.xml文件中添加如下配置:

<!-- 如果返回多个结果, mybatis会自动把返回的结果放在list容器中 --><!-- resultType的配置和返回一个结果的配置一样 --><select id="queryUserByUsername1" parameterType="String" resultType="cn.itcast.mybatis.pojo.User">    select * from `user` where username like #{username}</select>

测试程序:

@Testpublic void testQueryUserByUsername1() {    SqlSession sqlSession = sqlSessionFactory.openSession();    List<Object> list = sqlSession.selectList("queryUserByUsername1", "%王%");    for (Object user : list) {        System.out.println(user);    }    sqlSession.close();}

执行结果如下:

这里写图片描述

方法二

映射文件:在User.xml文件中添加如下配置:

<select id="queryUserByUsername2" parameterType="String" resultType="cn.itcast.mybatis.pojo.User">    select * from `user` where username like '%${value}%'</select>

测试程序:

@Testpublic void testQueryUserByUsername2() {    SqlSession sqlSession = sqlSessionFactory.openSession();    List<Object> list = sqlSession.selectList("queryUserByUsername2", "王");    for (Object user : list) {        System.out.println(user);    }    sqlSession.close();}

执行结果如下:

这里写图片描述

小结

#{}和${}

#{}表示一个占位符号, 通过#{}可以实现preparedStatement向占位符中设置值, 自动进行Java类型和jdbc类型的转换. #{}可以有效防止sql注入. #{}可以接受简单类型值或pojo属性值. 如果parameterType**传输单个简单类型值, #{}括号中可以是value或其他名称**.

sql,{}可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换, pojo,parameterType,{}括号中只能是value**.

parameterType和resultType

parameterType: 指定输入参数类型, mybatis通过ognl从输入对象中获取参数值拼接在sql中.

resultType: 指定输出结果类型, mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象. 如果有多条数据,则分别映射,并把对象放到List中

selectOne和selectList

selectOne查询一条记录, 如果使用selectOne查询多条记录,则抛出异常

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)

selectList可以查询一条或多条记录.

实现添加用户

在User.xml配置文件中添加如下内容:

<!-- 保存用户 --><insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">    insert into `user`     (username, birthday, sex, address)    values    (#{username},#{birthday},#{sex},#{address})</insert>

测试程序:

@Testpublic void testSaveUser() {    SqlSession sqlSession = sqlSessionFactory.openSession();    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();    sqlSession.close();}

执行结果
这里写图片描述

MySQL自增主键返回

上图中,保存成功,但是id=0, 需要解决id返回不正常的问题.

通过修改User.xml映射文件,可以将mysql自增主键返回. 修改如下:

<!-- 保存用户 --><insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">    <!-- selectKey: 实现主键返回 -->    <!-- keyColumn: 主键对应的表中哪一列 -->    <!-- keyProperty: 主键对应的pojo中的哪一个属性 -->    <!-- order: 设置在执行 insert语句前执行id的sql的sql,还是在执行insert语句之后执行查询id的sql. -->    <!-- resultType: 设置返回的id的类型 -->    <selectKey keyColumn="id" keyProperty="id" order="AFTER"        resultType="int">        SELECT LAST_INSERT_ID()    </selectKey>    INSERT INTO `user`    (username,birthday,sex,address) VALUES    (#{username},#{birthday},#{sex},#{address})</insert>

执行结果:
这里写图片描述

修改用户

在User.xml配置文件中添加如下内容:

<update id="updateUserById" parameterType="cn.itcast.mybatis.pojo.User">    update `user` set username = #{username} where id = #{id}</update>

测试程序:

@Testpublic void testUpdateUserById() {    SqlSession sqlSession = sqlSessionFactory.openSession();    User user = new User();    user.setId(26);    user.setUsername("关羽");    user.setSex("1");    user.setBirthday(new Date());    user.setAddress("蜀国");    sqlSession.update("updateUserById", user);    System.out.println(user);    // 需要进行事务提交    sqlSession.commit();    sqlSession.close();}

执行结果:

这里写图片描述

删除用户

在User.xml配置文件中添加如下内容:

<delete id="deleteUserById" parameterType="int">    delete from `user` where id = #{id}</delete>

测试程序:

@Testpublic void testDeleteUserById() {    SqlSession sqlSession = sqlSessionFactory.openSession();    sqlSession.update("deleteUserById", 32);    sqlSession.commit();    sqlSession.close();}

执行结果:

这里写图片描述

MyBatis解决jdbc编程的问题

  1. 数据库连接创建, 释放频繁造成系统资源浪费, 从而影响系统性能, 如果使用数据库连接池, 可解决此问题

    解决: 在SqlMapConfig.xml中配置数据连接池, 使用连接池管理数据库连接.

  2. Sql语句写在代码中造成代码不易维护, 实际应用sql变化的可能较大, sql变动需要改变java代码

    解决: 将sql语句配置在XXXmapper.xml文件中域java代码分离.

  3. 向sql语句传参数麻烦, 因为sql语句的where条件不一定, 可能多也可能少, 占位符需要和参数一一对应.

    解决: MyBatis自动将java对象映射至sql语句, 通过statement中的parameterType定义输入参数的类型.

  4. 对结果集解析麻烦, sql变化导致解析代码变化, 且解析前需要遍历, 如果能将数据库记录封装成pojo对象解析比较方便.

    解决: MyBatis将自动sql执行结果映射至Java对象, 通过statement中的resultType定义输出结果的类型.

mybatis与hibernate不同

MyBatis和hibernate不同, 它不完全是一个ORM框架, 因为MyBatis需要程序员自己编写sql语句. mybatis可以通过XML或注解方式灵活配置要运行的sql语句, 并将Java对象和sql语句映射成最终执行的sql, 最后将sql执行的结果再映射生成java对象.

MyBatis学习门槛低,简单易学, 程序员直接编写原生态sql, 严格控制sql执行性能, 灵活度高, 非常适合对关系型数据模型要求不高的软件开发, 例如互联网软件, 企业运营类软件等, 因为这类软件需求变化频繁, 一旦需求变化要求成果输出迅速. 但是灵活的前提是mybatis无法做到数据库无关性, 如果需要实现支持多种数据库的软件, 则需要定义多套sql映射文件, 工作量大.

Hibernate对象/关系映射能力强, 数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件), 如果使用Hibernate开发可以节省很多代码, 提高效率. 但是Hibernate的学习门槛高, 要精通门槛更高, 而且怎么设计O/R映射, 在性能和对象模型之间如何权衡, 以及怎样用好Hibernate需要很强的经验和能力才行.

总之, 按照用户的需求在有限的资源环境下只要能做出维护性, 扩展性良好的软件框架都是好架构, 所以框架只有合适才是最好的.

Dao开发方法

使用MyBatis开发Dao, 通常有两个方法, 即原始Dao开发方法和Mapper动态代理开发方法.

需求

使用MyBatis开发Dao实现以下功能:
1. 根据用户id查询一个用户信息
2. 根据用户名称模糊查询用户登录信息列表
3. 添加用户信息

SqlSession的使用范围

  1. SqlSession中封装了对数据库的操作, 如:查询,插入, 更新, 删除等.
  2. SqlSession通过SqlSessionFactory创建.
  3. SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建.

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder用于创建SqlSessionFactory, SqlSessionFactory一旦创建完成就不需要SqlSessionFactoryBuilder了, 因为SqlSession是通过SqlSessionFactory创建的. 所以可以将SqlSessionFactoryBuilder当成一个工具类使用, 最佳使用范围是方法范围, 即方法体内局部变量.

SqlSessionFactory

SqlSessionFactory是一个接口, 接口中定义了openSession的不同重载方法, SqlSessionFactory的最佳使用范围是整个应用运行期间, 一旦创建后可以重复使用, 通常以单例模式管理SqlSessionFactory

SqlSession

SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作方法.

每个线程都有他自己的sqlSession实例. SqlSession的实例不能共享使用, 它也是线程不安全的. 因此最佳的范围是请求或方法范围. 绝对不能将SqlSession实例的引用放在一个静态字段或实例字段中.

打开一个sqlSession;使用完毕就要关闭它. 通常把这个关闭操作放到finally块中以确保每次都能执行关闭. 如下:

SqlSession session = sqlSessionFactory.openSession();try {     // do work} finally {    session.close();}

原始Dao开发方式

原始Dao开发方式需要程序员编写Dao接口和Dao实现类

映射文件(User.xml)

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 --><mapper namespace="test">    <!-- 根据id查询用户 -->    <select id="queryUserById" parameterType="int"        resultType="cn.itcast.mybatis.pojo.User">        select * from user where id = #{id}    </select>    <!-- 根据username模糊查询用户 -->    <select id="queryUserByUsername" parameterType="string"        resultType="cn.itcast.mybatis.pojo.User">        select * from user where username like '%${value}%'    </select>    <!-- 保存用户 -->    <insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">        <selectKey keyProperty="id" keyColumn="id" order="AFTER"            resultType="int">            SELECT LAST_INSERT_ID()        </selectKey>        insert into user(username,birthday,sex,address)        values(#{username},#{birthday},#{sex},#{address})    </insert></mapper>

DAO接口创建

public interface UserDao {    // 根据id查询用户    User queryUserById(int id);    // 根据用户名模糊查询用户    List<User> queryUserByUsername(String username);    // 保存用户    void saveUser(User user);}

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 = this.sqlSessionFactory.openSession();        User user = sqlSession.selectOne("queryUserById", id);        sqlSession.close();        return user;    }    @Override    public List<User> queryUserByUsername(String username) {        SqlSession sqlSession = sqlSessionFactory.openSession();        List<User> list = sqlSession.selectList("queryUserByUsername", username);        sqlSession.close();        return list;    }    @Override    public void saveUser(User user) {        SqlSession sqlSession = sqlSessionFactory.openSession();        sqlSession.insert("saveUser", user);        sqlSession.commit();        sqlSession.close();    }}

Dao测试

public class UserDaoTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void init() throws IOException {        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");        this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);    }    @Test    public void testQueryUserById() {        UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);        User user = userDao.queryUserById(1);        System.out.println(user);    }    @Test    public void testQueryUserByUsername() {        UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);        List<User> list = userDao.queryUserByUsername("张");        for (User user : list) {            System.out.println(user);        }    }    @Test    public void testSaveUser() {        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);    }}

问题

原始Dao开发中存在以下问题:
- Dao方法体存在重复代码: 通过SqlSessionFactory创建SqlSession, 调用SqlSession的数据库操作方法
- 调用sqlSession的数据库操作方法需要指定statement的id, 这里存在硬编码, 不利于开发维护

Mapper动态代理方式

开发规范

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的类型相同

Mapper.xml(映射文件)

定义mapper映射文件UserMapper.xml, 将UserMapper.xml放在config下mapper目录下, 效果如下:

这里写图片描述

UserMapper.xml配置文件内容:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace: 命名空间, 用于隔离sql --><!-- 还有一个很重要的作用, 使用动态代理开发DAO, 1. namespace必须和Mapper类接口类路径一致 --><mapper namespace="cn.itcast.mybatis.mapper.UserMapper">    <!-- 根据用户id查询用户             1. id必须和Mapper接口方法名一致            2. parameterType必须和接口方法参数类型一致            3. resultType必须和接口方法返回值类型一致     -->     <select id="queryUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">        select * from user where id = #{id}     </select>     <select id="queryUserByUsername" parameterType="string" resultType="cn.itcast.mybatis.pojo.User">        select * from user where username like '%${value}%'     </select>     <insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">        <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="int">            select last_insert_id()        </selectKey>        insert into user(username, birthday, sex, address) values        (#{username}, #{birthday}, #{sex}, #{address})     </insert> </mapper>

UserMapper(接口文件)

public interface UserMapper {    User queryUserById(int id);    List<User> queryUserByUsername(String username);    void saveUser(User user);}

在SqlMapConfig.xml文件下,标签中添加如下内容

<mapper resource="mapper/UserMapper.xml"/>

编写测试方法:

public class UserMapperTest {    private SqlSessionFactory sqlSessionFactory;    @Before    public void init() throws IOException {        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");        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<User> 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("0");        user.setAddress("蜀国");        // 执行查询方法        userMapper.saveUser(user);        // 提交事务        sqlSession.commit();        // 和spring整合后由spring管理        sqlSession.close();    }}

小结

  • selectOne和selectList 动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定, 如果返回list则调用selectList方法, 如果返回单个对象, 则调用selectOne方法
  • namespace mybatis官方推荐使用mapper代理方法开发mapper接口, 程序员不用编写mapper接口实现类,使用mapper代理方法时, 输入参数可以使用pojo包装对象或map对象, 保证dao的通用性.

sqlMapConfig.xml配置文件

配置内容

properties (属性)

setting (全局配置参数)

typeAliases (类型别名)

typeHandlers (类型处理器)

objectFactory (对象工厂)

plugins (插件)

environments(环境集合属性对象)

  environment(环境子属性对象)

   transactionManager (事务管理)

   DataSource (数据源)

Mappers (映射器)

properties(属性)

SqlMapConfig.xml可以引用java属性文件中的配置信息, 如下

在config下定义db.properties文件,如下所示

这里写图片描述

db.properties配置文件内容如下:

jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8jdbc.username=rootjdbc.password=root

SqlMapConfig.xml引用如下:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <!-- 是用resource属性加载外部配置文件 -->    <properties resource="db.properties">        <!-- 在properties内部用property定义属性 -->        <!-- 如果外部配置文件有该属性, 则内部定义属性被外部属性覆盖 -->        <property name="jdbc.username" value="root123"/>        <property name="jdbc.password" value="root123"/>    </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.username}" />                <property name="password" value="${jdbc.password}" />            </dataSource>        </environment>    </environments>    <!-- 加载映射文件 -->    <mappers>        <mapper resource="mapper/UserMapper.xml"/>    </mappers></configuration>

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

自定义别名

在SqlMapConfig.xml中配置如下():

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>    <!-- 是用resource属性加载外部配置文件 -->    <properties resource="db.properties">        <!-- 在properties内部用property定义属性 -->        <!-- 如果外部配置文件有该属性, 则内部定义属性被外部属性覆盖 -->        <property name="jdbc.username" value="root123"/>        <property name="jdbc.password" value="root123"/>    </properties>    <typeAliases>        <!-- 单个别名定义 -->        <typeAlias alias="user" type="cn.itcast.mybatis.pojo.User"/>        <!-- 批量定义别名, 扫描整个包下的类, 别名为类名(大小写不敏感) -->        <package name="cn.itcast.mybatis.pojo"/>    </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.username}" />                <property name="password" value="${jdbc.password}" />            </dataSource>        </environment>    </environments>    <!-- 加载映射文件 -->    <mappers>        <mapper resource="mapper/UserMapper.xml"/>    </mappers></configuration>

mappers(映射器)

Mapper配置的集中方法:

<mapper resource="" />

使用相对于路径的资源(现在的使用方式)

如: <mapper resource="sqlmap/User.xml">

<mapper class="" />

使用mapper接口类路径

如:<mapper class="cn.itcast.mybatis.mapper.UserMapper" />

注意:此种方法要求mapper接口名称和mapper映射文件名称相同, 且放在同一个目录中

<package name="" />

注册指定包下的所有mapper接口

如:<package name="cn.itcast.mybatis.mapper" />

注意: 此种方法要求mapper接口名称和mapper映射名称相同, 且放在同一个目录中

原创粉丝点击