让我们重回到车辆管理系统和张三的故事中。

在 iBATIS SQL Maps 的世界里也存在 one-to-many、many-to-one 的关系,想必你已经对这些概念驾轻就熟了。好!还是每个 People 对应多条 AutoInfo 信息。

本系列文章第一部分提到过 iBATIS SQL Maps 的映射文件个数可以人为设定,但是,把一组有共性的操作放在一起是首选策略。下面我们看看为张三首次买车所生成的映射文件是怎样的:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMap
    PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">

<sqlMap namespace="AutoMag">

<insert id="insertPeople" parameterClass="bo.People">
    <![CDATA[ 
      insert into people (name, address) values (#name#, #address#)
    ]]>
      <selectKey resultClass="java.lang.Integer" keyProperty="id"> 
    <![CDATA[ 
          select last_insert_id(); 
    ]]>
      </selectKey>
  </insert>
  
  <insert id="insertAutoInfo" parameterClass="bo.AutoInfo">
    <![CDATA[ 
      insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)
    ]]>
  </insert>
</sqlMap>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMap
    PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">

<sqlMap namespace="AutoMag">

<insert id="insertPeople" parameterClass="bo.People">
    <![CDATA[ 
      insert into people (name, address) values (#name#, #address#)
    ]]>
      <selectKey resultClass="java.lang.Integer" keyProperty="id"> 
    <![CDATA[ 
          select last_insert_id(); 
    ]]>
      </selectKey>
  </insert>
  
  <insert id="insertAutoInfo" parameterClass="bo.AutoInfo">
    <![CDATA[ 
      insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)
    ]]>
  </insert>
</sqlMap>

sqlMap

sqlMap 元素拥有属性 namespace="…",定义了该 XML 文件命名空间。如果你在配置文件SqlMapConfig.xml 中指定了 settings 元素的属性 useStatementNamespaces="true",那么就可以按照命名空间的方式访问 Mapped statement,比如 namespace=" AutoMag",相应 Java 代码:sqlMap.insert("AutoMag.insertPeople",people)。这样做是为了防止不同映射文件中出现同名 Mapped statement 而产生冲突。什么是 Mapped statement ?

Mapped statement

iBATIS SQL Maps 的核心概念就是 Mapped statement!Mapped Statement 可以使用任意的 SQL 语句,利用 POJO、原始变量及其 Wrapper Class 作为输入(parameter class)和输出(result class)。

Mapped Statement 包含以下几种类型:

insert 对应数据库的 insert 操作,该操作返回本次操作插入记录的主键值。

select 对应数据库的 select 操作,该操作返回特定的 POJO 或 对象。

update 对应数据库的 update 操作,该操作返回被更新的记录个数。

delete 对应数据库的 delete 操作,该操作返回被删除的记录个数。

procedure 对应数据库存储过程。

statement 类型最为通用,可以代替以上所有的类型。但由于缺乏操作直观性故不推荐。

insert id="insertPeople" parameterClass="bo.People"

