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层我就总结到这里。

转载于:https://www.cnblogs.com/telwanggs/p/6911739.html

(转)MyBatis框架的学习(三)——Dao层开发方法相关推荐

  1. (转)MyBatis框架的学习(四)——Mapper.xml文件中的输入和输出映射以及动态sql

    http://blog.csdn.net/yerenyuan_pku/article/details/71893689 前面对MyBatis框架的学习中,我们对Mapper.xml映射文件多少有些了解 ...

  2. (转)MyBatis框架的学习(六)——MyBatis整合Spring

    http://blog.csdn.net/yerenyuan_pku/article/details/71904315 本文将手把手教你如何使用MyBatis整合Spring,这儿,我本人使用的MyB ...

  3. (转)MyBatis框架的学习(二)——MyBatis架构与入门

    http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图:  下面作简要概述: ...

  4. Mybatis 原始Dao层开发

    对Mybatis进行原始Dao层开发的举例子: 定义获取session工具类: package com.langsin.unit;import java.io.InputStream;import o ...

  5. (转)MyBatis框架的学习(七)——MyBatis逆向工程自动生成代码

    http://blog.csdn.net/yerenyuan_pku/article/details/71909325 什么是逆向工程 MyBatis的一个主要的特点就是需要程序员自己编写sql,那么 ...

  6. (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射

    http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...

  7. SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

    SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统( ...

  8. 1、MyBatis框架入门学习CRUD

    文章目录 框架前言 名词辨析 JavaWeb阶段回顾 框架介绍 ORM框架 MyBatis简介 MyBatis入门案例 1.环境准备 2.新建普通Maven项目,导入依赖 3.MyBatis核心配置文 ...

  9. (转)MyBatis框架的学习(一)——MyBatis介绍

    http://blog.csdn.net/yerenyuan_pku/article/details/71699343 MyBatis介绍 MyBatis本是apache的一个开源项目iBatis,2 ...

最新文章

  1. 苹果8怎么投屏到电视_苹果电脑电视不在一个房间可以投屏
  2. oracle 前端ui框架,Layui(前端UI框架) 2.6.4 官方最新版
  3. C/C++变量在嵌套域内可见
  4. 东西是好东西,可惜我们用的不好
  5. oracle buffer block,8 Oracle深度学习笔记——BUFFER CACHE深入一
  6. 服务端程序的初步实现
  7. 2021-04-14 Matlab实现模糊聚类分析 FCM
  8. 学习Python技术有哪些优势?
  9. WebKit 分析–for android【new】
  10. java选择排序百度_java选择排序
  11. PyTorch官方教程大更新:增加标签索引,新手更加友好
  12. JDK源码(12)-Enum
  13. 流行歌曲演唱的风格与技巧
  14. PADS2007常用快捷键
  15. A星算法(A*/A Star)
  16. java gzip解压请求_使用 gzip 压缩请求正文
  17. 谈谈如何提升工作效率,需要从这4方面入手!
  18. 使用NS流程图解析排序算法(1) 快速排序递归算法说明
  19. VS code + miktex + 内置pdf阅读器 配置latex编译环境
  20. matlab 分数阶混沌系统的完全同步控制

热门文章

  1. (11)Xilinx BRAM或者ROM初始化文件.COE制作(FPGA不积跬步101)
  2. (04)VHDL实现打两拍
  3. (19)FPGA乒乓操作
  4. (78)Verilog HDL条件编译:`ifdef
  5. rtosucos和linux区别,为什么我们需要uCos?带你透彻理解RTOS
  6. mysql yum安装包下载_yum 下载安装包
  7. Qt创建多线程的两种方法
  8. Linux的capability深入分析(1)
  9. hive對於數據是懶加載的_hive 安装 文档
  10. c语言读入图片matlab,将数据从matlab文件读入C语言