IBatis-学习-3

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/attack_breast/article/details/74065025

数据关联

一对多关联

User实例的addresses是一个List<Address>集合

<sqlMap namespace="User">

       <typeAlias    alias="user" type="com.ibatis.sample.User"/>

       <typeAlias     alias="address" type="com.ibatis.sample.Address"/>

       <resultMap id="get-user-result"class="user">

                 <result property="id" column="id"/>

                <result property="name" column="name"/>

                <result property="sex" column="sex"/>

                <result property="addresses"                 column="id"         select="User.getAddressByUserId"/>

        </resultMap>

      <select       id="getUsers"          parameterClass="java.lang.String"          resultMap="get-user-result">

                          <![CDATA[select id,name,sex from t_user where id = #id#]]>

      </select>

      <select id="getAddressByUserId"     parameterClass="int"    resultClass="address">

                     <![CDATA[select address,zipcode from t_address where user_id = #userid# ]]>

      </select>

</sqlMap>

一对一关联

<resultMap id="get-user-result" class="user">

    <result property="id" column="id"/>

    <result property="name" column="name"/>

    <result property="sex" column="sex"/>

    <result property="address" column="t_address.address"/>

    <result property="zipCode" column="t_address.zipcode"/>

</resultMap>

<select id="getUsers"          parameterClass="java.lang.String"            resultMap="get-user-result">

       <![CDATA[select * from t_user,t_addresswhere                          t_user.id=t_address.user_id ]]>

</select>

延迟加载

在运行上面的例子时,通过观察期间的日志输出顺序我们可以发现,在我们执行sqlMap.queryForList("User.getUsers", "")时,实际上ibatis只向数据库发送了一条select id,name, sex from t_user SQL。而用于获取Address记录的SQL,只有在我们真正访问address对象时,才开始执行。

这也就是所谓的延迟加载(Lazy Loading)机制。即当真正需要数据的时候,才加载数据。延迟加载机制能为我们的系统性能带来极大的提升。

Settings 节点有两个与延迟加载相关的属性lazyLoadingEnabled和enhancementEnabled,其中lazyLoadingEnabled设定了系统是否使用延迟加载机制,enhancementEnabled设定是否启用字节码强化机制(通过字节码强化机制可以为Lazy Loading带来性能方面的改进。为了使用延迟加载所带来的性能优势,这两项都建议设为"true"。

动态映射

<select id="" parameterClass="" resultClass="">

         select  * from  test

         <dynamic  prepend="WHERE">

                   <isNotEmpty  prepend="AND"  property="name">

                            name= #name#

         </dynamic>

</select>

Dynamic节点和判定节点中的prepend属性,指明了本节点中定义的SQL子句在主体SQL中出现时的前缀。其中WHERE 之后的语句是在dynamic 节点中所定义,因此以dynamic 节点的prepend设置("WHERE")作为前缀,而其中的”AND”,实际上是address属性所对应的isNotEmpty节点的prepend设定,它引领了对应节点中定义的SQL子句。至于name属性对应的isNotEmpty节点,由于ibatis会自动判定是否需要追加prepend前缀,这里(name like #name#)是WHERE 子句中的第一个条件子句,无需AND 前缀,所以自动省略。

一元判断

一元判断是针对属性值本身的判断,如属性是否为NULL,是否为空值等。

节点名称

节点描述

<isPropertyAvailable>

参数类中,是否提供了该属性

<isNotPropertyAvailable>

与<isPropertyAvailable>相反

<isNull>

属性值是否为NULL

<isNotNull>

与<isNull>相反

<isEmpty>

如果属性为Collection或String,则其size是否<1。

如果非以上两种类型,则通过String.valueof(属性值)获取其String类型值之后再判断其size是否<1。

<isNotEmpty>

与<isEmpty>相反

二元判断

二元判断则有两个判断参数,一个是属性名,另一个则是判断值

节点名称

属性值与compareValue的关系

<isEqual>

相等

<isNotEqual>

不相等

<isGreaterThan>

大于

<isGreaterEqual>

大于等于

<isLessThan>

小于

<isLessEqual>

小于等于

事务管理

基于JDBC的事务管理机制

ibatis提供了自动化的JDBC事务管理机制。对于传统JDBC Connection 而言,我们获取Connection实例之后,需要调用Connection.setAutoCommit设定事务提交模式。在 AutoCommit为true的情况下,JDBC会对我们的操作进行自动提交,此时,每个JDBC操作都是一个独立的任务。为了实现整体事务的原子性,我们需要将AutoCommit 设为false,并结合Connection.commit/rollback方法进行事务的提交/回滚操作。ibatis 的所谓“自动化的事务提交机制”,即ibatis 会根据当前的调用环境,自动判断操作是否需要自动提交。如果代码没有显式的调用SqlMapClient.startTransaction()方法,则ibatis会将当前的数据库操作视为自动提交模式(AutoCommit=true)

实际上,在执行update语句时,sqlMap会检查当前的Session是否已经关联了某个数据库连接,如果没有,则取一个数据库连接,将其AutoCommit属性设为true,然后执行update 操作,执行完之后又将这个连接释放。这样,上面两次update 操作实际上先后获取了两个数据库连接,而不是我们通常所认为的两次update 操作都基于同一个JDBCConnection。这点在开发时需特别注意。

对于多条SQL 组合而成的一个JDBC 事务操作而言,必须使用startTransaction、commit和endTransaction操作以实现整体事务的原子性。

基于JTA的事务管理机制

JTA提供了跨数据库连接(或其他JTA资源)的事务管理能力。这一点是与JDBC Transaction最大的差异。JDBC事务由Connnection管理,也就是说,事务管理实际上是在JDBC Connection中实现。事务周期限于Connection的生命周期。同样,对于基于JDBC的ibatis事务管理机制而言,事务管理在SqlMapClient所依托的JDBCConnection中实现,事务周期限于SqlMapClient 的生命周期。JTA事务管理则由JTA容器实现,JTA容器对当前加入事务的众多Connection进行调度,实现其事务性要求。JTA的事务周期可横跨多个JDBCConnection生命周期。同样,对于基于JTA事务的ibatis而言,JTA事务横跨可横跨多个SqlMapClient。

为了在ibatis中使用JTA事务管理,我们需要在配置文件中加以设定:

<transactionManager type="JTA">

.........

< /transactionManager>

sqlMap1 = xmlBuilder.buildSqlMap(db1Config);

sqlMap2 = xmlBuilder.buildSqlMap(db2Config);

try{

         sqlMap1.startTransaction();

         sqlMap1.update("User.updateUser",user);

         sqlMap2.update("User.updateUser",user);

         sqlMap1.commitTransaction();

}finally{

         sqlMap1.endTransaction();

}

上面的代码中,两个针对不同数据库的SqlMapClient 实例,在同一个JTA 事务中对user 对象所对应的数据库记录进行更新。外层的sqlMap1启动了一个全局事务,此事务将涵盖本线程内commitTransaction 之前的所有数据库操作。只要其间发生了异常,则整个事务都将被回滚。

外部事务管理

基于JTA的事务管理还有另外一个特殊情况,就是利用外部事务管理机制。对于外部事务管理,我们需要在配置文件中进行如下设定:

<transactionManager type="EXTERNAL">

……

</transactionManager>

下面是一个外部事务管理的典型示例:

UserTransaction tx = new InitialContext().lookup(“……”);

……

sqlMap1 = xmlBuilder.buildSqlMap(db1Config);

sqlMap2 = xmlBuilder.buildSqlMap(db2Config);

sqlMap1.update("User.updateUser", user);

sqlMap2.update("User.updateUser", user);

……

tx.commit();

此时,我们借助JTA UserTransaction实例启动了一个全局事务。之后的ibatis操作(sqlMap1.update 和sqlMap2.update)全部被包含在此全局事务之中,当UserTransaction提交的时候,ibatis操作被包含在事务中提交,反之,如果UserTransaction回滚,那么其间的ibatis操作也将作为事务的一部分被回滚。这样,我们就实现了ibatis外部的事务控制。

另一种外部事务管理方式是借助EJB 容器,通过EJB 的部署配置,我们可以指定

EJB方法的事务性下面是一个Session Bean的doUpdate方法,它的事务属性被申明为“Required”,EJB容器将自动维护此方法执行过程中的事务:

/**

* @ejb.interface-method

* view-type="remote"

*

* @ejb.transaction type = "Required"

**/

public void doUpdate(){

         //EJB环境中,通过部署配置即可实现事务申明,而无需显式调用事务

         ……

         sqlMap1 =xmlBuilder.buildSqlMap(db1Config);

         sqlMap2 =xmlBuilder.buildSqlMap(db2Config);

         sqlMap1.update("User.updateUser",user);

         sqlMap2.update("User.updateUser",user);

         ……

}//方法结束时,如果没有异常发生,则事务由EJB容器自动提交。

上面的示例中,ibatis数据操作的事务管理将全部委托给EJB容器管理,由EJB容器控制其事务调度

在上面JTA事务管理的例子中,为了保持清晰,我们省略了startTransaction、commitTransaction 和endTransaction 的编写,在这种情况下,调用ibatis的事务管理方法并非必须,不过在实际开发时,请酌情添加startTransaction、commitTransaction和endTransaction 语句,这样可以获得更好的性能(如果省略了startTransaction、commitTransaction和endTransaction 语句,ibatis将为每个数据操作获取一个数据连接,就算引入了数据库连接池机制,这样的无谓开销也应尽量避免。

Cache

在特定硬件基础上(同时假设系统不存在设计上的缺漏和糟糕低效的SQL 语句)Cache往往是提升系统性能的最关键因素)。

相对Hibernate 等封装较为严密的ORM 实现而言(因为对数据对象的操作实现了较为严密的封装,可以保证其作用范围内的缓存同步,而ibatis 提供的是半封闭的封装实现,因此对缓存的操作难以做到完全的自动化同步)。

ibatis 的缓存机制使用必须特别谨慎。特别是flushOnExecute 的设定,需要考虑到所有可能引起实际数据与缓存数据不符的操作。如果不能完全确定数据更新操作的波及范围,建议避免Cache的盲目使用。

cacheModel的几个重要属性:

属性名称

属性作用

readOnly

readOnly值的是缓存中的数据对象是否只读。这里的只读并不是意味着数据对象一

旦放入缓存中就无法再对数据进行修改。而是当数据对象发生变化的时候,如数据对

象的某个属性发生了变化,则此数据对象就将被从缓存中废除,下次需要重新从数据

库读取数据,构造新的数据对象。而 readOnly="false"则意味着缓存中的数据对象可更新。只读Cache能提供更高的读取性能,但一旦数据发生改变,则效率降低。系统设计时需根据系统的实际情况(数据发生更新的概率有多大)来决定Cache的读写策略。

serialize

如果需要全局的数据缓存,CacheModel的serialize属性必须被设为true。否则数据

缓存只对当前Session有效,局部缓存对系统的整体性能提升有限。在 serialize="true"的情况下,如果有多个Session同时从Cache 中读取某个数据对象,Cache 将为每个Session返回一个对象的复本,也就是说,每个Session 将得到包含相同信息的不同对象实例。因而Session 可以对其从Cache 获得的数据进行存取而无需担心多线程并发情况下的同步冲突。

type

与hibernate类似,ibatis通过缓冲接口的插件式实现,提供了多种Cache的实现机制可供选择:MEMORY、LRU、FIFO、OSCACHE

MEMORY类型的Cache

MEMORY 类型的Cache 实现,实际上是通过Java 对象引用进行。ibatis 中,其实现类为com.ibatis.db.sqlmap.cache.memory.MemoryCacheController,MemoryCacheController 内部,使用一个HashMap来保存当前需要缓存的数据对象的引用。

这里指的是:软引用、弱引用、虚引用

软引用:当内存不足时进行回收。

弱引用:随时可能被垃圾回收机制进行回收。

虚引用:它指向的对象实际上已经被JVM 销毁(finalize方法已经被执行),只是暂时还没被垃圾回收器收回而已。PhantomReference主要用于辅助对象的销毁过程,在实际应用层研发中,几乎不会涉及。

下面是一个典型的MEMORY类型Cache配置:

<cacheModel id="user_cache" type="MEMORY">

    <flushInterval hours="24"/>

    <flushOnExecute statement="updateUser"/>

    <property name="reference-type" value="WEAK"/>

</cacheModel>

其中flushInterval 指定了多长时间清除缓存,上例中指定每24 小时强行清空缓存区的所有内容。reference-type属性可以有以下几种配置:

1. STRONG:即基于传统的Java对象引用机制,除非对Cache显式清空(如到了flushInterval设定的时间;执行了flushOnExecute所指定的方法;或代码中对Cache执行了清除操作等),否则引用将被持续保留。此类型的设定适用于缓存常用的数据对象,或者当前系统内存非常充裕的情况。

2. SOFT:基于SoftReference的缓存实现,只有JVM 内存不足的时候,才会对缓冲池中的数据对象进行回收。此类型的设定适用于系统内存较为充裕,且系统并发量比较稳定的情况。

3. WEAK:基于WeakReference的缓存实现,当JVM 垃圾回收时,缓存中的数据对象将被JVM收回。一般情况下,可以采用WEAK的MEMORY型Cache配置。

LRU类型的Cache

当Cache达到预先设定的最大容量时,ibatis会按照“最少使用”原则将使用频率最少的对象从缓冲中清除。

<cacheModel id="userCache" type="LRU">

    <flushInterval hours="24"/>

    <flushOnExecute statement="updateUser"/>

    <property name="size" value="1000"/>

</cacheModel>

FIFO类型的Cache

先进先出型缓存,最先放入Cache中的数据将被最先废除。可配置参数与LRU型相同:

<cacheModel id="userCache" type="FIFO">

    <flushInterval hours="24"/>

    <flushOnExecute statement="updateUser"/>

    <property name="size" value="1000"/>

</cacheModel>

OSCACHE类型的Cache

与上面几种类型的Cache不同,OSCache来自第三方组织Opensymphony。在生产部署时,建议采用OSCache,OSCache 是得到了广泛使用的开源Cache实现(Hibernate 中也提供了对OSCache 的支持),它基于更加可靠高效的设计,更重要的是,最新版本的OSCache 已经支持Cache 集群。如果系统需要部署在集群中,或者需要部署在多机负载均衡模式的环境中以获得性能上的优势,那么OSCache在这里则是不二之选。

<cacheModel id="userCache" type="OSCACHE">

    <flushInterval hours="24"/>

    <flushOnExecute statement="updateUser"/>

    <property name="size" value="1000"/>

</cacheModel>

之所以配置简单,原因在于,OSCache拥有自己的配置文件(oscache.properties),下面是一个典型的OSCache配置文件:

#是否使用内存作为缓存空间

cache.memory=true

#缓存管理事件监听器,通过这个监听器可以获知当前Cache的运行情况

cache.event.listeners=com.opensymphony.oscache.plugins.clustersupport.JMSBroa

dcastingListener

#如果使用磁盘缓存(cache.memory=false),则需要指定磁盘存储接口实现

#cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.Disk

PersistenceListener

# 磁盘缓存所使用的文件存储路径

# cache.path=c:\\myapp\\cache

# 缓存调度算法,可选的有LRU,FIFO和无限缓存(UnlimitedCache)

# cache.algorithm=com.opensymphony.oscache.base.algorithm.FIFOCache

#cache.algorithm=com.opensymphony.oscache.base.algorithm.UnlimitedCache

cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache

#内存缓存的最大容量

cache.capacity=1000

# 是否限制磁盘缓存的容量

# cache.unlimited.disk=false

# 基于JMS的集群缓存同步配置

#cache.cluster.jms.topic.factory=java:comp/env/jms/TopicConnectionFactory

#cache.cluster.jms.topic.name=java:comp/env/jms/OSCacheTopic

#cache.cluster.jms.node.name=node1

# 基于JAVAGROUP的集群缓存同步配置

#cache.cluster.properties=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_

ttl=32;mcast_send_buf_size=150000;mcast_recv_buf_size=80000):PING(timeout

=2000;num_initial_members=3):MERGE2(min_interval=5000;max_interval=10000

):FD_SOCK:VERIFY_SUSPECT(timeout=1500):pbcast.NAKACK(gc_lag=50;retransm

it_timeout=300,600,1200,2400,4800):pbcast.STABLE(desired_avg_gossip=20000):

UNICAST(timeout=5000):FRAG(frag_size=8096;down_thread=false;up_thread=fal

se):pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_loc

al_addr=true)

#cache.cluster.multicast.ip=231.12.21.132

配置好之后,将此文件放在CLASSPATH 中,OSCache 在初始化时会自动找到此文件并根据其中的配置创建缓存实例。

猜你喜欢

转载自blog.csdn.net/attack_breast/article/details/74065025
今日推荐