Ibatis学习笔记

#远程过程调用RPC:Remote Procedure Call

String resource = "SqlMapConfig.xml";
Reader reader = Resources.getResourceAsRader(resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
  1)源代码
    public static SqlMapClient buildSqlMapCLient(Reader reaer);
	return new SqlMapConfigPaser().parse(reader);
  2)SqlMapConfigPaser类
     a)有成员变量private XmlParserState state = new XmlParserState();
        1)该类中有成员变量private SqlMapConfiguration config = new SqlMapConfiguration();
	2)进入SqlMapConfiguration构造方法;该类有成员变量SqlMapClientImpl client;SqlMapExecutorDelegate delegate;
           delegate = new SqlMapExecutorDelegate();
     	   client = new SqlMapClientImpl(delegate);//到此sqlMapClient已经得出,但是还没有设置事务管理器等
          
     b)构造方法有一个addTransactionManagerNodelets()方法,用来设置事务管理器
       类似parser.addNodelet("/sqlMapConfig/transactionManager/end()" //用来解析事务管理配置
       拿出TransactionConfig config = (TransactionConfig) Resources.instantiate(type);
       获取事务管理器 txManager = new TransactionManager(config);
       设置事务管理器 state.getConfig().setTransactionManager(txManager);
       setTransactionManager(txManager)方法会调用delegate.setTxManager(txManager);//delegate在构造state对象时已完成
       至此 client = new SqlMapClientImpl(delegate);就已经借用delegate事务管理器设置成功
  3)SqlMapExecutorDelegate-->TransactionManager-->TransactionConfig(事务类如JDBC,JTA);最终会引用到TransactionConfig

###############SqlMapConfig.xml文件#############

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE sqlMapConfig  PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0/" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
         <properties resource="db.properties"/>
         ##全局配置选项
         <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" maxRequests="32" maxSessions="10"
               maxTransactions="5" useStatementNamespaces="true" />
         ##事务管理器 //事务管理器要放在sqlMapClient中
         <transactionManager type="JDBC">
                   <dataSource type="SIMPLE">
                            <property name="JDBC.Driver" value="${driver}"/>
                   </dataSource>
         </transactionManger>
         ##对各个Sql Map文件的引用
         <sqlMap resource="sqlmap/Fitting.xml"/>
</sqlMapConfig>
###事务管理器类型 
  JDBC:用于提供简单的基于JDBC的事务管理。com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
  JTA:用于在你的应用程序中提供基于容器的事务管理
  EXTERNAL:用于提供非事务管理,并假定管理事务的是应用程序,而不是iBatis
   
