MyBatis框架自学之路——简易入门

目录

  • 目录
  • 介绍
  • 入门案例
    • 准备工作
    • MyBatis的CURD
      • MyBatis框架的基本原理
      • 使用MyBatis的工具类
      • 测试类
      • 查询操作
        • 根据用户ID查询对应的用户记录
        • 根据用户名查询一条或多条用户记录
      • 添加操作
        • 自增主键的返回
        • 非自增主键的返回
      • 删除操作
      • 更新操作
        • 修改用户信息
        • 根据性别修改用户信息
      • 小结
  • MyBatis中的类或接口
  • MyBatis开发dao的方法
    • 原始dao开发方法
    • mapper代理
  • 全局配置文件SqlMapConfig.xml
  • 知识点扩展或参考

介绍

什么是 MyBatis ?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

  MyBatis是一个基于Java的持久层框架,是Apache下的一个顶级项目。当然,下面主要讲解的也是MyBatis的入门使用。在这里也只是我学习过程中的一个记录。毕竟网上对MyBatis介绍以及使用的文章太多太多了。
  另外,学习MyBatis除了网上的文章或教程视频外,其过程我们不能脱离MyBatis的官方文档(传送门:MyBatis官方文档)。下面开始我们的MyBatis学习吧!!!

入门案例

准备工作

  创建一个新的工程,导入MyBatis的jar包及相关依赖的jar包。(这里使用的是mybatis-3.4.6)下载地址:mybatis 3
  为在使用MyBatis过程中看到MyBatis的执行日志(MyBatis默认使用log4j输出日志信息),我们先在项目的classpath下(这里我在项目的根目录创建了一个config目录,在config目录下)创建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

MyBatis的CURD

(1)创建MyBatis核心配置文件
  在config目录(该目录在项目根目录下被创建)下,(实际上也是classpath)创建MyBatis的核心配置文件,该文件的名称和位置并不是固定的。在这里我们将该文件命名为SqlMapConfig.xml
  MyBatis的核心配置文件包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。
  SqlMapConfig.xml,内容如下:



<configuration>
    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mydb_329?useUnicode=true&characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            dataSource>
        environment>
    environments>
configuration>

  这里采用的是名为mydb_329的数据库。
(2)创建po类,po类作为MyBatis进行SQL映射使用,po类通常与数据表对应,User.java如下:(这里已经在数据库中事先准备好了mb_user表,对应于该po类)

package com.wm103.po;

import java.util.Date;

/**
 * Created by DreamBoy on 2018/4/9.
 */
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 + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                '}';
    }
}

(3)在config/sqlmap目录中创建User.xml映射文件,该文件是主要用于配置输入参数同SQL的映射以及SQL操作结果同输出结果的映射关系,在映射文件中我们可以配置SQL语句。
  在原始的iBatis中,习惯以xxx.xml命名,如 User.xml;在MyBatis中mapper代理开发(后续会讲解“什么是mapper代理开发”以及“如何实现mapper代理”等)映射文件名称则习惯以xxxMapper.xml名称,如 UserMapper.xml。
  映射文件的基本内容如下:



<mapper namespace="user">
    

    

    

    

    

    

    

mapper>

  在这里需要重点说明的是mapper标签上的namespace属性,即该属性用于对SQL进行分类化管理,不同的映射文件中设置的mappernamespace不同。使用mapper代理方式开发,namespace有着特殊的重要作用,后续将进行讲解。
(4)在MyBatis核心配置文件中(这里指的是SqlMapConfig.xml)引入映射文件,如下:

<mappers>
    <mapper resource="sqlmap/User.xml"/>
mappers>

  引入后,在java代码中使用的即是该核心配置文件的内容,通过核心配置文件,可以知道各映射文件的配置,从而进行对应的SQL操作。那么在实际编写CURD操作之前,我们有必要了解,MyBatis在java程序中,是“如何加载配置文件的”,以及“加载配置文件后我们又该如何获取到实现CURD的对象进行CURD操作的”等一系列问题。

MyBatis框架的基本原理

  1. 配置MyBatis核心配置文件(如:SqlMapConfig.xml),该文件配置了数据源、事务等MyBatis运行环境;
  2. 配置映射文件(在该文件中配置SQL语句),并将这些文件引入核心配置文件中;
  3. 在java程序中加载核心配置文件,根据配置文件创建SqlSessionFactory,即会话工厂。在创建会话工厂的过程中,将配置文件的内容封装为Configuration对象,并在创建会话工厂时通过构造方法传给会话工厂;
  4. 通过SqlSessionFactory会话工厂,获取SqlSession对象(将会话工厂的Configuration和Executor执行器等对象传入),用于操作数据库;
  5. 而在SqlSession接口(SqlSession是一个面向程序员的接口)默认实现类的内部,使用Configuration对操作数据库存储封装,返回一个MappedStatement底层封装对象(可以简单的理解为是映射文件中SQL语句的封装对象,当然该对象远不止这么简单);
  6. 接着将MappedStatement对象传给Executor对象,使用Executor(Executor同样也是接口,有基本执行器、缓存执行器)执行器操作数据库。

使用MyBatis的工具类

  那么根据以上说明,实际上我们所需做的步骤是:
1. 加载配置文件,创建会话工厂(会话工厂可采用单例模式进行实现);
2. 通过会话工厂,获取SqlSession对象;
3. 通过SqlSession对象操作数据库。
  MyBatis的简单工具类 MyBatisUtil.java,如下:

package com.wm103.utils;

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 java.io.IOException;
import java.io.InputStream;

/**
 * Created by DreamBoy on 2018/4/9.
 */
public class MyBatisUtil {
    private final static SqlSessionFactory sqlSessionFactory;

    static {
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

    public static void closeSession(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

测试类

  下面使用该工具类,实现CURD操作,为了方便案例的演示,创建一个测试类TestMyBatis,如下:

package com.wm103.test;

import com.wm103.po.User;
import com.wm103.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

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

/**
 * Created by DreamBoy on 2018/4/9.
 */
public class TestMyBatis {

}

  在该测试类内实现CURD操作。

查询操作

根据用户ID查询对应的用户记录

(1)在User.xml映射文件中,配置SQL语句,如下:


<select id="findUserById" parameterType="int" resultType="com.wm103.po.User">
    SELECT * FROM mb_user WHERE id=#{id}
select>

说明:
1. select标签用于标识该SQL语句实现的是查询操作;
2. id属性标识该映射文件中的SQL(在讲原理的时候,我们提到操作数据库前,会将SQL语句封装到一个叫MappedStatement对象中,因此这里的id我们也可以称之为statement的id)
3. parameterType属性指定输入参数的类型,这里指定为int类型;
4. SQL语句中的#{}表示一个占位符,接收的是输入参数的内容,如:#{id},其中id表示接收输入的参数,参数名称为id。对于简单类型而言,#{}中的参数名可以任意,而对于对象而言,#{}中的参数名需要对象的属性名,对于HashMap来说,#{}中的参数名则为map的key。
5. resultType属性指定输出结果的映射类型,确切地说是指单条记录所映射的java对象类型,这里将查询结果映射到User对象中。
(2)测试方法,如下:

@Test
public void findUserByIdTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = sqlSession.selectOne("user.findUserById", 1);
    System.out.println(user);
    MyBatisUtil.closeSession(sqlSession);
}

说明:
通过SqlSession的方法操作数据库,这里使用的方法的参数:
1. 第一个参数:映射文件中的statement的id,即 映射文件的namesapce.statement的id,如user.findUserById;
2. 第二个参数:指定输入参数,与映射文件对应的配置所设置的parameterType属性值相匹配;
3. 方法的返回结果:与映射文件对应的配置所设置的resultType属性值相匹配。

根据用户名查询一条或多条用户记录

(1)在User.xml映射文件中,配置SQL语句,如下:


<select id="findUserByName" parameterType="java.lang.String" resultType="com.wm103.po.User">
    SELECT * FROM mb_user WHERE username LIKE '%${value}%'
select>

说明:
1. resultType属性在查询多条返回记录时,该属性值指的是单条记录所映射的java对象类型;
2. ${}表示将接收到的参数内容不加任何修饰的拼接在原有的SQL语句中,注意使用${}拼接SQL可能将引起SQL注入的问题。${}中,如果接收的输入参数为简单类型,那么拼接SQL语句时必须为${value}
(2)测试方法,如下:

@Test
public void findUserByNameTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    List list = sqlSession.selectList("user.findUserByName", "mb");
    System.out.println(list);
    MyBatisUtil.closeSession(sqlSession);
}

添加操作

(1)在User.xml映射文件中,配置SQL语句,如下:


<insert id="insertUser" parameterType="com.wm103.po.User">
    INSERT INTO mb_user(username, birthday, sex, address) value(#{username}, #{birthday}, #{sex}, #{address})
insert>

(2)测试方法,如下:

@Test
public void insertUserTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = new User();
    user.setUsername("mb_user03");
    user.setBirthday(new Date());
    user.setSex("女");
    user.setAddress("广东广州");
    sqlSession.insert("user.insertUser", user);
    sqlSession.commit();
    MyBatisUtil.closeSession(sqlSession);
}

