<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Database圈子</title>
    <description>学习和研究Database,自由的思想讨论中共同进步</description>
    <link>http://database.group.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>五种提高 SQL 性能的方法（转）</title>
        <author>mtou</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mtou.javaeye.com">mtou</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/207730" style="color:red;">http://database.group.javaeye.com/group/blog/207730</a>&nbsp;
          发表时间: 2008年06月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>有时，
为了让应用程序运行得更快，所做的全部工作就是在这里或那里做一些很小调整。啊，但关键在于确定如何进行调整！迟早您会遇到这种情况：应用程序中的
SQL
查询不能按照您想要的方式进行响应。它要么不返回数据，要么耗费的时间长得出奇。如果它降低了报告或您的企业应用程序的速度，用户必须等待的时间过长，他
们就会很不满意。就像您的父母不想听您解释为什么在深更半夜才回来一样，用户也不会听你解释为什么查询耗费这么长时间。（&ldquo;对不起，妈妈，我使用了太多的
LEFT JOIN。&rdquo;）用户希望应用程序响应迅速，他们的报告能够在瞬间之内返回分析数据。就我自己而言，如果在 Web
上冲浪时某个页面要耗费十多秒才能加载（好吧，五秒更实际一些），我也会很不耐烦。</p>
<p>为了解决这些问题，重要的是找到问题的根源。那么，从哪里开始呢？根本原因通常在于数据库设计和访问它的查询。在本月的专栏中，我将讲述四项技术，
这些技术可用于提高基于 SQL Server? 的应用程序的性能或改善其可伸缩性。我将仔细说明 LEFT JOIN、CROSS JOIN
的使用以及 IDENTITY
值的检索。请记住，根本没有神奇的解决方案。调整您的数据库及其查询需要占用时间、进行分析，还需要大量的测试。这些技术都已被证明行之有效，但对您的应
用程序而言，可能其中一些技术比另一些技术更适用。</p>
<br />
<p>&nbsp;</p>
<h2>从 INSERT 返回 IDENTITY </h2>
<p>我决定从遇到许多问题的内容入手：如何在执行 SQL INSERT 后检索 IDENTITY
值。通常，问题不在于如何编写检索值的查询，而在于在哪里以及何时进行检索。在 SQL Server
中，下面的语句可用于检索由最新在活动数据库连接上运行的 SQL 语句所创建的 IDENTITY 值：</p>
<pre class="codeSample">SELECT @@IDENTITY</pre>
<p>这个 SQL 语句并不复杂，但需要记住的一点是：如果这个最新的 SQL 语句不是 INSERT，或者您针对非 INSERT SQL
的其他连接运行了此 SQL，则不会获得期望的值。您必须运行下列代码才能检索紧跟在 INSERT SQL 之后且位于同一连接上的
IDENTITY，如下所示：</p>
<pre class="codeSample">INSERT INTO Products (ProductName) VALUES ('Chalk')
SELECT @@IDENTITY</pre>
<p>在一个连接上针对 Northwind 数据库运行这些查询将返回一个名称为 Chalk 的新产品的 IDENTITY 值。所以，在使用 ADO 的 Visual Basic? 应用程序中，可以运行以下语句：</p>
<pre class="codeSample">Set oRs = oCn.Execute(&quot;SET NOCOUNT ON;INSERT INTO Products _
(ProductName) VALUES ('Chalk');SELECT @@IDENTITY&quot;)
lProductID = oRs(0)</pre>
<p>此代码告诉 SQL Server 不要返回查询的行计数，然后执行 INSERT 语句，并返回刚刚为这个新行创建的 IDENTITY
值。SET NOCOUNT ON 语句表示返回的记录集有一行和一列，其中包含了这个新的 IDENTITY
值。如果没有此语句，则会首先返回一个空的记录集（因为 INSERT 语句不返回任何数据），然后会返回第二个记录集，第二个记录集中包含
IDENTITY 值。这可能有些令人困惑，尤其是因为您从来就没有希望过 INSERT 会返回记录集。之所以会发生此情况，是因为 SQL
Server 看到了这个行计数（即一行受到影响）并将其解释为表示一个记录集。因此，真正的数据被推回到了第二个记录集。当然您可以使用 ADO
中的 NextRecordset 方法获取此第二个记录集，但如果总能够首先返回该记录集且只返回该记录集，则会更方便，也更有效率。</p>
<p>此方法虽然有效，但需要在 SQL 语句中额外添加一些代码。获得相同结果的另一方法是在 INSERT 之前使用 SET NOCOUNT
ON 语句，并将 SELECT @@IDENTITY 语句放在表中的 FOR INSERT
触发器中，如下面的代码片段所示。这样，任何进入该表的 INSERT 语句都将自动返回 IDENTITY 值。</p>
<pre class="codeSample">CREATE TRIGGER trProducts_Insert ON Products FOR INSERT AS 
    SELECT @@IDENTITY 
GO
</pre>
<p>触发器只在 Products 表上发生 INSERT 时启动，所以它总是会在成功 INSERT 之后返回一个 IDENTITY。使用此技术，您可以始终以相同的方式在应用程序中检索 IDENTITY 值。</p>
<p>&nbsp;</p>
<h2>内嵌视图与临时表 </h2>
<p>某些时候，查询需要将数据与其他一些可能只能通过执行 GROUP BY
然后执行标准查询才能收集的数据进行联接。例如，如果要查询最新五个定单的有关信息，您首先需要知道是哪些定单。这可以使用返回定单 ID 的 SQL
查询来检索。此数据就会存储在临时表（这是一个常用技术）中，然后与 Products 表进行联接，以返回这些定单售出的产品数量：</p>
<pre class="codeSample">CREATE TABLE #Temp1 (OrderID INT NOT NULL, _
                     OrderDate DATETIME NOT NULL)
INSERT INTO #Temp1 (OrderID, OrderDate)
SELECT     TOP 5 o.OrderID, o.OrderDate
FROM Orders o ORDER BY o.OrderDate DESC
SELECT     p.ProductName, SUM(od.Quantity) AS ProductQuantity
FROM     #Temp1 t 
    INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
    INNER JOIN Products p ON od.ProductID = p.ProductID 
GROUP BY p.ProductName
ORDER BY p.ProductName
DROP TABLE #Temp1</pre>
<p>这些 SQL 语句会创建一个临时表，将数据插入该表中，将其他数据与该表进行联接，然后除去该临时表。这会导致此查询进行大量 I/O
操作，因此，可以重新编写查询，使用内嵌视图取代临时表。内嵌视图只是一个可以联接到 FROM 子句中的查询。所以，您不用在 tempdb
中的临时表上耗费大量 I/O 和磁盘访问，而可以使用内嵌视图得到同样的结果：</p>
<pre class="codeSample">SELECT p.ProductName, 
    SUM(od.Quantity) AS ProductQuantity
FROM     (
    SELECT TOP 5 o.OrderID, o.OrderDate
    FROM     Orders o 
    ORDER BY o.OrderDate DESC
    ) t 
    INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
    INNER JOIN Products p ON od.ProductID = p.ProductID 
GROUP BY
    p.ProductName
ORDER BY
    p.ProductName</pre>
<p>此查询不仅比前面的查询效率更高，而且长度更短。临时表会消耗大量资源。如果只需要将数据联接到其他查询，则可以试试使用内嵌视图，以节省资源。</p>
<p>&nbsp;</p>
<h2>避免 LEFT JOIN 和 NULL </h2>
<p>当然，有很多时候您需要执行 LEFT JOIN 和使用 NULL 值。但是，它们并不适用于所有情况。改变 SQL
查询的构建方式可能会产生将一个花几分钟运行的报告缩短到只花几秒钟这样的天壤之别的效果。有时，必须在查询中调整数据的形态，使之适应应用程序所要求的
显示方式。虽然 TABLE 数据类型会减少大量占用资源的情况，但在查询中还有许多区域可以进行优化。SQL 的一个有价值的常用功能是 LEFT
JOIN。它可以用于检索第一个表中的所有行、第二个表中所有匹配的行、以及第二个表中与第一个表不匹配的所有行。例如，如果希望返回每个客户及其定单，
使用 LEFT JOIN 则可以显示有定单和没有定单的客户。</p>
<p>此工具可能会被过度使用。LEFT JOIN 消耗的资源非常之多，因为它们包含与
NULL（不存在）数据匹配的数据。在某些情况下，这是不可避免的，但是代价可能非常高。LEFT JOIN 比 INNER JOIN
消耗资源更多，所以如果您可以重新编写查询以使得该查询不使用任何 LEFT JOIN，则会得到非常可观的回报（请参阅图 1 中的图）。</p>
<p>图 1 查询 </p>
<p>加快使用 LEFT JOIN 的查询速度的一项技术涉及创建一个 TABLE 数据类型，插入第一个表（LEFT JOIN
左侧的表）中的所有行，然后使用第二个表中的值更新 TABLE 数据类型。此技术是一个两步的过程，但与标准的 LEFT JOIN
相比，可以节省大量时间。一个很好的规则是尝试各种不同的技术并记录每种技术所需的时间，直到获得用于您的应用程序的执行性能最佳的查询。</p>
<p>测试查询的速度时，有必要多次运行此查询，然后取一个平均值。因为查询（或存储过程）可能会存储在 SQL Server
内存中的过程缓存中，因此第一次尝试耗费的时间好像稍长一些，而所有后续尝试耗费的时间都较短。另外，运行您的查询时，可能正在针对相同的表运行其他查
询。当其他查询锁定和解锁这些表时，可能会导致您的查询要排队等待。例如，如果您进行查询时某人正在更新此表中的数据，则在更新提交时您的查询可能需要耗
费更长时间来执行。</p>
<p>避免使用 LEFT JOIN 时速度降低的最简单方法是尽可能多地围绕它们设计数据库。例如，假设某一产品可能具有类别也可能没有类别。如果
Products 表存储了其类别的 ID，而没有用于某个特定产品的类别，则您可以在字段中存储 NULL 值。然后您必须执行 LEFT JOIN
来获取所有产品及其类别。您可以创建一个值为&ldquo;No Category&rdquo;的类别，从而指定外键关系不允许 NULL
值。通过执行上述操作，现在您就可以使用 INNER JOIN
检索所有产品及其类别了。虽然这看起来好像是一个带有多余数据的变通方法，但可能是一个很有价值的技术，因为它可以消除 SQL
批处理语句中消耗资源较多的 LEFT
JOIN。在数据库中全部使用此概念可以为您节省大量的处理时间。请记住，对于您的用户而言，即使几秒钟的时间也非常重要，因为当您有许多用户正在访问同
一个联机数据库应用程序时，这几秒钟实际上的意义会非常重大。 </p>
<p>&nbsp;</p>
<h2>灵活使用笛卡尔乘积 </h2>
<p>对于此技巧，我将进行非常详细的介绍，并提倡在某些情况下使用笛卡尔乘积。出于某些原因，笛卡尔乘积 (CROSS JOIN)
遭到了很多谴责，开发人员通常会被警告根本就不要使用它们。在许多情况下，它们消耗的资源太多，从而无法高效使用。但是像 SQL
中的任何工具一样，如果正确使用，它们也会很有价值。例如，如果您想运行一个返回每月数据（即使某一特定月份客户没有定单也要返回）的查询，您就可以很方
便地使用笛卡尔乘积。<a href="http://www.microsoft.com/china/MSDN/library/data/sqlserver/art/figures3.html" target="_blank"> 图 2 </a>
中的 SQL 就执行了上述操作。</p>
<p>虽然这看起来好像没什么神奇的，但是请考虑一下，如果您从客户到定单（这些定单按月份进行分组并对销售额进行小计）进行了标准的 INNER
JOIN，则只会获得客户有定单的月份。因此，对于客户未订购任何产品的月份，您不会获得 0
值。如果您想为每个客户都绘制一个图，以显示每个月和该月销售额，则可能希望此图包括月销售额为 0 的月份，以便直观标识出这些月份。如果使用 <a href="http://www.microsoft.com/china/MSDN/library/data/sqlserver/art/figures3.html" target="_blank">图 2 </a>
中的 SQL，数据则会跳过销售额为 0 美元的月份，因为在定单表中对于零销售额不会包含任何行（假设您只存储发生的事件）。</p>
<p><a href="http://www.microsoft.com/china/MSDN/library/data/sqlserver/art/figures3.html" target="_blank">图 3 </a>
中
的代码虽然较长，但是可以达到获取所有销售数据（甚至包括没有销售额的月份）的目标。首先，它会提取去年所有月份的列表，然后将它们放入第一个
TABLE 数据类型表 (@tblMonths) 中。下一步，此代码会获取在该时间段内有销售额的所有客户公司的名称列表，然后将它们放入另一个
TABLE 数据类型表 (@tblCus-tomers) 中。这两个表存储了创建结果集所必需的所有基本数据，但实际销售数量除外。
第一个表中列出了所有月份（12 行），第二个表中列出了这个时间段内有销售额的所有客户（对于我是 81 个）。并非每个客户在过去 12
个月中的每个月都购买了产品，所以，执行 INNER JOIN 或 LEFT JOIN
不会返回每个月的每个客户。这些操作只会返回购买产品的客户和月份。</p>
<p>笛卡尔乘积则可以返回所有月份的所有客户。笛卡尔乘积基本上是将第一个表与第二个表相乘，生成一个行集合，其中包含第一个表中的行数与第二个表中的
行数相乘的结果。因此，笛卡尔乘积会向表 @tblFinal 返回 972 行。最后的步骤是使用此日期范围内每个客户的月销售额总计更新
@tblFinal 表，以及选择最终的行集。</p>
<p>如果由于笛卡尔乘积占用的资源可能会很多，而不需要真正的笛卡尔乘积，则可以谨慎地使用 CROSS JOIN。例如，如果对产品和类别执行了
CROSS JOIN，然后使用 WHERE 子句、DISTINCT 或 GROUP BY 来筛选出大多数行，那么使用 INNER JOIN
会获得同样的结果，而且效率高得多。如果需要为所有的可能性都返回数据（例如在您希望使用每月销售日期填充一个图表时），则笛卡尔乘积可能会非常有帮助。
但是，您不应该将它们用于其他用途，因为在大多数方案中 INNER JOIN 的效率要高得多。</p>
<p>&nbsp;</p>
<h2>拾遗补零 </h2>
<p>这里介绍其他一些可帮助提高 SQL
查询效率的常用技术。假设您将按区域对所有销售人员进行分组并将他们的销售额进行小计，但是您只想要那些数据库中标记为处于活动状态的销售人员。您可以按
区域对销售人员分组，并使用 HAVING 子句消除那些未处于活动状态的销售人员，也可以在 WHERE 子句中执行此操作。在 WHERE
子句中执行此操作会减少需要分组的行数，所以比在 HAVING 子句中执行此操作效率更高。HAVING
子句中基于行的条件的筛选会强制查询对那些在 WHERE 子句中会被去除的数据进行分组。</p>
<p>另一个提高效率的技巧是使用 DISTINCT 关键字查找数据行的单独报表，来代替使用 GROUP BY 子句。在这种情况下，使用
DISTINCT 关键字的 SQL 效率更高。请在需要计算聚合函数（SUM、COUNT、MAX 等）的情况下再使用 GROUP
BY。另外，如果您的查询总是自己返回一个唯一的行，则不要使用 DISTINCT 关键字。在这种情况下，DISTINCT 关键字只会增加系统开销。</p>
<p>您已经看到了，有大量技术都可用于优化查询和实现特定的业务规则，技巧就是进行一些尝试，然后比较它们的性能。最重要的是要测试、测试、再测试。在
此专栏的将来各期内容中，我将继续深入讲述 SQL Server 概念，包括数据库设计、好的索引实践以及 SQL Server 安全范例。</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/207730#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 25 Jun 2008 09:10:39 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/207730</link>
        <guid>http://database.group.javaeye.com/group/blog/207730</guid>
      </item>
      <item>
        <title>表的自连接同子查询有区别？</title>
        <author>beyondsanli</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://beyondsanli.javaeye.com">beyondsanli</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/207051" style="color:red;">http://database.group.javaeye.com/group/blog/207051</a>&nbsp;
          发表时间: 2008年06月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>运用自连接同子查询之间有什么区别？请大家给点意见</p>
