`
yeshaoting
  • 浏览: 666664 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

[小结]Java数据库连接 - 集成第三方ORM框架 - iBatis

    博客分类:
  • J2SE
 
阅读更多

 

9. 集成第三方ORM框架 - iBatis

总体来说 iBATIS 的系统结构还是比较简单的,它主要完成两件事情:

<!--[if !supportLists]-->_        <!--[endif]-->根据 JDBC 规范建立与数据库的连接;

<!--[if !supportLists]-->_        <!--[endif]-->通过反射打通 Java 对象与数据库参数交互之间相互转化关系。

 

 

以下内容摘自: 百度百科

词条:iBatis

 

一站式

  iBATIS提供的持久层框架包括SQL MapsData Access ObjectsDAO),同时还提供一个利用这个框架开发的JPetStore实例。

  相对HibernateApache OJB一站式ORM解决方案而言,ibatis 是一种半自动化ORM实现。

 

纵观目前主流

  所谓半自动,可能理解上有点生涩。纵观目前主流的 ORM,无论 Hibernate 还是Apache OJB,都对数据库结构提供了较为完整的封装,提供了从POJO 到数据库表的全套映射机制。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过 Hibernate或者OJB 提供的方法完成持久层操作。程序员甚至不需要对 SQL 的熟练掌握,Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的 SQL 并调用 JDBC 接口加以执行。

 

新系统的开发

  大多数情况下(特别是对新项目,新系统的开发而言),这样的机制无往不利,大有一统天下的势头。但是,在一些特定的环境下,这种一站式的解决方案却未必灵光。

  在笔者的系统咨询工作过程中,常常遇到以下情况:

  1. 系统的部分或全部数据来自现有数据库,处于安全考虑,只对开发团队提供几条Select SQL(或存储过程)以获取所需数据,具体的表结构不予公开。

  2. 开发规范中要求,所有牵涉到业务逻辑部分的数据库操作,必须在数据库层由存储过程实现(就笔者工作所面向的金融行业而言,工商银行、中国银行、交通银行,都在开发规范中严格指定)

  3. 系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL语句(或存储过程)才能达到系统性能设计指标。

  面对这样的需求,再次举起 Hibernate 大刀,却发现刀锋不再锐利,甚至无法使用,奈何?恍惚之际,只好再摸出JDBC 准备拼死一搏……,说得未免有些凄凉,直接使用 JDBC进行数据库操作实际上也是不错的选择,只是拖沓的数据库访问代码,乏味的字段读取操作令人厌烦。

 

半自动化

刚好解决上述问题

  半自动化ibatis,却刚好解决了这个问题。

  这里的半自动化,是相对Hibernate等提供了全面的数据库封装机制的全自动化

  ORM 实现而言,全自动ORM 实现了 POJO 和数据库表之间的映射,以及 SQL 的自动

  生成和执行。而ibatis 的着力点,则在于POJO SQL之间的映射关系。也就是说,ibatis

  并不会为程序员在运行期自动生成 SQL 执行。具体的 SQL 需要程序员编写,然后通过映

  射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定 POJO

 

全自动

  使用ibatis 提供的ORM机制,对业务逻辑实现人员而言,面对的是纯粹的 Java对象,

  这一层与通过 Hibernate 实现 ORM 而言基本一致,而对于具体的数据操作,Hibernate

  会自动生成SQL 语句,而ibatis 则要求开发者编写具体的 SQL 语句。相对Hibernate

  全自动ORM机制而言,ibatis SQL开发的工作量和数据库移植性上的让步,为系统

  设计提供了更大的自由空间。作为全自动ORM实现的一种有益补充,ibatis 的出现显

  得别具意义。



  

 

一般步骤:

9.1 全局配置文件

该全局配置文件用来配置数据库连接,数据库事务,定义SQL映射配置文件

说明:

a) <properties>标签定义全局配置属性文件(.properties),只能出现0次或1

用于填充com.ibatis.sqlmap.engine.builder.xml.XmlParseStateglobalProps属性

另外,这个全局.properties可以动态配置.

在后面说到的数据库操作时,通过com.ibatis.sqlmap.client.SqlMapClientBuilder类中静态方法buildSqlMapClient(InputStream,Properties)buildSqlMapClient(Reader,Properties)获取com.ibatis.sqlmap.client.SqlMapClient对象时动态配置.如此,可以动态使用数据源,动态更改成不同的数据库连接等等.

 

:jdbc.properties设置了一些数据库连接的参数,其中键(e.g. driverClassName)可以通过如${driverClassName}sql-map-config.xml文件中使用.

b) <sqlMap>标签用来配置SQL映射文件.e.g sql-map-user.xmlSQL映射文件

c) <properties>标签、<sqlMap>标签的resource属性是相对于classpath路径.所以, / 开头是错误的.这二个标签还有一个url属性,可以配置绝对路径.

d) <transactionManager>标签用于定义数据库事务.type属性定义数据库事务类型,有三个选项: JDBC,JTA,EXTERNAL

e) <dataSource>标签用来定义数据库连接的一些属性.事务类型为JDBC,type属性定义数据库数据源

用户可以使用ibatis内置的三类数据源:

SIMPLE

<dataSource>标签内定义的数据源参数包装成一个Map对象,通过SimpleDataSourceFactoryinitialize()方法传入到com.ibatis.common.jdbc.SimpleDataSource,初始化SimpleDataSource数据源属性.

相关类 com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory

/**

 * DataSourceFactory implementation for the iBATIS SimpleDataSource

 */

public class SimpleDataSourceFactory implements DataSourceFactory {

 

  private DataSource dataSource;

 

  public void initialize(Map map) {

    dataSource = new SimpleDataSource(map);

  }

 

  public DataSource getDataSource() {

    return dataSource;

  }

 

}

 

相关类 com.ibatis.common.jdbc.SimpleDataSource

/**

 * This is a simple, synchronous, thread-safe database connection pool.

 * <p/>

 * REQUIRED PROPERTIES

 * -------------------

 * JDBC.Driver

 * JDBC.ConnectionURL

 * JDBC.Username

 * JDBC.Password

 * <p/>

 * Pool.MaximumActiveConnections

 * Pool.MaximumIdleConnections

 * Pool.MaximumCheckoutTime

 * Pool.TimeToWait

 * Pool.PingQuery

 * Pool.PingEnabled

 * Pool.PingConnectionsOlderThan

 * Pool.PingConnectionsNotUsedFor

 * Pool.QuietMode

 */

private void initialize(Map props) {

    ...

if (!(props.containsKey(PROP_JDBC_DRIVER)

          && props.containsKey(PROP_JDBC_URL)

          && props.containsKey(PROP_JDBC_USERNAME)

          && props.containsKey(PROP_JDBC_PASSWORD))) {

        throw new RuntimeException("SimpleDataSource: Some properties were not set.");

      } else {

 

        jdbcDriver = (String) props.get(PROP_JDBC_DRIVER);

        jdbcUrl = (String) props.get(PROP_JDBC_URL);

        jdbcUsername = (String) props.get(PROP_JDBC_USERNAME);

        jdbcPassword = (String) props.get(PROP_JDBC_PASSWORD);

 

        poolMaximumActiveConnections =

            props.containsKey(PROP_POOL_MAX_ACTIVE_CONN)

            ? Integer.parseInt((String) props.get(PROP_POOL_MAX_ACTIVE_CONN))

            : 10;

 

        poolMaximumIdleConnections =

            props.containsKey(PROP_POOL_MAX_IDLE_CONN)

            ? Integer.parseInt((String) props.get(PROP_POOL_MAX_IDLE_CONN))

            : 5;

 

        poolMaximumCheckoutTime =

            props.containsKey(PROP_POOL_MAX_CHECKOUT_TIME)

            ? Integer.parseInt((String) props.get(PROP_POOL_MAX_CHECKOUT_TIME))

            : 20000;

 

        poolTimeToWait =

            props.containsKey(PROP_POOL_TIME_TO_WAIT)

            ? Integer.parseInt((String) props.get(PROP_POOL_TIME_TO_WAIT))

            : 20000;

 

        poolPingEnabled =

            props.containsKey(PROP_POOL_PING_ENABLED)

                && Boolean.valueOf((String) props.get(PROP_POOL_PING_ENABLED)).booleanValue();

 

        prop_pool_ping_query = (String) props.get(PROP_POOL_PING_QUERY);

        poolPingQuery =

            props.containsKey(PROP_POOL_PING_QUERY)

            ? prop_pool_ping_query

            : "NO PING QUERY SET";

 

        poolPingConnectionsOlderThan =

            props.containsKey(PROP_POOL_PING_CONN_OLDER_THAN)

            ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_OLDER_THAN))

            : 0;

 

        poolPingConnectionsNotUsedFor =

            props.containsKey(PROP_POOL_PING_CONN_NOT_USED_FOR)

            ? Integer.parseInt((String) props.get(PROP_POOL_PING_CONN_NOT_USED_FOR))

            : 0;

 

        jdbcDefaultAutoCommit =

            props.containsKey(PROP_JDBC_DEFAULT_AUTOCOMMIT)

                && Boolean.valueOf((String) props.get(PROP_JDBC_DEFAULT_AUTOCOMMIT)).booleanValue();

    ...

}

     

DBCP

相关类 com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory

/**

 * DataSourceFactory implementation for DBCP

 */

public class DbcpDataSourceFactory implements DataSourceFactory {

 

  private DataSource dataSource;

 

  public void initialize(Map map) {

    DbcpConfiguration dbcp = new DbcpConfiguration(map);

    dataSource = dbcp.getDataSource();

  }

 

  public DataSource getDataSource() {

    return dataSource;

  }

 

}

 

相关类 com.ibatis.common.jdbc.DbcpConfigurationDataSource

/* REQUIRED PROPERTIES

 * -------------------

 * JDBC.Driver

 * JDBC.ConnectionURL

 * JDBC.Username

 * JDBC.Password

 * <p/>

 * Pool.ValidationQuery

 * Pool.MaximumActiveConnections

 * Pool.MaximumIdleConnections

 * Pool.MaximumWait

 */

private BasicDataSource legacyDbcpConfiguration(Map map) {

    BasicDataSource basicDataSource = null;

    if (map.containsKey("JDBC.Driver")) {

      basicDataSource = new BasicDataSource();

      String driver = (String) map.get("JDBC.Driver");

      String url = (String) map.get("JDBC.ConnectionURL");

      String username = (String) map.get("JDBC.Username");

      String password = (String) map.get("JDBC.Password");

      String validationQuery = (String) map.get("Pool.ValidationQuery");

      String maxActive = (String) map.get("Pool.MaximumActiveConnections");

      String maxIdle = (String) map.get("Pool.MaximumIdleConnections");

      String maxWait = (String) map.get("Pool.MaximumWait");

 

      basicDataSource.setUrl(url);

      basicDataSource.setDriverClassName(driver);

      basicDataSource.setUsername(username);

      basicDataSource.setPassword(password);

    ...

}

 

 

JNDI

/* REQUIRED PROPERTIES

 * -------------------

 * DataSource

 * or

 * DBJndiContext

 * or

 * DBFullJndiContext

 * or

 * DBInitialContext and DBLookup

 */

相关类 com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory

/**

 * DataSourceFactory implementation for JNDI

 */

public class JndiDataSourceFactory implements DataSourceFactory {

  public void initialize(Map properties) {

    ...

    if (properties.containsKey("DataSource")) {

        dataSource = (DataSource) initCtx.lookup((String) properties.get("DataSource"));

      } else if (properties.containsKey("DBJndiContext")) { // LEGACY --Backward compatibility       

        dataSource = (DataSource) initCtx.lookup((String) properties.get("DBJndiContext"));

      } else if (properties.containsKey("DBFullJndiContext")) { // LEGACY --Backward compatibility

        dataSource = (DataSource) initCtx.lookup((String) properties.get("DBFullJndiContext"));

      } else if (properties.containsKey("DBInitialContext")

          && properties.containsKey("DBLookup")) { // LEGACY --Backward compatibility

        Context ctx = (Context) initCtx.lookup((String) properties.get("DBInitialContext"));

        dataSource = (DataSource) ctx.lookup((String) properties.get("DBLookup"));

      }

    ...

  }

}

 

sql-map-config.xml数据库事务片段:

<transactionManager type="JDBC" >

    <dataSource type="JNDI">

       <property name="DataSource" value="java:comp/env/jdbc/mydb"/>

    </dataSource>

</transactionManager>

 

java:comp/env/jdbc/mydb是通过JNDI方式获取数据源

简述:建立一个context.xml文件放置在WebRoot/META-INF目录下,详见 7.1 配置JNDI数据源.

 

sql-map-config.xml

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

<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd" >

 

<sqlMapConfig>

 

    <properties resource="com/iteye/jarg/resources/jdbc.properties" />

   

    <transactionManager type="JDBC">

       <dataSource type="SIMPLE">

           <property name="JDBC.Driver" value="${driverClassName}" />

           <property name="JDBC.ConnectionURL" value="${url}" />

           <property name="JDBC.Username" value="${username}" />

           <property name="JDBC.Password" value="${password}" />

           <property name="Pool.MaximumActiveConnections" value="${maxActive}" />

           <property name="Pool.MaximumIdleConnections" value="${maxIdle}" />

           <property name="Pool.TimeToWait" value="${maxWait}" />

       </dataSource>

    </transactionManager>

 

  <sqlMap resource="com/iteye/jarg/resources/sql-map-user.xml" />

 

</sqlMapConfig>

 

 

9.2 SQL映射文件

SQL映射文件用来配置数据库表字段与POJO对象的映射

sql-map-user.xml用于将数据库中User表字段映射到com.iteye.jarg.bean.User属性

说明:

<typeAlias>配置别名,将一长串,用一个别名代替

<resultMap><parameterMap>用来设置生成的结果集中每条记录的类型及包含的数据

<insert><delete><update><select>对应SQL的增删改查操作语句

<statement>可以用来代替增删改查的四个标签,也即其中的内容可以是任意的数据库增删改查操作

<procedure>对应数据库调用存储过程

注意:

a) 花括号是必须的

b) 通过call关键字来调用存储过程

c) 问号用来占位,代表一个输入输出参数

:

    <procedure id="swapEmailAddresses" parameterMap="swapParameters" >

       { call swap_email_address (?, ?) }

    </procedure>

   

<insert><delete><update><select><statement><procedure>这些标签必不可少的是id属性,供数据库操作函数调用对应的SQL语句,也是区别于其他的标签SQL内容的依据.id属性必须是在全局配置文件(e.g. sql-map-config.xml)中定义的所有SQL映射文件(e.g. sql-map-user.xml)中独一无二的,:所有SQL映射文件中的所有id属性不可出现重复情况.

另外,常用的属性有

设置传入参数类型  parameterClass,parameterMap

设置传出参数类型  resultClass,resultMap

设置调整缓存类型  cacheModel

parameterClass,resultClass用于指定已经存在的数据对象类型

parameterMap,resultMap用于指定自定义的数据对象类型

:

    <resultMap class="User" id="UserMap">

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

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

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

    </resultMap>

   

 

<cacheModel>标签

用于设置数据库预编译SQL语句,缓存SQL语句的缓存模式

A cacheModel is used to describe a cache for use with a query mapped statement. Each query mapped statement can use a different cacheModel, or the same one.

:

    <cacheModel id="product-cache" type="LRU">

       <flushInterval hours="24"/>

       <flushOnExecute statement="insertProduct"/>

       <flushOnExecute statement="updateProduct"/>

       <flushOnExecute statement="deleteProduct"/>

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

    </cacheModel>

   

 

sql-map-user.xml

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

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

 

<sqlMap>

   

    <typeAlias alias="User" type="com.iteye.jarg.bean.User" />

   

    <resultMap class="User" id="UserMap">

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

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

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

    </resultMap>

   

    <select id="user.queryUserById" parameterClass="java.lang.Integer" resultMap="UserMap">

       select *

       from user

       where id = #id#

    </select>

   

    <select id="user.queryUser" parameterClass="User" resultMap="UserMap">

       select *

       from user

       where username=#username# and password=#password#

    </select>

   

    <select id="user.queryUsersById" parameterClass="java.lang.Integer" resultMap="UserMap">

       select *

       from user

       where id != #id#

    </select>

   

    <select id="user.queryAllUser" resultMap="UserMap">

       select *

       from user

    </select>

   

</sqlMap>

 

 

9.3 数据库操作

9.3.1 获取com.ibatis.sqlmap.client.SqlMapClient对象

 

摘自com.ibatis.sqlmap.client.SqlMapClientBuilder类描述:

Builds SqlMapClient instances from a supplied resource (e.g. XML configuration file)

 

The SqlMapClientBuilder class is responsible for parsing configuration documents and building the SqlMapClient instance. Its current implementation works with XML configuration files (e.g. sql-map-config.xml).

 

Example:

 

 Reader reader = Resources.getResourceAsReader("properties/sql-map-config.xml");

 SqlMapClient client = SqlMapClientBuilder.buildSqlMapClient (reader);

 

 

通过com.ibatis.sqlmap.client.SqlMapClientBuilder类中静态方法

public static SqlMapClient buildSqlMapClient(Reader reader)

public static SqlMapClient buildSqlMapClient(Reader reader, Properties props)

public static SqlMapClient buildSqlMapClient(InputStream inputStream)

public static SqlMapClient buildSqlMapClient(InputStream inputStream, Properties props)

获取com.ibatis.sqlmap.client.SqlMapClient对象

 

 

 

9.3.2 利用com.ibatis.sqlmap.client.SqlMapClient接口中内置的数据库操作方法完成数据库操作

com.ibatis.sqlmap.client.SqlMapClient是一个接口,继承了SqlMapExecutor,SqlMapTransactionManager接口.

public interface SqlMapClient extends SqlMapExecutor, SqlMapTransactionManager

 

SqlMapExecutor接口中定义了所有数据库操作的方法(This interface declares all methods involved with executing statements and batches for an SQL Map.)

e.g. queryForList(),queryForObject(),querForMap()

queryForList可以用来获取包含多条记录的结果集

queryForObject可以用来获取包含0条或1条记录的结果集

 

以上二种方式最多可以包含三个参数,最少一个参数.

id对应于SQL映射文件(e.g. sql-map-user.xml)中包含数据库操作SQL语句的标签编号,是必不可少的.

parameterObject指定SQL语句参数

resultObject指定需要的结果数据(当结果集中包含数据所需要的数据量时,可以通过这个参数过滤掉一些不需要的结果数据)

Parameters:

    id:        The name of the statement to execute.

    parameterObject:  The parameter object (e.g. JavaBean, Map, XML etc.).

    resultObject: The result object instance that should be populated with result data.

 

Map queryForMap(String id, Object parameterObject, String keyProp) throws SQLException;

用来获取结果集中键为keyProp的结果

 

Map queryForMap(String id, Object parameterObject, String keyProp, String valueProp) throws SQLException;

用来获取结果集中键为keyProp,值为valueProp的结果

 

 

SqlMapTransactionManager接口中定义了数据库事务操作的方法(This interface declares methods for demarcating SQL Map transactions.)

e.g. startTransaction(),commintTransaction(),endTransaction()

 

默认情况,当完成一次数据库操作(select,insert,update,delete),数据库事务总是自动提交,即使使用了startTransaction(),commitTransaction(),endTransaction().

 

在全局配置文件(e.g. sql-map-config.xml),<transactionManager>标签的commitRequired属性设置为true,通过commitTransaction()来手动提交数据库事务.

这种方式的优点:

a) 一次执行多条数据库操作,出错时可以回滚,将数据库还原到出错前.

b) 一次提交效率高,节省多次提交的多次数据库建立,连接资源

 

 

摘自com.ibatis.sqlmap.client.SqlMapClient类描述:

A thread safe client for working with your SQL Maps (Start Here). This interface inherits transaction control and execution methods from the SqlMapTransactionManager and SqlMapExecutor interfaces.

 

The SqlMapClient is the central class for working with SQL Maps. This class will allow you to run mapped statements (select, insert, update, delete etc.), and also demarcate transactions and work with batches. Once you have an SqlMapClient instance, everything you need to work with SQL Maps is easily available.

 

The SqlMapClient can either be worked with directly as a multi-threaded client (internal session management), or you can get a single threaded session and work with that. There may be a slight performance increase if you explicitly get a session (using the openSession() method), as it saves the SqlMapClient from having to manage threads contexts. But for most cases it won't make much of a difference, so choose whichever paradigm suits your needs or preferences.

 

An SqlMapClient instance can be safely made static or applied as a Singleton. Generally it's a good idea to make a simple configuration class that will configure the instance (using SqlMapClientBuilder) and provide access to it.

 

9.3.3 数据库操作示例

a)读取数据全局配置文件

b) 获取数据库操作句柄

c) 查询操作并返回结果

 

    String filePath = "com/iteye/jarg/resources/sql-map-config.xml";

    InputStream inputStream = IbatisDbUtil.class.getClassLoader().getResourceAsStream(filePath);

   

    sqlMapper = SqlMapClientBuilder.buildSqlMapClient(inputStream);

 

    int id = 2;

    User user = (User)sqlMapper.queryForObject("user.queryUserById", id);

   

    System.out.println("id:\t\t" + user.getId());

    System.out.println("username:\t" + user.getUsername());

    System.out.println("password:\t" + user.getPassword());

 

  • 大小: 121.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics