SSM框架开发web项目系列(二) MyBatis真正的力量

  前言

  上篇SSM框架环境搭建篇,演示了咱们进行web开发必不可少的一些配置和准备工做,若是这方面还有疑问的地方,能够先参考上一篇“SSM框架开发web项目系列(一) 环境搭建篇”。本文主要介绍MyBatis的基础内容,包括基本概念、开发步骤、使用实例等。提及MyBatis,工做中作过SSH/SSM相关Web开发的或者正在学习MyBatis的人或多或少都会接触到相似“MyBatisHibernate有什么区别?”,“MyBatisHibernate哪一个更好?”,“为何Mybatis用的人愈来愈多?”等等...记得面试问题,区别问的最多,有次被面试官问到更喜欢用哪个?明明已经知道这个公司介绍用的是SSM了,我答了个Hibernate,并说先用的也是Hibernate,或许初恋的感受过于深入吧...谁好谁差这种主观性问题,咱们不争论,可是不容质疑的是二者都为企业级开发作出巨大贡献。同时,带着问题和求知欲去学习每每会让学习效率大大提升,由于许多问题在困惑你的同时,也为你指引了方向。就正如你得会用它,理解它了,才知道它在某方面为何会不足。html

  MyBatis介绍

  早些时候,Apache有一个开源项目iBatis,后来更名了叫Mybatis,因此咱们在网上有时候会看到一些早期的文章有时候看到iBatis,实际上是同一个东西。同时,你们能够看下MyBatis源码工程结构,以下图,也能发现这个问题。java

 

  MyBatis是一个半自动-ORM-持久层框架,下面分别介绍这三个概念,若是了解的的能够直接跳过以节省时间。mysql

  首先持久层比较好理解,就是针对数据库的各类操做持久化层面,咱们用jdbc也能够对数据库中表进行增删可查;WEB开发分层结构中,相似的还有业务层、控制层,MyBatis所作用的层,主要是用于与数据库打交道。web

  其次ORMObject Relational Mapping,对象关系映射)是一种技术,也是思想,若是用过jdbc的原生方式操做数据的应该都知道,其中各类获取和更新的操做都已有相关的API了,可是这个过程实在太繁琐,获取链接、构造语句、发送SQL和接收数据、最后是处理数据和关闭流等等...实际的工做开发中,显然不太适用。也许是有人想到,最麻烦的地方在于获取数据后的处理过程,为了简化这一过程,以Java中面向对象的思惟构造了这一个ORM关系模型,即每张数据表对应一个Java类,数据表中每一条记录分别对应Java类的一个实体对象,数据表中每一个字段对应Java类中的一个属性,这样一来Java中一切皆对象,数据库里的东西既然已经对应映射到Java概念中来,咱们再用面向对象的思惟去操做数据库,就大大简化了开发流程。面试

  最后,解释半自动,既然有半自动,应该就有全自动,例如Hibernate就是一个全自动的ORM持久层框架,它在创建数据库和bean对象关系映射模型的同时,提供的api还会帮助咱们自动生成和发送SQL语句去操做数据库,而MyBatis略有不一样,它也创建了对象关系映射模型,可是并不会帮助咱们生成SQL语句,须要咱们本身写SQL语句,很多人可能会以为别人均可以帮你自动生成了这还要本身写,不是没事找事吗?可是偏偏相反,不少人由于这点喜欢上了MyBatis,灵活且透明,本身动手不解释。sql

  MyBatis重要对象

  关于MyBatis的学习使用过程当中,依次要注意的四个对象有SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper实例。数据库

  1.SqlSessionFactoryBuilderapache

  SqlSessionFactoryBuilder是一个类,里面定义许多重载的build方法,经过build方法能够获取SqlSessionFactory对象,即SqlSession工厂实例api

  2.SqlSessionFactorysession

  SqlSessionFactory是一个接口,看名字应该能明白是SqlSession工厂,里面定义了许多重载的openSession方法,用于获取SqlSession对象

  3.SqlSession

  SqlSession是一个很关键的接口,经过它咱们能够执行发送SQL语句、得到Mapper实例等等。以它的第一个方法为例, <T> T selectOne(String statement); 方法名很容易理解,获取数据表的一条记录(在Java中对应返回一个实体类对象),前面的泛型对应的就是实体类的类型,关于String类型的参数statement,用过原生的JDBC操做数据库的应该不会陌生,在使用JDBC过程当中,有个Statement对象,经过该对象的相似 ResultSet executeQuery(String sql) throws SQLException; 等方法能够发送SQL语句,方法里的字符串类型参数 sql 就是咱们要发送的sql语句,而前面的String statement 一样也是表明咱们的sql语句总的来讲,selectOne中的statement表明sql语句,JDBC中的statement是能发送sql语句的对象实例,不可混淆。

  4.Mapper实例

  Mapper实例就是咱们在dao层定义的定义的接口实例,咱们在service层中注入dao对象时关联的是该接口名,而实际上咱们拿到了该接口实例也就是Mapper实例。而咱们在web开发过程当中,持久层的相关方法都定义在Mapper接口中,因此四个对象里咱们在前面环境搭建篇比较容易发现的也就是这个Mapper实例所属接口,即PersonMappr接口。Mapper实例能够经过SqlSession的getMapper方法得到。

  MyBatis配置文件

  

  以上为MyBatis配置文件下节点的结构分布图,熟悉DTD的根据org/apache/ibatis/builder/xml下的约束文件mybatis-*-config.dtd也能够获取XML规范。

  environment(环境)

  在开发中,咱们要链接到数据库,每每都须要配置一个数据源,其中包括数据库url参数,用户名和密码等等,另外还有事事务管理器配置。MyBatis中也不例外,在这里针对一个数据库的链接,有一个environment(环境)与之对应,若是有多个数据库,咱们能够在environments下定义多个environment。environment下面经过dataSource和transactionManager的property属性进行数据源和事务管理器配置。

  typeAliases(类型命名)

  这个是开发中很实用的配置,前面配置篇中mybatis-config.xml中的<typeAlias alias="person" type="com.mmm.pojo.Person" />,给类绑定别名,而后在其余地方引用时能够不用写类的全名(相似com.xxx.Xxx形式),直接写别名便可,例如后面personMapper.xml映射文件中<resultMap type="person" id="personResultMap" >,这里的person别名就表明该类了,若是没有前面的别名绑定,咱们在全部须要类型type指定com.mmm.pojo.Person的时候,都须要写全名。另外MyBatis中自己已经绑定了许多相似的别名,在Configuration类的构造中已经预先注册绑定了相关别名,这也是咱们在用Spring集成开发的配置文件中那些特殊别名能获得解析的关键所在,以下所示