<p>&nbsp;</p>
<p>SELECT R1.readerid,R1.readername,R1.unit,R1.bookcount<br />FROM ReaderInfo AS R1,ReaderInfo AS R2<br />WHERE R2.readerid=9704<br />AND R1.bookcount&gt;R2.bookcount&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--连接关系<br />ORDER BY R1.bookcount</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>SELECT readerid,readername,unit,bookcount<br />FROM ReaderInfo<br />WHERE bookcount&gt;(SELECT bookcount&nbsp;&nbsp;&nbsp;--使用子查询<br />FROM ReaderInfo <br />WHERE readerid=9704)<br />ORDER BY bookcount</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/207051#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 23 Jun 2008 11:54:48 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/207051</link>
        <guid>http://database.group.javaeye.com/group/blog/207051</guid>
      </item>
      <item>
        <title>MYSQL常用命令</title>
        <author>mingliangfeng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mingliangfeng.javaeye.com">mingliangfeng</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/206828" style="color:red;">http://database.group.javaeye.com/group/blog/206828</a>&nbsp;
          发表时间: 2008年06月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>MYSQL常用命令:</p>
<p>&nbsp;</p>
<p>服务器：</p>
<p>1. 启动mysql服务</p>
<p>&nbsp;&nbsp;&nbsp; &gt; cd mysql-5.0/bin <br />
&nbsp;&nbsp;&nbsp;&nbsp;&gt; <span style="color: #800000;">mysqld </span>
<span style="color: #000000;">--console --verbose </span>
</p>
<p>&nbsp;</p>
<p>2. 停止mysql服务</p>
<p>&nbsp;&nbsp;&nbsp; &gt; <span style="color: #800000;">mysqladmin</span>
 -u root shutdown</p>
<p>&nbsp;</p>
<p>客户端：</p>
<p>1. 客户端连接至mysql</p>
<p>&nbsp;&nbsp;&nbsp; &gt; <span style="color: #800000;">mysql</span>
 -h localhost -u root -p</p>
<p>&nbsp;</p>
<p>2. 显示当前所有数据库</p>
<p>&nbsp;&nbsp;&nbsp; &gt; <span style="color: #000000;">show databases;</span>
</p>
<p>&nbsp;</p>
<p>3. 创建数据库</p>
<p>&nbsp;&nbsp;&nbsp; &gt; create database test;</p>
<p>&nbsp;</p>
<p>4. 删除数据库</p>
<p>&nbsp;&nbsp;&nbsp; &gt; drop database test;</p>
<p>&nbsp;</p>
<p>5. 切换数据库</p>
<p>&nbsp;&nbsp;&nbsp; &gt; use test;</p>
<p>&nbsp;</p>
<p>6. 显示当前数据库中的所有表</p>
<p>&nbsp;&nbsp;&nbsp; &gt; show tables;</p>
<p>&nbsp;</p>
<p>7. 显示某张表的结构信息</p>
<p>&nbsp;&nbsp;&nbsp; &gt; desc t_some_table;&nbsp;</p>
<p>&nbsp;</p>
<p>8. 客户端推出</p>
<p>&nbsp;&nbsp;&nbsp; &gt; exit;&nbsp;</p>
<p>&nbsp;</p>
<p>用户：</p>
<p>1. 查询所有用户</p>
<p>&nbsp;&nbsp;&nbsp; &gt; select host, user from mysql.user;</p>
<p>&nbsp;</p>
<p>2. 创建用户（用户名：test；密码：test）</p>
<p>&nbsp;&nbsp;&nbsp; &gt; create user test identified by 'test';&nbsp;</p>
<p>&nbsp;</p>
<p>3. 赋予用户权限</p>
<p>&nbsp;&nbsp;&nbsp; &gt; grant all privileges on test.* to <a href="mailto:'test'@'%'">'test'@'%'</a>
;&nbsp;</p>
<p>&nbsp;</p>
<p>4. 删除用户</p>
<p>&nbsp;&nbsp;&nbsp; &gt; drop user test;&nbsp;</p>
<p>&nbsp;</p>
<p>备份与恢复：</p>
<p>1. 备份数据库（后面不能加分号&quot;;&quot;，否则会报error 1049）</p>
<p>&nbsp;&nbsp;&nbsp; &gt; <span style="color: #800000;">mysqldump</span>
 -u root -p test&gt;d:/test_backup.sql </p>
<p>&nbsp;</p>
<p>2. 恢复数据库</p>
<p>&nbsp;&nbsp;&nbsp; &gt; <span style="color: #800000;">mysql</span>
 -uroot -p test&lt; d:/test_backup.sql;&nbsp;&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/206828#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 22 Jun 2008 13:15:37 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/206828</link>
        <guid>http://database.group.javaeye.com/group/blog/206828</guid>
      </item>
      <item>
        <title>网络管理系统OpenNMS的安装配置</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/203477" style="color:red;">http://database.group.javaeye.com/group/blog/203477</a>&nbsp;
          发表时间: 2008年06月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近公司本人为做了一个内部项目，是有关网络监控方面，想借鉴Open Source的资源，上网搜索了一下，基本可以分两类：<br />1） 主要对流量及主机在线状态监控软件,如最初的MRTG,PRGT,CACTI,Hobbit,<br />2） 能对服务器的关键服务及进程进行监控的软件,如Big Brother,Nagios,OpenNMS<br />    综合考虑后，决定选用OpenNMS，从公司的角度方面考虑主要它是用java开发，可以和公司的有关软件进行集成，从监控的应用方面，OpenNMS的界面虽然不是很美观，但其在监控服务方面有出色的表现，功能也在众多相关软件里是表现非常出色的。选定以后，首先当然就是安装配置，然后进行重新的分析和设计，参考网络的一些经验，现在就其安装和配置做相关总结：<br />	<br />一、安装 Sun J2SE Java SDK <br />之前已经安装好，故就不安装，就设置相关的环境变量：<br />JAVA_HOME=/usr/jdk/jdk1.5.0_01<br />export PATH=$PATH:$JAVA_HOME/bin <br /><br />二、安装postgresql<br />下载postgressql然后运行一下命令：<br />./configure --prefix=/usr/local/pgsql<br />make <br />make install<br />安装成功后，开始配置postgresql：<br />PostgreSQL 不能以 root 用户运行，必须建立对应的用户和组。<br /><br /># useradd postgre （自动建立 postgre 组）<br /><br /># vi ~postgre/.bash_profile<br />添加：<br />PGLIB=/usr/local/pgsql/lib<br />PGDATA=$HOME/data<br />PATH=$PATH:/usr/local/pgsql/bin<br />MANPATH=$MANPATH:/usr/local/pgsql/man<br />export PGLIB PGDATA PATH MANPATH<br /><br />修改设定参数postgresql.conf及pg_hba.conf <br />vi /export/home/postgres/data/postgresql.conf <br />修改下列参数 <br />tcpip_socket = true <br />max_connections = 256 <br />shared_buffers = 1024 <br />vi /var/lib/pgsql/data/pg_hba.conf <br />加入下面二行 <br />local all all trust <br />host all all 127.0.0.1 255.255.255.255 trust <br />原文提到如果您的系统支持IPv6则可加入(不支持建议不要增加) <br />host all all ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff trust <br /><br />以 postgres 用户登录，<br /># su - postgre<br />建立数据库目录：<br />$ mkdir data<br /><br />启动数据库引擎：<br />$ initdb    <br />最后会显示如下:<br />/usr/local/pgsql/bin/postgres -D /export/home/postgres/data<br /><br />/usr/local/pgsql/bin/pg_ctl -D /export/home/postgres/data start<br />建立数据库<br />$createdb mydb <br />$psql mydb<br />建立表<br />CREATE TABLE mytable (<br />id varchar(20),<br />name varchar(30));<br />插入数据<br />INSERT INTO mytable values('Author', 'MartriWang');<br /><br />三、安装 Tomcat4 <br />现阶段，OpenNMS只支持tomcat4（安全认证问题），不能支持tomcat5，故只能采用tomcat4<br />到 http://archive.apache.org/dist/jakarta/tomcat-4/archive/v4.1.24/rpms/ 处下载 <br />tomcat4-4.1.24-full.2jpp.noarch.rpm <br />tomcat4-webapps-4.1.24-full.2jpp.noarch.rpm <br />vim /etc/tomcat4/tomcat4.conf <br />修改下面二行 <br />JAVA_HOME="/usr/java/j2sdk1.4.2_08/" <br />TOMCAT_USER="root" <br /><br />四、安装 rrdtool <br />下载源代码，然后运行一下命令：<br />./configure --prefix=/usr/local/rrdtool<br />make <br />make install<br /><br />五、安装 OpenNMS <br />1.利用svn下载OpenNMS:<br />svn co https://opennms.svn.sourceforge.net/svnroot/opennms/opennms/branches/OPENNMS_1_2_BRANCH opennms<br /><br />2.配置OpenNMS环境变量<br />export OPENNMS_HOME=/opt/Opennms <br /><br />3.编译OpenNMS：<br />sh build.sh compile<br />sh build.sh install<br /><br />4.配置OpenNMS<br />$OPENNMS_HOME/bin/runjava -s <br />/usr/local/opennms/bin/install -disU <br />$OPENNMS_HOME/bin/install -y -w $CATALINA_HOME/webapps -W $CATALINA_HOME/server/lib <br /> <br />修改$OPENNMS_HOME/etc/discovery-configuration.xml <br />&lt;discovery-configuration threads="1" packets-per-second="1" <br />initial-sleep-time="300000" restart-sleep-time="86400000" <br />retries="3" timeout="800"> <br /> <br />&lt;include-range retries="2" timeout="3000"> <br />&lt;begin>192.168.31.1&lt;/begin> <br />&lt;end>192.168.31.254&lt;/end> <br />&lt;/include-range> <br /> <br />&lt;include-url>file:/opt/OpenNMS/etc/include&lt;/include-url> <br /> <br />&lt;/discovery-configuration> <br /> <br />您可以侦测多个网段只要加入以下的参数在设定档中即可。 <br />&lt;include-range retries="2" timeout="3000"> <br />&lt;begin>192.168.X.1&lt;/begin> <br />&lt;end>192.168.X.254&lt;/end> <br />&lt;/include-range> <br /> <br />启动执行： /usr/local/opennms/bin/opennms start<br />开启浏览器，地址<br />http://192.168.0.6:8080/opennms <br />预设使用者为：admin密码：admin<br /><br />    至此OpenNMS安装成功，最后可以利用elipse进行开发，通过FTP进行同步。以后想配置一个ANT文件进行自动编译管理。至于改造设计，以后将近一步开发探索。
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/203477#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 15 Jun 2008 15:22:56 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/203477</link>
        <guid>http://database.group.javaeye.com/group/blog/203477</guid>
      </item>
      <item>
        <title>Generate a table with number sequence in sql</title>
        <author>ktnd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ktnd.javaeye.com">ktnd</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/202028" style="color:red;">http://database.group.javaeye.com/group/blog/202028</a>&nbsp;
          发表时间: 2008年06月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>Generate a table with number sequence in sql<br /></strong>=========</p>
