Java的MyBatis框架中Mapper映射配置的使用及原理解析

Mapper的内置方法
model层就是实体类,对应数据库的表。controller层是Servlet,主要是负责业务模块流程的控制,调用service接口的方法,在struts2就是Action。Service层主要做逻辑判断,Dao层是数据访问层,与数据库进行对接。至于Mapper是mybtis框架的映射用到,mapper映射文件在dao层用。

下面是介绍一下Mapper的内置方法:

1、countByExample ===>根据条件查询数量

int countByExample(UserExample example);

//下面是一个完整的案列
UserExample example = new UserExample();
 Criteria criteria = example.createCriteria();
 criteria.andUsernameEqualTo("joe");
 int count = userDAO.countByExample(example);

相当于:select count(*) from user where username='joe'
 
2、deleteByExample ===>根据条件删除多条

int deleteByExample(AccountExample example);

//下面是一个完整的案例
UserExample example = new UserExample();
 Criteria criteria = example.createCriteria();
 criteria.andUsernameEqualTo("joe");
 userDAO.deleteByExample(example);
 相当于:delete from user where username='joe'

3、deleteByPrimaryKey===>根据条件删除单条

int deleteByPrimaryKey(Integer id);
userDAO.deleteByPrimaryKey(101); 

相当于:

delete from user where id=101

4、insert===>插入数据

int insert(Account record);

//下面是完整的案例
User user = new User();
 //user.setId(101);
 user.setUsername("test");
 user.setPassword("123456")
 user.setEmail("674531003@qq.com");
 userDAO.insert(user);

相当于:

insert into user(ID,username,password,email) values(101,'test','123456','674531003@qq.com');

5、insertSelective===>插入数据

int insertSelective(Account record);

6、selectByExample===>根据条件查询数据

List<Account> selectByExample(AccountExample example);

//下面是一个完整的案例
UserExample example = new UserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo("joe");
criteria.andUsernameIsNull();
example.setOrderByClause("username asc,email desc");
List<?>list = userDAO.selectByExample(example);
相当于:select * from user where username = 'joe' and username is null order by username asc,email desc

//注:在iBator 生成的文件UserExample.java中包含一个static 的内部类 Criteria ,在Criteria中有很多方法,主要是定义SQL 语句where后的查询条件。

 7、selectByPrimaryKey===>根据主键查询数据

Account selectByPrimaryKey(Integer id);//相当于select * from user where id = 变量id

8、updateByExampleSelective===>按条件更新值不为null的字段

int updateByExampleSelective(@Param("record") Account record, @Param("example") AccountExample example);

//下面是一个完整的案列
UserExample example = new UserExample();
 Criteria criteria = example.createCriteria();
 criteria.andUsernameEqualTo("joe");
 User user = new User();
 user.setPassword("123");
 userDAO.updateByPrimaryKeySelective(user,example);
 相当于:update user set password='123' where username='joe'

9、updateByExampleSelective===>按条件更新

int updateByExample(@Param("record") Account record, @Param("example") AccountExample example);

10、updateByPrimaryKeySelective===>按条件更新

int updateByPrimaryKeySelective(Account record);

//下面是一个完整的案例

 User user = new User();
user.setId(101);
user.setPassword("joe");
userDAO.updateByPrimaryKeySelective(user);

相当于:

update user set password='joe' where id=101
int updateByPrimaryKeySelective(Account record);

//下面是一个完整的案例

 User user = new User();
user.setId(101);
user.setPassword("joe");
userDAO.updateByPrimaryKeySelective(user);

相当于:update user set password='joe' where id=101

11、updateByPrimaryKey===>按主键更新

int updateByPrimaryKey(Account record);

//下面是一个完整的案例
User user =new User();
 user.setId(101);
 user.setUsername("joe");
 user.setPassword("joe");
 user.setEmail("joe@163.com");
 userDAO.updateByPrimaryKey(user);

相当于:

update user set username='joe',password='joe',email='joe@163.com' where id=101
int updateByPrimaryKey(Account record);

//下面是一个完整的案例
User user =new User();
 user.setId(101);
 user.setUsername("joe");
 user.setPassword("joe");
 user.setEmail("joe@163.com");
 userDAO.updateByPrimaryKey(user);

相当于:

update user set username='joe',password='joe',email='joe@163.com' where id=101

解析mapper的xml配置文件
我们来看看mybatis是怎么读取mapper的xml配置文件并解析其中的sql语句。

我们还记得是这样配置sqlSessionFactory的:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configLocation" value="classpath:configuration.xml"></property>
  <property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/>
  <property name="typeAliasesPackage" value="com.tiantian.mybatis.model" />
</bean>