#####################Spring+Ibatis整合事务##################
 1).Spring的SqlMapClientFactoryBean,也就是接收sqlmap-config.xml文件的类默认使用
    EXTERNAL这种事务管理,可以通过transactionConfigClass属性注入别的事务管理器.
    例子: <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
		<property name="configLocation" value="classpath:sqlmap-config.xml" />
                //这里面也可以配置数据源,但是会被别的覆盖
	</bean>

 2). 也可以在外部显示的配置事务管理器,然后将这个事务管理器交给<tx:标签或者交给事务拦截器,这样可以达到让Spring管理事务的目的
    上面1)似乎无法使用Spring的Aop来管理事务,因为事务管理器是内置的,无法被外部引用。
   <bean id="transactionManager"
 	 class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
  </bean>

 3).操作的时候可以继承SqlMapClientTemplate或者SqlMapClientDaoSupport这两种方式,其实都是用的SqlMapClientTemplate
    并且SqlMapClientTemplate是依赖sqlMapClient,也就是不管哪种方式都需要sqlMapClient.

 4).Spring中的SqlMapClientTemplate会用到sqlMapClient,而sqlMapClient会用到事务管理器和数据源,
   数据源可以再sqlMapClient中配置,也可以在SqlMapClientTemplate中配置,貌似在SqlMapClientTemplate
   中配置的数据源会覆盖在sqlMapClient中的配置.
 
 5).##从下面的代码可以看到SqlMapClientDaoSupport 内部也是用的SqlMapClientTemplate,并且会把sqlMapClient交给SqlMapClientTemplate。
   public abstract class SqlMapClientDaoSupport extends DaoSupport{ 
    private SqlMapClientTemplate sqlMapClientTemplate = new SqlMapClientTemplate();
    public final void setSqlMapClient(SqlMapClient sqlMapClient) {
		if (!this.externalTemplate) {////这里会把外部的sqlMapClient穿过来,并交给SqlMapClientTemplate
			this.sqlMapClientTemplate.setSqlMapClient(sqlMapClient);
		}
	}



####<settings的全局配置选项################

enhancementEnabled:用于指定是否使用cglib中那些已优化的类来提高延迟加载的性能。默认是true
useStatementNamespaces:是否使用命名空间,如果是true,在操作时就必须加上命名空间
cacheModelsEnabled:用于指定是否想让Ibatis使用该技术,为了充分利用高速缓存技术,还
  必须为已映射语句配置高速缓存模型--暂时不明白
maxrequests:默认值512,这里将其缩小为32,这样我们的数据库一次最多只允许有32个请求处于活动状态.
maxSessions:会话是一种线程机制,用于跟踪关于一组相关事务和请求的信息。在这个例子中,任何时候都
 只允许10个会话,而不是采用其默认值128。
maxTransactions:最大活动事务,默认32,减小到了5
 一般不需要显示的配置,如果真的要配置,情确保requests>sessions>transactions
 这些值不会对连接池中的连接数或者应用程序服务器负责管理的哪些资源产生直接影响。


占位符# 和 $
 where aa = #value# :ibatis首先会翻译成这样 where aa=?,然后用prepareStatement处理
 where aa = $value$ :ibatis会翻译成这样 where aa=value,这种方式容易sql注入
Sql注入例子:
 where aa = '%$value$%': 传入这样的参数,burg'; drop table Account;--
 ibatis会把语句翻译成这样: where aa = '%burg';drop table Account; --%'

###############################################
Ibatis是将sql语句映射为对象
Hibernate是将数据库中的表映射为对象

##参数映射的属性
nullValue:90,个人理解:
 1,在写入数据库时,如果JavaBean中获得的值是90,那就在数据库中插入null
 2,在读出数据库时,如果读出的字段值是null,那就吧90给JavaBean
typeHandler:
 如果想要指定类型处理器(而不是让iBatis根据javaType和jdbcType属性来选择类型处理器)
 ,就可以指定typeHandler属性.

JDBC在设置参数值的时候,用这种方式setNull,setString等
Ibatis如果不指定的话会用反射的方式---这句有待证明
要吧参数类型告诉Ibatis有两种方式,一是外联参数,
二是内联参数,内联用这种方式   【#value:DATE#,也可以用#value,jdbcType=DATE#】
  //这样IbatiS就知道用怎么用JDBC了,【参数名:数据库类型:空值占位符】
DATE是java.sql.Types常量类中代表某个数据库类型的常量名


iBatis能把结果映射为任意类型,但是它只返回Object实例

######################显式和隐式映射的理解##############
#value#这种是隐式映射,因为是隐式,所以在加载时就需要吧这些转换成显示映射,
 并保存在内存中。
使用显示映射时,Ibatis就不需要在应用程序加载时立即把所有这些显示映射都
载入内存,而是在需要时才载入。运行更快,并且所使用的内存更少。


##内联参数映射,告诉ibatis如何用jdbc设置值
 <insert id="aaa"> //不需要指定参数类型????????比如paramClass="Account"
	insert into account(
			name,password
		)
	values(
		#name:VARCHAR#,#password:VARCHAR#//如果不指定,ibatis可以根据参数值进行反射,
			//如果java类型是java.util.Date,数据库会对应Date,TimeStamp等,ibatis会自己选一个。
	)
 </insert>

##外部参数映射 //当然可以不指定jdbcType,但是会反射,性能不好
 <parmeterMap id="aaa" class="Accout">
	<parameter property="name" jdbcType="VARCHAR"/>
	<parameter property="password" jdbcType="VARCHAR"/>
 </parameterMap>
 <insert id="bbb" parameterMap="aaa">
	insert into account(
		name,password
	)
	values(
		?,?
	)
 </insert>
======注意:这是参数paramMap不是resultMap,一个是输入,一个是输出

##内联参数映射和外部参数映射的区别就在于维护的代价和性能---
 这两者都可以通过使用外部参数映射得到提高。

##Sql Server插入时返回主键
 <insert id="insert">
	insert into Account(name,password)
	values (#username#,#password#)
	<selectKey keyProperty="accountId" resultClass="int">
		select scope_identity()
	</selectKey>
 </insert>

##用Ibatis执行批处理 
 sqlMapClient.startTranscation();//开启事务
 sqlMapClient.startBatch();//将以下语句进行打包
 	insert into  ...
 sqlMapClient.executeBatch();//执行批处理
 sqlMapClient.commitTransaction();//数据提交
 sqlMapClent.endTransaction();//结束事务
 
##复杂映射特性,对象中还有对象的那种,比如账户-->订单-->订单项
  <sqlMap namespace="Ch6">
	##AccountInfo对象的结果映射
	<resultMap id="ResultAccountInfoMap" class="com.masf.AccountInfo">
		##property是不是="accountId"就可以了啊,有待考证????
		<result property="account.accountId" column="accountId"/>
 		##orderList是一个List对象,它的数据来自"Ch6.getOrderInfoList"已映射语句,
                ##column="accountId"表示会将accountId作为参数传给"Ch6.getOrderInfoList"已映射语句。
		<result property="orderList" select="Ch6.getOrderInfoList" column="accountId"/>
        </resultMap>
      
 	##OrderInfo对象的结果映射
        <resultMap id="ResultOrderInfoMap" class="com.masf.OrderInfo">
		<result property="order.orderId" column="orderId"/>
		<result property="orderItemList" column="orderId" select="Ch6.getOrderItemList"/>
	</resultMap>

	##OrderItem对象映射结果 
        <resultMap id="ResultOrderItemInfoMap" class="com.masf.OrderItem">
		<result property="orderId" column="orderId"/>
		<result property="orderItemId" column="orderItemId"/>
	</resultMap>

	###查询已映射语句
        <select id="getAccountInfoList" resultMap="ResultAccountInfoMap">
		select accountId from Account
	</select>
        <select id="getOrderInfoList" resultMap="ResultOrderInfoMap">
		select orderId from orders where accountId=#value#
	</select>
	<select id="getOrderItemList" resultMap="ResultOrderItemMap">
		select orderId,orderItemId from orderItem where orderId=#value#
	</select>
   </sqlMap> 

###这种映射会有两种问题“数据库I/O”、“N+1查询”
 数据库I/O:一下查询太多的数据
 N+1查询:试图加载和父记录相关的子记录。例如一条sql查出N个订单记录,
     要获取父记录中的子记录订单项,就需要根据父id查出所以子记录。 

###解决方案,决绝N+1,使用left join, 这种方式内存受不了
 使用<resultMap 的groupBy属性
 <resultMap id="AccountInfoMap" class="AccountInfo"
	    //告诉Ibatis只有当accountId改变时,才重新创建AccountInfo对象
            //账户-->订单,因为多个不同的订单,账户可能是一样的,这样就能保证
            //查询出的东西是一个AccountInfo对象,里面有多个Order对象
            groupBy="accountId"> //是对象属性,不是数据库列字段,记得查询时要排序
	<reslut property="accountId" column="accountId"/>
        <reslut property="orderList" resultMap="Ch6.OrderInfoMap"/>
  </resultMap>
  
  <resultMap id="OrderInfoMap" class="OrderInfo" 
             groupBy="orderId">
	<result property="orderId" column="orderId"/>
  	<result property="orderItemList" resultMap="Ch6.OrderItemMap"/>
  </resultMap>

  <resultMap id="OrderItemMap" class="OrderItem">
	<result property="orderId" column="orderId"/>
        <result property="orderItemId" column="orderItemId"/>
  </resultMap>

  ##用join进行查询,记得要排序
  <select id="getAccountInfo" resultMap="AccountInfoMap">
	select 
		account.accountId as accountId,
		orders.orderId as orderId,
		orderitem.orderitemId as orderitemId
	from account
	join orders on account.accountId=orders.accountId
	join orderItem on orders.orderId=orderitem.orderId
        order by accountId,orderid,orderitemid //排序
  </select>

##映射继承
 <resultMap id="document" class="Document">
	<result property="id" column="document_id"/>
	<discriminator column="type" javaType="string">
		##如果type列取值为book,那么就使用名为book的结果映射
		<subMap value="book" resultMap="Book"/>
		##如果type取值为newspaper,那么就使用名为News的结果映射
		<subMap value="newspaper" resultMap="News"/>
	</discriminator>
 </resultMap>

##行映射器接口RowHandler
 它允许你在某个已映射语句的结果集的处理中插入自己的动作
 public interface RowHandler{
	void handleRow(Object valueObject);
 }对于已映射语句的返回的结果集中的每一条记录,iBatis都会调用一次handleRow方法。
 使用这个接口,可以处理大型数据集,而不需要一次兴地将它们全部加载到内存中。

##行映射器的用法
 RowHandler rh = new RowHandaler();
 sqlMapClient,queryWithRowHandler("id",参数,rh);
 String data = rh.getData();//可以再行映射器中处理一些逻辑,然后返回。

##IBATIS事务范围
 自动事务:针对那些简单的仅有一条语句构成的事务,这种事务不需要显示第划定事务边界。
 局部事务:作用于同一个数据库
 全局事务:作用于不同的数据库或其他具有事务能力的资源
 定制事务:
Ibatis默认是自动事务

特性语法(#...#) 文字(literal)语法($...$)

##动态标签
 //如果下面的标签的内容体经Ibatis处理后没有产生任何文本,那么prepend将被忽略
 <dynamic prepend="where"></dynamic>
 <dynaimc标签隐式地支持removeFirstPrepend功能,其它标签需要显示的支持 
 
##二元标签
   //将property属性同compareProperty属性或compareValue属性比较,确定它们是否相同
   //compareProperty:比较对象的属性,compareValue:比较给出的指定值,比如3
   <isEqual>
   <isNotEqual>
   <isGreaterThan> 是否大于
   <isGreaterEqual>是否大于等于
   <isLessThan> 是否小于
   <isLessEqual>是否小于等于

##一元标签
   //确定参数对象中是否存在所指定的字段。对于bean,它找一个特性 ;map,它找一个键
   <isPropertyAvailable>
   <isNotPropertyAvailable>
   <isNull>
   <isNotNull>
   <isEmpty>  //空字符串,集合中无东西也算
   <isNotEmpty>

##参数标签
   考虑到Ibatis允许定义没有参数的已映射语句,参数标签就是用来检查某个特定
   参数是否传递给了已映射语句
   <isParameterPresent>  //确定参数对象是否出现
   <isNotParameterPresent>//确定参数对象是否不存在
 例子:
   <select id="fdsf" resultClass="Product">//没有定义参数的已映射语句
      select * from products
      <isParameterPresent prepend="where">//如果参数存在才会加上"where"
         <isNotEmpty property="productType">
		productType=#productType#
	 </isNotEmpty>
      </isParameterPresent>
   </select>

##Iterate标签,遍历集合属性   --------查
  例子:
    <select id="getProducts" parameterClass="Product" resultClass="Product">
	select * from products
        <dynamic prepend="where productType in">
	   <iterate property="productTypes" //循环这个集合属性
			 open="(" close=")"
		         conjunction=","> //连接符。会不会把会后一个","删掉??
              productType=#productType[]# //有点不明白,必须加上[]????,用于表示当前遍历中的列表元素
           </iterate>
        </dynamic>
    </select>
  奇怪例子:
   <dynamic prepend="where">
     <iterate property="categoryIds" open="p.categoryId IN(" 
 		 close=")" conjunction="," prepend="伪造">
	 #categoryIds[]#
     </iterate>
   </dynamic>

##ibatis分页
  queryForList(String id, Object paramObject, int skip, int max)
 从skip开始查max个记录

##高速缓存
  1)cacheModel标签属性
    readOnly=true:表明你不期望高速缓存中的对象可以被更改。
    Type:高速缓存类型(MEMORY|LRU|FIFO|OSCACHE)
    serialize:指定在读取时,是否对缓存内容进行深复制
  2)Type中,值所代表的意思
    MEMORY:缓存在内存中,知道垃圾收集器将它移除 //<property name="reference-type" value="WEAK"/>
    FIFO:固定数量的缓存,用先进先出来移除数据
    LRU:同样固定,用最近最少使用算法来移除数据 //<property name="size" value="200"/>指定大小
    OSCACHE:使用OpenSymphony(OS)高速缓存
 <sqlMap namespace="Category">
	##定义一个高速缓存模型
	<cacheModel id="categoryCache" type="MEMORY" readOnly="true/false" serialize="true/false">
		//当Category.insert已映射语句执行时,就清除高速缓存
		<flushOnExecute statement="Category.insert"/>
		//依时间间隔来清楚缓存(hours|minutes|seconds|milliseconds)
		<flushInterval hours="10" />
                <property name="reference-type" value="WEAK"/>//弱引用???
	</cacheModel>
        <select id="getCategory" parameterClass="Category" resultClass="Category"
                      cacheMode="categoryCache">//使用categoryCache这个缓存模型
		select * from Category where parentCategoryId=#categoryId#
	<select>
	<insert id="insert" parameterClass="Category">
		insert into Category(title,description,sequence)
		values(#title#,#description#,#sequence#)
	</insert>
 </sqlMap>


##工厂根据配置将实现和接口绑定


##自定义类型转换器TypeHandlerCallback;
 负责为已映射语句设置参数,从结果集中获取结果,以及转换空值替换以将可为空的数据库映射为不可为空的java类型
 1) public class MyTypeHandlerCallback implements TypeHandlerCallback{
         //存入数据库时,将java类型转成某个类型,比如Date转成字符串
	 public void setParameter(ParameterSetter setter,Object parameter){
		//setter 传递到数据中的值;有setString() setDate()等
                //parameter: 需要被转换的java值
		比如传过来的parameter是布尔值,在存入数据库的时候,要存成YES或NO字符串,
		String str;
		if(parameter = true) str ="YES";else str="NO";
                setter.setString(str);
	 }
         //从数据库获取到YES或者NO时,将其转换为java的布尔值
	 public Object getResult(ResultGetter getter){
	     String str = getter.getString();//从数据库获得一个字符串,也可以过得一个日期;getter.getDate();
             return true/false; 
	 }
         //读数据库的时候会用,写的时候不会用
	 //处理数据库中的空;<result property="aaa" column="Enabled" nullValue="890"/>
         //如果从数据库中获得的值是null那么传入的参数s=890
	 public Object valueOf(String s){
		//正常情况下,我们会把890转换为整形,例如Integer.valueOf(s);
		//这里我们把他弄成布尔的
                return true/false;
	 }

   }
 2)注册自定义类型转换器
   //下面标签,可以放在SqlMapConfig.xml、SqlMap.xml、结果映射或参数映射
   <typeHandler callback="com.masf.MyTypeHandlerCallback"
	javaType="boolean" //指定要处理的java类型; 也可以指定javaType="com.masf.Person"
		jdbcType="VARCHAR" />//如果没有指定,将被所有的布尔类型使用????不明白,不就是类似于Struts2的类型转换器吗
 3>目前的理解是,只要遇到数据库VARCHAR,并且对应的java类型是boolean的时候就会使用该类型转换器。
   如果没有指定jdbcType,那么所有遇见java类型的boolean的时候都会使用该转换器.


 

你可能感兴趣的:(ibatis)