<p>&nbsp;</p>
<p><em>1 Pre-defined collection types in Oracle</em><br />---------</p>
<pre name="code" class="sql">select to_number(column_value) as num from
   table(sys.dbms_debug_vc2coll(1,2,3,4,5,6,7,8,9));

select to_number(column_value) as num from
   table(sys.KU$_VCNT(1,2,3,4,5,6,7,8,9));

select column_value as num from
   table(sys.KU$_OBJNUMSET(1,2,3,4,5,6,7,8,9));

</pre>
<p>&nbsp;</p>
<p><em>2 Join: pure sql </em><br />---------</p>
<pre name="code" class="sql">select a.i+b.i+c.i+d.i+1 as num
from
   (select 0 i from dual union all select 1 from dual) a,
   (select 0 i from dual union all select 2 from dual) b,
   (select 0 i from dual union all select 4 from dual) c,
   (select 0 i from dual union all select 8 from dual) d
order by num;
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><em>3 How to make number dynamic?<br /></em>---------<br />?</p>
<p>&nbsp;</p>
<p><em>3.1 Define function with collection type as return value</em></p>
<p>-----------------</p>
<p>It's not only sql.</p>
<p>And myaybe it's not correct or appropriate for dynamic.</p>
<p>However, I like it.</p>
<p>&nbsp;</p>
<p>First, define a type:</p>
<p>&nbsp;</p>
<pre name="code" class="sql">create or replace type T_NUM_TAB as TABLE OF NUMBER;</pre>
<p>&nbsp;</p>
<p>Then, define the function:</p>
<p>&nbsp;</p>
<pre name="code" class="sql">CREATE OR REPLACE FUNCTION NUM_COLLECT (
       NUM_START IN NUMBER, 
       NUM_END IN NUMBER,
       NUM_STEP IN NUMBER DEFAULT 1)
  RETURN T_NUM_TAB
AS
  l_tab   T_NUM_TAB := T_NUM_TAB();
  l_idx   NUMBER;
BEGIN
  l_idx := NUM_START;
  WHILE l_idx &lt;= NUM_END LOOP
    l_tab.extend;
    l_tab(l_tab.last) := l_idx;
    l_idx := l_idx + NUM_STEP;
  END LOOP;

  RETURN l_tab;
END;</pre>
<p>&nbsp;</p>
<p>Now, use it as follows:</p>
<p>&nbsp;</p>
<pre name="code" class="sql">SQL&gt; select * from table(num_collect(2,10,2));

COLUMN_VALUE
------------
           2
           4
           6
           8
          10</pre>
<pre name="code" class="sql"> </pre>
<p>&nbsp;<span style="text-decoration: underline;"><em><strong>Is it a good idea to take sql string as parameter for the function?</strong></em></span></p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/202028#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 11 Jun 2008 09:57:00 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/202028</link>
        <guid>http://database.group.javaeye.com/group/blog/202028</guid>
      </item>
      <item>
        <title>也说QQ的QZone</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/199910" style="color:red;">http://database.group.javaeye.com/group/blog/199910</a>&nbsp;
          发表时间: 2008年06月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          QQ的东西,一直以来不是很喜欢,感觉就是一个圈套圈,让人不断在这个圈钻进另一个圈,总之就是圈钱,但它经过这么多年的积累,已经聚集了太多的人气,让人很难割舍,还有说到最大的一个圈就是圈人;就好象QQ游戏,凭着强大的人气,很快就打败了联众和中国游戏中心.真是让人敢怒不敢言.说到QQ的技术,那就是跟风,从来没有走在技术时代的潮流,见网络游戏比较火热,就开通了QQ游戏和QQ幻想,结果是联众和中国游戏中心成了其踏脚石,现在见Blog很火暴,全球喊着进入web2.0时代,也想着称热捞一把,边开通了QZone,估计在不久的将来博客中国等没有门户支撑和充足的庞大资金支持的即将成为下一个联众和中国游戏中心,呵呵,这些都不关我的事情,我就是见那里有人气,我就跟到那里,鹿死谁手,我是乐见其成. <br />   Google一直以来是我的至爱，它的简单和个性让人耳目一新,支持个性主页,可以开辟Google <br />Groups,并可以Email通知成员,别具一格;技术是引领时代潮流,当然人们总有枪打出头鸟的习惯,故最近它是官司不断,是非缠身,先有李开复事件,后有私隐官司.现在中国又搞不过中国特色的Baidu,但它依然是王者之风. <br />   Baidu去年一度成就一代神话,尽管现在股票价格大降,那也是成就了一代中国搜索的领跑者,喜欢Baidu是因为的中文搜索确实比Google确实好,正所谓浪里淘沙，始得金。 <br />总之，胜者为王，不管是QQ的霸气，Google的王者之风，还是Baidu的少年得志，谁是强者，谁就是胜者。
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/199910#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jun 2008 22:49:15 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/199910</link>
        <guid>http://database.group.javaeye.com/group/blog/199910</guid>
      </item>
      <item>
        <title>让心灵去旅行</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/199905" style="color:red;">http://database.group.javaeye.com/group/blog/199905</a>&nbsp;
          发表时间: 2008年06月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          旅行，迁徙，流动，这是社会进步和文明发展的必然趋势；股票起伏，潮水汹涌，见证了经济和历史的变迁；青山遮不住，毕竟东流去； <br />  1.历史的变迁； <br />  回想自己的历程，原来也是充满了迁徙，从清远农村迁徙到韶关，见证了改革开发的变迁；从高中毕业到大学学习，见证了中国教育的改革；从大学毕业到硕士学习，见证了研究生教育的回归；从学习到工作，见证了中国股票一路高歌而有调整起伏的发展；原来这些都是社会的进步和人生的起伏； <br />  2.城市的变迁； <br />  清远的朴实和纯朴，令人清新一亮；韶关的沉重和孕育，让人稳重和踏实；长沙的厚重和底蕴；让人开阔和进取；广州的开放和现实，让人成熟和成长； <br />  3.心灵的变迁； <br />  从笛卡尔的“我思故我在”，到“莫斯科不相信眼泪”，从少年的轻狂到现在的沉重和现实；让人陶醉又让人迷惘，没有人能够改变，更没有人能够摆脱，对应这开幅的”青山依旧在，几度夕阳红“
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/199905#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jun 2008 22:34:00 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/199905</link>
        <guid>http://database.group.javaeye.com/group/blog/199905</guid>
      </item>
      <item>
        <title>论文终于录用拉～～～～</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/199902" style="color:red;">http://database.group.javaeye.com/group/blog/199902</a>&nbsp;
          发表时间: 2008年06月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          经过一段时间的忙碌，蚂蚁搬家似的写作，历尽一年多的酝酿，终于在十一之前写完了在学校期间的第一篇论文《基于动态负载均衡的网络监控系统》，在这期间，写的论文改了又改，修了又修，但总觉得欠些什么，在时间紧迫的情况下，只好把一些想法束之高阁，让论文在前后的承接上更为自然，毕竟有些工作还没有到位，无法继续下去^_^，只好在理论上写出的新意出来，在负载均衡的机制上，由于在公司时候经常用到ORACLE，它在判断空闲块使用机制有一定出色的表现，故借鉴了其中一些判断机制，可以算的上是一种借鉴吧，也算是本论文的一大特色^_^<br />     历经两个多月的审稿，终于被《计算机工程》录用了，现正在退修状态上，继续努力，最近在SDB项目做开发，突然想到了一个以前想写的题目，但一直不知道如何下手，现有了一定的酝酿，暂命名为《大型企业呼叫中心技术研究》，结合上篇论文展开铺垫，希望进展顺利，毕业前将其完成^_^
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/199902#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jun 2008 22:19:37 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/199902</link>
        <guid>http://database.group.javaeye.com/group/blog/199902</guid>
      </item>
      <item>
        <title>Another Way to Solve Last Problem.</title>
        <author>ktnd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ktnd.javaeye.com">ktnd</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/196318" style="color:red;">http://database.group.javaeye.com/group/blog/196318</a>&nbsp;
          发表时间: 2008年05月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><a href="http://www.oracle.com/technology/oramag/code/tips2004/050304.html">http://www.oracle.com/technology/oramag/code/tips2004/050304.html</a></p><br />
<p>1. create a function to concatenate strings.</p>
<pre name="code" class="sql">CREATE OR REPLACE FUNCTION rowtocol( 
       p_slct IN VARCHAR2,
       p_dlmtr IN VARCHAR2 DEFAULT ',' 
) RETURN VARCHAR2 

  AUTHID CURRENT_USER AS

TYPE c_refcur IS REF CURSOR;

     lc_str VARCHAR2(4000);
     lc_colval VARCHAR2(4000);
     c_dummy c_refcur;

     BEGIN

     OPEN c_dummy FOR p_slct;
     LOOP
       FETCH c_dummy INTO lc_colval;
       EXIT WHEN c_dummy%NOTFOUND;
       lc_str := lc_str || p_dlmtr || lc_colval;
     END LOOP;

     CLOSE c_dummy;
     RETURN SUBSTR(lc_str,2);

     /* 
     EXCEPTION 
     WHEN OTHERS THEN
       lc_str := SQLERRM;
  
       IF c_dummy%ISOPEN THEN
          CLOSE c_dummy;
       END IF;
  
       RETURN lc_str;
     */
    END;