  有时在执行添加操作后,我们需要获取到刚被添加记录的主键的值。那么应如何实现呢?下面将分两个部分进行讲解,包括自增主键的获取、非自增主键的获取。以MySQL为例。

自增主键的返回

  为获取自增主键的值,在映射文件中应做如下配置,如:


<insert id="insertUser" parameterType="com.wm103.po.User">
    
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
      SELECT LAST_INSERT_ID()
    selectKey>
    INSERT INTO mb_user(username, birthday, sex, address) value(#{username}, #{birthday}, #{sex}, #{address})
insert>

说明:
1. selectKey标签用来执行额外操作,并将结果设置到输入参数值中。属性如下(摘录自官方文档):

属性 描述
keyProperty selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn 匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
resultType 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。
order 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。
statementType 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。

2. SELECT LAST_INSERT_ID():查询刚执行插入的记录的主键ID,配合insert语句使用,在insert之后调用。

非自增主键的返回

  使用MySQL的uuid()函数生成主键,需要修改表中id字段类型为string,且长度为35位。这种非自增主键的实现方式如下:
1. 首先通过uuid()查询到主键;
2. 将查询到的主键作为SQL语句中的insert的值;
3. 执行insert语句。
  因此从这里我们也可以看出执行uuid()语句(即获取主键的操作)的顺序相对于insert语句之前执行。映射文件的配置如下:


<insert id="insertUser2" parameterType="com.wm103.po.User">
    
    <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
        SELECT uuid()
    selectKey>
    INSERT INTO mb_user(id, username, birthday, sex, address) value(#{id}, #{username}, #{birthday}, #{sex}, #{address})
insert>

删除操作

(1)在User.xml映射文件中,配置SQL语句,如下:


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

(2)测试方法,如下:

@Test
public void deleteUserTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    sqlSession.delete("user.deleteUser", 1);
    sqlSession.commit();
    MyBatisUtil.closeSession(sqlSession);
}

更新操作

修改用户信息

(1)在User.xml映射文件中,配置SQL语句,如下:


<update id="updateUser" parameterType="com.wm103.po.User">
    UPDATE mb_user SET username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} WHERE id = #{id}
update>

(2)测试方法,如下:

@Test
public void updateUserTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = new User();
    user.setId(2);
    user.setUsername("mb_user01");
    user.setSex("男");
    user.setBirthday(new Date());
    user.setAddress("新地址");
    sqlSession.update("user.updateUser", user);
    sqlSession.commit();
    MyBatisUtil.closeSession(sqlSession);
}

根据性别修改用户信息

(1)在User.xml映射文件中,配置SQL语句,如下:


<update id="updateUserBySex" parameterType="com.wm103.po.User">
    UPDATE mb_user SET username = #{username}, birthday = #{birthday}, address = #{address} WHERE sex = #{sex}
update>

(2)测试方法,如下:

@Test
public void updateUserBySexTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = new User();
    user.setUsername("mb_user");
    user.setSex("男");
    user.setBirthday(new Date());
    user.setAddress("新地址23333");
    sqlSession.update("user.updateUserBySex", user);
    sqlSession.commit();
    MyBatisUtil.closeSession(sqlSession);
}

