MyBatis框架的学习(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yerenyuan_pku/article/details/71700957

使用MyBatis开发Dao层,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。本文案例代码的编写是建立在前文MyBatis框架的学习(二)——MyBatis架构与入门案例基础之上的!

需求

明确开发需求,在实际开发中,我们总归是要开发Dao层的,所以在本文中我使用MyBatis这个框架技术开发Dao层来将以下功能一一实现:

  1. 根据用户id查询一个用户信息
  2. 根据用户名称模糊查询用户信息列表
  3. 添加用户信息

MyBatis常用API的使用范围

在讲解使用MyBatis这个框架技术开发Dao层之前,首先来说一下MyBatis常用的API。

SqlSessionFactoryBuilder的使用范围

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

SqlSessionFactory的使用范围

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

SqlSession的使用范围

SqlSession中封装了对数据库的操作,如查询、插入、更新、删除等。通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建的。
SqlSession是一个面向程序员的接口,SqlSession中定义了数据库操作方法,所以SqlSession作用是操作数据库,并且SqlSession对象要存储数据库连接、事务和一级缓存结构等。
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它是线程不安全的(多线程访问系统,当多线程同时使用一个SqlSession对象时会造成数据冲突问题)。由于SqlSession对象是线程不安全的,因此它的最佳使用范围是请求或方法范围(也可说为SqlSession的最佳使用场合是在方法体内作为局部变量来使用),绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个SqlSession,使用完毕就要关闭它。通常把这个关闭操作放到finally块中以确保每次都能执行关闭。如下:

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

    原始Dao开发方式

    原始Dao开发方法需要程序员自己编写Dao接口和Dao实现类。
    在工程的src目录下创建一个cn.itheima.mybatis.dao包,并在该包下编写一个UserDao接口:

    public interface UserDao {
    
        User getUserById(int id);
        List<User> getUserByName(String username);
        void insertUser(User user);
    
    }
     
     

      接着再在src目录下创建一个cn.itheima.mybatis.dao.impl包,在该包下编写UserDao接口的实现类——UserDaoImpl.java:

      public class UserDaoImpl implements UserDao {
      
          private SqlSessionFactory sqlSessionFactory;
      
          public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
              this.sqlSessionFactory = sqlSessionFactory;
          }
      
          @Override
          public User getUserById(int id) {
              SqlSession sqlSession = sqlSessionFactory.openSession();
              // 根据id查询用户信息
              User user = sqlSession.selectOne("getUserById", id);
              // 关闭sqlSession
              sqlSession.close();
              return user;
          }
      
          @Override
          public List<User> getUserByName(String username) {
              // 创建一个SQLSession对象
              SqlSession sqlSession = sqlSessionFactory.openSession();
              // 执行查询
              List<User> list = sqlSession.selectList("getUserByName", username);
              // 释放资源
              sqlSession.close();
              return list;
          }
      
          @Override
          public void insertUser(User user) {
              // 创建一个SQLSession对象
              SqlSession sqlSession = sqlSessionFactory.openSession();
              // 插入用户
              sqlSession.insert("insertUser", user);
              // 提交事务
              sqlSession.commit();
              // 释放资源
              sqlSession.close();
          }
      
      }
       
       

        最后创建一个JUnit的测试类——UserDaoTest.java,对UserDao接口进行测试。步骤如下:

        1. 在工程下新建一个源码目录,专门用于存放JUnit的单元测试类。
        2. 右键【UserDao.java】→【New】→【JUnit Test Case】
          这里写图片描述
        3. 在弹出的对话框中,修改单元测试类的存放目录为test源码目录
          这里写图片描述
        4. 点击【Next】按钮,在弹出的对话框中勾选全部测试方法,然后点击【Finish】完成
          这里写图片描述

        这样就会产生如下效果:
        这里写图片描述
        最后将JUnit的单元测试类——UserDaoTest.java修改为:

        public class UserDaoTest {
        
            private SqlSessionFactory sqlSessionFactory = null; // 工厂对象一般在我们的系统中是单例的
        
            @Before
            public void init() throws IOException {
                // 第一步,创建SqlSessionFactoryBuilder对象
                SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
                // 第二步,加载配置文件
                InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
                // 第三步,创建SqlSessionFactory对象
                sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
            }
        
            @Test
            public void testGetUserById() {
                UserDao userDao = new UserDaoImpl(sqlSessionFactory);
                User user = userDao.getUserById(10);
                System.out.println(user);
            }
        
            @Test
            public void testGetUserByName() {
                UserDao userDao = new UserDaoImpl(sqlSessionFactory);
                List<User> list = userDao.getUserByName("张");
                for (User user : list) {
                    System.out.println(user);
                }
            }
        
            @Test
            public void testInsertUser() {
                UserDao userDao = new UserDaoImpl(sqlSessionFactory);
                User user = new User();
                user.setUsername("赵云");
                user.setAddress("正定");
                user.setBirthday(new Date());
                user.setSex("1");
                userDao.insertUser(user);
            }
        
        }
         
         

          读者可自行测试,在此不做过多赘述。

          原始Dao开发方式所带来的问题

          从以上UserDaoImpl类的代码可看出原始Dao开发存在以下问题:

          1. dao接口实现类方法中存在大量的重复代码,这些重复的代码就是模板代码。
            模板代码为:

          • 先创建sqlsession
          • 再调用sqlsession的方法
          • 再提交sqlsession
          • 再关闭sqlsession

          设想能否将这些代码提取出来,这可大大减轻程序员的工作量。

          • 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
          • 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
          • 下面我来使用mapper代理方法来开发Dao层,来解决上面我们所发现的问题。

            Mapper动态代理开发方式

            开发规范

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

            1. Mapper.xml文件中的namespace与mapper接口的类路径相同,即namespace必须是接口的全限定名。
            2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
            3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
            4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

            接下来我就来编码使用mapper动态代理方式来开发Dao层。

            编写Mapper.xml(映射文件)

            我们可在config源码目录下新建一个mapper的普通文件夹,该文件夹专门用于存放映射文件。然后在该文件夹下创建一个名为mapper.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="cn.itheima.mybatis.mapper.UserMapper">
                <select id="getUserById" parameterType="int" resultType="USer">
                    select * from user where id=#{id};
                </select>
            
                <select id="getUserByName" parameterType="string" resultType="cn.itheima.mybatis.po.User">
                    SELECT * FROM `user` WHERE username LIKE '%${value}%'
                </select>
            
                <insert id="insertUser" parameterType="cn.itheima.mybatis.po.User">
                    <selectKey keyProperty="id" resultType="int" order="AFTER">
                        SELECT LAST_INSERT_ID()
                    </selectKey>
                    INSERT INTO `user` (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
                </insert>
            </mapper>
             
             

              编写Mapper接口

              在工程的src目录下新建一个cn.itheima.mybatis.mapper包,并在该包下创建一个Mapper接口——UserMapper.java:

              public interface UserMapper {
              
                  User getUserById(int id);
                  List<User> getUserByName(String username);
                  void insertUser(User user);
              
              }
               
               

                接口定义有如下特点:

                1. mapper接口方法名和mapper.xml中定义的statement的id相同。
                2. mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同。
                3. mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同。

                加载mapper.xml映射文件

                在SqlMapConfig.xml文件添加如下配置:

                <mapper resource="mapper/mapper.xml"/>
                 
                 
                • 1

                如此一来,SqlMapConfig.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>
                    <!-- 和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="yezi" />
                            </dataSource>
                        </environment>
                    </environments>
                
                    <!-- 加载mapper文件 -->
                    <mappers>
                        <!-- resource是基于classpath来查找的 -->
                        <mapper resource="sqlmap/user.xml"/>
                        <mapper resource="mapper/mapper.xml"/>
                    </mappers>
                </configuration>
                 
                 

                  编写测试程序

                  最后编写UserMapper接口的一个单元测试类,具体步骤我已在本文中详细说明了。修改UserMapperTest这个单元测试类的内容为:

                  public class UserMapperTest {
                  
                      private SqlSessionFactory sqlSessionFactory = null; // 工厂对象一般在我们的系统中是单例的
                  
                      @Before
                      public void init() throws IOException {
                          // 第一步,创建SqlSessionFactoryBuilder对象
                          SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
                          // 第二步,加载配置文件
                          InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
                          // 第三步,创建SqlSessionFactory对象
                          sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
                      }
                  
                  
                      @Test
                      public void testGetUserById() {
                          // 和Spring整合后就省略了
                          SqlSession sqlSession = sqlSessionFactory.openSession();
                  
                          // 获得代理对象(到时候就只需要通过Spring容器拿到UserMapper接口的代理对象就可以了)
                          UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                          User user = userMapper.getUserById(10);
                          System.out.println(user);
                  
                          // 和Spring整合后就省略了
                          sqlSession.close();
                      }
                  
                      @Test
                      public void testGetUserByName() {
                          // 和Spring整合后就省略了
                          SqlSession sqlSession = sqlSessionFactory.openSession();
                  
                          // 获得代理对象(到时候就只需要通过Spring容器拿到UserMapper接口的代理对象就可以了)
                          UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                          List<User> list = userMapper.getUserByName("张");
                          for (User user : list) {
                              System.out.println(user);
                          }
                  
                          // 和Spring整合后就省略了
                          sqlSession.close();
                      }
                  
                      @Test
                      public void testInsertUser() {
                          // 读者自己编写代码测试......
                      }
                  
                  }
                   
                   

                    以上方法测试均没问题,读者如若不信可亲测。

                    小结

                    selectOne和selectList

                    动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

                    namespace

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

                    SqlMapConfig.xml配置文件

                    配置内容

                    SqlMapConfig.xml文件中配置的内容和顺序如下:

                    1. properties(属性)
                    2. settings(全局配置参数)
                    3. typeAliases(类型别名)
                    4. typeHandlers(类型处理器)
                    5. objectFactory(对象工厂)
                    6. plugins(插件)
                    7. environments(环境集合属性对象)
                      • environment(环境子属性对象)
                        • transactionManager(事务管理)
                        • dataSource(数据源)
                    8. mappers(映射器)

                    properties(属性)

                    在SqlMapConfig.xml配置文件中,我们可把数据库连接信息配置到properties标签当中,类似如下:

                    <!-- 配置属性 -->
                    <properties>
                        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
                        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                    </properties>
                     
                     

                      接下来把以上配置信息添加到SqlMapConfig.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>
                          <!-- 配置属性 -->
                          <properties>
                              <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
                              <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                          </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="root" />
                                      <property name="password" value="yezi" />
                                  </dataSource>
                              </environment>
                          </environments>
                      
                          <!-- 加载mapper文件 -->
                          <mappers>
                              <!-- resource是基于classpath来查找的 -->
                              <mapper resource="sqlmap/user.xml"/>
                              <mapper resource="mapper/mapper.xml"/>
                          </mappers>
                      </configuration>
                       
                       

                        SqlMapConfig.xml文件向上面这样配置,虽然没问题,但是我们不觉得这样很不爽吗?我们一般都是将数据库连接信息配置到一个java属性文件中,然后再来引用其中的配置信息。我按照这种指导思想在classpath下定义一个db.properties文件,SqlMapConfig.xml文件引用该属性文件中的配置信息如下:

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

                          如此一来,SqlMapConfig.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>
                              <!-- 配置属性 -->
                              <properties resource="db.properties">
                                  <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
                                  <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                              </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>
                          
                              <!-- 加载mapper文件 -->
                              <mappers>
                                  <!-- resource是基于classpath来查找的 -->
                                  <mapper resource="sqlmap/user.xml"/>
                                  <mapper resource="mapper/mapper.xml"/>
                              </mappers>
                          </configuration>
                           
                           

                            注意:MyBatis将按照下面的顺序来加载属性:

                            1. 在properties元素体内定义的属性首先被读取
                            2. 然后会读取properties元素中resource或url加载的属性,它会覆盖已读取的同名属性

                            按照我自己的话说就是:先加载property元素内部的属性,然后再加载db.properties文件外部的属性,如果有同名属性则会覆盖。

                            如若读者不信,可以将properties元素的内容修改为:

                            <properties resource="db.properties">
                                <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
                                <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                                <property name="jdbc.username" value="hello"/>
                            </properties>
                             
                             

                              以上故意将数据库连接的用户名给整错,结果发现仍然好使,这足以说明问题了。

                              typeAliases(类型别名)

                              mybatis支持别名

                              mybatis支持别名如下表:

                              别名映射的类型
                              _bytebyte
                              _longlong
                              _shortshort
                              _intint
                              _integerint
                              _doubledouble
                              _floatfloat
                              _booleanboolean
                              stringString
                              byteByte
                              longLong
                              shortShort
                              intInteger
                              integerInteger
                              doubleDouble
                              floatFloat
                              booleanBoolean
                              dateDate
                              decimalBigDecimal
                              bigdecimalBigDecimal
                              mapMap

                              自定义别名

                              如果我们在SqlMapConfig.xml配置文件添加如下配置信息:

                              <!-- 配置pojo的别名 -->
                              <typeAliases>
                                  <!-- 单个别名定义 -->
                                  <typeAlias type="cn.itheima.mybatis.po.User" alias="user"/>
                              </typeAliases>
                               
                               

                                以上就为User类定义了一个别名(user)。
                                那么我们就可以在mapper.xml映射文件中使用这个别名了,如下:

                                <mapper namespace="cn.itheima.mybatis.mapper.UserMapper">
                                                                                <!-- 别名不区分大小写 -->
                                    <select id="getUserById" parameterType="int" resultType="USer">
                                        select * from user where id=#{id};
                                    </select>
                                
                                    ......
                                </mapper>
                                 
                                 

                                  注意:resultType属性的值就是User类的别名,且别名是不区分大小写的。
                                  聪明的小伙伴们肯定发现如果像这样为每一个pojo定义一个别名,并不是明智之举,万一一个项目里面有很多pojo类呢?所以我们可以批量定义别名,如下:

                                  <!-- 配置pojo的别名 -->
                                  <typeAliases>
                                      <!-- 批量别名定义,扫描包的形式创建别名,别名就是类名,且不区分大小写 -->
                                      <package name="cn.itheima.mybatis.po"/>
                                  </typeAliases>
                                   
                                   

                                    SqlMapConfig.xml文件加载mapper.xml文件

                                    Mapper(映射器)配置的几种方法:

                                    1. <mapper resource=" " />
                                      使用相对于类路径的资源,如

                                    <mapper resource="sqlmap/user.xml"/>
                                     
                                     
                                    • 1

                                    这里写图片描述

                                  • <mapper class=" " />
                                    使用mapper接口类路径,如:

                                  • <mapper class="cn.itheima.mybatis.mapper.UserMapper"/>
                                     
                                     
                                    • 1

                                    这里写图片描述
                                    注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

                                  • <package name=""/>
                                    注册指定包下的所有mapper接口,如:

                                  • <package name="cn.itheima.mybatis.mapper"/>
                                     
                                     
                                    • 1

                                    这里写图片描述
                                    注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

                                    虽然Mapper(映射器)配置有以上三种方法,但是实际开发中就用第三种方法。至此,使用MyBatis开发Dao层我就总结到这里。

                                  评论
                                  添加红包

                                  请填写红包祝福语或标题

                                  红包个数最小为10个

                                  红包金额最低5元

                                  当前余额3.43前往充值 >
                                  需支付:10.00
                                  成就一亿技术人!
                                  领取后你会自动成为博主和红包主的粉丝 规则
                                  hope_wisdom
                                  发出的红包
                                  实付
                                  使用余额支付
                                  点击重新获取
                                  扫码支付
                                  钱包余额 0

                                  抵扣说明:

                                  1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                                  2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                                  余额充值