定义了 insert 类型的 Mapped Statement。属性 id="insertPeople" 定义操作名称,parameterClass="bo.People" 定义传入参数为 People 对象实例,框架可确保其属性持久化到数据库相应字段中。由于 SQL 语句会包含“<>”这样的符号,容易和 XML 产生冲突,放进 <![CDATA[……]]> 区域就可避免。insert into people (name, address) values (#name#, #address#),是一条普通的 SQL 语句,“#name#、#address#”利用 Java 反射机制访问 People 对象实例的相应属性。

selectKey resultClass="java.lang.Integer" keyProperty="id"

iBATIS SQL Maps 通过 <insert> 元素的子元素 < selectKey> 来支持主键自动生成。resultClass="java.lang.Integer" 定义返回对象为 int 的 Wrapper Class。keyProperty="id" 定义了主键名称。本例是 MySQL 主键生成方式,参考官方文档,MySQL 的主键生成无需人为来控制,也就是说可不使用<selectKey> 而由数据库自动处理。但我测试发现,在执行 insert 操作以后,程序没有返回本次操作插入记录的主键值。在官方论坛上也有很多用户提出这样的疑惑,作者的答复是:这和 JDBC Driver 有关系。不可能把驱动一一测试吧?一劳永逸的办法是使用 <selectKey> 元素。以下是 Oracle 和 SQL Server 主键生成方法:

< !- Oracle ->
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product"> 
  <selectKey resultClass="int" keyProperty="id" >
    SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL 
  </selectKey> 
  insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#) 
</insert>

<!- Microsoft SQL Server ->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
  insert into PRODUCT (PRD_DESCRIPTION) values (#description#) 
  <selectKey resultClass="int" keyProperty="id" > 
  SELECT @@IDENTITY AS ID 
  </selectKey> 
</insert>

< !- Oracle ->
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product"> 
  <selectKey resultClass="int" keyProperty="id" >
    SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL 
  </selectKey> 
  insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#) 
</insert>

<!- Microsoft SQL Server ->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
  insert into PRODUCT (PRD_DESCRIPTION) values (#description#) 
  <selectKey resultClass="int" keyProperty="id" > 
  SELECT @@IDENTITY AS ID 
  </selectKey> 
</insert>

insert into auto_info (license_plate, owner_no) VALUES (#licensePlate#, #ownerNo.id#)

在插入了 people 记录后,要为 auto_info 插入记录。基本原则和之前遇到过的一样,只是”owner_no”这个字段值由 AutoInfo 对象属性”ownerNo”获得,该属性类型为 People。这是由于我沿用了 Hibernate 产生的 POJO,如果你愿意,完全可以把”ownerNo”替换为 Integer 类型。

编程中几个关键对象

com.ibatis.common.resources.Resources 对象负责从 XML 得到 java.io.Reader 抽象类的实例,供工厂方法调用。

com.ibatis.sqlmap.client.SqlMapClientBuilder 构造 SqlMapClient 实例。

com.ibatis.sqlmap.client.SqlMapClient 是 iBATIS SQL Maps 核心组件,可以说我们的编程工作都是围绕着它展开。

形成的 one-to-many 保存如下:

package test;

import java.io.Reader;

import com.ibatis.sqlmap.client.*;
import com.ibatis.common.resources.*;

import bo.*;

public class AutoMag {

private Reader reader;
 private SqlMapClient sqlMap;
 private String resource = "SqlMapConfig.xml";
 
 public void insertPeople() throws Exception{
  try{
   reader = Resources.getResourceAsReader(resource);
      sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
      sqlMap.startTransaction();
      
      People people=new People();
      people.setName("张三");
      people.setAddress("中国");
            sqlMap.insert("insertPeople",people);

AutoInfo autoInfo=new AutoInfo();
            autoInfo.setLicensePlate("A00001");
            autoInfo.setOwnerNo(people);            
      sqlMap.insert("insertAutoInfo",autoInfo);
      
      sqlMap.commitTransaction();
  }finally{
   sqlMap.endTransaction();
  }
 }
}

package test;

import java.io.Reader;

import com.ibatis.sqlmap.client.*;
import com.ibatis.common.resources.*;

import bo.*;

public class AutoMag {

private Reader reader;
 private SqlMapClient sqlMap;
 private String resource = "SqlMapConfig.xml";
 
 public void insertPeople() throws Exception{
  try{
   reader = Resources.getResourceAsReader(resource);
      sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
      sqlMap.startTransaction();
      
      People people=new People();
      people.setName("张三");
      people.setAddress("中国");
            sqlMap.insert("insertPeople",people);

AutoInfo autoInfo=new AutoInfo();
            autoInfo.setLicensePlate("A00001");
            autoInfo.setOwnerNo(people);            
      sqlMap.insert("insertAutoInfo",autoInfo);
      
      sqlMap.commitTransaction();
  }finally{
   sqlMap.endTransaction();
  }
 }
}

程序和 Hibernate 写法差不多,我感觉甚至比 Hibernate 更简单。我可以显示的进行 insert 操作,符合传统JDBC 编程习惯。iBATIS SQL Maps 支持自动事务处理,可以不用写明 startTransaction、commitTransaction。但如果 People insert 操作成功,而 AutoInfo insert 操作失败,就破坏了两次 insert 操作的原子性。最后endTransaction 包含异常情况下的回滚事务和关闭连接池连接两种功能。

iBATIS SQL Maps的更多相关文章

  1. Ibatis学习总结1--ibatis简介和SQL Maps

    最佳维护的一个项目使的是ibatis框架,在闲暇之余将手头的开发手册和平时开发的理解做一下总结,言归正传. 简介 使用 SQL Map,能够大大减少访问关系数据库的代码.SQL Map 使用简单的 X ...

  2. Ibatis sql语句1

    <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMap PUBLIC "-/ ...

  3. Ibatis sql语句

    <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMap PUBLIC "-/ ...

  4. iBatis + SQL Server 项目开发实战小结

    几年前跟随项目经理做的一个ERP小项目,自己业余时间整理的开发手册,供参考. 开发环境配置:编程环境为Microsoft Visual Studio 2010,数据库是SQL Server 2008 ...

  5. iBATIS sql(XML)中的大于、小于、like等符号写法

    其实就是xml的特殊符号,因为它的配置就是xml,所以可以用下面这种写法转义 <          <     >          >      <>   < ...

  6. Spring3+ibatis (SQL Server)+pager-taglib.tld查询分页的实现

    pager-taglib分页開始~ 查了好多关于分页的技术,终于选定下面方法实现~ 1.首先下载jar包:pager-taglib.jar,pager-taglib.jar放在WEB-INF/lib文 ...

  7. IBATIS sql 小于(<) 写法 特殊符号写法

    SELECT * FROM XXX where  column1  <![CDATA [ < 100 ]]> ************************************ ...

  8. 数据持久层框架iBatis, Hibernate 与 JPA 比较

    在本文中我们介绍并比较两种最流行的开源持久框架:iBATIS和Hibernate,我们还会讨论到Java Persistence API(JPA).我们介绍每种解决方案并讨论其所规定的品质,以及在广泛 ...

  9. ibatis的动态Mapped Statement 标签

    动态Mapped Statement 直接使用JDBC 一个非常普遍的问题是动态SQL.使用参数值.参数本身和数据列都是动态的SQL,通常非常困难.典型的解决方法是,使用一系列if-else 条件语句 ...

随机推荐

  1. RDLC 动态列

    很久没有写博客了,关于动态列,国内很少资料有介绍动态列的,所想写点心得给哥们 啥是动态列呢?通常我们用存储过程时有列转行和行转列的做法,那么在RDLC 怎么支持呢?其实很简单,就是利用了RDLC的 C ...

  2. 从头学习MVC4基础之视图

    实例一:首先简单显示实例: 控制器代码: public class WujyTestController : Controller { public ActionResult Index() { Li ...

  3. 请MVC5 WebApi2 支持OData协议查询

    一.配置项 1.WebApiConfig.cs添加如下代码: // api 支持 cors允许Ajax发起跨域的请求(nuget 中搜索 ASP.NET Cross-Origin Support,然后 ...

  4. CLightLock:一个简单AutoLock

    原理: 标准的RAII, 利用构造函数进行加锁,利用析构函数进行解锁. #ifndef _C_LIGTHT_LOCK_HPP #define _C_LIGTHT_LOCK_HPP class CLig ...

  5. Android异步下载

    概述 实现App常见下载公共 支持通知栏显示 支持 暂停.取消功能,使用Service.AsyncTask实现异步下载.特点简单.实用.方便源码扩展修改 详细 代码下载:http://www.demo ...

  6. cxf 生成客户端代码调用服务

    cxf是另一种发布webservice的方式,与jdk提供的相比 jdk提供的是wsimport cxf 提供的是 wsdl2java- d 地址 根据http://www.cnblogs.com/f ...

  7. mysql的rand函数

    项目中需要动态随机生成一些固定位数的随机数,如8位,5位等. 之前看到的写法是这样 ROUND(ROUND(RAND(),5)*100000) 这样写不太准确,有几率出现4位的情况,Rand() 函数 ...

  8. HTML5学习笔记 拖放

    拖放(Drag和drop)是html5标准的组成部分 拖放是一种常见的特性,即抓取对象以后拖到另一个位置 在html5中,拖放是标准一部分,任何元素都能够拖放. 设置元素为可拖放 首先,为了使元素可拖 ...

  9. MS SQL自定义函数IsPositiveInteger MS SQL自定义函数IsNumeric 水晶报表使用IEnumerable<T>数据源

    MS SQL自定义函数IsPositiveInteger   判断字符串是否为正整数,0开始的的数字不算. SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON ...

  10. web项目的路径问题

    一.使用base标签,使相对路径和绝对路径可以同时使用 但是,base标签对Ie低版本不兼容(IE8及IE8以下) 不过,鉴于IE在国内具有无与伦比的统治地位,所以,换了个写法: <script ...