iBatis学习心得

来源:互联网 发布:linux free 参数 编辑:程序博客网 时间:2024/05/18 12:32
Notice:
1) 在Spring配置文件中,一定要为SqlMapClientFactoryBean配置DataSource属性。
    另外经过测试,DAO不需要配置dataSoure。
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    <!-- 此处应注入ibatis配置文件,而非sqlMap文件,否则会出现“there is no statement.....异常” -->
    <property name="configLocation">
        <value>classpath:sql_resources/SqlMapConfig.xml</value>
        <!-- <value>WEB-INF/classes/sql_resources/SqlMapConfig.xml</value>
-->
    </property>
    <!-- 一定要配置DataSource,否则会出现“NullPointerException” -->
    <property name="dataSource" ref="dataSource" />
</bean>

2) <![CDATA[……]]>
通过<![CDATA[……]]>节点,可以避免SQL 中与XML 规范相冲突的字符对XML映射文件的合法性造成影响。

3) $#获取值的方式是一样的,只是$获取是对应参数的值,#会根据不同类型而生产字符串。
如 String 类型 : aa ---> 'aa', 将获取的值加上引号,变为字符串类型。
所以,一般用$来获取表名,列名,构造sql;而用#获取要插入的值,适合传值。
#方式能够很大程度防止sql注入。$方式无法防止sql注入,一般能用#的就别用$.

<insert id="customPO_insert" parameterClass="customPO">
    INSERT INTO $moduleTable$ (parentID
    <iterate property="fieldValueList" prepend="," conjunction=","> --fieldValueList是customPO一属性
        $fieldValueList[].key$   --循环fieldValueList[]这个数组,因为此数组每个对象是map,可获得map的key.
    </iterate>
    )
    VALUES (#parentID#
    <iterate property="fieldValueList" prepend="," conjunction=",">
        #fieldValueList[].value#
    </iterate>
    )
    <selectKey resultClass="int" keyProperty="parentID">
        SELECT last_insert_id()
    </selectKey>
</insert>


4) 很多数据库支持自动生成主键的数据类型。不过这通常(并不总是)是个私有的特性。SQL Map通过<insert></insert>的子元素<selectkey></selectkey>来支持自动生成的键值。它同时支持预生成(如Oracle)和后生成两种类型(如MS-SQL Server)。下面是两个例子:
  1. < !—Oracle SEQUENCE Example -->   
  2. <insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">   
  3.    <selectKey resultClass="int" keyProperty="id" >   
  4.      SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL   
  5.    </selectKey>   
  6.    insert into PRODUCT (PRD_ID,PRD_DESCRIPTION)   
  7.    values (#id#,#description#)   
  8. <insert>   
  9. <insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">   
  10.     insert into PRODUCT (PRD_DESCRIPTION)   
  11.     values (#description#)   
  12.     <selectKey resultClass="int" keyProperty="id" >   
  13.       SELECT @@IDENTITY AS ID   
  14.     </selectKey>   
  15. <insert>  
也就是说对于oracle来说,<selectKey>   statement是必须前置的,并且必须把sequence id放到insert into 句子中,就会没有问题的。
其实也可以用在insert语句中直接调用SEQUENCE.nextVal的方法来生成sequence id,唯一的缺点就是你不能同时通过

 

Object newKey = getSqlMapClientTemplate().insert("insertProduct-ORACLE", product);

同时得到newKey这个sequence id。

 
5) 用HashMap(Map不行)解决Ibatis未知列名和列数的查询结果的resultClass映射,例如:
 <select id="getDynamicTable" resultClass="HashMap">
      <![CDATA[
          select t.* from some_table t where t.status = ?
      ]]>
 </select>
Dao层实现类:
    List test = this.sqlMapClientTemplate.queryForList("some.getDynamicTable", new Integer(1));
得到的test是一个list的结果集,里面每一个元素都是一个Map对象,Map结构如下:
    {STATUS=1, NOTE=12, URL=http://www.tc84.cn/,SQUENCE=12, ID=327105}

6) Settings 节点中errorTracingEnabled代表是否启用错误日志,在开发期间建议设为"true",以方便调试

Advantage:
1. 类的别名,别名可通过typeAlias节点指定,如:
<typeAlias alias="user" type="com.ibatis.sample.User"/>
经过测试,我发现对于一些java自有的数据类型:Long,Map,HashMap等等,可直接使用类名,不需要写全路径;
而当parameterClass="map"等java自有数据类型时,可以在statement中省略;

2. 动态SQL片段, 可以方便的重复利用sql片段(支持引用其他sqlMap的sql片段,只要在引用时加上namespace就行)
        <!-- 动态条件分页查询-->
        <sqlid="sql_count">
        select count(*)
        </sql>
        <sqlid="sql_select">
        select *
        </sql>
        <sqlid="sql_where">
        from tb_user
        ......
        </sql>
        <selectid="findByParamsForCount"parameterClass="map"resultClass="int">
            <includerefid="sql_count"/>
            <includerefid="sql_where"/>
        </select>
        <selectid="findByParams"parameterClass="map"resultMap="get_user_result">
            <includerefid="sql_select"/>
            <includerefid="sql_where"/>
        </select>

3. 在配置文件中循环
1) 多个输入参数循环次数不对称。如下例:要更新的字段Opr的值只有一个,而user_id的值却有多个(String[] appDevIds)。
<update id="user.update_user" parameterClass="user">
    update tb_user
        set Opr=#operation#
        where user_id in
    <iterate conjunction="," open="(" close=")" property="appDevIds">
        #appDevIds[]#
    </iterate>

 </update>