这里配置了一个mapperLocations属性,它是一个表达式,sqlSessionFactory会根据这个表达式读取包com.xxx.mybaits.mapper下面的所有xml格式文件,那么具体是怎么根据这个属性来读取配置文件的呢?

答案就在SqlSessionFactoryBean类中的buildSqlSessionFactory方法中:

if (!isEmpty(this.mapperLocations)) {
   for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
     continue;
    } 

    try {
     XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
       configuration, mapperLocation.toString(), configuration.getSqlFragments());
     xmlMapperBuilder.parse();
    } catch (Exception e) {
     throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
     ErrorContext.instance().reset();
    } 

    if (logger.isDebugEnabled()) {
     logger.debug("Parsed mapper file: '" + mapperLocation + "'");
    }
   }
  }

mybatis使用XMLMapperBuilder类的实例来解析mapper配置文件。

public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
  this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
    configuration, resource, sqlFragments);
 } 

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
  super(configuration);
  this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
  this.parser = parser;
  this.sqlFragments = sqlFragments;
  this.resource = resource;
 }

接着系统调用xmlMapperBuilder的parse方法解析mapper。

public void parse() {
  //如果configuration对象还没加载xml配置文件(避免重复加载,实际上是确认是否解析了mapper节点的属性及内容,
  //为解析它的子节点如cache、sql、select、resultMap、parameterMap等做准备),
  //则从输入流中解析mapper节点,然后再将resource的状态置为已加载
  if (!configuration.isResourceLoaded(resource)) {
   configurationElement(parser.evalNode("/mapper"));
   configuration.addLoadedResource(resource);
   bindMapperForNamespace();
  }
  //解析在configurationElement函数中处理resultMap时其extends属性指向的父对象还没被处理的<resultMap>节点
  parsePendingResultMaps();
  //解析在configurationElement函数中处理cache-ref时其指向的对象不存在的<cache>节点(如果cache-ref先于其指向的cache节点加载就会出现这种情况)
  parsePendingChacheRefs();
  //同上,如果cache没加载的话处理statement时也会抛出异常
  parsePendingStatements();
 }

mybatis解析mapper的xml文件的过程已经很明显了,接下来我们看看它是怎么解析mapper的:

private void configurationElement(XNode context) {
  try {
   //获取mapper节点的namespace属性
   String namespace = context.getStringAttribute("namespace");
   if (namespace.equals("")) {
    throw new BuilderException("Mapper's namespace cannot be empty");
   }
   //设置当前namespace
   builderAssistant.setCurrentNamespace(namespace);
   //解析mapper的<cache-ref>节点
   cacheRefElement(context.evalNode("cache-ref"));
   //解析mapper的<cache>节点
   cacheElement(context.evalNode("cache"));
   //解析mapper的<parameterMap>节点
   parameterMapElement(context.evalNodes("/mapper/parameterMap"));
   //解析mapper的<resultMap>节点
   resultMapElements(context.evalNodes("/mapper/resultMap"));
   //解析mapper的<sql>节点
   sqlElement(context.evalNodes("/mapper/sql"));
   //使用XMLStatementBuilder的对象解析mapper的<select>、<insert>、<update>、<delete>节点,
   //mybaits会使用MappedStatement.Builder类build一个MappedStatement对象,
   //所以mybaits中一个sql对应一个MappedStatement
   buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
   throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
 }

configurationElement函数几乎解析了mapper节点下所有子节点,至此mybaits解析了mapper中的所有节点,并将其加入到了Configuration对象中提供给sqlSessionFactory对象随时使用。这里我们需要补充讲一下mybaits是怎么使用XMLStatementBuilder类的对象的parseStatementNode函数借用MapperBuilderAssistant类对象builderAssistant的addMappedStatement解析MappedStatement并将其关联到Configuration类对象的:

public void parseStatementNode() {
  //ID属性
  String id = context.getStringAttribute("id");
  //databaseId属性
  String databaseId = context.getStringAttribute("databaseId"); 

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
   return;
  }
  //fetchSize属性
  Integer fetchSize = context.getIntAttribute("fetchSize");
  //timeout属性
  Integer timeout = context.getIntAttribute("timeout");
  //parameterMap属性
  String parameterMap = context.getStringAttribute("parameterMap");
  //parameterType属性
  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);
  //resultMap属性
  String resultMap = context.getStringAttribute("resultMap");
  //resultType属性
  String resultType = context.getStringAttribute("resultType");
  //lang属性
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang); 

  Class<?> resultTypeClass = resolveClass(resultType);
  //resultSetType属性
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); 

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  //是否是<select>节点
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  //flushCache属性
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  //useCache属性
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  //resultOrdered属性
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); 

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode()); 

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver); 

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  //resultSets属性
  String resultSets = context.getStringAttribute("resultSets");
  //keyProperty属性
  String keyProperty = context.getStringAttribute("keyProperty");
  //keyColumn属性
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
   keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
   //useGeneratedKeys属性
   keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
     configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
     ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  } 

  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
    fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
    resultSetTypeEnum, flushCache, useCache, resultOrdered,
    keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
 }