小结

  1. parameterType属性:在映射文件中通过parameterType指定输入参数的类型;
  2. resultType属性:在映射文件中通过resultType指定(在查询返回多条记录的情况下,指单条记录)输出结果的类型;
  3. #{}${}
      #{}表示一个占位符,接收输入参数,参数的类型可以是简单类型、pojo、HashMap。如果#{}接收简单类型,则#{}中可以写成value或其他名称,如#{value}等;如果#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性....的方法获取对象属性值;如果#{}接收HashMap类型,则#{}中为HashMap的key值。
      ${}则是一个拼接符号,会引起SQL注入,所以不建议使用。${}接收输入参数,参数类型可以是简单类型、pojo、HashMap。如果接收${}接收简单类型,则${}必须写成${value};如果接收pojo或者HashMap则同#{}的规则一致。
  4. selectOneselectList
      selectOne查询一条记录进行映射。查询一条记录时,也可以使用selectList实现,只是使用selectList后返回结果中List只有一个值,即最后需要的那一条查询记录。
      selectList查询出一个列表(多条记录)进行映射。查询结果不止一条记录时,不能使用selectOne查询,否则将报如下错误:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2

  我们可以使用如下方法测试一下:

@Test
public void findUserByNameTestError() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = sqlSession.selectOne("user.findUserByName", "mb");
    System.out.println(user);
    MyBatisUtil.closeSession(sqlSession);
}

MyBatis中的类或接口

  对于SqlSessionFactory而言,我们可以采用单例模式对SqlSessionFactory进行管理,无需创建多个SqlSessionFactory对象,MyBatis同Spring整合后,我们将采用单例的方式创建SqlSessionFactory对象。
  SqlSession是一个面向程序员的接口,它提供了许多操作数据库的方法。此处之外,需要特别注意的是,SqlSession是线程不安全的,在SqlSession的实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。因此SqlSession最佳应用场合是在方法体内,定义成局部变量使用。

MyBatis开发dao的方法

  MyBatis中开发dao的方法有两种,一种是原始的dao开发方法,需要我们程序员编写dao接口和dao实现类;两一种则为mapper代理方法,我们程序员只需编写mapper接口(相当于dao接口),除此之外,还需遵守一些规范。

原始dao开发方法

(使用同上述案例相同的映射文件,即User.xml)
(1)首先编写dao接口,这里以User为例,即创建UserDao接口,如下:

package com.wm103.dao;

import com.wm103.po.User;

/**
 * Created by DreamBoy on 2018/4/10.
 */
public interface UserDao {
    User findUserById(int id) throws Exception;

    void insertUser(User user) throws Exception;

    void deleteUser(int id) throws Exception;
}

(2)创建UserDao的实现类UserDaoImpl,在实现类中需要注入SqlSessionFactory,用于获取SqlSession以实现数据库的操作,如下:(这里剩余了一些方法的具体实现)

package com.wm103.dao;

import com.wm103.po.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

/**
 * Created by DreamBoy on 2018/4/10.
 */
public class UserDaoImpl implements UserDao {
    private SqlSessionFactory sqlSessionFactory;

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public User findUserById(int id) throws Exception {
        SqlSession sqlSession = this.sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("user.findUserById", id);
        sqlSession.close();
        return user;
    }

    @Override
    public void insertUser(User user) throws Exception {

    }

    @Override
    public void deleteUser(int id) throws Exception {

    }
}

(3)编写测试类TestUserDaoImpl,如下:

package com.wm103.test;

import com.wm103.dao.UserDao;
import com.wm103.dao.UserDaoImpl;
import com.wm103.po.User;
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 java.io.IOException;
import java.io.InputStream;

/**
 * Created by DreamBoy on 2018/4/10.
 */
public class TestUserDaoImpl {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException {
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void findUserByIdTest() throws Exception {
        UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
        User user = userDao.findUserById(2);
        System.out.println(user);
    }
}

mapper代理

(1)创建新的映射文件,这里我们遵循mapper代理方式的命名规范,即UserMapper.xml。我们在config/mapper目录下进行创建;
(2)在MyBatis核心配置文件中,引入映射文件:

<mappers>
    <mapper resource="sqlmap/User.xml"/>
    <mapper resource="mapper/UserMapper.xml"/>
mappers>

(3)编写新的映射文件中的内容和mapper接口,要求如下:
1. 在映射文件中mapper的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




<mapper namespace="com.wm103.mapper.UserMapper">
    
