zoukankan      html  css  js  c++  java
  • (转)MyBatis框架的学习(三)——Dao层开发方法

    http://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);
        }
    
    }
    • 1
    • 2

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

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

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

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

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

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

    2. 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
    3. 调用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>
    • 1

    编写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"/>

    如此一来,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() {
            // 读者自己编写代码测试......
        }
    
    }
    • 1

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

    小结

    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>
    • 1

    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支持别名如下表:

    别名映射的类型
    _byte byte
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    map Map

    自定义别名

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

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

    以上就为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>
    • 1

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

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

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

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

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

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

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

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


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

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

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


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

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

  • 相关阅读:
    Entity Framework Core 2.0 新特性
    asp.net core部署时自定义监听端口,提高部署的灵活性
    asp.net core使用jexus部署在linux无法正确 获取远程ip的解决办法
    使用xshell连接服务器,数字键盘无法使用解决办法
    使用Jexus 5.8.2在Centos下部署运行Asp.net core
    【DevOps】DevOps成功的八大炫酷工具
    【Network】Calico, Flannel, Weave and Docker Overlay Network 各种网络模型之间的区别
    【Network】UDP 大包怎么发? MTU怎么设置?
    【Network】高性能 UDP 应该怎么做?
    【Network】golang 容器项目 flannel/UDP相关资料
  • 原文地址:https://www.cnblogs.com/telwanggs/p/6911739.html
Copyright © 2011-2022 走看看