2) 多个输入参数循环次数是对称的(List<SortCriteria> sortingPrefs)
<sql id="sql_select">
      SELECT Z.*,dense_rank() over(order by
      <isNotEmpty property="sortingList">
        <iterate property="sortingList" open="" close="" conjunction=",">
             $sortingList[].fieldName$ $sortingList[].sortingType$
        </iterate>
    </isNotEmpty>
    ) rank
  </sql>

4. 分页查询(select, deprecated)
PaginatedList list = sqlMap.queryForPaginatedList (“getProductList”, null, 10);
list.nextPage();
list.previousPage();

不推荐,因为ibatis的PaginatedList这个类是专门负责翻页的,但它的翻页是假翻页,还是把所有数据都读到result中再进行翻页,而且也不提供记录总数的值

5. 动态映射 (可以改善我们系统中许多在Dao层拼接Sql的情况)
在复杂查询过程中,我们常常需要根据用户的选择决定查询条件,这里发生变化的并不只是SQL 中的参数,包括Select 语句中所包括的字段和限定条件,都可能发生变
化。典型情况,如在一个复杂的组合查询页面,我们必须根据用户的选择和输入决定查询的条件组合。对于ibatis 这样需要预先指定SQL 语句的ORM 实现而言,传统的做法无非通过if-else 语句对输入参数加以判定,然后针对用户选择调用不同的statement 定义。对于上面这种简单的情况(两种查询条件的排列组合,共4 种情况)而言,statement 的重复定义工作已经让人不厌其烦,而对于动辄拥有七八个查询条件,乃至十几个查询条件的排列组合而言,琐碎反复的statement定义实在让人不堪承受。