    <select id="findUserById" parameterType="int" resultType="com.wm103.po.User">
        SELECT * FROM mb_user WHERE id=#{id}
    select>

    
    <select id="findUserByName" parameterType="java.lang.String" resultType="com.wm103.po.User">
        SELECT * FROM mb_user WHERE username LIKE '%${value}%'
    select>

    
    <insert id="insertUser" parameterType="com.wm103.po.User">
        
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
          SELECT LAST_INSERT_ID()
        selectKey>
        INSERT INTO mb_user(username, birthday, sex, address) value(#{username}, #{birthday}, #{sex}, #{address})
    insert>

    
    <insert id="insertUser2" parameterType="com.wm103.po.User">
        
        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
        selectKey>
        INSERT INTO mb_user(id, username, birthday, sex, address) value(#{id}, #{username}, #{birthday}, #{sex}, #{address})
    insert>

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

    
    <update id="updateUser" parameterType="com.wm103.po.User">
        UPDATE mb_user SET username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} WHERE id = #{id}
    update>

    
    <update id="updateUserBySex" parameterType="com.wm103.po.User">
        UPDATE mb_user SET username = #{username}, birthday = #{birthday}, address = #{address} WHERE sex = #{sex}
    update>
mapper>

mapper接口 UserMapper.java

package com.wm103.mapper;

import com.wm103.po.User;

import java.util.List;

/**
 * Created by DreamBoy on 2018/4/10.
 */
public interface UserMapper {
    User findUserById(int id) throws Exception;

    List findUserByName(String name) throws Exception;

    void insertUser(User user) throws Exception;

    void deleteUser(int id) throws Exception;
}

(4)创建测试类,通过SqlSession根据mapper接口获取对应的代理对象,显示查询操作,无需我们mapper接口下的方法的具体逻辑。如下:

package com.wm103.test;

import com.wm103.mapper.UserMapper;
import com.wm103.po.User;
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.Before;
import org.junit.Test;

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

/**
 * Created by DreamBoy on 2018/4/10.
 */
public class TestUserMapper {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws IOException {
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void findUserByIdTest() throws Exception {
        SqlSession sqlSession = this.sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(2);
        sqlSession.close();
        System.out.println(user);
    }

    @Test
    public void findUserByNameTest() throws Exception {
        SqlSession sqlSession = this.sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List list = userMapper.findUserByName("mb");
        sqlSession.close();
        System.out.println(list);
    }
}

  注:mapper接口方法参数只能设置一个,我们可以使用包装类型的pojo满足不同的业务需求。
  通过SqlSession获取的代理对象,针对查询操作而言,根据mapper接口定义的查询结果返回类型而调用selectOneselectList方法。如果mapper方法定义的返回值类型为单个pojo对象(即非集合对象),则代理对象内部会使用selectOne查询数据库;如果mapper方法定义的返回值类型为集合对象,则代理对象内部会使用selectList查询数据库。(记住,如果使用selectOne查询多条记录的话,要抛出异常哦!!!

全局配置文件SqlMapConfig.xml

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

- properties 属性
- settings 设置
- typeAliases 类型别名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
|- environment 环境变量
|-- transactionManager 事务管理器
|-- dataSource 数据源
- databaseIdProvider 数据库厂商标识
- mappers 映射器

  这里将不对每个标签进行一一讲解,而是对某些入门所需的标签进行说明。
(1)properties
  使用properties可以加载外部的配置文件,并使用配置文件中的配置项。如:在config目录下,我们可以定义额外的配置文件,用来保存数据库的设置,如下:
  config.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mydb_329?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=

  在核心配置文件SqlMapConfig.xml中加载该配置文件,并采用${}的方式使用该配置文件的配置,如下:



<configuration>
    <properties resource="config.properties"/>
    
    <environments default="development">
        <environment id="development">
            
            <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>
        <package name="com.wm103.mapper"/>
    mappers>
configuration>

  这样做的好处是,将项目中一些重要的配置,如数据库配置,进行统一管理,也可以共享配置,有利于后续的维护。
  除了加载配置文件的作用后,properties内部可以使用property子标签配置属性,如:

<properties resource="config.properties">
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
properties>

  如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性(即在创建SqlSessionFactory时传入Properties对象)。

      因此,建议只采用加载外部配置文件的方式,将属性值定义在该文件中
      此外,值得注意的是,如果配置文件属性名同映射文件中SQL传入的参数对象的属性同名的话,在执行SQL语句时传入的参数对象的属性的值将为配置文件中同名的属性的值,而非statement传入的对象的属性值。如:
      config.properties 做如下设置:

username=root

  在映射文件中,如UserMapper.xml中:

"updateUserByUsername" parameterType="com.wm103.po.User">
    UPDATE mb_user SET birthday = #{birthday}, address = #{address} WHERE username LIKE '%${username}%'

  这时使用该statement,传入User对象,以username属性作为where条件参数时,就会发现执行数据库操作时所执行的SQL语句并非同我们预想的一样。${username}很意外地使用了config.properties文件中username属性的值,而非我们传入的user对象的值。如:

@Test
public void updateUserByUsernameTest() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    User user = new User();
    user.setUsername("mb");
    user.setBirthday(new Date());
    user.setAddress("新地址23333");
    sqlSession.update("user.updateUserByUsername", user);
    sqlSession.commit();
    MyBatisUtil.closeSession(sqlSession);
}

  运行结果后,我们发现执行的SQL语句为:

UPDATE mb_user SET birthday = ?, address = ? WHERE username LIKE '%root%'

  因此,建议在properties文件中定义的属性名要有一定的特殊性
(2)settings(配置)
  MyBatis全局配置参数,该设置将会影响MyBatis的运行行为,如是否开启延迟加载、缓存配置等。这里先不做过多讲解,具体可参考官方文档。
(3)typeAliases(类型别名)
  在映射文件中,我们可能需要配置输入映射(parameterType)或输出映射(resultType),而输入映射或输出映射为对象类型的话,那么则需要配置该对象的全路径。如果存在过多的相同配置的映射类型的话,实际上将不方便开发,因此,可以在核心配置文件中使用typeAliases标签定义别名,后续映射类型使用定义的别名。
  在MyBatis中默认支持了许多别名,如:java.lang.Integer别名为int。也因为如此,我们在映射文件中,可以使用int作为输入映射类型或输出映射类型。如:

<select id="findUserById" parameterType="int" resultType="com.wm103.po.User">
    SELECT * FROM mb_user WHERE id=#{id}
select>

  那么如何自定义别名呢?自定义别名的方式有两种:一种是单个定义别名;另一种是批量定义别名(这也是一种常用的方式)。
  单个定义别名,如下:

<typeAliases>
    <typeAlias type="com.wm103.po.User" alias="user"/>
typeAliases>

  做如上定义后,我们可以直接使用user作为输入映射或输出映射的映射类型。
  批量定义别名,通过指定包名,MyBatis会自动扫描包中的po类,自动定义别名,而自定定义出来的别名是类名或首字母小写的类名,如User或user均可作为别名引用。如下:

<typeAliases>
    
    <package name="com.wm103.po"/>
typeAliases>

(4)mappers(映射配置)

  • 通过resource加载单个映射文件,如下:
<mappers>
    <mapper resource="sqlmap/User.xml"/>
    <mapper resource="mapper/UserMapper.xml"/>
mappers>
  • 批量加载mapper,通过指定mapper接口的包名,MyBatis会自动扫描包下的所有mapper映射文件进行加载。此时,需要遵循一些规范,包下的映射文件才会被扫描到且正确加载。规范如下:
1. mapper接口名同对应的映射文件的名称保持一致;
2. mapper接口和对应的映射文件在同一目录中;
3. 采用的是mapper代理方式

  设置批量加载mapper方式,如下:

<mappers>
    <mapper resource="sqlmap/User.xml"/>
    
    <package name="com.wm103.mapper"/>
mappers>

知识点扩展或参考

  1. MyBatis官方文档
  2. IDEA构建一个mybatis项目
  3. Mybatis配置之属性配置元素详述

你可能感兴趣的:(JavaEE)