Mybatis (一) 初见Mybatis

初见Mybatis

概述

​ 在技术进步的过程中,往往是因为新的技术比老的使用操作更方便,或是性能更优。而现在要接触的Mybatis无疑比传统的JDBC和hibernate等框架有更优的地方,才会有其存在的理由。下面我们通过JDBC与Mybatis的简单操作进行对比,来认识Mybatis的好。

简介

来自官网:

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

说一下:Mybatis官网对于英语不好的工程师来说很友好,因为它是为数不多的有中文文档的技术官网。

JDBC VS Mybatis

使用JDBC操作数据库

DBUtils:

/**
 * 描述:
 * 类【DBUtils】
 *
 * @author ouYangHao
 * @create 2019-09-17 14:55
 */
public class DBUtils {

    /*用户名*/
    private static final String username = "root";
    /*密码*/
    private static final String password = "root";
    /*驱动名*/
    private static final String driverClassName = "com.mysql.jdbc.Driver";
    /*数据库连接地址*/
    private static final String dbUrl = "jdbc:mysql://120.79.167.xxx:3306/mybatis
      ?useUnicode=true
      &characterEncoding=UTF-8
      &allowMultiQueries=true
      &autoReconnect=true
      &useSSL=false";


    public static List<User> findUsersByUsername(String name){
        List<User> users = new ArrayList<>();
        /*连接*/
        Connection connection = null;
        /*预编译statement*/
        PreparedStatement statement = null;
        /*结果集*/
        ResultSet resultSet = null;
        try {
            /*加载数据库驱动*/
            Class.forName(driverClassName);
            /*获取数据库连接*/
            connection = DriverManager.getConnection(dbUrl, username, password);
            /*定义SQL*/
            String sql = " select * from tb_user where username = ? ";
            /*创建预编译处理的statement*/
            statement = connection.prepareStatement(sql);
            /*设置参数*/
            statement.setString(1,name);
            /*执行SQL*/
            resultSet = statement.executeQuery();

            /*遍历结果集,封装对象*/
            while (resultSet.next()){
                User user = new User();
                user.setId(resultSet.getInt("id"));
                user.setUserId(resultSet.getString("user_id"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                user.setEmail(resultSet.getString("email"));
                user.setPhone(resultSet.getString("phone"));
                user.setGender(resultSet.getInt("gender"));
                user.setBirthday(resultSet.getTime("birthday"));
                user.setStatus(resultSet.getInt("status"));
                user.setCreateTime(resultSet.getTimestamp("create_time"));
                user.setCreateUser(resultSet.getString("create_user"));
                user.setModifyTime(resultSet.getTimestamp("modify_time"));
                user.setModifyUser(resultSet.getString("modify_user"));
                users.add(user);
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (connection != null){
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (statement != null){
                    statement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (resultSet != null){
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return users;
    }

    public static void main(String[] args) {
        List<User> users = DBUtils.findUsersByUsername("admin");
        System.out.println(users);
    }
}

使用Mybatis操作数据库

Mapper接口的方法:

List<User> findUsersByUsername(String username);

Mapper.xml文件的SQL:

<sql id="Base_Column_List">
  id, user_id, username, password, email, phone, gender, birthday, status, create_time,
  create_user, modify_time, modify_user
</sql>

<!--查询-->
<select id="findUsersByUsername" resultType="user">
    select
    <include refid="Base_Column_List"/>
    from tb_user
    <where>
      username = #{username}
    </where>
</select>

以上代码除去Mybatis的配置文件外,上面仅仅几行就可以实现JDBC几十行代码可以实现的效果。

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>

    <!--
    通过源码我们可以分析读取优先级:
       1、在 properties 内部自定义的属性值第一个被读取
    2、然后读取 resource 路径表示文件中的属性,如果有它会覆盖已经读取的属性;
            如果 resource 路径不存在,那么读取 url 表示路径文件中的属性,如果有它会覆盖第一步读取的属性值
    3、最后读取 parameterType 传递的属性值,它会覆盖已读取的同名的属性
    -->

    <!--引入properties的属性文件-->
    <properties resource="mybatis.properties">
        <property name="username" value="root"/>
    </properties>

    <!--设置使用驼峰命名-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logPrefix" value="##Mybatis##"/>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <!-- mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写) -->
    <typeAliases>
        <package name="com.ooyhao.mybatis.bean"/>
    </typeAliases>

    <!--配置环境-->
    <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>

    <!--mapper xml文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

mybatis.properties:

jdbc.driver= com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://120.79.167.xx:3306/mybatis
		?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true&useSSL=false
jdbc.username = root
jdbc.password = root

测试:

public TestDemo{
  SqlSession sqlSession = null;

  @Before
  public void init(){
      String resource = "mybatis-configuration.xml";
      InputStream inputStream = RoleTest.class.getClassLoader().getResourceAsStream(resource);
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
      sqlSession = build.openSession(true);
  }
  @Test
  public void testFindUsersByUsername() {
      //使用Mapper文件形式
      String username = "admin";
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      List<User> userList = mapper.findUsersByUsername(username);
      System.out.println(userList);
      sqlSession.close();
  }
}

我们可以看一下测试类:我们通过SqlSessionFactoryBuilder的build方法,结合Mybatis的全局配置文件创建出SqlSessionFactory,再利用SqlSessionFactory通过openSession方法创建一个SqlSession。然后通过SqlSession来操作SQL。

看一下项目结构:

SqlSession执行SQL

操作步骤

  1. 需要在对应的mapper文件中添加相关的sql配置。
  2. 获得配置文件的路径并通过Resources的getResourceAsStream/AsReader方法获取流
    1. InputStream getResourceAsStream(String resource) 返回值为字节流
    2. Reader getResourceAsReader(String resource) 返回值为字符流
  3. 通过SqlSessionFactoryBuilder创建对象
  4. 使用SqlSessionFactoryBuilder对象的build(Stream/Reader)方法创建SqlSessionFactory对象。
  5. 通过SQLSessionFactory对象的openSession方法创建一个SqlSession对象
  6. 通过SQLSession对象的相关方法进行对数据库的crud操作。
  7. 增删改时需要提交事务。

上面已经进行了数据库操作,我们再回头看一下是不是这样的步骤:

@Test
public void testFindUsersByUsername() {
  //1.mybatis的全局配置文件。
  String resource = "mybatis-configuration.xml";
  //2.获取文件并将其读取成流。
  InputStream inputStream =
    			RoleTest.class.getClassLoader().getResourceAsStream(resource);
  //3.通过SqlSessionFactoryBuilder创建对象。
  //4.通过build创建SQLSessionFactory。
  SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
  //5.创建一个SqlSession对象
  sqlSession = build.openSession(true);
 
  //使用Mapper接口形式
  String username = "admin";
  UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  //6.进行数据库操作
  List<User> userList = mapper.findUsersByUsername(username);
  System.out.println(userList);
  //7.前面设置了自动提交,所以不同提交事务,这里关闭资源。
  sqlSession.close();
}

我们通过SqlSession指定Sql的有下列几种方式:

方式1

使用SqlSession直接通过namespace.id的形式操作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.ooyhao.mybatis1.mybatis.mapper.UserMapper">

  <!--查询-->
  <select id="findUsersByUsername" resultType="user">
      select
      <include refid="Base_Column_List"/>
      from tb_user
      <where>
          username = #{username}
      </where>
  </select>
</mapper>

测试方法:

@Test
public void testMybatisFindUsersByUsernameXML() {
    /*使用XML形式文件*/
    String username = "admin";
    List<User> list = sqlSession.selectList
      ("com.ooyhao.mybatis1.mybatis.mapper.UserMapper.findUsersByUsername", username);
    System.out.println(list);
    sqlSession.close();
}

提示:com.ooyhao.mybatis1.mybatis.mapper.UserMapper是xml配置文件的namespace。findUsersByUsername是sql的id。

方式2

使用Mapper接口结合注解的方式来执行SQL:

@Select(" select * from tb_user where username = #{value} ")
List<User> findUsersByUsername(String username);

测试方法:

@Test
public void testFindUsersByUsername() {
    //使用Mapper文件形式
    String username = "admin";
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.findUsersByUsername(username);
    System.out.println(userList);
    sqlSession.close();
}

提示:这种方式通过在接口方法上使用注解,来替代在xml文件中的SQL,简单sql可以使用注解。

方式3

使用Mapper接口和XML文件关联来查询SQL并执行:

Mapper接口:

List<User> findUsersByUsername(String username);

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.ooyhao.mybatis1.mybatis.mapper.UserMapper">

  <!--查询-->
  <select id="findUsersByUsername" resultType="user">
      select
      <include refid="Base_Column_List"/>
      from tb_user
      <where>
          username = #{username}
      </where>
  </select>
</mapper>

单元测试方法:

@Test
public void testFindUsersByUsername() {
    //使用Mapper文件形式
    String username = "admin";
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.findUsersByUsername(username);
    System.out.println(userList);
    sqlSession.close();
}

这种情况后面使用最多,但是有一些需要遵循的规则:

//遵循四个原则
1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同


mapper动态代理
	Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

	mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。

完整的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>
    <!--
        通过源码我们可以分析读取优先级:
           1、在 properties 内部自定义的属性值第一个被读取
        2、然后读取 resource 路径表示文件中的属性,如果有它会覆盖已经读取的属性;
                如果 resource 路径不存在,那么读取 url 表示路径文件中的属性,
				如果有它会覆盖第一步读取的属性值
        3、最后读取 parameterType 传递的属性值,它会覆盖已读取的同名的属性
        -->

    <!--引入properties的属性文件,数据库配置-->
    <properties resource="mybatis.properties">
    </properties>

    <!--设置使用驼峰命名-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logPrefix" value="##Mybatis##"/>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <!-- mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写) -->
    <typeAliases>
        <package name="com.ooyhao.mybatis3.bean"/>
    </typeAliases>

    <!--配置环境-->	
    <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>

    <!--mapper xml文件-->
    <mappers>
        <!--<package name="mapper"/>-->
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/RoleMapper.xml"/>
        <mapper resource="mapper/VehicleMapper.xml"/>
    </mappers>
</configuration>

配置文件解析

【通过properties 的resource属性引入properties文件】

<!--引入properties的属性文件,数据库配置-->
<properties resource="mybatis.properties"></properties>

【延迟加载技术】

<settings>
  	<!--开启驼峰命名-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
  	<!--日志输出-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
  	<!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
  	<!--关闭强制延迟加载技术-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

【包别名】

<!--设置别名-->
<!-- 
mybatis自动扫描包中的po类,自动定义别名,别名是类名(首字母大写或小写都可以,一般用小写)
 -->
<typeAliases>
  	<package name="com.ooyhao.mybatis3.bean"/>
</typeAliases>

【配置数据库信息】(要根据自己的环境来设置)

<!--配置环境-->
<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>

【通过在mapper标签中引入mapper文件】

(一定要注意每新创建一个mapper文件需要在配置文件中配置)

<!--mapper xml文件-->
<mappers>
    <!--<package name="mapper"/>-->
    <mapper resource="mapper/UserMapper.xml"/>
    <mapper resource="mapper/RoleMapper.xml"/>
    <mapper resource="mapper/VehicleMapper.xml"/>
</mappers>

properties属性文件

jdbc.driver= com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://120.79.167.xxx:3306/mybatis
			?useUnicode=true&characterEncoding=UTF-8
			&allowMultiQueries=true&autoReconnect=true&useSSL=false
jdbc.username = root
jdbc.password = root

UserMapper接口文件

public interface UserMapper {
    User findUserWithRolesByUserId(Integer 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.ooyhao.mybatis3.mapper.UserMapper">

    <resultMap id="BaseResultWithRole" type="com.ooyhao.mybatis3.bean.User">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="user_id" jdbcType="VARCHAR" property="userId"/>
        <result column="username" jdbcType="VARCHAR" property="username"/>
        <result column="password" jdbcType="VARCHAR" property="password"/>
        <result column="email" jdbcType="VARCHAR" property="email"/>
        <result column="phone" jdbcType="VARCHAR" property="phone"/>
        <result column="gender" jdbcType="INTEGER" property="gender"/>
        <result column="birthday" jdbcType="DATE" property="birthday"/>
        <result column="status" jdbcType="INTEGER" property="status"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
        <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
        <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
        <collection property="roles" ofType="role"  column="id"  select="selectRole" />
    </resultMap>

    <resultMap id="selectRole" type="role">
        <id column="cid" jdbcType="INTEGER" property="id"/>
        <result column="role_name" jdbcType="VARCHAR" property="roleName"/>
        <result column="description" jdbcType="VARCHAR" property="description"/>
        <result column="status" jdbcType="INTEGER" property="status"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
        <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
        <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    </resultMap>


    <select id="findUserWithRolesByUserId" resultMap="BaseResultWithRole">
        select
        a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
        a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.create_user
        from tb_user a
        where a.id = #{id}
    </select>

    <select id="selectRole" resultType="role" >
        select b.* from tb_user_role a
        left join tb_role b on a.role_id = b.id
        where a.user_id = #{id}
    </select>
</mapper>

单元测试

public class UserTest {

  SqlSession sqlSession = null;

  @Before
  public void init(){
    	String resource = "mybatis-configuration.xml";
    	InputStream inputStream =
          		UserTest.class.getClassLoader().getResourceAsStream(resource);
    	sqlSession = new
         		SqlSessionFactoryBuilder().build(inputStream).openSession(true);
  }

  @Test
  public void findUserWithRolesByUserId(){
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.findUserWithRolesByUserId(1);
    System.out.println(JSONObject.toJSONString(user));
    sqlSession.close();
  }
}

测试结果

总结

​ 这一节仅仅通过JDBC和Mybatis的操作比较,来引出Mybatis,初一看,可能会觉得Mybatis配置文件很繁琐,但是当我们书写多个SQL之后,就会觉得Mybatis仅仅是在第一次配置有些繁琐,后面书写SQL会很方便,而使用JDBC方式,每次写sql都会连带着5,60%的与业务无关的代码 ,而且每次都写的是一样的,比如获取Connection,获取Statement,关闭资源。

源码地址:

https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Mybatis

最后

如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习

posted @ 2019-09-21 11:12  ooyhao  阅读(489)  评论(0编辑  收藏  举报