考虑到这个问题,ibatis引入了动态映射机制,即在statement定义中,根据不同的查询参数,设定对应的SQL语句。
<select id="getUsers" parameterClass="user" resultMap="get-user-result">
    select id,name,sex from t_user
    <dynamic prepend="WHERE">
        <isNotEmpty prepend="AND" property="name">
            (name like #name#)
        </isNotEmpty>
        <isNotEmpty prepend="AND" property="address">
            (address like #address#)
        </isNotEmpty>
    </dynamic>
</select>
通过dynamic 节点,我们定义了一个动态的WHERE 子句。此WHERE 子句中将可能包含两个针对name 和address 字段的判断条件。而这两个字段是否加入检索取决
于用户所提供的查询条件(字段是否为空[isNotEmpty])。

6. resultMap 则大多用于嵌套查询以及存储过程的处理,之所以这样,原因是由于存储过程相对而言比较封闭(很多情况下需要调用现有的存储过程,其参数命名和返回的数据字段命名往往不符合Java编程中的命名习惯,并且由于我们难以通过Select SQL的as子句进行字段名转义,无法使其自动与POJO中的属性名相匹配)。此时,使用resultMap建立字段名和POJO属性名之间的映射关系就显得非常有效。另一方面,由于通过resultMap 指定了字段名和字段类型,ibatis无需再通过JDBC ResultSetMetaData 来动态获取字段信息,在一定程度上也提升了性能表现。例如:
1) Procedure
<resultMap id="get_user_result" class="user">
    <result property="name" column="xingming" jdbcType="VARCHAR" javaType="java.lang.String"/>
    <result property="sex" column="xingbie" jdbcType="int" javaType="java.lang.Integer"/>
    <result property="id" column="id" jdbcType="int" javaType="java.lang.Integer"/>
</resultMap>
<parameterMap id="update_user_para" class="redemption" >
    <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="" />
    <parameter property="sex" jdbcType="int" javaType="java.lang.Integer" nullValue="" />
</parameterMap>

<procedure id="getUserList" resultMap="get_user_result">
    {call sp_getUserList()}
</procedure>
<procedure id="doUserUpdate" parameterMap="update_user_para">
    {call sp_doUserUpdate(#id#,#name#,#sex#)}
</procedure>

2) 嵌套查询
<typeAlias alias="employee" type="com.cathywu.mywebtest.Bean.EmployeeInfoBean"/>
<typeAlias alias="elog" type="com.cathywu.mywebtest.Bean.EmployeeLogInfo"/>

<resultMap class="employee" id="employee-result">
      <result property="eId" column="eId"/>
      <result property="name" column="name"/>
      <result property="department" column="department"/>
      <result property="logList" column="eId" select="getEmployeeLogById"/>
</resultMap>

<select id="getEmployeeLogById" parameterClass="java.lang.Integer" resultClass="elog">
      <![CDATA[
          select l_id as lId,
               e_id as eId,
               l_desc as description,
               create_time as createTime
          from t_employee_log
         where e_id=#eId#
      ]]>
</select>

7.Using SqlMapClientTemplate and SqlMapClientDaoSupport

The SqlMapClientDaoSupport class offers a supporting class similar to the SqlMapDaoSupport. We extend it to implement our DAO:

 

public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {

    public Account getAccount(String email) throws DataAccessException {
        return (Account) getSqlMapClientTemplate().queryForObject("getAccountByEmail", email);
    }

    public void insertAccount(Account account) throws DataAccessException {
        getSqlMapClientTemplate().update("insertAccount", account);
    }
}

Note that a SqlMapTemplate instance could also be created manually, passing in the SqlMapClient as constructor argument. The SqlMapClientDaoSupport base class simply pre-initializes a SqlMapClientTemplate instance for us.

 

The SqlMapClientTemplate also offers a generic execute method, taking a custom SqlMapClientCallback implementation as argument. This can, for example, be used for batching:


public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {

    public void insertAccount(Account account) throws DataAccessException {
        getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                executor.startBatch();
                executor.update("insertAccount", account);
                executor.update("insertAddress", account.getAddress());
                executor.executeBatch();
            }
        });
    }
}


In general, any combination of operations offered by the native SqlMapExecutor API can be used in such a callback. Any SQLException thrown will automatically get converted to Spring's generic DataAccessException hierarchy.

以Spring的强大的注入功能,将SqlMapClientTemplate注入到DAO层之中
<!--根据sqlMapClient创建一个SqlMapClient模版类-->
<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
    <property name="sqlMapClient">
        <ref bean="sqlMapClient" />
    </property>
</bean>

 

org.springframework.orm.ibatis.SqlMapClientTemplate 是Spring为基于ibatis的数据访问操作提供的模板方法类,我们可以通过SqlMapClientTemplate结合 org.springframework.orm.ibatis.SqlMapClientCallback回调接口完成所有基于ibatis的数据访问操作。 SqlMapClientTemplate管理事务,异常处理,资源管理等方面的事情,而SqlMapClientCallback则使得开发人员专注于具体的数据访问逻辑。

 

SqlMapClientTemplate中的execute(SqlMapClientCallback)方法是整个SqlMapClientTemplate实现的核心:

public Object execute(SqlMapClientCallback action) throws DataAccessException

所有其他的模板方法都是在该模板方法的基础之上为了进一步提供便利而提供的。

 

execute(SqlMapClientCallback)模板方法以统一的方式对基于ibatis的数据访问操作进行了封装并于一处管理, 可谓集资源管理,异常处理以及事务控制机能于一身:

 

8. 如果容器中的SqlMapClientFactoryBean仅限于一个SqlMapClientTemplate使用的话,可以像如下配置那样将SqlMapClientFactoryBean配置为“inner bean”的形式, 当然,这不是必须的。

 

<bean id="mainSqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"><property name="sqlMapClient"><bean class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"><property name="dataSource"><ref bean="mainDataSource"/></property><property name="configLocation"><value>file:conf/ibatis/ibatis-config.xml</value></property></bean></property></bean>
参考:http://chenxingjing1981.spaces.live.com/Blog/cns!3A0F5A15C5FAEB2C!162.entry
原创粉丝点击