public Configuration() {
    typeAliasRegistry.registerAlias(
"JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class);    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); }

  mappers(映射器)

<mappers>
        <mapper resource="com/xxx/xxx/mapper/xxxMapper.xml" />
</mappers>

  以上即为一个映射器配置样式,经过该配置,Mybatis会找到相应的SQL映射文件(前面环境搭建篇中为personMapper.xml),下面详细介绍SQL映射文件。

  MyBatis真正的力量

  The true power of MyBatis is in the Mapped Statements(MyBatis真正的力量就在SQL映射语句里)。这是MyBatis对SQL映射文件做介绍的第一句话,足以体现其重要性,前面说到的半自动的MyBatis须要本身写SQL语句,这个写SQL语句的地方就在这里。

  根节点mapper有一个属性namespace(命名空间),对应的是Mapper接口的全名,经过这个属性值的设置,MyBatis才能找到该SQL映射文件对象Mapper接口,从而构造相应的Mapper实例对象。  

  以上为MyBatis的SQL映射xml文件的元素结构,子节点中还有一个parameterMap,不过已经被废弃了,就没画上去。前面四个,看单词意思,应该不难理解,分别用于定义增删改查SQL语句。下面以查询<select>为例

<!-- 根据主键id查找记录 -->
<select id="selectById" resultType="person">
        select * from `TBL_PERSON` where id = #{id}
</select>    

  上面select节点中id为该节点的惟一标识,与其它节点区分,另外一方面,id名对应咱们的Mapper接口中的方法名,在这里对应PersonMapper的以下方法

//根据主键id查找Person对象
Person selectById(String id);

  因为是查询,会有返回数据内容,基于ORM思想,这里的T_PERSON表返回记录有一个实体类与之对应,那么具体对应哪一个实体类怎么指定?这里的resultType即指定返回数据对应类型,而且用到了别名,因此这里其实就是指定了com.mmm.pojo.Person类做为对应实体类。另外还有一种resultMap定义方式,也能够指定返回类型,二者不能同时使用,下面会具体讲到。若是方法没有返回值,也能够不指定类型。

  节点中的语句即为该方法对应的SQL语句,这里的#{id}表示参数,对应PersonMapper接口中对应方法的参数String id。

   另外三种<delete><updata><insert>相似,不过要注意,<insert>做为插入节点,有几个特殊属性,useGeneratedKeys、keyProperty、keyColumn,这三个属性都是<insert>特有的,也仅对其有效,keyProperty属性指定数据表主键值对应的实体类属性,这里为Person类中的id;keyColumn属性指定数据表主键字段名,这里为id;useGeneratedKeys指定是否使用数据表主键策略生成的主键值,例如在Mysql中主键能够设置自增,而后咱们在插入记录(或者说是写SQL语句)时,即便不指定主键值,插入也会成功,而且主键会自动赋值。为了以示区别,文中构建示例我会使用MySQL的自增主键,而不是以前的随机字符串做为主键。

  <sql>被用来定义可重用的SQL代码段,能够包含在其余语句中。

  再来看<resultMap>,以前的Person实例中,数据表的字段名和实体类属性名都是同样的,id,name,gender。若是不同的话,咱们再采用以前的写法就会有问题了,resultMap给咱们提供了解决办法,下面咱们经过完整构建一个Mybatis环境来演示总体内容。

  MyBatis实践

  这里咱们单独讲Mybatis,并未使用Spring集成环境,也并未用到web层的Spring MVC,因此不须要构建web项目。因此首先经过maven新建一个java项目,pom.xml依赖能够参考前面的SSM环境搭建,最后项目结构大体以下图所示

  数据库中准备一张员工表TBL_EMP,三个字段:主键id自增,姓名emp_name,性别emp_gender,插入若干数据,以下图

  实体类(Emp.java)

package com.mmm.pojo;
//员工实体类
public class Emp {
    private Integer id;            //主鍵
    private String name;        //员工姓名
    private String gender;        //员工性别
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}

  配置文件(mybatis-config.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>
    <!-- 这里能够定义类的别名,在mapper.xml文件中应用会方便不少 -->
    <typeAliases>
        <typeAlias alias="emp" type="com.mmm.pojo.Emp" />
    </typeAliases>
    <!-- 环境配置 -->
    <environments default="envir">
        <environment id="envir">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/mmm/mapper/empMapper.xml"/>
    </mappers>
</configuration>

  Mapper接口(EmpMapper.java)

package com.mmm.mapper;

import java.util.List;

import com.mmm.pojo.Emp;

public interface EmpMapper {
    
    //新增一个Emp对象
    void insert(Emp p);
    
    //根据主键id删除Emp对象
    void deleteById(Integer id);
    
    //修改一个Emp对象
    void update(Emp p);
    
    //根据主键id查找Emp对象
    Emp selectById(Integer id);
    
    //查找全部Emp对象,返回集合类型
    List<Emp> selectAll();
}

  SQL映射文件(EmpMapper.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命名空间绑定Mapper接口 -->
<mapper namespace="com.mmm.mapper.EmpMapper">
    
    <!-- resultMap定义,property对应实体类中属性,column对应数据表字段名 -->
    <resultMap type="emp" id="empResultMap" >
        <id property="id" column="id"></id>
        <result property="name" column="emp_name"></result>
        <result property="gender" column="emp_gender"></result>
    </resultMap>
    
    <!-- 新增一条记录,这里并未在SQL语句中设置主键id值 -->
    <insert id="insert" parameterType="emp" useGeneratedKeys="true" keyProperty="id" keyColumn="id"  >
        insert into `TBL_EMP`(emp_name,emp_gender) values (#{name},#{gender})
    </insert>
    
    <!-- 删除一条记录 -->
    <delete id="deleteById">
        delete from `TBL_EMP` where id = #{id}
    </delete>
    
    <!-- 更新一条记录 -->
    <update id="update" parameterType="EMP">
        update `TBL_EMP` set emp_name = #{name}, emp_gender = #{gender}
    </update>
    
    <!-- 查找全部记录 -->
    <select id="selectAll" resultMap="empResultMap">
        select * from `TBL_EMP`
    </select>
    
    <!-- 根据主键id查找记录 -->
    <select id="selectById" resultMap="empResultMap">
        select * from `TBL_EMP` where id = #{id}
    </select>
</mapper>

  测试(TestMyBatis.java)

package com.mmm.test;

import java.io.IOException;
import java.io.Reader;
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.junit.Test;

import com.mmm.mapper.EmpMapper;
import com.mmm.pojo.Emp;

public class TestMyBatis {
    
    
    
    @Test
    public void testCore() throws IOException {
        //直接实例SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //MyBatis配置文件路径
        String path = "mybatis-config.xml";
        //经过路径获取输入流
        Reader reader = Resources.getResourceAsReader(path);
        //经过reader构建sessionFactory
        SqlSessionFactory sessionFactory = builder.build(reader);
        //获取SqlSession对象
        SqlSession sqlSession = sessionFactory.openSession();
        //获取Mapper实例
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        
        //获取全部记录并遍历展现
        List<Emp> list = mapper.selectAll();
        for(Emp emp:list) {
            System.out.println("姓名:"+emp.getName()+",性别:"+emp.getGender());
        }
        
    }
}

  运行程序成功,结果以下

  小结

  本文主要介绍了单独以MyBatis构建数据库访问程序的方法步骤,涉及的都是MyBatis基础和核心的内容,数据库也是针对的单表操做。MyBatis的关联查询、动态SQL、事务管理和Spring集成MyBatis等内容准备梳理一番了以后写出来。

相关文章
相关标签/搜索