</pre>
<br />
<p>&nbsp;Usage : </p>
<pre name="code" class="sql">select distinct t.author, 
          rowtocol('select book from table_name where author=''' || t.author|| '''')
from table_name t
;

</pre><br />
<p>&nbsp;or</p><br />
<pre name="code" class="sql">select t.author, rowtocol('select book from table_name where author=''' || t.author|| '''', '#')
from table_name t
group by t.author
;</pre>
<p>&nbsp;</p><br />
<p>cons: less natural than user-defined aggregate function&nbsp;</p>
<p>pros: more flexible, aggregate function&nbsp;could take only one parameter</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/196318#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 24 May 2008 11:05:07 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/196318</link>
        <guid>http://database.group.javaeye.com/group/blog/196318</guid>
      </item>
      <item>
        <title>User-defined Aggregate Function in Oracle</title>
        <author>ktnd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ktnd.javaeye.com">ktnd</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/196265" style="color:red;">http://database.group.javaeye.com/group/blog/196265</a>&nbsp;
          发表时间: 2008年05月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">Author    Book                     Author    Books
--------+--------                     --------+--------
poly    |   A                          poly   |   A,B,C
poly    |   B                          amy    |   D,E
poly    |   C         ====&gt;
amy     |   D
amy     |   E</pre>
<p><span style="color: #ff0000;">&nbsp;</span></p>
<p><span style="color: #ff0000;">HOW?</span></p>
<pre name="code" class="sql">select author, to_list(book) as books
  from table_name
group by author;</pre>
<p><span style="color: #ff0000;">&nbsp;</span></p>
<p><span style="color: #ff0000;">SO SIMPLE?</span>&nbsp;</p>
<p>Yes, we just need to create a user-defined aggregate function as follows: </p>
<p>&nbsp;</p>
<p>1. create type object</p>
<pre name="code" class="sql">create or replace 
type tolist as object 
( 
  list varchar2(2000), 

  static function ODCIAggregateInitialize 
  ( actx in out tolist 
  ) return number, 

  member function ODCIAggregateIterate 
  ( self  in out tolist , 
    value in varchar2 
  ) return number, 

  member function ODCIAggregateTerminate 
  ( self in tolist , 
    returnValue  out varchar2, 
    flags in varchar2 
  ) return number, 

  member function ODCIAggregateMerge 
  (self in out tolist , 
   ctx2 in     tolist  
  ) return number 
) 
</pre>
<p>&nbsp;</p>
<p>&nbsp;2. create type body</p>
<pre name="code" class="sql">create or replace 
type body tolist is 

static function ODCIAggregateInitialize 
  ( actx in out tolist
  ) return number is 
  begin
    actx := tolist('');
    return ODCIConst.Success; 
  end; 


member function ODCIAggregateIterate 
  ( self  in out tolist, 
    value in varchar2
  ) return number is 
  begin 
        if self.list is null then self.list := value;
        else
           self.list := self.list || ',' || value; 
        end if;     
     return ODCIConst.Success; 
  end; 


 member function ODCIAggregateTerminate 
  ( self in tolist, 
    returnValue out varchar2, 
    flags in varchar2 
  ) return number is 
  begin 
   returnValue:= self.list; 
   return ODCIConst.Success; 
  end; 


  member function ODCIAggregateMerge 
  (self in out tolist, 
   ctx2 in     tolist 
  ) return number is 
  begin 
    if ctx2.list &lt;&gt; '' then 
       self.list := self.list || ',' || ctx2.list; 
    end if; 
    return ODCIConst.Success; 
  end; 

end; 
</pre>
<p>&nbsp;</p>
<p>3. create function </p>
<pre name="code" class="sql">CREATE OR REPLACE FUNCTION to_list
( x varchar2
) RETURN varchar2
PARALLEL_ENABLE
AGGREGATE USING tolist;
</pre>
<p>&nbsp;</p>
<p>done <img src="../../../images/smiles/icon_biggrin.gif" alt="" /></p>
<p>&nbsp;</p>
<p>---------------------------------------------</p>
<p><a href="http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96595/dci11agg.htm#1005029">http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96595/dci11agg.htm#1005029</a></p>
<p><a href="http://www.oracle.com/technology/oramag/oracle/06-jul/o46sql.html">http://www.oracle.com/technology/oramag/oracle/06-jul/o46sql.html</a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>==========================</p>
<p>Supplement</p>
<p>&nbsp;</p>
<p>"<a href="http://www.williamrobertson.net/documents/one-row.html" title="Displaying multiple records in one row" class="quote_div" target="_blank">Displaying multiple records in one row</a>"&nbsp;give a summary :</p>
<p>1. SELECT wmsys.wm_concat(dname) departments FROM dept;&nbsp;&nbsp;(10g)</p>
<p>2. A way to get around the restriction that user-defined aggregates may only have one argument,&nbsp;which allows you to specify an alternative separator character. </p>
<p>3. A really delicate way using just sql</p>
<pre name="code" class="sql">SELECT deptno
     , LTRIM(MAX(SYS_CONNECT_BY_PATH(ename,','))
       KEEP (DENSE_RANK LAST ORDER BY curr),',') AS concatenated
FROM   ( SELECT deptno
              , ename
              , ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) AS curr
              , ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS prev
         FROM   emp )
GROUP BY deptno
CONNECT BY prev = PRIOR curr AND deptno = PRIOR deptno
START WITH curr = 1;
</pre>
<p>4. Another approach involves harnessing the dark power of XML</p>
<pre name="code" class="sql">SELECT deptno
     , RTRIM
       ( xmlagg (xmlelement (c, ename || ',') order by ename).extract ('//text()')
       , ',' ) AS concatenated
FROM   emp
GROUP BY deptno;
</pre>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/196265#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 23 May 2008 22:07:13 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/196265</link>
        <guid>http://database.group.javaeye.com/group/blog/196265</guid>
      </item>
      <item>
        <title>EntityEntry, EntityKey, Cache..Hibernate源码研究碎得(13)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/195878" style="color:red;">http://database.group.javaeye.com/group/blog/195878</a>&nbsp;
          发表时间: 2008年05月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="padding-left: 30px;"><br />如上篇结束时所言,本篇中着重研究upgradeLock这个方法.</p>
<p>&nbsp;</p>
<p>进入方法后第一句就是这个requestedLockMode.greaterThan( entry.getLockMode())判断,若不成立,就马上退出此方法.</p>
<p>&nbsp;</p>
<p>有些疑问的是EntityEntry类型的entry里getLockMode有什么意义?再往下追就是EntityEntry里的lockMode是什么时候什么情景下赋值的?</p>
<p><br />.......................</p>
<p><br />刚才用Eclipse里实际debug了一番,现在明白了些.</p>
<p style="padding-left: 30px;"><br />&nbsp;1,那个LockMode所Lock的是缓存里的Entity,把数据从DB中取出并封装成Java中的对象后,先通过addEntity的方式以entityKey为Key,以实际的Entity为value放入到PersistenceContext中的Map类型的entitiesByKey里,(这也就是SessionLevel的Cache吧?),接下来通过下面的参数:</p>
<p style="padding-left: 30px;"><br />&nbsp;EntityEntry e = new EntityEntry(<br />&nbsp;&nbsp;&nbsp;&nbsp;status,<br />&nbsp;&nbsp;&nbsp;&nbsp;loadedState,<br />&nbsp;&nbsp;&nbsp;&nbsp;rowId,<br />&nbsp;&nbsp;&nbsp;&nbsp;id,<br />&nbsp;&nbsp;&nbsp;&nbsp;version,<br />&nbsp;&nbsp;&nbsp;&nbsp;lockMode,<br />&nbsp;&nbsp;&nbsp;&nbsp;existsInDatabase,<br />&nbsp;&nbsp;&nbsp;&nbsp;persister,<br />&nbsp;&nbsp;&nbsp;&nbsp;session.getEntityMode(),<br />&nbsp;&nbsp;&nbsp;&nbsp;disableVersionIncrement,<br />&nbsp;&nbsp;&nbsp;&nbsp;lazyPropertiesAreUnfetched<br />&nbsp;);</p>
<p style="padding-left: 30px;"><br />组建一个EntityEntry对象,再利用如下语句entityEntries.put(entity, e)把这个新建的EntityEntry作为value同时那个DB数据的包装产物entity作为Key也放入到PersistenceContext里类型为Map的entityEntries对象中.这样就完成了数据从DB到PersistenceContext里迁移,而且在PersistenceContext里已有了Hibernate意义上的LockMode.</p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;">&nbsp;2,这样当开一个Session并利用此Session来get某一个Java对象时,就会先从PersistenceContext里Map来取,若PersistenceContext里有,就取出.这时就要比较当前Session里取Java对象时的LockMode与PersistenceContext里对应EntityEntry里已有的LockMode,若前者大于后者就要来一次upgradeLock,毕竟当把这个Java对象从PersistenceContext里传给业务层后就要保证这个Java对象拥有一个高级别的排它锁,以防止别的Session偷偷地给改掉.这就是方法upgradeLock产生的背景,也正因为此upgradeLock方法也只出现在loadFromSessionCache里. --&gt;不错,把upgradeLock这个方法放在PersistenceContext里来理解一下就通了.</p>
<p>有了上面的分析,对upgradeLock方法的理解就完成一半了.再往下走,就是看这个目标怎么实现了.</p>
<p>就我现在的理解来看,在实现上这个upgradeLock有三个层面的操作:</p>
<p><br />&nbsp;1,二级缓存层面:通过生成一个包含当前Session及相关重要属性的CacheKey来锁定,也就是注明在二级缓存中想的那个Java对象已有CacheKey占有了,其它的"哥们儿"就别再花心思想这事了.</p>
<p><br />&nbsp;2,PersistenceContext层面:有判断是否isVersioned,从而来保证Version的一致;在PersistenceContext层面来更新LockMode的级别.</p>
<p><br />&nbsp;3,DB层面的事务隔离级别:<br />&nbsp;&nbsp;persister.lock( entry.getId(), entry.getVersion(), object, requestedLockMode, source );<br />&nbsp;&nbsp;下面又有:<br />&nbsp;&nbsp;&nbsp;PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );<br />&nbsp;&nbsp;于是我就连蒙带猜地认为在DB层面上触及到事务的隔离级别了.</p>
<p>&nbsp;</p>
<p>到结尾处有一个问题:<br />&nbsp;if ( persister.hasCache() ) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;persister.getCache().release(ck, lock );<br />&nbsp;}</p>
<p>&nbsp;</p>
<p>怎么finally里的代码块把前面刚刚Lock的给CacheKey的Entity给放了呢?这又不是session里的Trasaction已经结束了.想不明白,这个以后再慢慢体会吧.</p>
<p>&nbsp;</p>
<p>不过看了对finally做的如下注释,<br />&nbsp;"the database now holds a lock + the object is flushed from the cache,so release the soft lock"<br />虽说没有明白这个finally的作用,但似乎有些验证了前面关于"DB层面的事务隔离级别"的猜想.</p>
<p><br />这个方法终于搞完了.</p>
<p>作为补充,说一下,Hibernate在commit Trasaction时会把EntityEntry里的LockMode重置成最低级别的LockMode.NONE.</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/195878#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 23 May 2008 08:41:33 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/195878</link>
        <guid>http://database.group.javaeye.com/group/blog/195878</guid>
      </item>
      <item>
        <title>Groovy Code To Export Oracle Procedures</title>
        <author>ktnd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ktnd.javaeye.com">ktnd</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/195835" style="color:red;">http://database.group.javaeye.com/group/blog/195835</a>&nbsp;
          发表时间: 2008年05月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>It's really a pleasure to code in groovy.</p>
<p>My first try: </p>
<pre name="code" class="java">import groovy.sql.Sql

sql = Sql.newInstance("jdbc:oracle:thin:@ip:port:db","usr","pwd","oracle.jdbc.driver.OracleDriver")

def base  = "/usr/"
def names = []
def today = new java.text.SimpleDateFormat("yyyyMMddHH").format(new Date())

sql.eachRow("select * from user_procedures",{
 names &lt;&lt; it.object_name
 })

def cnt = names.size()
names.eachWithIndex({ prc, i -&gt;
 def d = new File(base, "${today}")
 d.mkdir()

 println "${i+1}/$cnt exporting ${prc}.prc...."
 def f = new File(d, "${prc}.prc")
 f &lt;&lt; "CREATE OR REPLACE "
 sql.eachRow("select text from user_source where name=? order by line", [prc], {
  f&lt;&lt; it.text 
  })
 })

println "done"
</pre>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/195835#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 22 May 2008 23:24:33 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/195835</link>
        <guid>http://database.group.javaeye.com/group/blog/195835</guid>
      </item>
      <item>
        <title>甚是无奈的&quot;亲子鉴定&quot;--Hibernate源码研究碎得(12)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/195487" style="color:red;">http://database.group.javaeye.com/group/blog/195487</a>&nbsp;
          发表时间: 2008年05月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Hibernate中"亲子鉴定"</p>
<p>本篇中,我们来看doLoad方法,按上篇的分析方法,我们将这个方法分为三部分:<br />&nbsp;step1,loadFromSessionCache()的调用和对返回结果的判断.<br />&nbsp;step2,loadFromSecondLevelCache()的调用和对返回结果的判断.<br />&nbsp;step3,若上面两个方法的的返回值都为null的话,最后调用loadFromDatasource并返回此方法的返回值.</p>
<p>首先看step1,先不管loadFromDatasource这个方法的执行细节,值得注意的是并没有把结果的判断放入到loadFromDatasource方法里,而是通过判断返回值是否为REMOVED_ENTITY_MARKER或INCONSISTENT_RTN_CLASS_MARKER而分情况地做出log,虽说这两种情况下的最终返回值都是null.另一个值得注意的是虽然loadFromSessionCache可能返回null,但也并没有直接将其返回.这也算是一种<span style="color: #339966;">编程规范</span>吧:把方法调用及对返回的判断与方法的执行分开,这也很想对Exception的处理,有时根据需要并没有把一些异常都catch起来,而是将其抛出.</p>
<p>关于这个step1,还有一个很值得关注的,那就是特别注意整个系统中debug的安排,这里就特意为debug的需要而创建了两个Object类型的Marker.别的地方虽说也有将debug信息记录下来,但那都是随手而来的debug,不像这特意地<span style="color: #339966;">绕个弯儿来照顾debug的需要</span>.</p>
<p>有了对step1的分析,doLoad里的step2和step3就没什么特别了的.</p>
<p>下面看loadFromSessionCache方法的执行情况.</p>
<p>由于这里的目标很单一,也就不用再细分N多步了.<br />首先看第一句,SessionImplementor session = event.getSession();<br />值得注意的是虽然这个SessionImplementor在onLoad里就从event里获得了,但为了保持代码的精简并没有将那里取得的SessionImplementor作为参数再传到loadFromSessionCache里,而是在这个方法是重新获得,这也正是利用了"指针"的好处,想要什么时就直接获得它的"指针".</p>
<p>接着往下走,从sesion中getEntityUsingInterceptor,这个方法是第一次见,看对这个方法的注释发现了"calling the Interceptor if necessary"这句话.</p>
<p>还没有往下看以前,就觉得有些奇怪,既然这里已经取得了那个Entity为什么不直接返回呢?还要来个处理?接着往下看就发现了这样处理的高明之处:看这个get的Entity的状态,从而决定是否将其返回还是返回那个REMOVED_ENTITY_MARKER或INCONSISTENT_RTN_CLASS_MARKER, 这样的实质也就是任务注册逐层分解,同时也管理好每一次任务分配时的Message反馈,<span style="color: #339966;">而不是把任务交给你就不管了,同时还要充分考虑到你执行当前任务时可能出现的Message反馈.</span></p>
<p>以前对EntityEntry这个类老是不能很好地理解,觉得这个名字本身起的就有些怪怪的,Entity就Entity吧,怎么还再来个Entry呢?现在结合这里的实际应用,终于有了很直观的感觉了.<br />这里有从oldEntry里getStatus的语句,根据这个大致就可以推断出EntityEntry一个用途,那就是Hibernate利用这个类来获得Entity里与Hibernate相关的信息,这样也就理解了Entry一词在这的作用了.</p>
<p>接下往下看,总觉得这段话有点意思:<br />if ( options.isAllowNulls() ) {<br />&nbsp;&nbsp;EntityPersister persister = event.getSession().getFactory().getEntityPersister( event.getEntityClassName() );<br />&nbsp;&nbsp;if ( ! persister.isInstance( old, event.getSession().getEntityMode() ) ) {<br />&nbsp;&nbsp;&nbsp;return INCONSISTENT_RTN_CLASS_MARKER;<br />&nbsp;&nbsp;}<br />}</p>
<p>先不看那个isAllowNulls的作用,下面的isInstance的判断就些不对劲,<span style="color: #339966;">感觉夫妻俩生了孩子后不是高兴,而是马上给这个孩子做亲子鉴定!本来嘛,这个Entity就是在与之对应的EntityPersister的一手监管下取得的,而这个Entity"独立"后,竟然对这个Entity</span><span style="color: #ff0000;">做"亲子鉴定",有这个必要吗?</span></p>
<p>当然,我的这个形象的理解是有问题的,Hibernate经过这么久的考验了,这种"亲子鉴定"真正意义上也没有发生.不过通过这个反映出以前我对EntityPersister的理解还不全,需要进一步的修正.在这里也通过这个形象点的比喻来加强对这个问题重视.</p>
<p>下面是另一个方法upgradeLock了,追着看了下,这个方法是从DefaulLoadEventListener的父类AbstractLockUpgradeEventListener里继承来的.</p>
<p>这个方法很长,只能交给下篇来研究了.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/195487#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 22 May 2008 08:51:28 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/195487</link>
        <guid>http://database.group.javaeye.com/group/blog/195487</guid>
      </item>
      <item>
        <title>从源码层面来看load与get的区别(七):  Hibernate源码研究碎得(11)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/194713" style="color:red;">http://database.group.javaeye.com/group/blog/194713</a>&nbsp;
          发表时间: 2008年05月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>在这篇中本来是没想着画EntityPersister接口继承关系及其实现类的类图,可看着看着觉得这个继承关系很壮美,也就很想看到它的全貌,于是就有在这<a href="../../blog/194711" title="类图" target="_blank">另一篇博客里的类图</a>.</p>
<p>言归正传,来看DefaultLoadEventListener类里的load方法,这个方法里可分为如下三步:<br />&nbsp;step1,判断并处理instanceToLoad不为null时的情况.<br />&nbsp;step2,做实质的doLoad方法,返回Object类型的对象.<br />&nbsp;step3,判断step2里返回的结果,若不符合就抛出相应的异常,从而结束这一超长的get/load操作.</p>
<p>先看step1,这里有个event.getSession().getPersistenceContext().getEntry(event.getInstanceToLoad()) != null判断,(这个Entry是干啥的?)若不为null就抛出"attempted to load into an instance that was already associated with the session: "Message的Exception.对,从这个message也能理解了不为null也就意味着当前PersistenceContext里已有instance.也就是说这个instance与session已关联起来了,那这样先前所假想的那个refresh功能就是错的了.看下面的persister.setIdentifier()方法,我们就可以修正先前的那个关于refresh的假想,或者说是去evolve那个假说:通过设置这个instanceToLoad,我们是想把处于detached状态的对象再次绑定到session里.这不是update的功能么?难道说update在底层就是调用了这个load方法? ----&gt; 以后做进一步的验证.</p>
<p>看step2的doLoad方法,这又是一个独立的方法.只能留到下一篇来详细研究了,不妨也学Hibernate里proxy概念那样,这个load方法将返回一个Object对象,也就是我们想get/load的那个对象.</p>
<p>下面是step3.<br />&nbsp;怎么若instanceToLoad不为null就是isOptionalInstance呢?也是说若设置了那个instanceToLoad就说明了这是一种optionalInstance.不理解.<br />&nbsp;再看下面的判断逻辑,若是optionalInstance或返回结果不能为null的话,就抛出ObjectNotFoundException.这里的"返回结果不为null"可以理解,但optionalInstance...理解了,这个isOptionalInstance也就意味着,事先已设定了那个instanceToLoad,这样即使get/load没有得到更好的结果(hit database),也不能把已有的那个给能丢了吧,估计正是出于这样的考虑,就把这个也加入ObjectNotFoundException异常的管理之内了.<br />&nbsp;有了刚才的分析,下面的这个判断也就好理解了:isOptionalInstance &amp;&amp; entity != event.getInstanceToLoad(),也就是说,我已设置了instanceToLoad,你那边get/load到的东西就得放到这个instanceToLoad里边,不能"狸猫换太子"地给调了包,那岂不是赔大了,辛苦了半天到头来给别人做了一番嫁衣.<br />&nbsp;至些,经过上面两层的异常筛选,我们就可以返回期盼已久的entity了.<br />&nbsp;下一篇中将讨论那个真正的功臣:doLoad.</p>
<p>*****************************************************<br />这篇blog写完了,总有些意犹未尽的感觉，今天写时有些文如泉涌,呵呵,有些自夸了.思路还很清晰.总得想想/总结下原因吧,对Hibernate的研究是很漫长的过程,这个过程中的研究方法也得慢慢完善起来.</p>
<p>现在总结,最先进入mind里的是刚开始画出的那个类图.很直观,扫清了障碍，也更坚定了信心,心里不再疙疙瘩瘩的,可以亮堂堂地前行.这是成功的心理因素.<br />第二,今天下班后看救灾方面的新闻,看到了一个女子在被埋的几十个小时里把自己小腿用石块砸开,用自己的鲜血维持着生命!在感叹生命力量如此之大之震撼之余,也让我更深刻地理解了生命潜力的巨大.这样常人能以想像的行动,再发观自己呢,生活中工作中的一点麻烦事就感觉天塌了似的,这怎么行?!有了这样坚定的信念,那个类图也就画出来了,今天的分析也很是成功.<br />第二,分析方法有了很大的改善.就是把load方法先分为三步,而不是像往常那样,不管三七二十一上来就看.这样的化整为零很见效,事实上,大致地看了一眼这个load方法后也有些犯嘀咕.可当分为三步后,目标一下子更明淅了,心情也就更好了,最终有了后面的文如泉涌.<br />&nbsp;呵呵....</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/194713#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 May 2008 09:09:12 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/194713</link>
        <guid>http://database.group.javaeye.com/group/blog/194713</guid>
      </item>
      <item>
        <title>满城尽是Interface,接口真的那么神奇?--Hibernate源码研究碎得(10)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/194711" style="color:red;">http://database.group.javaeye.com/group/blog/194711</a>&nbsp;
          发表时间: 2008年05月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>满城尽是Interface,接口真的那么神奇?</p>
<p>晚饭后由于不能很集中地思考,就大致地看了EntityPersister这个接口的继承关系及其实现类.</p>
<p>呵,真是不看不知道,一看吓一跳,又是一个很庞大的继承树,而且还里有那么多的接口,不禁就感叹了:为什么Hibernate里这么多的接口?这样的接口设计能达到想像的效果么?在解藕合方面真能那么灵验?感觉有些接口过度了,不丁点的小事动不动就来一个接口.说句"大言不惭"的话,在自已的设计中是绝对想不到用这么多接口的.</p>
<p>写着写着,就意识到了:用众多接口而不用众多类是一个以前没想到(或没有这么强烈感觉到)的好处的,那就是利用接口可以视具体情况而做到细粒度的划分,此种情况下不必去考虑其实现类的多少,或更进一步就是不必把实现类也划分的那么细,(若把实现类也划分到接口那样的精细程度,在维护上是很费精力的.)而把这些接口的实现都集中在一起,这样就极好地达到服务与实现的解藕合.或许这就是解藕合的实质吧.</p>
<p>写到这,不由地又想起来这些天稍不留神地会去想去找映证的一个问题:究竟面向对象有什么好处?现在项目虽说是用的Java这一面向对象的语言,但像C语言那样面向过程的设计/编程方式随处可见.每当看到披着Java语言的面向过程就不由地反问,面向对象真的就像某些人所说的那样神奇?</p>
<p>今天由Hibernate里EntityPersister想到的接口带来的好处虽说不能完全消除对"挂着养头卖狗肉"的疑惑,但对面向对象的神奇与伟大有了很直观很切身的体会,再次感谢Hibernate!再次感谢开源软件给俺带来的启发与思考!</p>
<p>****************************************</p>
<p>以前对接口好处的认识也仅仅停留在JavaEE中的Data Object Accessor上,也正是由于这种情况下接口应用很常见,久而久之,习以为常,钝化了思维,心里不免觉得接口设计也不过尔尔嘛,偶尔还自以为是地对自己说掌握了接口设计的精髓.</p>
<p>呵呵,汗颜呀.DAO仅仅是接口发挥威力的一个小小场所.</p>
<p>解藕合:调用与运行分开,服务与实现分开.</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/194711#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 May 2008 09:03:37 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/194711</link>
        <guid>http://database.group.javaeye.com/group/blog/194711</guid>
      </item>
      <item>
        <title>集万千宠爱于一身的SessionImpl:get研究(四):  Hibernate源码研究碎得(8)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/193883" style="color:red;">http://database.group.javaeye.com/group/blog/193883</a>&nbsp;
          发表时间: 2008年05月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>上一篇重点介绍了LoadEventListener里的LoadType这个static final类及其在LoadEventListener里的定义的几个对象后,简单</p>
<p>地介绍了DefaultLoadEventListener里的三个static final属性,现在在这篇中将重点研究这里的onLoad方法.<br />&nbsp;进入onLoad方法后,第一句就是从event中getSession,这个返回值以SessionImplementor类型的变量保存住,如下所示:<br />&nbsp;&nbsp;final SessionImplementor source = event.getSession();<br />&nbsp;这里边有些蹊跷,LoadEvent类型的变量event里getSession的返回的为EventSource,而看这个EventSource是个接口,再</p>
<p>追着看,EventSource继承自SessionImplementor和Session两个接口,Session就是我们日常所熟悉的org.hibernate.Session接</p>
<p>口,那么这个SessionImplementor接口做何解释?<br />&nbsp;看这个接口的继承及实现类关系,此接口继承自Serializable,这没什么特别的;此接口有一个子接口,也就是上面所提</p>
<p>到的EventSource,另有一个抽象类AbstractSessionImpl实现此SessionImplementor接口,此抽象类有两个SessionImpl与</p>
<p>StatelessSessionImpl,这里的SessionImpl就是我们要利用的org.hibernate.Session接口的实现类.</p>
<p>说了半天有些绕,现在再总结下,org.hibernate.Session和SessionImplementor继承自java.io.Serializable,这两个接口下又</p>
<p>有一个共同的了接口EventSource,而org.hibernate.Session有一个子接口</p>
<p>org.hibernate.classic.Session,SessionImplementor有一个抽象实现类AbstractSessionImpl,而Hibernate自身相当重要的一</p>
<p>个类SessionImpl继承自AbstractSessionImpl,并同时实现了EventSource和org.hibernate.classic.Session两个接口,当然它</p>
<p>还实现了另一个接口org.hibernate.jdbc.JDBCContext.Context,由于现在还没有接触这个接口所涉及到的事,先不于考虑.至此</p>
<p>就形成了如下所示的类图(看到这个图后,一下子想起了集万千宠爱于一身这词了,呵呵,能力大责任大.).</p>
<p>&nbsp;先在这停一下,一直不明白为什么Hibernate里有org.hibernate.classic.Session这个接口,看它里面的22个方法都是</p>
<p>deprecated的,难道说这个接口是历史遗留问题?早与现在常见的org.hibernate.Session接口?这又说不通,若它早于</p>
<p>org.hibernate.Session的话,应该是org.hibernate.Session继承自org.hibernate.classic.Session,而现在正好相反.这做何</p>
<p>解释呢?留到日后随着对Hibernate的慢慢熟悉再来解决吧.<br />&nbsp;另外有一个问题,为什么要来一个SessionImplementor接口?心里纳闷Sesion自己就是一个接口了,怎么还又来这么个接</p>
<p>口还起名为SessionImplementor?这个问题从刚开始看Hibernate源码就隐隐约约地感觉到了,随着这些较为专注的研究,终于提</p>
<p>出这个问题,现在把整个关键接口(类)图画出后看出了点门道,准确说是一个猜想:感觉这个SessionImplementor接口有点像JDBC</p>
<p>里给DataBase厂商提供的那套接口,而org.hibernate.Session这个接口是Hibernate给用户用的,这也正像JDBC提供给用户用的</p>
<p>那套接口.现在就先做出这样的假想吧:这个SessionImplementor接口是Hibernate自己方便专门用的一个接口,用它来抽象那些</p>
<p>为实现org.hibernate.Session这个标准接口所要做的一些子功能.<br />&nbsp;又说的远了些,回过头来看上面提到的那外第一句.不过现在有以刚才那么几长段的分析,也就很容易明白从event中</p>
<p>getSession后交给一个SessionImplementor"保管"了,虽说放到event里时是已EventSource放的,但取出时也完全可以用</p>
<p>SessionImplementor来保管,毕竟这个SessionImplementor是EventSource的父接口之一.<br />&nbsp;<br />&nbsp;<br />&nbsp;现在已深夜12点了,只能明天再写关于第二句的EntityPersister persister;的EntityPersister类了.呵呵,想来有些</p>
<p>好笑,今天晚就写了对一句话的分析.</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/193883#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 17 May 2008 13:57:36 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/193883</link>
        <guid>http://database.group.javaeye.com/group/blog/193883</guid>
      </item>
      <item>
        <title>二、Streams捕获进程</title>
        <author>shevliu</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shevliu.javaeye.com">shevliu</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/190327" style="color:red;">http://database.group.javaeye.com/group/blog/190327</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>Streams捕获进程<br /><br />重做日志与捕获进程</strong><br /><br />每个Oracle数据库都包含至少两个重做日志文件，这些文件被共同称为数据库重做日志。重做日志的主要用途是用来记录数据库产生的所有变更。<br />	当人为或硬件原因导致数据出错时，重做日志能确保数据恢复到之前的状态。捕获进程是Oracle的可选后台组件之一，扫描重做日志以捕获对数据库进行的DML和DDL操作，该重做日志所在的数据库称为源数据库。<br /><br /><strong>LCRs（Logical Change Records，逻辑变更记录）</strong><br />捕获进程从重做日志中捕获到变更事件后将其格式化为LCRs。LCR是一种用来描述数据库变更事件的特殊消息，分为两种类型：row LCRs 和 DDL LCRs。<br /><br /><strong>Row LCRs</strong><br /><br />Row LCR 是对库表中单行记录变更的描述信息，一条DML可能同时更新多行记录，所以一条DML可能产生多条Row LCR。而对某一行的LONG,LONG RAW,或是LOB类型字段更新时，同样可能产生多条Row LCR。<br />Row LCR将被包装为LCR$_ROW_RECORD类型，包含以下属性：<br />	source_database_name：源数据库名称。<br />	command_type：产生LCR的DML操作类型，可以是下列类型：INSERT, UPDATE, DELETE, LOB ERASE, LOB WRITE, LOB TRIM。<br />	object_owner: schema名称。<br />	object_name：产生LCR的Table名称。<br />	tag：用以追踪LCR的RAW标签。<br />	transaction_id：运行DML操作的事务标识符。<br />	scn：变更记录写入重做日志时的SCN(system change number)。<br />	old_values：数据更新之前的值。如果DML类型是UPDATE或DELETE，本属性值将是DML操作之前的值，如果DML类型是INSERT，本属性值为空。<br />	new_values：数据更新之后的值。如果DML类型是UPDATE或INSERT，本属性值将是DML操作之后的值，如果DML类型是DELETE，本属性值为空。<br /><br />Row LCR也可包含事务控制语句，例如COMMIT,ROLLBACK。应用过程通过此功能使源数据库与目标数据库保持事务一致性。<br /><br /><strong>DDL LCRs</strong><br /><br />	DDL LCR是对DDL操作的描述信息，DDL通过CREATE,ALTER,DROP等操作改变数据库的结构。<br />	DDL LCR包含以下信息：<br />	source_database_name：源数据库名称。<br />	command_type：产生LCR的DDL操作类型，如ALTER TABLE 或CREATE INDEX。<br />	object_owner：schema名称。<br />	object_name：产生LCR的数据库对象名称。<br />	object_type：产生LCR的数据库对象类型，如TABLE 或 PACKAGE。<br />	ddl_text：DDL语句内容。<br />	logon_user：执行DDL操作的数据库用户。<br />	current_schema：当前使用的schema。<br />	base_table_owner：基础表用户。<br />	base_table_name：基础表名称。<br />	tag：用以追踪LCR的标签。<br />	transaction_id：运行DDL操作的事务标识符。<br />	scn：变更记录写入重做日志时的SCN。<br /><br /><strong>捕获规则</strong><br /><br />	捕获进程根据用户定义的规则对数据库变更进行捕获。我们可以为捕获进程创建正规则集（positive rule set）和负规则集（negative rule set）。<br />	当数据库产生变更时，假如正规则集中的某条规则计算结果为TRUE，捕获进程将截获该条信息；若负规则集中的某条规则计算结果为TRUE，捕获进程将忽略该条信息。若捕获进程同时具有正规则集和负规则集，那么负规则集将优先进行计算。<br /><br /><strong>捕获类型</strong><br /><br />捕获进程能捕获到以下类型字段的变更：<br />_ VARCHAR2<br />_ NVARCHAR2<br />_ NUMBER<br />_ LONG<br />_ DATE<br />_ BINARY_FLOAT<br />_ BINARY_DOUBLE<br />_ TIMESTAMP<br />_ TIMESTAMP WITH TIME ZONE<br />_ TIMESTAMP WITH LOCAL TIME ZONE<br />_ INTERVAL YEAR TO MONTH<br />_ INTERVAL DAY TO SECOND<br />_ RAW<br />_ LONG RAW<br />_ CHAR<br />_ NCHAR<br />_ CLOB<br />_ NCLOB<br />_ BLOB<br />_ UROWID<br /><br />捕获进程不能捕获到以下类型字段的变更：BFILE, ROWID, 用户自定义类型（包括object types,REFs, varrays, nested tables, 以及 Oracle-supplied types），以及经过加密的字段。当捕获进程尝试创建LCR时，若包含加密字段或是不支持的类型，将向用户抛出异常。<br /><br /><strong>实例化Streams环境</strong><br /><br />在单个数据库或是集群环境中，源数据库是指产生消息的数据库，而接收消息的数据库被称为目标数据库。无论是捕获进程已经开始或即将开始捕获数据库变更，还是这些变更即将被本地程序接收或是传递到远程数据库，我们都应该保证这些操作对应的数据库对象已经被实例化。<br />在Streams中，实例化对象有下列步骤：<br />1.	在源数据库中准备好要实例化的对象。<br />2.	如果在目标数据库中没有这些对象的拷贝，则根据源数据库中的对象在目标数据库中创建。我们可以利用export/import, transportable tablespaces, 或者 RMAN来拷贝对象。如果目标数据库中已经有了拷贝，则本步骤省略。<br />3.	在目标数据库中设置实例化SCN（instantiation SCN）。<br /><br />有时，第一步和第三步会由系统自动完成。例如，当我们使用DBMS_STREAMS_ADM包为捕获进程在正规则集中添加规则时，第一步会自动完成。同样，当我们使用export/import 或者 transportable tablespaces将对象从源数据库拷贝到目标数据库时，第三步会自动完成。在apply process从队列中取出消息之前，即使只是将LCR发送给另外的apply handler而非自己进行操作，对象也必须实例化。<br /><br /><strong>本地捕获和远程捕获<br /><br />本地捕获</strong><br /><br />运行在本地源数据库的捕获进程称为本地捕获。配置本地捕获进程时，源数据库将执行以下操作：<br />	运行DBMS_CAPTURE_ADM.BUILD过程以提取或创建重做日志数据字典。<br />	源数据库中的Supplemental logging将附加信息写入重做日志，当apply process对消息进行处理的时候，可能会需要这些信息。<br />	在捕获进程首次运行时，Oracle根据从重做日志中提取出来的数据字典，创建LogMiner数据字典，LogMiner是从主数据字典里分离出来的子集。其他的捕获进程可以共用该LogMiner数据字典，也可以自行创建。<br />	捕获进程通过LogMiner来扫描重做日志。<br />	规则引擎根据规则集中的规则来确定捕获何种数据库变更事件。<br />	捕获进程将满足规则定义的变更截获并放入本地ANYDATA队列中。<br />	若有多个数据库共享该条消息，那么将有多个Propagation将这条消息从源数据库传递到各个目标数据库。<br /><br /><strong>本地捕获的优点</strong><br /><br />本地捕获的优点如下：<br />	配置和管理较为容易。使用本地捕获，不需要将重做日志拷贝到远程数据库，直接在本地进行管理。<br />	本地捕获进程能在数据库变更写入重做日志存档文件（archived redo log file）之前扫描联机日志。如果使用远程捕获，需要在变更写入重做日志存档文件之后将其拷贝到远程数据库，有时甚至需要拷贝重做日志文件（redo log files）。<br />	由于不用将整个重做日志文件拷贝到远程数据库，所以减少了通过网络传递的数据量。即使是在传递已截获的消息时，我们也可以根据传递规则选取必要的消息进行传递。<br />	由于只有本地源数据库能访问重做日志，所以提高了系统安全性。举例来说：假设我们只想捕获schema hr中的变更，只有源数据库有权限扫描重做日志，截获hr中的变更并将其放入队列；但如果使用远程捕获，由于需要拷贝重做日志文件到目标数据库，所以实际上捕获的不仅仅是hr中的变更，而是整个数据库的变更。<br />	如果消息捕获和消息处理都在同一个数据库中，那么配置将会相对简单，查询和计算所需的资源也相对较少。
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/190327#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 17:57:12 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/190327</link>
        <guid>http://database.group.javaeye.com/group/blog/190327</guid>
      </item>
      <item>
        <title>一、Streams概述</title>
        <author>shevliu</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shevliu.javaeye.com">shevliu</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/190189" style="color:red;">http://database.group.javaeye.com/group/blog/190189</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>Streams概述</strong><br />	Oracle Streams使信息共享化。每一条共享的信息在Oracle Streams中都被称为一条消息。Stream能在同一个数据库内部或者不同数据库之间传递消息，并且能根据路由配置将指定的消息传递到指定的目的地。它在捕获消息，管理消息，以及在不同数据库或应用程序之间共享消息等方面提供了比传统解决方案更为强大的功能和扩展性,并且提供了分布式企业级应用、数据仓库、高有效性解决方案等功能。我们可以同时使用Oracle Streams提供的所有功能。当需求变更时，我们可以实现新的功能，而不影响现有的功能。<br />	通过Oracle Streams，我们能够掌控诸如：将哪些信息放入stream中，数据库之间消息的流向，消息传递到数据库时做何处理，何时关闭stream等等。根据我们自己制定的规范，Streams能够在数据库中自动的捕获、储存和管理因DML、DDL等操作产生的消息。我们可以向stream中放入自定义的消息，Streams能够自动的将消息传递到其他数据库或者应用程序。根据用户定义的规范，Streams将决定如何处理传递到目的地的消息。<br /><br /><strong>Streams能做什么？</strong><br /><br /><strong>捕获数据库消息</strong><br />      捕获进程（capture process）能够捕获到table、schema、甚至整个数据库的变更。这些变更都会记录到数据库重做日志中，捕获进程从重做日志中捕获到这些变更并将其格式化为LCR（logical change record ， 逻辑变更记录）。哪些消息将被捕获由规则确定，这些消息被称为Captured messages。<br />      产生消息的重做日志所在的数据库被称为源数据库。捕获进程能捕获到本地源数据库或是远程数据库的变更，并将LCR放入相应的队列（Queue），这种方式常被称为隐式捕获。<br />      用户或应用程序可以手动的将LCR或用户自定义的消息放入队列，这种方式常被称为显式捕获。<br /><br /><strong>储存消息到队列</strong><br />	捕获进程将消息放入ANYDATA队列，ANYDATA队列能存放不同类型消息。用户和应用程序同样可以将消息放入ANYDATA队列，也可以放入指定类型的队列，指定类型的队列只能存放同类型的消息。<br /><br /><strong>队列间的消息传递</strong><br />	Streams propagations能够将消息从一个队列传递到另一个队列，这些队列可以在同一个数据库中，也可以在不同数据库中。传递哪些消息同样由规则确定。<br /><br /><strong>消费消息</strong><br />	当消息从队列中出队时将被消费。应用过程（apply process）隐式的将消息出队，而用户、应用程序或是消息客户端显式的将消息出队。消费消息的数据库被称为目标数据库（destination database），目标数据库和源数据库可以是同一个。<br /><br /><br /><strong>Streams有什么用？<br /><br />消息队列</strong><br />	Oracle Streams 高级队列（AQ）使用户程序能将消息入队，传递到目的地，并且在目标数据库中将消息出队。AQ可设置为只能存储单一类型的消息，也可设置为可存储ANYDATA类型的消息，绝大部分的消息类型都可包装为ANYDATA类型以存储到ANYDATA队列。AQ遵循消息队列系统规范，诸如：点对点模式，订阅/发布模式，路由功能，广域网消息传播，消息转换，以及消息网关等。<br />	在数据库中创建队列后，应用程序显式的将消息放入队列，订阅程序或消息客户端可以立即接收到该消息。若想在远程订阅源数据库中发布的消息，可在远程数据库中创建一个队列，通过队列间的消息传递交互信息，远端程序便可使用本地队列接收到消息。另外，远端程序也可通过各种标准协议直接从源数据库队列中接收到消息。<br /><br /><strong>数据复制</strong><br />	Streams能够捕获到数据库中因DML和DDL操作引起的数据变更，并可将这些数据变更复制到其他数据库中。Streams 捕获进程捕获到源数据库中的数据变更并将这些信息格式化为LCR，通过AQ传递到目标数据库。<br />	目标数据库接收到数据变更消息后，可以对自身数据做出同样的DML或DDL操作，这样便可实现多个数据库之间的数据复制。即使彼此间表结构和数据都不同，也可完成同样的功能。<br /><br /><strong>事件管理和通知</strong><br />	应用程序能将事件描述包装为消息并显式的放入队列，而Streams 捕获进程能捕获到数据库DML或DDL事件并将其转化为LCR。Propagations 通过各种队列将消息传播到目的地，最后，用户程序或应用过程将消息出队。若必要的话，应用过程可以将出队的消息再次入队。<br />	通过配置，我们能指定当消息被消费后仍然驻留在队列中的时间。<br />	Streams 捕获进程、Propagations（传播）、应用过程、消息客户端都是基于规则的。Oracle中内置有规则引擎以计算何种事件将被捕获并传播。当捕获进程根据规则捕获到事件，将事件描述包装为消息并放入队列，消息客户端或应用程序接收到消息后，可以通过发送email或是发送短信通知客户。<br /><br /><strong>数据保护</strong><br />	要想保护数据，创建一个备份数据库是常见的解决方案。一旦发生人为或自然因素引起的灾难性后果，使用备份数据库能很快的恢复数据。Streams为此提供了高扩展性、高可用性的功能。<br />	此外，我们还可以使用与Streams采用同一架构的Oracle Data Guard，创建并维护与生产环境数据库相同的备份数据库。通过LCR的交互使备份数据库始终与生产环境数据库保持一致，并且备份数据库对外开放读写权限，这就使得备份数据库成为生产环境数据库的有力补充。<br /><br /><strong>数据库升级和维护</strong><br />	通过Oracle Streams，我们只需使数据库关闭极短时间甚至不需关闭，便可完成数据库的升级和维护操作。维护操作包括将数据库移植到其他平台，改变数据库的字符集，修改schema信息，给Oracle打补丁等。
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/190189#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 14:10:35 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/190189</link>
        <guid>http://database.group.javaeye.com/group/blog/190189</guid>
      </item>
      <item>
        <title>Hibernate源码研究碎得(2)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/189807" style="color:red;">http://database.group.javaeye.com/group/blog/189807</a>&nbsp;
          发表时间: 2008年05月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><br />1, 为什么tables用的是TreeMap这个类呢?而别的大都用的是HashMap<br />2, Environment.getProperties()每次调用都是新建一个Properties对象,把这个对象传对调用者.这样做用什么特殊的好处呢?<br />3, 怪,怎么会出现一模一样的两行LogInfo呢?<br />&nbsp;[2008-05-05 09:16:36,750] INFO&nbsp; org.hibernate.cfg.Environment Hibernate 3.2.6 <br />&nbsp;[2008-05-05 09:16:36,750] INFO&nbsp; org.hibernate.cfg.Environment Hibernate 3.2.6<br />&nbsp;<br />&nbsp;这是log4j配置时哪出错了?<br />4,跟Tomcat对配置文件的解析相比,Hibernate对配置文件的解析用的是那种DOM,也就是说把XML文件生成一个Document放在内存中,什么时候用就直接用.而Tomcat像是用SAX解析方式,根据解析事件去配置相应的Component.</p>
<p>5,url.openStream()?为什么要用这个而不用那种直接的打开方式呢?</p>
<p>6,注意有两个类Mappings与Mapping,<br />&nbsp;Mappings是在解析hbm.xml时</p>
<p>7,hibernate-mapping里的子元素Meta还没有用过.这个怎么与别的联系起来?</p>
<p>8,有专门的extractRootAttributes方法来get RootNote下的attributes,这也进一步证明了上面的Meta是RootNode的子元素而不是其属性.</p>
<p>9,看hibernate-mapping-3.0.dtd又进一步想到了正则表达式中也会用到的*?等对个数限制的描述符.<br />&nbsp;*: 表示任意个数,0 --&gt; N.<br />&nbsp;?: 表示0 or 1吗现在也不确定了.<br />10,dtd描述文件中的像如下所示中的<br />&nbsp;&lt;!ATTLIST hibernate-mapping default-cascade CDATA "none"&gt;</p>
<p>&nbsp;CDATA是什么意思?</p>
<p>11,Map put时的一个新发现:<br />&nbsp;Object old = classes.put( persistentClass.getEntityName(), persistentClass );<br />&nbsp;&nbsp;if ( old!=null ) {<br />&nbsp;&nbsp;&nbsp;throw new DuplicateMappingException( "class/entity", persistentClass.getEntityName() );<br />&nbsp;}</p>
<p>&nbsp;Hibernate中有如上的应用,也就是说可以能过put的返回值是否为null来判断是否覆盖了Map中与Key相对的原用Entity.不错.</p>
<p>&nbsp;再看API,对put的返回值有如下描述:<br />&nbsp;the previous value associated with&nbsp; key , or null&nbsp; if there was no mapping for&nbsp; key .(A&nbsp; null&nbsp; return can also indicate that the map previously associated&nbsp; null&nbsp; with&nbsp; key , if the implementation supports&nbsp; null&nbsp; values.)</p>
<p>12,HmbBinder类中的bindPojoRepresentation这三个方法bind的是从DB返回后的包装类型么？</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/189807#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 05 May 2008 16:46:05 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/189807</link>
        <guid>http://database.group.javaeye.com/group/blog/189807</guid>
      </item>
      <item>
        <title>编译Squid问题总结</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/189467" style="color:red;">http://database.group.javaeye.com/group/blog/189467</a>&nbsp;
          发表时间: 2008年05月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近学习Squid，想从中学习一些关键技术，如内存池，hash，epool等，但再编译原代码时发生一些问题，现总结如下：<br /><br />1.gcc问题，卸除原版本，安装新版本<br />-bash-3.00# pkginfo |grep gcc<br />application FSFgcc                           gcc<br />system      SUNWgcc                          gcc - The GNU C compiler<br />system      SUNWgccruntime                   GCC Runtime libraries<br />-bash-3.00# pkgrm SUNWgcc<br />-bash-3.00# pkgrm FSFgcc<br />-bash-3.00# pkgrm SUNWgccruntime<br />2.提示host问题，按提示用./configure --host=x86-sun-solaris编译通过<br />3.Nothing to be done for `install-exec-am'和Nothing to be done for `all-am'一开始以为是错误，其实可以忽忽略置之不理<br />4.Solaris的cp命令没有-a选项，应用cp -RPp 代替cp -a<br />5.磁盘充足却提示不足，发现移动硬盘的分区格式是 FAT32 的，大于4G则会有问题，必须将其改成 NTFS 用 convert H: /fs:NTFS 命令转换分区文件系统格式后，开始拷贝，一切正常
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/189467#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 04 May 2008 18:01:04 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/189467</link>
        <guid>http://database.group.javaeye.com/group/blog/189467</guid>
      </item>
      <item>
        <title>Cacti在Solaris10下的安装</title>
        <author>martri</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://martri.javaeye.com">martri</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/189450" style="color:red;">http://database.group.javaeye.com/group/blog/189450</a>&nbsp;
          发表时间: 2008年05月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          从学习Squid源代码，到安装Cacti监视Squid，感觉是理所当然，cacti真是一个很好的开源监控系统，利用php技术，结各mysql以及rrdtool技术，将监控系统分模块划分，使系统很容易拓展，它的社区也有很高的活跃度，是一个监控系统很好的学习模范。从安装到自定义模版，其实还是要花不少功夫的：<br />Solaris下MySQL的安装：<br />1.Solaris下的MySQL目录：<br />/usr/sfw/sbin:守护进程mysqld,mysqld_safe<br />/usr/sfw/bin:管理工具mysqladmin,mysqlcheck等<br />/usr/sfw/share/mysql/:配置文件 my-medium.cnf等<br />/var/mysql：MySQL数据目录<br />/usr/sfw/include/mysql：MySql的头文件<br />2.配置MySQL配置文件<br />-bash-3.00#cp /usr/sfw/share/mysql/my-medium.cnf /etc/my.cnf<br />-bash-3.00#vi /etc/my.cnf<br /># The following options will be passed to all MySQL clients<br />[client]<br />#password       = your_password<br />port            = 3306<br />##Modified content MartriWang@gmail.com<br />#socket         = /var/lib/mysql/mysql.sock<br />socket          = /home/mysql/5.0/mysql/mysql.sock<br /><br /># Here follows entries for some specific programs<br /># The MySQL server<br />[mysqld]<br />port            = 3306<br />#socket         = /var/lib/mysql/mysql.sock<br />##Modified content MartriWang@gmail.com<br />socket          = /home/mysql/5.0/mysql/mysql.sock<br />skip-locking<br />key_buffer = 16M<br />max_allowed_packet = 1M<br />table_cache = 64<br />sort_buffer_size = 512K<br />net_buffer_length = 8K<br />read_buffer_size = 256K<br />read_rnd_buffer_size = 512K<br />myisam_sort_buffer_size = 8M<br />3.创建mysql用户<br />-bash-3.00#useradd -u 1002 mysql <br />-bash-3.00#groupadd -g 1002 mysql mysql<br />-bash-3.00#mkdir /export/home/mysql<br />4.启动mysql<br />-bash-3.00#./mysqld &<br />发生如下错误：<br />080424 15:45:53 InnoDB: Started<br />080424 15:45:53 Fatal error: Can't open privilege tables: Table 'mysql.host' doesn't exist<br />080424 15:45:53 Aborting<br />解决方法：<br />rm -rf /pathtodatadir<br />then<br />cd /usr/local/mysql1/bin<br />./mysql_install_db --user=mysql --ldata=/var/mysql<br />./mysqld_safe --datadir=/var/mysql --user=mysql &<br />5.安装成功<br />备份:[root@MT src]#mysqldump -u root -p --opt mysql > mysql_back<br />恢复:[root@MT src]#mysql -u root -p mysql &lt; mysql_back<br />=====================================================================================================================<br />Solaris下apache的安装<br />1.Solaris下的Apache目录：<br />/usr/apache2：主目录，如/usr/apache2/bin下有apachectl，httpd<br />/etc/apache2/:配置文件，如httpd.conf-example<br />/var/apache2/logs：日志文件<br />/var/run/apache2/httpd.pid：运行目录<br />2.配置文件<br />-bash-3.00#cp /etc/apache2/httpd.conf-example  /etc/apache2/httpd.conf<br />-bash-3.00#vi /etc/apache2/httpd.conf<br />    Set ServerName if necessary (default is 127.0.0.1)<br />    Set ServerAdmin to a valid email address<br />如没有目录/var/run/apache2/则需要创建此目录<br />3.运行./usr/apache2/bin/apachectl start &<br />或者，启用apache服务：<br />-bash-3.00# svcadm enable apache2<br />=====================================================================================================================<br />Solaris下PHP的安装<br />1.编译安装<br />./configure --prefix=/usr/local/php --with-apxs2=/usr/apache2/bin/apxs --with-mysql=/usr/sfw/include/mysql <br />--with-config-file-path=/usr/local/php/etc <br />错误一：<br />checking for int8... (cached) no<br />checking base type of last arg to accept... (cached) socklen_t<br />checking return type of qsort... (cached) void<br />configure: error: Cannot find MySQL header files under /usr/<br />将--with-mysql=/usr/sfw/include/mysql改为--with-mysql=/usr/sfw/便可解决<br />错误二：<br />Configuring extensions<br />checking whether to enable LIBXML support... yes<br />checking libxml2 install dir... no<br />checking for xml2-config path... /usr/bin/xml2-config<br />configure: error: libxml2 version 2.6.11 or greater required.<br />解决方法有二：<br />一是采用低版本的php-4.4.8.tar.gz<br />二是升级libxml2和重新安装一个libxml2（不推荐，容导致系统版本不一直而崩溃）或重新安装一个新的libxml2（推荐）<br />tar zxvf libxml2-2.6.26.tar.gz<br />./configure –prefix=/usr/local/libxml<br />make;make install<br />重启机子，更新模块<br />2.配置PHP<br />检查htppd.conf文件中是否插入“LoadModule php5_module modules/libphp5.so“<br />手动插入行“AddType application/x-httpd-php .php .phtml”<br />注意不要象一些文档说的加入两句：<br />#AddHandler php4-script .php<br />AddType application/x-httpd-php .php<br />这是错误<br />测试页面：&lt;?phpinfo()?><br />=====================================================================================================================<br />Solaris下net-snmp和rrdtool的安装<br />1.net-snmp的安装<br />./configure<br />make;make install<br />如CACTI出现如下错误：<br />.Encryption support not enabled.<br />snmpbulkwalk: USM encryption error<br />Encryption support not enabled.<br />解决方法：<br />You probably need to install the openssl-dev package and rebuild net-snmp.<br />2.修改snmp的配置文件：<br />#vi /usr/local/share/snmp/snmpd.conf<br />修改下面几部分内容：<br />1)com2sec notConfigUser default public<br />改为：com2sec notConfigUser localhost public<br />2)access notConfigGroup "" any noauth exact systemview none none<br />改为：access notConfigGroup "" any noauth exact all none none<br />3)#view all included .1 80<br />4)createUser frederic MD5 mypassphrase DES<br />group groupv3   usm        frederic<br />access groupv3   ""      any       auth      exact  all    all    all<br /><br />修改计划任务，使CACTI每5分钟获得一次数据：<br />#crontab -e 用户名<br />0,5,10,15,20,25,30,35,40,45,50,55 * * * * /usr/local/php/bin/php /var/apache2/htdocs/cacti/poller.php > /dev/null 2>&1<br /><br />3.测试SNMP<br />snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.100.6.0<br />snmpget -v 3 -l AuthNoPriv -u frederic -A mypassphrase 192.168.0.6 sysName.0<br /><br />2)Test configuration <br />snmpwalk -v 1 -c public localhost .1.3.6.1.2.1.1.1.0<br />--query which Net-SNMP version is running on a host <br />snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.100.2.0 <br />snmpwalk -v 1 -c foo localhost .1.3.6.1.2.1.1.1.0<br />--An incorrect passphrase will result in this error message <br />snmpget -v 3 -l AuthNoPriv -u frederic -A badpassphrase 192.168.0.6 sysName.0<br />--This query will show you what filesystems are mounted <br />snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.9.1.2<br />--display your network interfaces <br />snmpwalk -v 1 -c public localhost .1.3.6.1.2.1.2.2.1.2<br /><br /><br />snmpwalk -v 2c -c public 192.168.0.6 if<br />/usr/local/php/bin/php /var/apache2/htdocs/cacti/cli/poller_reindex_hosts.php -id=All<br /><br /><br />Timeout: No Response from 192.168.0.6:161.<br />04/28/2008 12:14:55 PM - SYSTEM STATS: Time:4.4020 Method:cmd.php Processes:1 Threads:N/A Hosts:2 <br />HostsPerProcess:2 DataSources:14 RRDsProcessed:0<br />=====================================================================================================================<br />1.rrdtool的安装<br /><br />perl /scripts/unix_users.pl cacti<br /><br />/usr/local/cactid/bin/cactid --conf=/usr/local/cactid/etc/cactid.conf <br /><br />1)rrdtool dump two_gw_call_monitor_connectednew_2954.rrd > /tmp/2954.xml<br /><br />2)sed -i -e 's/NaN/2.4300000000e+02/' 2954.xml<br /><br />3)rrdtool restore -f 2954.xml two_gw_call_monitor_connectednew_2954.rrd <br /><br />/usr/local/php/bin/php /var/apache2/htdocs/cacti/poller.php<br />/var/spool/cron/crontabs/cacti<br /><br />/usr/local/rrdtool-1.2.19/bin/rrdtool  fetch localhost_mem_buffers_14.rrd  AVERAGE <br />/usr/local/rrdtool-1.2.19/bin/rrdtool  info localhost_mem_buffers_14.rrd<br /><br />php rebuild_poller_cache.php -d<br />=====================================================================================================================<br />Solaris下CACTI的安装<br />1.修改配置<br />grant all privileges on *.* to 'cacti'@'localhost' identified by 'cacti'<br />/usr/sfw/bin/mysql -ucacti -pcacti cacti &lt; cacti.sql  <br /><br />#vi include/config.php<br />修改如下段落：<br />$database_type = "mysql";<br />$database_default = "cacti";<br />$database_hostname = "localhost";<br />$database_username = "cactiuser";<br />$database_password = "cactiuser";<br />$database_port = "3306";<br />改为：<br />$database_type = "mysql";<br />$database_default = "cacti";<br />$database_hostname = "localhost";<br />$database_username = "用户名";<br />$database_password = "你的密码";<br />$database_port = "3306"; <br /><br />2.修改CACTI的配置文件<br />#chown -R rra/ log/<br />这样cacti就基本配置完成了<br />在浏览器里输入IP/cacti,错误提示：<br />Please make sure you have specified a valid MySQL database name in 'include/config.php' <br />解决方法为修改/etc/my.cnf文件：<br />socket = /tmp/mysql.sock <br />##MartriWang@gmail.com 20080425<br />#socket = /var/lib/mysql/mysql.sock<br />##http://192.168.0.6/cacti<br />用户名和密码是admin和admin<br />3.crontab自启动<br />注意Solaris下，命令行可以有结果，而crontab没有，则是因为使用的环境变量不同所致，<br />应该加上/export/home/cacti/.bash_profile，如下：<br />0,5,10,15,20,25,30,35,40,45,50,55 * * * * <br />. /export/home/cacti/.bash_profile; /opt/php/bin/php /opt/cacti/cacti-0.8.6b/poller.php<br /><br />=====================================================================================================================<br />CACTI使用技巧：<br />1、cacti的流程是从Data Input Methods开始，创建一个信息采集的方法先，里面定义了采集的方法和采集需要的脚本（注意：<br />如果使用CACTID最好使用可执行文件的全路径），可以定义一个输入和输出。输入可以为你执行这个脚本带入参数，比如连接<br />数据库的参数，这样只要设置这个参数就可以连上不同的数据库了。输出是信息采集的输入结果，以列名＋冒号＋采集结果的<br />形式返回。<br />2、然后是数据模版，里面需要选择Data Input Methods，如果数据输入方法中有参数的，这里可以设置参数，也可以选择忽略<br />这个设置，然后到最后配置图形的时候再输入特定的参数。<br />3、创建图形模版，里面需要定义图形的ITEM，可以使用AREA方式先画一个框，然后把其他值以STACK的方式往前面的AREA上面<br />累加。也可以使用GPRINT方式在最下面的备注那里输出些信息（一般是输入最后采集时候的值）。这里可以定义CDEF function，<br />这个函数可以把你显示的ITEM进行转换，比如把8k转换成8192字节等，这个函数可以自定义的（可以参考Graph Management中的<br />CDEFs中的定义来进行自定义）。<br />4、创建DATASOURCE，然后创建GRAPH。<br />最后可以把他们放到一个TREE上
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/189450#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 04 May 2008 17:27:56 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/189450</link>
        <guid>http://database.group.javaeye.com/group/blog/189450</guid>
      </item>
      <item>
        <title>Hibernate源码研究碎得(1)</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/187604" style="color:red;">http://database.group.javaeye.com/group/blog/187604</a>&nbsp;
          发表时间: 2008年04月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>1,
Collections.unmodifiableMap( new HashMap(0) );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看到了这样的语句,不知道个unmodifiableMap在实际中有什么用?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是说整个执行过程中,某一个变量所指向的Instance没有是不可modifiable的吧?而不是这个Map中的具体内容.
这样再结合应用的上下文就通了.

</p>
<p>&nbsp;</p>
<p>2,Hibernate中有个SecondPass的概念,这个是干啥用的?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看源码时,它的实现类中有这样的关系:
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接口SecondPass有两个直接实现类:
	</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CollectionSecondPass和ManyToOneSecondPass,另还有一个继承于此接口的子接口QuerySecondPass
		
		</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; CollectionSecondPass在HbmBinder类中又定义了一个static的类名为HbmBinder.CollectionSecondPass,&nbsp;&nbsp; 这个静态类在HbmBinder中又分别有三个子类(也是静态的)它们是IdentifierCollectionSecondPass,ListSecondPass,MapSecondPass.

		</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ManyToOneSecondPass这个类也是静态的,它也是在HbmBinder类中定义的内部类.
		
		</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QuerySecondPass接口有两个实现类ResultSetMappingSecondPass和&nbsp;&nbsp;&nbsp; NamedSQLQuerySecondPass,这两个类都ResultSetMappingBinder的子类.

</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>3,Hibernate N步曲:
	</p>
<p>&nbsp;&nbsp;&nbsp; 1&gt;,new Configuration(): 找hibernate.properties文件,初始化所有Listeners,classes,imports,secondPasses....
	</p>
<p>&nbsp;&nbsp;&nbsp; 2&gt;,configure(): 找到hibernate.hbm.xml文件,将其中的元素解析出来放到Configuration类中的相应的properties里,据cfg.xml文件中指定的hbm.xml名称找到并解析XXX.hbm.xml文件,将配置放到Configuration类中相应的属性中,如classes,imports,collections,tables,sqlFunctions,namedQueries....</p>
<p>&nbsp;&nbsp;&nbsp; 3&gt;,buildSessionFactory:据第二步中生成的Configurations类中的相应属性生成CRUD相关的Sql语句,identifierGenerators,entityPersisters,collectionPersisters... 这一步是Mapping路上实质性的一步.</p>
<p>&nbsp;&nbsp;&nbsp; 4&gt;,有了第三步的准备,就可以调用Session接口中面向对象的CRUD操作了.</p>
          <br/>
          <span style="color:red;">
            <a href="http://database.group.javaeye.com/group/blog/187604#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 28 Apr 2008 16:28:50 +0800</pubDate>
        <link>http://database.group.javaeye.com/group/blog/187604</link>
        <guid>http://database.group.javaeye.com/group/blog/187604</guid>
      </item>
      <item>
        <title>JavaPersistenceWithHibernate读书笔记(6)--持久层与另外可用替代方案</title>
        <author>rmn190</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://rmn190.javaeye.com">rmn190</a>&nbsp;
          链接：<a href="http://database.group.javaeye.com/group/blog/179866" style="color:red;">http://database.group.javaeye.com/group/blog/179866</a>&nbsp;
          发表时间: 2008年04月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1.3 Persistence layers and alternatives<br />&nbsp;&nbsp; In a medium- or large-sized application, it usually makes sense to organize classes by concern<span style="color: #ff0000">(<span style="color: #ff0000">这个by concern写的不错呀!</span>).</span> Persistence is one concern; others include presentation, workflow,and business logic. A typical object-oriented architecture includes layers of code that represent the concerns. It&rsquo;s normal and certainly best practice to group all classes and components responsible for persistence into a separate persistence layer in a layered system architecture. <br />&nbsp;In this section, we first look at the layers of this type of architecture and why we use them. After that, we focus on the layer we&rsquo;re most interested in&mdash;the persistence layer&mdash;and some of the ways it can be implemented. <br />&nbsp;<br />1.3.1 Layered architecture<br />&nbsp;&nbsp; &nbsp;A layered architecture defines interfaces between code that implements the various concerns, allowing changes to be made to the way one concern is implemented without significant disruption to code in the other layers. Layering also determines the kinds of interlayer dependencies that occur. The rules are as follows: <br />&nbsp;&nbsp; &nbsp;■ Layers communicate from top to bottom. A layer is dependent only on the layer directly below it. <br />&nbsp;&nbsp; &nbsp;■ Each layer is unaware of any other layers except for the layer just below it. <br />&nbsp;&nbsp; &nbsp;Different systems group concerns differently, so they define different layers. A typical, proven, high-level application architecture uses three layers: one each for presentation, business logic, and persistence, as shown in figure 1.4. <br />&nbsp;&nbsp; Let&rsquo;s take a closer look at the layers and elements in the diagram<span style="color: #ff0000"><span style="color: #ff0000">(见附件)</span></span>: <br />&nbsp;<br />&nbsp;&nbsp; &nbsp;■ Presentation layer&mdash;The user interface logic is topmost. Code responsible for the presentation and control of page and screen navigation is in the presentation layer. <br />&nbsp;&nbsp; &nbsp;■ Business layer&mdash;The exact form of the next layer varies widely between applications. It&rsquo;s generally agreed, however, that the business layer is responsible for implementing any business rules or system requirements that would be understood by users as part of the problem domain. This layer usually includes some kind of controlling component&mdash;code that knows when to invoke which business rule. In some systems, this layer has its own internal representation of the business domain entities, and in others it reuses the model defined by the persistence layer. We revisit this issue in chapter 3. &nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp; &nbsp;■ Persistence layer&mdash;The persistence layer is a group of classes and components responsible for storing data to, and retrieving it from, one or more data stores. This layer necessarily includes a model of the business domain entities (even if it&rsquo;s only a metadata model). <br />&nbsp;&nbsp; &nbsp;■ Database&mdash;The database exists outside the Java application itself. It&rsquo;s the actual, persistent representation of the system state. If an SQL database is used, the database includes the relational schema and possibly stored procedures. <br />&nbsp;&nbsp; &nbsp;■ Helper and utility classes&mdash;Every application has a set of infrastructural helper or utility classes that are used in every layer of the application (such as Exception classes for error handling). These infrastructural elements don&rsquo;t form a layer, because they don&rsquo;t obey the rules for interlayer dependency in a layered architecture. <span style="color: #ff0000">(这个就是那个界定layer的rules了)</span><br /><br />&nbsp;&nbsp; &nbsp;Let&rsquo;s now take a brief look at the various ways the persistence layer can be implemented by Java applications. Don&rsquo;t worry&mdash;we&rsquo;ll get to ORM and Hibernate soon.There is much to be learned by looking at other approaches.<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp; &nbsp;1.3.2 Hand-coding a persistence layer with SQL/JDBC<br />&nbsp;&nbsp; &nbsp;The most common approach to Java persistence is for application programmers to work directly with SQL and&nbsp; JDBC. After all, developers are familiar with relational database management systems, they understand SQL, and they know how to work with tables and foreign keys. Moreover, they can always use the well-known and widely used data access object (DAO) pattern to hide complex JDBC code and nonportable SQL from the business logic. <br />&nbsp;&nbsp; &nbsp;<br />&nbsp;The DAO pattern is a good one&mdash;so good that we often recommend its use even with ORM<span style="color: #ff0000">(<span style="color: #ff0000">就是应用了ORM后,DAO也很是有用</span>)</span>. However, the work involved in manually coding persistence for each domain class is considerable, particularly when