由以上代码可以看出mybaits使用XPath解析mapper的配置文件后将其中的resultMap、parameterMap、cache、statement等节点使用关联的builder创建并将得到的对象关联到configuration对象中,而这个configuration对象可以从sqlSession中获取的,这就解释了我们在使用sqlSession对数据库进行操作时mybaits怎么获取到mapper并执行其中的sql语句的问题。

时间: 2016-05-31

Mybatis实现Mapper动态代理方式详解

一.实现原理 Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法. Mapper接口开发需要遵循以下规范: 1.Mapper.xml文件中的namespace与mapper接口的类路径相同. 2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 3.Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的para

mybatis的动态sql详解(精)

MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力.如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号.动态 SQL 可以彻底处理这种痛苦. 通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种情形,这种语言可以被用在任意映射的SQL语句中. 动态SQL元素和使用 JSTL或其他相似的基于XML的文本处理器相似.在MyBatis之前的版本中,有很多

Mybatis中SqlMapper配置的扩展与应用详细介绍(1)

奋斗了好几个晚上调试程序,写了好几篇博客,终于建立起了Mybatis配置的扩展机制.虽然扩展机制是重要的,然而如果没有真正实用的扩展功能,那也至少是不那么鼓舞人心的,这篇博客就来举几个扩展的例子. 这次研读源码的起因是Oracle和MySQL数据库的兼容性,比如在Oracle中使用双竖线作为连接符,而MySQL中使用CONCAT函数:比如Oracle中可以使用DECODE函数,而MySQL中只能使用标准的CASE WHEN:又比如Oracle中可以执行DELETE FORM TABLE WHER

