zoukankan      html  css  js  c++  java
  • 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

    最后

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

    程序yuan
  • 相关阅读:
    JavaScript进阶系列06,事件委托
    JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数
    JavaScript进阶系列04,函数参数个数不确定情况下的解决方案
    JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象
    JavaScript进阶系列02,函数作为参数以及在数组中的应用
    JavaScript进阶系列01,函数的声明,函数参数,函数闭包
    委托、Lambda表达式、事件系列07,使用EventHandler委托
    委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
    委托、Lambda表达式、事件系列05,Action委托与闭包
    委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
  • 原文地址:https://www.cnblogs.com/ooyhao/p/11562023.html
Copyright © 2011-2022 走看看