Mybatis增删改查mapper文件写法详解

  1. 插入 <mapper namespace="需要实现接口的全类名"> <insert id="需要实现的接口里的方法名" parameterType="方法参数类型,如果是对象要写全类名"> INSERT sql命令(命令里通过#{}获取对象属性) <!--注意属性名区分大小写 --> </insert> <mapper> EG: <mapper namespace=&q

使用Mybatis Generator结合Ant脚本快速自动生成Model、Mapper等文件的方法

MyBatis简介: MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 相关阅读:MyBatis入门学习教程(一)-MyBatis快速入门 使用过Mybatis的同学都知道,针对每一个项目中使用到的数据库表都需要建

MyBatis MapperProvider MessageFormat拼接批量SQL语句执行报错的原因分析及解决办法

最近在项目中有这么一段代码:下载服务器基础业务数据进行本地批量插入操作,因项目中使用mybatis进行持久化操作,故直接考虑使用mybatis的批量插入功能. 1.以下是Mapper接口的部分代码 public interface PrintMapper { @InsertProvider(type = PrintMapperProvider.class,method = "insertAllLotWithVehicleCode4H2") void insertAllLotWithVe

详解MyBatis的getMapper()接口、resultMap标签、Alias别名、 尽量提取sql列、动态操作

一.getMapper()接口 解析:getMapper()接口 IDept.class定义一个接口, 挂载一个没有实现的方法,特殊之处,借楼任何方法,必须和小配置中id属性是一致的 通过代理:生成接口的实现类名称,在MyBatis底层维护名称$$Dept_abc,selectDeptByNo() 相当于是一个强类型 Eg 第一步:在cn.happy.dao中定义一个接口 package cn.happy.dao; import java.util.List; import cn.happy.e

oracle+mybatis 使用动态Sql当插入字段不确定的情况下实现批量insert

最近做项目遇到一个挺纠结的问题,由于业务的关系,DB的数据表无法确定,在使用过程中字段可能会增加,这样在insert时给我造成了很大的困扰. 先来看一下最终我是怎么实现的: <insert id="batchInsertLine" parameterType="HashMap"> <![CDATA[ INSERT INTO tg_fcst_lines(${lineColumn}) select result.*,sq_fcst_lines.next

MyBatis实践之DAO与Mapper

MyBatis简介 MyBatis前身是iBatis,是一个基于Java的数据持久层/对象关系映射(ORM)框架. MyBatis是对JDBC的封装,使开发人员只需关注SQL本身,而不需花费过多的精力去处理如注册驱动.设置参数.创建Connection/Statement.解析结果集等JDBC过程性代码.MyBatis基于XML/注解的方式配置Statement,执行SQL,并将执行结果映射成Java对象, 大大降低了数据库开发的难度. MyBatis is a first class pers

MyBatis Mapper代理使用方法详解

MyBatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 下文重点给大家介绍mapper代理使用方法. 一.开发人员需要完成的任务: mapper.xml映射文件和mapper.java 二.开发规范

对Linux终端使用socks5代理的方法详解

背景 github 上克隆/下载代码太慢 go get 安装第三方包太慢 any more - 安装proxychains ubuntu系统 $ sudo apt-get install proxychains-ng 配置proxychains $ sudo vim /etc/proxychains.conf 把配置文件中最后一行改为shadowsocks的本地ip跟端口 # /etc/proxychains.conf # 修改前 socks4 127.0.0.1 9095 # 修改后 sock

vue项目打包部署_nginx代理访问方法详解

我又来了,今天部署了下vue项目,使用nginx做了代理,这样可以解决跨域的问题,这里做一个简单讲解. 1.先看vue项目打包(我这里使用的是vscode开发工具) 这里是我的项目结构: 打包之前需要修改如下配置文件: 配置文件一:build>>utils.js (修改publicPath:"../../" , 这样写是处理打包后找不到静态文件的问题) 配置文件二:config>>index.js(修改assetsPublicPath:'./' ,修改目的是为了

Mybatis分页插件使用方法详解

本文实例为大家分享了Mybatis分页插件使用的具体代码,供大家参考,具体内容如下 1.分页插件简介 pagehelper源码 都说这是史上最好用的分页插件,支持多种数据库以多种方式分页. 2.分页插件的使用 2.1导入maven依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>最新

利用node.js实现反向代理的方法详解

本文主要给大家介绍的是关于利用node.js实现反向代理的相关内容,分享出供大家参考学习,下面话不多说,来一起看看详细的介绍: 跨域问题是前端开发很常见的问题解决方案有很多种 jsonp返回 Access-Control-Allow-Origin:'*' (需要注意的是 对于post请求会变成option请求需求后端支持) 前端添加代理 前端添加代理 以vue-cli为例,前端添加代理 dev: { env: require('./dev.env'), port: 8888, autoOpenB

python单例模式获取IP代理的方法详解

引言 最近在学习python,先说一下我学Python得原因,一个是因为它足够好用,完成同样的功能,代码量会比其他语言少很多,有大量的丰富的库可以使用,基本上前期根本不需要自己造什么轮子.第二个是因为目前他很火,网上各种资料都比较丰富,且质量尚可.接下来不如正题 在学习Python爬虫的时候,经常会遇见所要爬取的网站采取了反爬取技术导致爬取失败.高强度.高效率地爬取网页信息常常会给网站服务器带来巨大压力,所以同一个IP反复爬取同一个网页,就很可能被封,这里讲述一个爬虫技巧,设置代理IP 为什么需

Mybatis中Mapper映射文件使用详解

紧接上文所述,在这篇文章中我将对Mapper映射文件进行详细的说明. Mapper映射文件是一个xml格式文件,必须遵循相应的dtd文件规范,如ibatis-3-mapper.dtd.我们先大体上看看支持哪些配置?如下所示,从Eclipse里截了个屏: 从上图可以看出,映射文件是以<mapper>作为根节点,在根节点中支持9个元素,分别为insert.update.delete.select(增删改查);cache.cache-ref.resultMap.parameterMap.sql. 下

基于Spring + Spring MVC + Mybatis 高性能web构建实例详解

一直想写这篇文章,前段时间痴迷于JavaScript.NodeJs.AngularJS,做了大量的研究,对前后端交互有了更深层次的认识. 今天抽个时间写这篇文章,我有预感,这将是一篇很详细的文章,详细的配置,详细的注释,看起来应该很容易懂. 用最合适的技术去实现,并不断追求最佳实践.这就是架构之道. 希望这篇文章能给你们带来一些帮助,同时希望你们可以为这个项目贡献你的想法. 源码地址:https://github.com/Eliteams/quick4j 点击打开 源码地址:https://gi

Mybatis分页插件的实例详解

Mybatis分页插件的实例详解 1.前言: 我们知道,在MySQL中,分页的sql是使用limit来做,如果我们自己写sql,那分页肯定是没有任何问题的.但是一旦model多了起来,复杂了起来,我们很自然的想到使用mybatis的逆向工程来生成相应的po和mapper,但是同时也会带来弊端,比如这里的分页问题就不好解决了. 可能有人会说,我可以修改生成的文件,没错,这是可行的,但是一般我们通过逆向工程生成的文件,都不会去动它,所以这个时候,就需要使用分页插件来解决了. 如果你也在用Mybati

Mybatis Generator最完美配置文件详解(完整版)

最近没做项目,重新整理了一个最完整的Mybatis Generator(简称MBG)的最完整配置文件,带详解,再也不用去看EN的User Guide了: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"