<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>hideto</title>
    <description>MSN/Skype/Gmail/AIM: hideto.bj@gmail.com</description>
    <link>http://hideto.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>深入ActionMailer，使用Sendmail发邮件</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/210980" style="color:red;">http://hideto.javaeye.com/blog/210980</a>&nbsp;
          发表时间: 2008年07月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          来自: <a href="http://www.beyondrails.com/blogs/11" target="_blank">http://www.beyondrails.com/blogs/11</a><br />ActionMailer现在的实现只支持smtp和Sendmail两种方式发送邮件，配置分别如下:<br /><pre name="code" class="java">
# ActionMailer::Base.delivery_method = :smtp
# ActionMailer::Base.smtp_settings = SMTP_SETTINGS
ActionMailer::Base.delivery_method = :sendmail
ActionMailer::Base.sendmail_settings = SENDMAIL_SETTINGS
SMTP_SETTINGS  = {
  :address => "smtp.gmail.com",
  :port => 587,
  :domain => "beyondrails.com",
  :authentication => :login,
  :user_name => "beyondrails@gmail.com",
  :password => "password"
}
SENDMAIL_SETTINGS = {
  :location       => '/usr/sbin/sendmail',
  :arguments      => '-i -t'
}
</pre><br /><br />为什么只支持这两种方式呢？让我们看下ActionMailer::Base的源码:<br /><pre name="code" class="java">
module ActionMailer
  class Base
    class &lt;&lt; self
      def method_missing(method_symbol, *parameters)#:nodoc:
        case method_symbol.id2name
          when /^create_([_a-z]\w*)/  then new($1, *parameters).mail
          when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
          when "new" then nil
          else super
        end
      end
    end
    def deliver!(mail = @mail)
      raise "no mail object available for delivery!" unless mail
      logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
      begin
        send("perform_delivery_#{delivery_method}", mail) if perform_deliveries
      rescue Exception => e  # Net::SMTP errors or sendmail pipe errors
        raise e if raise_delivery_errors
      end
      return mail
    end
    private
      def perform_delivery_smtp(mail)
        destinations = mail.destinations
        mail.ready_to_send

        Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain], 
            smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
          smtp.sendmail(mail.encoded, mail.from, destinations)
        end
      end

      def perform_delivery_sendmail(mail)
        IO.popen("#{sendmail_settings[:location]} #{sendmail_settings[:arguments]}","w+") do |sm|
          sm.print(mail.encoded.gsub(/\r/, ''))
          sm.flush
        end
      end
    end
  end
end
</pre><br />我们可以清楚的看到ActionMailer的调用顺序，首先是method_missing捕捉deliver_xxx方法，然后进入deliver!方法，然后根据deliver_method进入perform_deliver_xxx方法。<br /><br />可以看到，sendmail方式的实现也非常简单，直接使用IO.popen方法来调用系统命令。<br />使用Sendmail这一Linux下高性能邮件服务器发送邮件，可以避免使用Gmail等邮件系统提供的smtp服务不稳定或需要Recaptcha图片验证等麻烦问题。
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/210980#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><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><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 03 Jul 2008 11:41:56 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/210980</link>
        <guid>http://hideto.javaeye.com/blog/210980</guid>
      </item>
      <item>
        <title>代发Ruby on Rails人才招聘</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/207783" style="color:red;">http://hideto.javaeye.com/blog/207783</a>&nbsp;
          发表时间: 2008年06月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>北京华美汉盛软件技术有限公司诚聘Ruby on rails工程师</strong><br /><br /><strong>基本要求</strong>： <br /><ul><li>1. 对软件工程的相关知识有一定了解；对面向对象开发有较深的理解；</li><li>2. 具备快速/独立的学习能力和思考能力； </li><li>3. 善于沟通，理解团队工作的好处，享受团队作战的乐趣； </li><li>4. 具备良好的英语沟通能力。</li></ul><br /><br /><strong>技能要求</strong>： <br /><ul><li>1. 熟练掌握 Ruby on rails，熟练掌握基本和常用的数据结构； </li><li>2. 良好的关系数据库基础，熟悉至少一种商品化关系数据库产品；</li><li>3. 有相关的WEB开发经验。</li></ul><br /><br /><strong>加分</strong>： <br /><ul><li>1. 掌握一到两门其它的WEB编程语言； </li><li>2. 熟悉 MVC pattern （plus: 能用自己最擅长的语言简单模拟一个 MVC 实现）； </li><li>3. 熟悉 HTTP互联网协议； </li><li>4. 熟悉 HTML/CSS/Javascript, 能在工具/文档的帮助下完成项目界面需求 （plus: 能手写出干净的 HTML/CSS layout 以及 Javascript; 理解 Ajax, 有过 Ajax 的开发经验）；</li><li>5. 熟悉表单处理。 </li></ul><br /><br /><strong>我们公司提供</strong>：<br /><ul><li>1. 平等，独立的小团队工作伙伴关系和宽松、灵活的工作环境；</li><li>2. 具有竞争力的待遇，以及潜在的长期收益机会； </li><li>3. 良好的软件开发过程的学习和实践环境（Scrum + XP）。 </li></ul><br /><br />有意者请发送简历至: <a href="zhaopin@cn-acg.com" target="_blank">zhaopin@cn-acg.com</a>.
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/207783#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><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>Wed, 25 Jun 2008 11:03:23 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/207783</link>
        <guid>http://hideto.javaeye.com/blog/207783</guid>
      </item>
      <item>
        <title>实现Email队列</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/206448" style="color:red;">http://hideto.javaeye.com/blog/206448</a>&nbsp;
          发表时间: 2008年06月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Rails部署环境下使用lighttpd进程实时发送email比较耗时间，对于要求不太紧急的email，可以暂存在Email队列里，利用linux的crontab定时读取发送<br /><br />1,加一张表email_queue:<br /><pre name="code" class="java">
class CreateEmailQueues &lt; ActiveRecord::Migration
  def self.up
    create_table :email_queues do |t|
      t.string :subject
      t.text :content
      t.string :recipient
      t.timestamps
    end
  end

  def self.down
    drop_table :email_queues
  end
end
</pre><br />需要发送Email时就向该表插数据即可<br /><br />2,EmailQueue<br /><pre name="code" class="java">
class EmailQueue &lt; ActiveRecord::Base
  def self.send_all_email_in_queue
    EmailQueue.find(:all, :order => "created_at asc").each do |email|
      ExceptionNotifier.deliver_sys_email(email.recipient, email.subject, email.content)
      email.destroy
    end
  end
end
</pre><br /><br />3,写一个send_sys_email_job.rb文件<br /><pre name="code" class="java">
ENV['RAILS_ENV'] = 'production'
require File.dirname(__FILE__)+'/config/environment'
EmailQueue.send_all_email_in_queue
</pre><br /><br />4,写一个send_sys_email.sh文件<br /><pre name="code" class="java">
#!/bin/bash
S=`ps aux|grep send_sys_email_job|grep -v grep`
if ["$RS" = ""]; then
  echo "No send_sys_email instance, start a new one!"
  /usr/bin/ruby /var/www/vhosts/hideto/html/www.beyondrails.com/send_sys_email_job.rb
else
  echo "Already exists a send_sys_email_job instance, exit!"
  exit
fi
</pre><br />这里有陷阱，调用send_sys_email_job.rb文件时必须写全绝对路径，因为crontab里没有环境变量<br /><br />5,crontab -e<br /><pre name="code" class="java">
*/10 * * * * /var/www/vhosts/hideto/html/www.beyondrails.com/send_sys_email.sh
</pre><br /><br />这样就会每间隔10分钟检查一遍Email队列里有没有邮件并发送
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/206448#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</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, 20 Jun 2008 20:03:32 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/206448</link>
        <guid>http://hideto.javaeye.com/blog/206448</guid>
      </item>
      <item>
        <title>Rails里如何结合ExceptionNotification配置gmail账户发邮件</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/205948" style="color:red;">http://hideto.javaeye.com/blog/205948</a>&nbsp;
          发表时间: 2008年06月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1，安装ExceptionNotification<br /><pre name="code" class="java">
ruby script\plugin install http://dev.rubyonrails.org/svn/rails/plugins/exception_notification/
</pre><br />光安装这个插件是不能利用gmail发送邮件的，因为gmail需要https，所以还需要安装一个插件<br /><br />2，安装action_mailer_tls<br /><pre name="code" class="java">
ruby script/plugin install http://svn.nanorails.com/plugins/action_mailer_tls  
</pre><br /><br />3，修改exception_notifier.rb，添加一个方法<br /><pre name="code" class="java">
# line 40
def exception_notification
  # ...
end

def sys_email(recipients, subject, data={})
  subject    subject
  recipients recipients
  from       sender_address
  body       data
end
</pre><br /><br />4，config目录写一个sys_config.rb文件<br /><pre name="code" class="java">
class SysConfig

  EXCEPTION_NOTIFIER = {
    :delivery_method => :smtp,
    :sender_address => %w(beyondrails@gmail.com),
    :email_prefix   => "BeyondRails",
    :recipients     => %w(hideto.bj@gmail.com),
    :smtp_settings  => {
                        :address => "smtp.gmail.com",
                        :port => 587,
                        :domain => "beyondrails.com",
                        :authentication => :login,
                        :user_name => "beyondrails@gmail.com",
                        :password => "beyondrails@gmail.com的密码"
                          },

  }

end
</pre><br /><br />5，修改environment.rb<br /><pre name="code" class="java">
# ExceptionNotifier settings
ExceptionNotifier.sender_address =  SysConfig::EXCEPTION_NOTIFIER[:sender_address]
ExceptionNotifier.email_prefix = SysConfig::EXCEPTION_NOTIFIER[:email_prefix]
ExceptionNotifier.exception_recipients = SysConfig::EXCEPTION_NOTIFIER[:recipients]
ActionMailer::Base.delivery_method = SysConfig::EXCEPTION_NOTIFIER[:delivery_method]
ActionMailer::Base.smtp_settings = SysConfig::EXCEPTION_NOTIFIER[:smtp_settings]
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.default_charset = "utf-8"
</pre><br /><br />好了！，可以在ruby script\console下面试试发送一封email：<br /><pre name="code" class="java">
 ExceptionNotifier.deliver_sys_email("hideto.bj@gmail.com", "email title", "email data.")
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/205948#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><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 19 Jun 2008 19:56:48 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/205948</link>
        <guid>http://hideto.javaeye.com/blog/205948</guid>
      </item>
      <item>
        <title>lighttpd配置子域名和重定向</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/204962" style="color:red;">http://hideto.javaeye.com/blog/204962</a>&nbsp;
          发表时间: 2008年06月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          前提是域名服务商支持泛域名，加这样三条A记录<br /><pre name="code" class="java">
beyondrails.com       120.72.34.135
www. beyondrails.com   120.72.34.135
*.beyondrails.com     120.72.34.135
</pre><br /><br />1，重定向beyondrails.com到www.beyondrails.com<br /><pre name="code" class="java">
$HTTP["host"] =~ "^beyondrails\.com$" {
  url.redirect = ( "^/(.*)" => "http://www.beyondrails.com/$1" )
}
</pre><br /><br />2，二级域名端口转发<br /><pre name="code" class="java">
$HTTP["host"] =~ "^.*\.beyondrails\.com$" {
  proxy.balance = "hash"
  proxy.server = (
    "" => (("host" => "127.0.0.1", "port" => 9999))
  )
}
</pre><br /><br />3，具体的lighttpd配置<br /><pre name="code" class="java">
$HTTP["host"] =~ "^.*\.beyondrails\.com$" {
  server.document-root = #...
}
</pre><br /><br /><a href="http://trac.lighttpd.net/trac/wiki/Docs%3AModRewrite" target="_blank">lighttpd的URL Rewrites配置语法</a>:<br /><pre name="code" class="java">
url.rewrite-once = ( "&lt;regex>" => "&lt;relative-uri>" )
</pre><br /><br />正则表达式:<br /><pre name="code" class="java">
Patterns ("wildcards") are matched against a string
pecial characters:
        * . (full stop) - match any character
        * * (asterisk) - match zero or more of the previous symbol
        * + (plus) - match one or more of the previous symbol
        * ? (question) - match zero or one of the previous symbol
        * \? (backslash-something) - match special characters
        * ^ (caret) - match the start of a string
        * $ (dollar) - match the end of a string
        * [set] - match any one of the symbols inside the square braces.
        * (pattern) - grouping, remember what the pattern matched as a special variable
        * {n,m} - from n to m times matching the previous character (m could be ommited to mean >=n times)
Normal alphanumeric characters are treated as normal
</pre><br /><br />替换模式:<br /><pre name="code" class="java">
If the matched regex contains groups in parentheses, $1..$9 in the replacement refer to the captured text in the matching group "$1" meaning the first group, "$2" the second, and so on.

You can also use certain meta-patterns in replacement text:
    * %% => % sign
    * %0 => domain name + tld (Top Level Domain, like .com or .net)
    * %1 => tld
    * %2 => domain name without tld
    * %3 => subdomain 1 name
    * %4 => subdomain 2 name
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/204962#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</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>Wed, 18 Jun 2008 00:37:02 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/204962</link>
        <guid>http://hideto.javaeye.com/blog/204962</guid>
      </item>
      <item>
        <title>使用coderay和railscasts样式进行代码高亮</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/204525" style="color:red;">http://hideto.javaeye.com/blog/204525</a>&nbsp;
          发表时间: 2008年06月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://coderay.rubychan.de/" target="_blank">CodeRay</a>是一个语法高亮的Ruby库，效率很不错。<br />CodeRay目前支持的语法包括：<br /><ol><li>Ruby</li><li>C</li><li>Delphi</li><li>HTML</li><li>RHTML (Rails)</li><li>Nitro-XHTML</li><li>YAML</li><li>SQL</li><li>Python</li><li>Perl</li><li>PHP</li><li>Java</li></ol><br /><br /><a href="http://www.railscasts.com" target="_blank">railscasts</a>的播主Ryan Bates自定义了一些css，让ruby、rhtml等代码看起来非常cool！<br /><br />1，安装coderay gem<br /><pre name="code" class="java">
gem install coderay
</pre><br /><br />2，在application.rb中<br /><pre name="code" class="java">
require 'coderay'
</pre><br /><br />3，在application_helper.rb里添加一个helper方法<br /><pre name="code" class="java">
def parse_coderay(text)
  text.scan(/(\[code\:([a-z].+?)\](.+?)\[\/code\])/m).each do |match|
    text.gsub!(match[0],CodeRay.scan(match[2].strip, match[1].to_sym).div( :line_numbers => :table,:css => :class))
  end
  return text
end
</pre><br />这样，我们的文本输入框就支持这样的code标签了:<br /><pre name="code" class="java">
\[code:ruby\]
def aaa
   puts "aaaa"
end
\[\/code\]
</pre><br />code:ruby这样的标签中ruby可以用上面提到的支持的语言来替代<br /><br />4，借用railscasts的coderay样式<br /><a href="http://railscasts.com/stylesheets/coderay.css" target="_blank">http://railscasts.com/stylesheets/coderay.css</a><br /><br />5，在html.erb页面中<br /><pre name="code" class="java">
&lt;div class="CodeRay">&lt;%= parse_coderay @post.content %>&lt;/div>
</pre><br /><br />效果如下：<br /><img src="http://www.javaeye.com/upload/attachment/27903/90c8ab2a-465c-3ed9-b6ad-c228f84920c3.jpg" />
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/204525#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/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 17 Jun 2008 00:16:49 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/204525</link>
        <guid>http://hideto.javaeye.com/blog/204525</guid>
      </item>
      <item>
        <title>Capistrano试用</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/204287" style="color:red;">http://hideto.javaeye.com/blog/204287</a>&nbsp;
          发表时间: 2008年06月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1，客户端机器安装Capistrano<br /><pre name="code" class="java">
gem install -y capistrano
</pre><br /><br />2，应用到项目<br /><pre name="code" class="java">
cd D:\projects\beyondrails
capify .
</pre><br /><br />3，修改config\deploy.rb<br /><pre name="code" class="java">
DEPLOY_PATH = "/var/www/vhosts/hideto/html/www.beyondrails.com"

set :application, "beyondrails"
set :repository, "https://beyondrails.googlecode.com/svn/trunk/beyondrails"
set :scm_username, "hideto.bj"
set :scm_password, "######"

set :deploy_to, DEPLOY_PATH
set :user, "hideto"
set :password, "######"
ssh_options[:port] = 2048

role :app, "120.72.34.135"
role :web, "120.72.34.135"
role :db,  "hideto", :primary => true


desc "Restart the web server for www.beyondrails.com"
task :restart, :roles => :app do
  run "cd ~;./restart-www.beyondrails.com.sh"
  quit
end

desc "Stop the web server for www.beyondrails.com"
task :stop, :roles => :app do
  run "cd ~;./stop-www.beyondrails.com.sh"
end

desc "Update and restart web server for www.beyondrails.com"
task :update_and_restart, :roles => :app do
  run "cd #{DEPLOY_PATH};svn up"
  run "cd #{DEPLOY_PATH};rake db:migrate"
  run "cd ~;./restart-www.beyondrails.com.sh"
end
</pre><br /><br />4，调用task<br /><pre name="code" class="java">
cap update_and_restart
cap stop
cap restart
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/204287#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/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 16 Jun 2008 19:05:07 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/204287</link>
        <guid>http://hideto.javaeye.com/blog/204287</guid>
      </item>
      <item>
        <title>lighttpd真垃圾啊</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/200227" style="color:red;">http://hideto.javaeye.com/blog/200227</a>&nbsp;
          发表时间: 2008年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          使用lighttpd+fcgi跑Rails程序，文件上传会silently failed，任何报错都没有<br /><a href="http://trac.lighttpd.net/trac/ticket/338" target="_blank">http://trac.lighttpd.net/trac/ticket/338</a><br />我研究了一下午，未果。<br />大家有没有解决方法？
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/200227#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><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><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 04 Jun 2008 18:38:46 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/200227</link>
        <guid>http://hideto.javaeye.com/blog/200227</guid>
      </item>
      <item>
        <title>将gem变成plugin</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/200032" style="color:red;">http://hideto.javaeye.com/blog/200032</a>&nbsp;
          发表时间: 2008年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          有什么样的需求就有什么样的对策<br /><br />当vhost上的帐号没有gem install权限时，我们可以利用ruby、rails灵活多变的特性，将gem改为Rails插件来用<br /><br />首先本地安装gem，然后按照plugin目录结构创建init.rb和lib文件夹，然后将本地gem目录里的lib文件夹里的rb文件copy到plugin的lib文件夹，然后修改init.rb，require位于plugin下的lib文件夹里的主文件，ok！
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/200032#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/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 04 Jun 2008 11:27:27 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/200032</link>
        <guid>http://hideto.javaeye.com/blog/200032</guid>
      </item>
      <item>
        <title>模拟Ajax提交上传文件</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/199941" style="color:red;">http://hideto.javaeye.com/blog/199941</a>&nbsp;
          发表时间: 2008年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          XMLHTTP不支持文件上传这种form提交，但是我们可以模拟ajax上传文件:<br /><pre name="code" class="java">
&lt;iframe name="upload_iframe" style="display: none;">&lt;/iframe>

&lt;form target="upload_iframe">
  ...
&lt;/form>
</pre><br />这样form提交时target为一个隐藏的iframe<br /><br />上传成功后返回的页面里可以加javascript来留为callback，需要注意的是iframe里调用外部DOM时需要这样做:<br /><pre name="code" class="java">
  parent.document.getElementById("upload_form").xxx
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/199941#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</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>Wed, 04 Jun 2008 00:24:39 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/199941</link>
        <guid>http://hideto.javaeye.com/blog/199941</guid>
      </item>
      <item>
        <title>在Rails里使用ReCaptcha添加验证码</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/199757" style="color:red;">http://hideto.javaeye.com/blog/199757</a>&nbsp;
          发表时间: 2008年06月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1，去<a href="http://recaptcha.net/" target="_blank">http://recaptcha.net/</a>sign up，获得pub key和priv key<br />2，安装recaptcha gem<br /><pre name="code" class="java">
gem install --source http://www.loonsoft.com/recaptcha/pkg/ recaptcha
</pre><br />3，在environment.rb里设置key<br /><pre name="code" class="java">
require 'recaptcha'
RCC_PUB = 'pub key'
RCC_PRIV = 'priv key'
</pre><br />4，修改application.rb<br /><pre name="code" class="java">
class ApplicationController &lt; ActionController::Base
  include ReCaptcha::AppHelper
</pre><br />5，修改application_helper.rb<br /><pre name="code" class="java">
module ApplicationHelper
  include ReCaptcha::ViewHelper
</pre><br />6，在页面上显示ReCaptcha验证码<br /><pre name="code" class="java">
&lt;%= get_captcha %>
</pre><br />7，在Controller里验证验证码<br /><pre name="code" class="java">
if validate_recap(params, @comment.errors) && @comment.save
  flash[:notice] = 'Comment was successfully created.'
  format.html { redirect_to post_path(@comment.post.url_slug) }
  format.xml  { render :xml => @comment, :status => :created, :location => @comment }
</pre><br />看了下recaptcha源码，对于本地访问时validate_recap始终为true，对于错误的域名也始终为true<br /><br />最终的样子：<br /><img src="http://recaptcha.net/images/captchaHomePage.gif" />
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/199757#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><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><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jun 2008 15:51:03 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/199757</link>
        <guid>http://hideto.javaeye.com/blog/199757</guid>
      </item>
      <item>
        <title>重启lighttpd进程的脚本</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/199550" style="color:red;">http://hideto.javaeye.com/blog/199550</a>&nbsp;
          发表时间: 2008年06月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">
#!/bin/sh
HTTPD_PID=`cat /tmp/hideto-lighttpd-9527.pid`
SUCCEED_FLAG="Syntax OK"
TEST_RESULT=`lighttpd -t -f /var/www/vhosts/hideto/conf/lighttpd/9527.conf`
echo $TEST_RESULT;
if [ "$TEST_RESULT" = "$SUCCEED_FLAG" ]; then
    echo "Now stopping lighttpd at 9527...";
    kill -INT $HTTPD_PID
    echo "Starting new lighttpd at 9527..."
    lighttpd -f /var/www/vhosts/hideto/conf/lighttpd/9527.conf &
    echo "9527 done."
else
    echo "Config Error, nothing to do. Please change your config and retry";
fi
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/199550#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</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, 02 Jun 2008 21:31:45 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/199550</link>
        <guid>http://hideto.javaeye.com/blog/199550</guid>
      </item>
      <item>
        <title>《Project 宝典》笔记1，理解项目</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197952" style="color:red;">http://hideto.javaeye.com/blog/197952</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          《Project 宝典》笔记1，理解项目<br /><br /><strong>什么是项目</strong><br />项目是一系列的步骤，通常由一个以上的人员执行<br /><pre name="code" class="java">
1，项目具有特定的和可测量的目标
2，项目具有特定的期限
3，项目使用资源
4，组成项目的所有相互依赖的或单独的步骤都称为任务
</pre><br /><br /><strong>项目管理和项目经理所关注的是下列关键领域</strong><br /><pre name="code" class="java">
1，日程安排
2，预算
3，资源管理
4，进度跟踪与报告
</pre><br /><br /><strong>使用项目管理软件可以执行下列任务</strong><br /><pre name="code" class="java">
1，预先计划
2，查看进度
3，识别冲突
4，进行调整
5，生成专业报告
</pre><br /><br /><strong>使用Project管理项目</strong><br />创建日程表<br /><pre name="code" class="java">
1，单个任务名称
2，任务工期
3，任务相关性
</pre><br />跟踪任务成本<br /><pre name="code" class="java">
1，人力和物质资源清单，以及在标准时间和超时状态下的成本
2，对特定任务的资源分配
</pre><br />在整个生命周期内跟踪项目<br /><pre name="code" class="java">
1，任务进度
2，任务执行时间或相关性的变化
3，资源的变化
4，资源时间委托和成本的变化
</pre><br /><br /><strong>项目的生命周期</strong><br /><pre name="code" class="java">
1，确定目标和项目的范围
2，规划
3，修订
4，跟踪
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197952#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</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>Wed, 28 May 2008 17:46:39 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197952</link>
        <guid>http://hideto.javaeye.com/blog/197952</guid>
      </item>
      <item>
        <title>《从优秀到卓越》4-6章笔记</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197887" style="color:red;">http://hideto.javaeye.com/blog/197887</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          《从优秀到卓越》4-6章笔记<br /><br /><span style="color: red"><strong>第四章 直面残酷的现实（但决不失去信念）</strong></span><br /><br /><strong>对国家领导人而言，没有什么错误会比误以为事情会自行解决的妄想更令人不可饶恕了</strong><br /><br /><strong>追求卓越的愿望本身并没有什么错。实现跨越的公司也着手追求卓越，但与对照公司不同的是，它们依靠残酷的现实来不断监督它们通向成功的道路</strong><br /><br /><strong>如何形成问题真相不被掩盖的气氛</strong><br /><pre name="code" class="java">
1, 领导应多提出些问题，少要求些答案
2, 要对话、要争论，但不要强制
3, 作彻底的事后分析，不要相互指责
4, 建立“红旗”机制，把信息转化成无法忽视的信息
</pre><br /><br /><strong>要将公司领导得出色，不是意味着要从答案开始，然后让你的雇员跟着你走，而是意味着要虚怀若谷，你得承认这样的事实，你还没有了解全部情况，应该多提些问题来帮助自己更好把握情况</strong><br /><br /><strong>在争吵和战斗中前进</strong><br /><br /><strong>斯托克代尔悖论：坚持你一定会成功的信念，同时，要面对现实中最残忍的事实，不论有多大困难，不论它们是什么</strong><br /><br /><strong>若不首先诚实面对现实，就永远不可能作出一系列正确的决定</strong><br /><br /><strong>将公司从优秀领向卓越的首要任务，是创造这样的一个文化氛围：在那里，人们有无数的机会会被倾听，这样，事实最终也可以被听到</strong><br /><br /><strong>有魄力可以是一种财富，也可以是一种累赘。过于强硬的领导个性，会阻碍一个人直面残酷的现实</strong><br /><br /><strong>领导不是始于远见卓识，而是始于让人面对残酷的现实，并积极地采取行动</strong><br /><br /><strong>花时间与精力来“激励”人是巨大的浪费。真正的问题不是“如何激励员工”。如果你有合适的人，他们就会自我激励。关键是不要打击他们的积极性。而最令人泄气的事情，莫过于忽视残酷的现实。</strong><br /><br /><span style="color: red"><strong>第五章 刺猬理念（三环内部的简化）</strong></span><br /><br /><strong>认知你自己</strong><br /><br /><strong>那些实现跨越公司的精英，在某种程度上都是刺猬。他们运用自己的刺猬本性，为公司努力建立我们今天所称的“刺猬理念”。那些领导对照公司的人倾向于做狐狸，从来没有获得刺猬理念的优势。他们的思想是分散的、不集中的、不连贯的。</strong><br /><br /><strong>实现跨越公司与对照公司的本质区别，表现在两个基本方面：第一，实现跨越的公司把战略建立在对三个主要方面的深刻理解上--就是我们所称的三环；第二，实现跨越的公司把它们的理解转化为一个简单明确的理念来指导所有工作--就是“刺猬理念”</strong><br /><br /><strong>三环：</strong><br /><pre name="code" class="java">
1，你能够在什么方面成为世界上最优秀的
2，是什么驱动你的经济引擎
3，你对什么充满热情
</pre><br /><br /><strong>实现跨越，要求对三环的相交有深刻理解。这三个圆环被概括为一个简单而清晰的理念（刺猬理念）</strong><br /><br /><strong>如果你不能在你的核心业务上成为世界上最优秀的，那么它就不能构成你的刺猬理念的基础</strong><br /><br /><strong>实现跨越的公司更像刺猬--简单而不引人注目，只知道“一件大事”，并且坚持不懈。对照公司更像狐狸--狡猾而诡诈的动物，知道很多事情，但是缺乏一致性</strong><br /><br /><span style="color: red"><strong>第六章 训练有素的文化</strong></span><br /><br /><strong>自由是片面的，并不完全是真理。所以我建议将东海岸的自由女神像替换成西海岸的责任神像。</strong><br /><br /><strong>实现跨越的公司建立了一贯制度，但他们也给予员工制度框架下的自由和责任。他们聘用严于自律无需管理的人，公司只需管理系统，而不需管理这些人</strong><br /><br /><strong>每个人都想成为最好的，但大多数组织缺乏纪律，不了解自己，不清楚自己的最大优势是什么，凭借什么把潜力变成现实。他们缺乏严格的训练有素的文化规范自己</strong><br /><br /><strong>是文化而不是暴政</strong><br /><br /><strong>实现跨越的公司完全彻底遵循这样一条原则：与刺猬理念不一致的，我们就不用。我们不会涉足无关行业，不会做无关的兼并。只要不合适，我们就不做</strong><br /><br /><strong>对待难得的机会要敢于说“不”。因为如果它不适合三环理论，即便它是“一生中惟一的机会”也不相干</strong><br /><br /><strong>在从优秀到卓越转变时期，预算用来决定哪些行业应全力投资，哪些根本不要投资。换句话说，预算过程不是估算每个项目应投多少钱，而是决定哪些项目最符合刺猬理念应该重点做好，哪些应该完全放弃</strong><br /><br /><strong>列出不能做的事项比列出打算做的事项更重要</strong>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197887#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/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</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>Wed, 28 May 2008 15:56:42 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197887</link>
        <guid>http://hideto.javaeye.com/blog/197887</guid>
      </item>
      <item>
        <title>MySQL存储程序开发最佳实践</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197842" style="color:red;">http://hideto.javaeye.com/blog/197842</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL存储程序开发最佳实践<br /><br /><strong>1，开发流程</strong><br /><pre name="code" class="java">
1，写代码之前设立标准和指南
2，遇到一个问题30分钟不能解决之后寻求帮助
3，相互review代码
4，利用独立的测试人员进行测试
5，使用代码版本控制
</pre><br /><br /><strong>2，编码风格和惯例</strong><br /><pre name="code" class="java">
1，使用一致的、可读性强的代码风格
2，使用有逻辑意义的、一致的命名规范
3，使用block和loop labels作为自我诠释文档
4，复杂的表达式使用括号
5，使用纵向的代码排列来强调纵向关系
6，注释代码简明扼要并且只写“增值”信息
</pre><br /><br /><strong>3，变量</strong><br /><pre name="code" class="java">
1，使用一致的而且有意义的变量命名风格
2，避免在内部block里覆盖变量声明
3，使用function来替代复杂的表达式
4，移除不用的变量和代码
5，不要假设表达式的结果为TRUE或FALSE，它可能为NULL
6，尽量少使用用户变量作为全局数据
7，在strict mode下创建存储程序来避免非法的数据赋值
</pre><br /><br /><strong>4，条件逻辑</strong><br /><pre name="code" class="java">
1，对互斥的条件语句使用ELSEIF
2，只使用IF...ELSEIF来测试单独的、简单的条件
3，确保CASE语句能够包含所有的情况，或者构建一个Handler来捕获没有匹配的情况
4，不要混合使用CASE和IF
</pre><br /><br /><strong>5，循环处理</strong><br /><pre name="code" class="java">
1，确保循环会终结
2，在循环的终结条件里显式的声明所有条件，不要在内部使用LEAVE或RETURN
3，在简单的循环里使用一个单独的LEAVE
4，使用一个简单的循环来避免循环必需的冗余代码
</pre><br /><br /><strong>6，异常处理</strong><br /><pre name="code" class="java">
1，处理不可避免但又能够预期的异常
2，使用named condition来提高代码可读性
3，在异常处理时使用一致的SQLSTATE或MySQL error code，不要混合使用
4，避免使用全局的SQLEXCEPTION Handler，除非MySQL实现了SIGNAL和SQLCODE特性
</pre><br /><br /><strong>7，存储程序里的SQL</strong><br /><pre name="code" class="java">
1，显式使用START TRANSACTION语句来打开事务
2，不要让事务“悬空”，要负责结束事务
3，避免使用savepoint，它们让程序逻辑变模糊，并降低程序效率
4，使用合适的锁策略
5，保持事务简短
6，在完成cursor循环之后一定要设置NOT FOUND变量
7，当需要更新记录时使用SELECT FOR UPDATE
8，避免在可能用在SQL里的function里引入SQL语句，嵌套查询会严重消耗性能
</pre><br /><br /><strong>8，动态SQL</strong><br /><pre name="code" class="java">
1，在动态SQL字符串里使用绑定而不是连接变量值，这样可以避免SQL注入
2，小心检查任何可能用于构建动态SQL的参数值
3，考虑为执行动态SQL的存储过程添加INVOKER权限
</pre><br /><br /><strong>9，程序构建</strong><br /><pre name="code" class="java">
1，封装业务逻辑和方程到准确命名的function里
2，使用function和procedure模板来标准化模块
3，使用模块化来限制程序代码50-60行
4，避免程序的副作用
5，避免条件和循环的深层嵌套
6，限制执行部分只有一个单独的RETURN语句
7，使用存储程序来实现对多个trigger公用的代码
</pre><br /><br /><strong>10，性能</strong><br /><pre name="code" class="java">
1，专注于SQL调优来提高存储程序性能
2，为你的程序小心创建最好的索引集
3，避免意外的全表扫描
4，优化必要的全表扫描
5，避免将计算消耗过大的逻辑放在存储程序里
6，将循环中不变的表达式移出循环
7，优化条件结构
8，将匹配率较大的条件放在IF和CASE语句的前面
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197842#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/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 28 May 2008 13:56:01 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197842</link>
        <guid>http://hideto.javaeye.com/blog/197842</guid>
      </item>
      <item>
        <title>MySQL join的文章</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197822" style="color:red;">http://hideto.javaeye.com/blog/197822</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.blogjava.net/chenpengyi/archive/2005/10/17/15747.html" target="_blank">MySQL的联结（Join）语法</a>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197822#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/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</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, 28 May 2008 13:00:21 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197822</link>
        <guid>http://hideto.javaeye.com/blog/197822</guid>
      </item>
      <item>
        <title>MySQL索引系列文章</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197820" style="color:red;">http://hideto.javaeye.com/blog/197820</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.easyjf.com/blog/html/20071231/1015809.html" target="_blank">MySQL索引使用</a><br /><a href="http://www.blogjava.net/jiangshachina/archive/2007/07/03/127728.html" target="_blank">MySQL索引</a><br /><a href="http://dev.mysql.com/doc/refman/5.1/zh/optimization.html#indexes" target="_blank">MySQL 5.1参考手册 :: 7. 优化</a><br /><a href="http://www.knowsky.com/2788.html" target="_blank">MySQL索引分析和优化</a>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197820#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><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, 28 May 2008 12:51:03 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197820</link>
        <guid>http://hideto.javaeye.com/blog/197820</guid>
      </item>
      <item>
        <title>MySQL存储程序权限控制</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197812" style="color:red;">http://hideto.javaeye.com/blog/197812</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL存储程序权限控制<br /><br />MySQL5.0引入了一些管理存储程序的权限:<br /><pre name="code" class="java">
CREATE ROUTINE: 允许用户创建新的存储程序
ALTER ROUTINE: 允许用户修改security mode/SQL mode/comment
EXECUTE: 允许用户只需存储程序
</pre><br /><br />给用户赋予权限:<br /><pre name="code" class="java">
GRANT CREATE ROUTINE ON mydatabase.* TO sp_creator;
GRANT ALTER ROUTINE ON mydatabase.* TO sp_creator;
GRANT EXECUTE ON mydatabase.* TO sp_creator;
GRANT EXECUTE ON PROCEDURE mydatabase.test1 TO sp_creator;
</pre><br /><br />存储程序的执行安全模式:<br /><pre name="code" class="java">
SQL SECURITY DEFINER: 默认模式，存储程序的定义者才能执行
SQL SECURITY INVOKER: 调用者都可以执行，但是权限细粒度的控制转交到存储程序里的SQL语句的执行权限
</pre><br /><br />存储程序里封装访问权限的例子:<br /><pre name="code" class="java">
CREATE PROCEDURE sp_employee_list(in_department_id DECIMAL(8,0)
    SQL SECURITY DEFINER READS SQL DATA
BEGIN
    DECLARE l_user_name VARCHAR(30);
    DECLARE l_not_found INT DEFAULT 0;
    DECLARE l_department_name VARCHAR(30);
    DECLARE l_manager_id INT;

    DECLARE user_csr CURSOR FOR
        SELECT d.department_name, e.manager_id
            FROM departments d JOIN employees e USING(department_id)
            WHERE db_user=l_user_name;

    DECLARE CONTINUE HANDLER FOR  NOT FOUND SET l_not_foun=1;

    /* Strip out the host from the user name */
    SELECT SUBSTR(USER(),1,INSTR(USER(), '@')-1)
        INTO l_user_name;

    OPEN user_csr;
    FETCH user_csr INTO l_department_name, l_manager_id;
    CLOSE user_csr;

    IF l_department_name='PAYROLL' OR l_manager_id IN (0, 1) THEN
        SELECT surname, firstname, salary
            FROM employees
            WHERE department_id=in_department_id
            ORDER BY employee_id;
    ELSE
        /* Not authorized to see salary */
        SELECT surname, firstname, 'XXXXXXXX' AS salary
            FROM employees
            WHERE department_id=in_department_id
            ORDER BY employee_id;
    END IF;

END;
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197812#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><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, 28 May 2008 12:29:43 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197812</link>
        <guid>http://hideto.javaeye.com/blog/197812</guid>
      </item>
      <item>
        <title>MySQL的Stored Function和Trigger</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197570" style="color:red;">http://hideto.javaeye.com/blog/197570</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL的Stored Function和Trigger<br /><br /><strong>Stored Function</strong><br />存储Function可以作为表达式在内建方法可以调用的地方使用以及SELECT、UPDATE、DELETE、INSERT语句中使用<br /><pre name="code" class="java">
CREATE FUNCTION function_name(parameter[,...])
    RETURNS datatype
    [LANGUAGE SQL]
    [ [NOT] DETERMINISTIC]
    [ {CONTAINS SQL | NO SQL | MODIFIES SQL DATA | READS SQL DATA} ]
    [ SQL SECURITY {DEFINER|INVOKER} ]
    [COMMENT comment_string ]
    function_statements
</pre><br />三点与存储过程不同的地方：<br />1，必须要一个RETURNS语句来定义返回值类型<br />2，不能指定参数的IN、OUT或INOUT修饰符，所有参数隐式的为IN<br />3，Function体必须包含RETURN语句来终结Function执行并返回指定的结果给调用者<br /><br />Example:<br /><pre name="code" class="java">
CREATE FUNCTION cus_status(in_status CHAR(1))
    RETURNS VARCHAR(20)
BEGIN
    DECLARE long_status VARCHAR(20);

    IF in_status = 'O' THEN
        SET long_status='Overdue';
    ELSEIF in_status = 'U' THEN
        SET long_status='Up to date';
    ELSEIF in_status = 'N' THEN
        SET long_status='New';
    END IF;

    RETURN(long_status);
END;
</pre><br />好像MySQL当前最新版本(5.1)还不支持嵌套的Stored Function，仅支持嵌套的Stored Procedure<br /><br /><strong>Trigger</strong><br />Trigger是数据库中的事件触发，当前MySQL的实现是对特定table的DML语句(INSERT/UPDATE/DELETE)调用时触发<br /><pre name="code" class="java">
CREATE [DEFINER={user|CURRENT_USER}] TRIGGER trigger_name
  {BEFORE|AFTER}
  {INSERT|UPDATE|DELETE}
ON table_name
FOR EACH ROW
trigger_statements
</pre><br />AFTER类型的Trigger不能修改NEW记录的值<br />如果同时对大量的行做操作，Trigger可能性能开销较大，所以尽量避免在Trigger里放入性能消耗大的SQL语句<br /><br />Example: Using trigger to implememt audit logging<br /><pre name="code" class="java">
CREATE TRIGGER account_balance_au
    AFTER UPDATE ON account_balance FOR EACH ROW
    BEGIN
        INSERT into transaction_log
            (user_id, description)
            VALUES(user(),
                CONCAT('Adjusted account ',
                    NEW.account_id, ' from ', OLD.balance,
                        ' to ', NEW.balance));
END;
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197570#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/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 18:58:16 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197570</link>
        <guid>http://hideto.javaeye.com/blog/197570</guid>
      </item>
      <item>
        <title>Rails里给文件上传添加progress_bar</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197517" style="color:red;">http://hideto.javaeye.com/blog/197517</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          文件上传很慢时，UI没有什么用户提示，这样让人很费解，所以我们可以给文件上传添加一个动态而美观的progress_bar<br /><br />首先给form_for添加一个onsubmit事件，并在form_for下紧跟一个显示progress_bar的div：<br /><pre name="code" class="java">
&lt;% form_for(:asset, :url => assets_path, :html => { :multipart => true, :onsubmit => "show_progress_bar(this);" }) do |f| %>
  &lt;p>
    &lt;b>上传文件&lt;/b>&lt;br />
    &lt;%= f.file_field :uploaded_data %>
  &lt;/p>

  &lt;p>
    &lt;%= submit_tag "提交" %>
  &lt;/p>
&lt;% end %>
&lt;div class="progress_bar_div" id="progress_bar_div" style="display: none;">
	&lt;dl>
		&lt;dt>正在上传中，请稍候...&lt;/dt>
		&lt;dd>
			&lt;span>&lt;em id="progress_bar" style="left:200px">&lt;/em>&lt;/span>
		&lt;/dd>
	&lt;/dl>
&lt;/div>

&lt;script>
	function show_progress_bar(x) {
		x.setAttribute("style", "display: none;");
		document.getElementById("progress_bar_div").setAttribute("style", "display: inline;");
	}
&lt;/script>
</pre><br />这样，在form提交之前隐藏表单，并显示progress_bar，直到form提交完成，页面跳转<br /><img src="http://hideto.javaeye.com/upload/attachment/25636/70ab70f8-4d11-33dd-b029-c47966370ca2.jpg" /><br /><img src="http://hideto.javaeye.com/upload/attachment/25638/448a9240-6e8f-34c1-b547-e71ccc1de276.jpg" />
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197517#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 17:00:22 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197517</link>
        <guid>http://hideto.javaeye.com/blog/197517</guid>
      </item>
      <item>
        <title>attachment_fu的一个bug</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/197503" style="color:red;">http://hideto.javaeye.com/blog/197503</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          上传文件的size经常结果为0，让人很费解<br />解决办法，attachment_fu.rb:<br /><pre name="code" class="java">
# about line 300
def uploaded_data=(file_data)
  return nil if file_data.nil? || file_data.size == 0
  self.content_type = file_data.content_type
  self.filename     = file_data.original_filename if respond_to?(:filename)
  if file_data.is_a?(StringIO)
    file_data.rewind
    self.temp_data = file_data.read
  else
    self.temp_path = file_data
    self.size = file_data.size
  end
end


# about line 380
def set_size_from_temp_path
  self.size = File.size(temp_path) if save_attachment? && self.size.nil?
end
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/197503#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/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 16:25:47 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/197503</link>
        <guid>http://hideto.javaeye.com/blog/197503</guid>
      </item>
      <item>
        <title>MySQL内建Function</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/195747" style="color:red;">http://hideto.javaeye.com/blog/195747</a>&nbsp;
          发表时间: 2008年05月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL内建Function<br /><br />在MySQL存储程序（存储过程、存储Function、触发器）里可以使用几乎任何MySQL内建Function<br />常用的Function为：<br /><pre name="code" class="java">
1，字符串function
2，数字function
3，时间和日期function
4，其他function
</pre><br /><br /><strong>1，字符串function</strong><br />字符串function处理VARCHAR/CHAR/TEXT等字符串数据类型<br /><br />1)ASCII<br /><pre name="code" class="java">
string = ASCII(string)
</pre><br />返回字符串中首个字符的ASCII码<br /><br />2)CHAR<br /><pre name="code" class="java">
string = CHAR(acsii_code[, ...])
</pre><br />返回ASCII码相应的字符<br /><br />3)CHARSET<br /><pre name="code" class="java">
character_set = CHARSET(string)
</pre><br />返回字符串对应的字符集<br /><br />4)CONCAT<br /><pre name="code" class="java">
string = CONCAT(string [, ...])
</pre><br />返回字符串相加的结果<br />如果sql_mode='ANSI'，还可以使用||操作符来进行字符串相加<br /><br />5)CONCAT_WS<br /><pre name="code" class="java">
string = CONCAT_WS(delimiter, string [, ...])
</pre><br />大致和CONCAT相同，但是会在字符串之间插入delimiter，相当于ruby里的join<br /><br />6)INSERT<br /><pre name="code" class="java">
string = INSERT(original_string, position, length, new_string)
</pre><br />在指定position将new_string插入original_string，直到length个字符<br /><br />7)INSTR<br /><pre name="code" class="java">
position = INSTR(string, substring)
</pre><br />返回substring在string中第一个出现的position，如果没有找到，则返回0<br /><br />8)LCASE<br /><pre name="code" class="java">
string = LCASE(string)
</pre><br />返回string转换为lowercase的形式<br /><br />9)LEFT<br /><pre name="code" class="java">
string = LEFT(string, length)
</pre><br />返回string最左边length长度的字符串<br /><br />10)LENGTH<br /><pre name="code" class="java">
characters = LENGTH(string)
</pre><br />返回字符串的长度，对于多字节字符串则应该使用CHAR_LENGTH来返回字符的长度而不是字节的长度<br /><br />11)LOAD_FILE<br /><pre name="code" class="java">
string = LOAD_FILE(file_name)
</pre><br />读取MySQL服务器上的一个文件并返回合适类型（BLOG或TEXT）<br /><br />12)LOCATE<br /><pre name="code" class="java">
position = LOCATE(substring, string [,start_position])
</pre><br />类似于INSTR，它在string里搜索一个substring，并可以知道开始搜索的位置，如果没有找到则返回0<br /><br />13)LPAD<br /><pre name="code" class="java">
string = LPAD(string, length, pad)
</pre><br />将pad加到string的左边直到输出的字符串的长度达到length<br /><br />14)LTRIM<br /><pre name="code" class="java">
string = LTRIM(string)
</pre><br />去掉string左边所有的空格<br /><br />15)REPEAT<br /><pre name="code" class="java">
string = REPEAT(string, count)
</pre><br />返回string重复count次数的字符串<br /><br />16)REPLACE<br /><pre name="code" class="java">
string = REPLACE(string, search_string, replace_string)
</pre><br />在string中搜索search_string，用replace_string来替换<br /><br />17)RPAD<br /><pre name="code" class="java">
string = RPAD(string, length, pad)
</pre><br />将pad加到string的右边直到输出的字符串的长度达到length<br /><br />18)RTRIM<br /><pre name="code" class="java">
string = RTRIM(string)
</pre><br />去掉string右边所有的空格<br /><br />19)STRCMP<br /><pre name="code" class="java">
string = STRCMP(string1, string2)
</pre><br />比较两个字符串大小，类似于C里的strcmp函数，返回值为0，-1，1<br /><br />20)SUBSTRING<br /><pre name="code" class="java">
string = SUBSTRING(string, position, [,length])
</pre><br />返回从指定position开始length长度的子字符串，position可以为负数<br /><br />21)TRIM<br /><pre name="code" class="java">
string = TRIM([[BOTH|LEADING|TRAILING] [padding] FROM] string)
</pre><br />默认trim空格，指定padding则trim padding，可以知道trim前面、后面或两者<br /><br />22)UCASE<br /><pre name="code" class="java">
string = UCASE(string)
</pre><br />返回string转换为uppercase的形式<br /><br />23)其他function<br /><pre name="code" class="java">
BINARY
BIT_LENGTH
CHAR_LENGTH
CHARACTER_LENGTH
COMPRESS
DECODE
ELT
ENCODE
ENCRYPT
EXPORT_SET
FIELD
INET_ATON
INET_NTOA
LOWER
MID
OCTET_LENGTH
ORD
PASSWORD
POSITION
QUOTE
REVERSE
RIGHT
SHA
SHA1
SOUNDEX
SPACE
SUBSTRING_INDEX
UNCOMPRESSED_LENGTH
UNCOMPRESS
UNHEX
UPPER
</pre><br /><br /><strong>2，数字function</strong><br />数字function处理INT/FLOAT等数字数据类型<br /><br />1)ABS<br /><pre name="code" class="java">
number = ABS(number)
</pre><br />返回绝对值<br /><br />2)BIN<br /><pre name="code" class="java">
binary_number = BIN(decimal_number)
</pre><br />返回decimal_number的二进制形式<br /><br />3)CEILING<br /><pre name="code" class="java">
number = CEILING(numer)
</pre><br />返回稍大的整数<br /><br />4)CONV<br /><pre name="code" class="java">
number = CONV(number,from_base,to_base)
</pre><br />将number从from_base进制转换为to_base进制<br /><br />5)FLOOR<br /><pre name="code" class="java">
number = FLOOR(number)
</pre><br />返回比number稍小的整数<br /><br />6)FORMAT<br /><pre name="code" class="java">
string = FORMAT(number, decimal_places)
</pre><br />使用指定的decimal_place小数位来格式化number，并在每个千位上加上逗号来进行分隔<br /><br />7)HEX<br /><pre name="code" class="java">
HexNumber = HEX(DecimalNumber)
</pre><br />返回16进制形式<br /><br />8)LEAST<br /><pre name="code" class="java">
number = LEAST(number1, number 2 [, ...])
</pre><br />返回最小的数<br /><br />9)MOD<br /><pre name="code" class="java">
remmainder = MOD(numerator, denominator)
</pre><br />求模<br /><br />10)POW/POWER<br /><pre name="code" class="java">
result = POWER(number, power)
</pre><br />求幂，power可以为整数、小数、负数<br /><br />11)RAND<br /><pre name="code" class="java">
number = RAND([seed])
</pre><br />返回大于0小于1的随机浮点数，指定seed可以用来初始化随机数generator从而避免生产重复的sequence<br /><br />12)ROUND<br /><pre name="code" class="java">
integer = ROUND(number [,decimals])
</pre><br />将浮点数转换为最接近的整数，四舍五入，可以指定精确到小数位decimals<br /><br />13)SIGN<br /><pre name="code" class="java">
number = SIGN(number)
</pre><br />返回-1、0、1来分别表示负数、0、正数<br /><br />14)SQRT<br /><pre name="code" class="java">
number = SQRT(number)
</pre><br />求平方根，相当于POWER(number, .5)<br /><br />15)其他function<br /><pre name="code" class="java">
ACOS
ASIN
COT
CRC32
DEGREE2
EXP
LN
LOG
LOG10
LOG2
PI
RADIANS
SIN
TAN
</pre><br /><br /><strong>3，时间和日期function</strong><br />时间和日期function处理DATE/DATETIME等时间数据类型<br /><br />1)ADDTIME<br /><pre name="code" class="java">
date = ADDTIME(date, time_interval)
</pre><br />在date的基础上加上time_interval的时间并返回，time_interval的格式为hh:mm:ss.hh<br /><br />2)CONVERT_TZ<br /><pre name="code" class="java">
datetime = CONVERT_TZ(datetime, fromTZ, toTZ)
</pre><br />改变时区，合法的时区值可以在mysql.time_zone_name表里找到<br /><br />3)CURRENT_DATE<br /><pre name="code" class="java">
date = CURRENT_DATE()
</pre><br />返回当前日期，不包含时间<br /><br />4)CURRENT_TIME<br /><pre name="code" class="java">
time = CURRENT_TIME()
</pre><br />放回当前时间，不包含日期<br /><br />5)CURRENT_TIMESTAMP<br /><pre name="code" class="java">
datetime = CURRENT_TIMESTAMP()
</pre><br />返回当前日期和时间，格式为yyyy-mm-dd hh:mm:ss<br /><br />6)DATE<br /><pre name="code" class="java">
date = DATE(datetime)
</pre><br />返回datetime的date部分<br /><br />7)DATE_ADD<br /><pre name="code" class="java">
date = DATE_ADD(date, INTERVAL interval_value interval_type)
</pre><br />在date的基础上加上时间间隔并返回，interval_type可以为:<br /><pre name="code" class="java">
DAY               dd
DAY_HOUR          ddhh
DAY_MINUTE        ddhhmm
DAY_SECOND        ddhhmmss
HOUR              hh
HOUR_MINUTE       hhmm
HOUR_SECOND       hhmmss
MINUTE            mm
MINUTE_SECOND     mmss
MONTH             mm
SECOND            ss
YEAR              yyyy
</pre><br /><br />8)DATE_FORMAT<br /><pre name="code" class="java">
string = DATE_FORMAT(datetime, FormatCodes)
</pre><br />格式化datetime，FormatCodes为:<br /><pre name="code" class="java">
%%                 %
%a                 星期，Mon-Sun
%b                 月，Jan-Dec
%c                 月，1-12
%d                 日期，1-31
%D                 日期，1st, 2nd, 3rd, etc.
%e                 日期，1-31
%h                 小时，1-12
%H                 小时，00-23
%i                 分钟，00-59
%I                 小时，1-12
%j                 天，1-365
%k                 小时，00-23
%l                 小时，1-12
%m                 月，1-12
%M                 月，January-December
%p                 AM/PM
%r                 小时、分钟、秒，12小时制，hh:mm:ss AM|PM
%s                 分钟0-59
%S                 分钟0-59
%T                 小时、分钟、秒，24小时制，HH:mm:ss
%u                 星期，0-52，Monday是一个星期的开始
%U                 星期，0-52，Sunday是一个星期的开始
%v                 星期，1-53，Monday是一个星期的开始
%V                 星期，1-53，Sunday是一个星期的开始
%w                 一个星期的数字表示，0=Sunday，6=Saturday
%W                 星期，Sunday-Saturday
%y                 年，2位数字
%Y                 年，4位数字
</pre><br /><br />9)DATE_SUB<br /><pre name="code" class="java">
date = DATE_SUB(date, INTERVAL interval_value interval_type)
</pre><br />在date的基础上减去时间间隔并返回，interval_type同上<br /><br />10)DATEDIFF<br /><pre name="code" class="java">
days = DATEDIFF(date1, date2)
</pre><br />返回两个date之间的间隔时间，当date1小于date2时结果可以为负数<br /><br />11)DAY<br /><pre name="code" class="java">
day = DAY(date)
</pre><br />返回一个月中第几天<br /><br />12)DAYNAME<br /><pre name="code" class="java">
day = DAYNAME(date)
</pre><br />返回一个星期中哪一天，Sunday、Monday格式<br /><br />13)DAYOFWEEK<br /><pre name="code" class="java">
day = DAYOFWEEK(date)
</pre><br />返回数字表示一个星期中哪一天，1表示Sunday，7表示Saturday<br /><br />14)DAYOFYEAR<br /><pre name="code" class="java">
day = DAYOFYEAR(date)
</pre><br />返回一年中第几天，1月1日为1，12月31日为365（如果润年则返回366）<br /><br />15)EXTRACT<br /><pre name="code" class="java">
date_part = EXTRACT(interval_name FROM date)
</pre><br />从时间里取出HOUR、YEAR、MONTH、HOUR_SECOND、DAYMINUTE等等<br /><br />16)GET_FORMAT<br /><pre name="code" class="java">
format = GET_FORMAT(datetime_type, locale)
</pre><br />返回各种datetime类型+locale对应的format，然后可以拿这个format来给DATE_FORMAT使用<br />datetime_type可以为:<br /><pre name="code" class="java">
DATE
TIME
DATETIME
TIMESTAMP
</pre><br />locale可以为:<br /><pre name="code" class="java">
INTERNAL
ISO
JIS
USA
EUR
</pre><br /><br />17)MAKEDATE<br /><pre name="code" class="java">
date = MAKEDATE(year, day)
</pre><br />使用年份year和一年的第几天day来返回一个date<br /><br />18)MAKETIME<br /><pre name="code" class="java">
time = MAKETIME(hour, minute, second)
</pre><br />使用hour、minute和second返回一个time<br /><br />19)MONTHNAME<br /><pre name="code" class="java">
monthname = MONTHNAME(date)
</pre><br />返回给定date的完整的月份名，如July<br /><br />20)NOW<br /><pre name="code" class="java">
datetime = NOW()
</pre><br />返回当前日期和时间<br /><br />21)SEC_TO_TIME<br /><pre name="code" class="java">
time = SEC_TO_TIME(seconds)
</pre><br />对给定的秒seconds来返回一个time<br /><br />22)STR_TO_DATE<br /><pre name="code" class="java">
date = STR_TO_DATE(string, format)
</pre><br />使用一个字符串格式的date/time和一个format来转换成一个date/time类型并返回<br /><br />23)TIME_TO_SEC<br /><pre name="code" class="java">
seconds = TIME_TO_SEC(time)
</pre><br />将给定的时间time转换为秒<br /><br />24)TIMEDIFF<br /><pre name="code" class="java">
time = TIMEDIFF(datetime1, datetime2)
</pre><br />返回两个datetime之间的间隔时间，当datetime1小于datetime2时结果可以为负的time，格式为(-)hh:mm:ss<br /><br />25)TIMESTAMP<br /><pre name="code" class="java">
datetime = TIMESTAMP(date, time)
</pre><br />从指定的date和time返回一个datetime类型<br /><br />26)TIMESTAMPADD<br /><pre name="code" class="java">
date_time = TIMESTAMPADD(interval_type, interval_value, date_time)
</pre><br />在date_time的基础上根据interval_type类型加上interval_value的时间间隔，并返回datetime类型的结果<br /><br />27)TIMESTAMPDIFF<br /><pre name="code" class="java">
interval_value = TIMESTAMPDIFF(interval_type, date_time1, date_time2)
</pre><br />返回两个datetime的的时间间隔并以interval_type类型来表示<br /><br />28)WEEK<br /><pre name="code" class="java">
number = WEEK(date_time [,start_of_week])
</pre><br />返回当前年里到date_time为止有多少个星期，可以指定一个星期的start day，默认为Sunday<br /><br />29)WEEKDAY<br /><pre name="code" class="java">
number =WEEKDAY(date)
</pre><br />返回date对应的是一个星期的哪一天的数字表示，Monday为0<br /><br />30)YEAR<br /><pre name="code" class="java">
number = YEAR(datetime)
</pre><br />返回datetime的year部分<br /><br />31)YEARWEEK<br /><pre name="code" class="java">
YearAndWeek = YEARWEEK(datetime [, StartOfWeek])
</pre><br />返回对给定datetime的year和week，可以指定一个星期的start day，默认为Sunday<br /><br />32)其他function<br /><pre name="code" class="java">
ADDDATE
CURDATE
CURTIME
DAYOFMONTH
FROM_DAYS
HOUR
LAST_DAY
LOCALTIME
LOCALTIMESTAMP
MICROSECOND
MINUTE
MONTH
PERIOD_ADD
PERIOD_DIFF
QUARTER
SECOND
SUBDATE
SUBTIME
SYSDATE
TO_DAYS
WEEKOFYEAR
</pre><br /><br /><strong>4，其他</strong><br />1)BENCHMARK<br /><pre name="code" class="java">
zero = BENCHMARK(no_of_repeats, expressions)
</pre><br />重复执行指定的表达式，可以用于MySQL调优<br /><br />2)COALESCE<br /><pre name="code" class="java">
value = COALESCE(value [,...])
</pre><br />返回列表里第一个非空的值<br /><br />3)CURRENT_USER<br /><pre name="code" class="java">
username = CURRENT_USER()
</pre><br />返回当前MySQL用户的用户名和主机名<br /><br />4)DATEBASE<br /><pre name="code" class="java">
database_name = DATABASE()
</pre><br />返回当前使用的数据库名字<br /><br />5)GET_LOCK<br /><pre name="code" class="java">
return_code = GET_LOCK(lock_name, timeout)
</pre><br />给定名字lock_name来定义锁，返回锁号码<br /><br />6)RELEASE_LOCK<br /><pre name="code" class="java">
return_code = RELEASE_LOCK(lock_name)
</pre><br />解锁，返回锁号码<br /><br />7)IS_FREE_LOCK<br /><pre name="code" class="java">
integer = IS_FREE_LOCK(lock_name)
</pre><br />如果用户定义的名为lock_name的锁可以获得，则返回1，否则返回0<br /><br />8)IFNULL<br /><pre name="code" class="java">
value = IFNULL(value, nullvalue)
</pre><br />如果value不为NULL则返回value，否则返回第二个参数nullvalue<br /><br />9)INTERVAL<br /><pre name="code" class="java">
position = INTERVAL(search, number, ...)
</pre><br />返回search应该在后面的列表的位置，从0开始，后面的列表应该为升序<br /><br />10)ISNULL<br /><pre name="code" class="java">
integer = ISNULL(value)
</pre><br />如果value是NULL则返回1，否则返回0<br /><br />11)NULLIF<br /><pre name="code" class="java">
value = NULLIF(value1, value2)
</pre><br />如果value1和value2相等，则返回NULL，否则返回value1<br /><br />12)SESSION_USER<br />同USER<br /><br />13)SYSTEM_USER<br />同USER<br /><br />14)USER<br /><pre name="code" class="java">
username = USER()
</pre><br />返回当前MySQL连接的用户名和主机名，表示用于建立连接的用户名和主机名，而CURRENT_USER表示mysql.user表里实际上使用的用户名<br /><br />15)UUID<br /><pre name="code" class="java">
string = UUID()
</pre><br />返回128位的Universal Unique Identifier(UUID)，唯一值，部分来自你的计算机名、当前日期和时间。<br /><br />16)VERSION<br /><pre name="code" class="java">
string = VERSION()
</pre><br />返回当前MySQL服务器的版本
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/195747#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</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>Thu, 22 May 2008 17:25:45 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/195747</link>
        <guid>http://hideto.javaeye.com/blog/195747</guid>
      </item>
      <item>
        <title>MySQL存储过程之事务管理</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/195275" style="color:red;">http://hideto.javaeye.com/blog/195275</a>&nbsp;
          发表时间: 2008年05月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          MySQL存储过程之事务管理<br /><br />ACID:Atomic、Consistent、Isolated、Durable<br />存储程序提供了一个绝佳的机制来定义、封装和管理事务。<br /><br /><strong>1，MySQL的事务支持</strong><br />MySQL的事务支持不是绑定在MySQL服务器本身，而是与存储引擎相关：<br /><pre name="code" class="java">
MyISAM：不支持事务，用于只读程序提高性能
InnoDB：支持ACID事务、行级锁、并发
Berkeley DB：支持事务
</pre><br /><br />隔离级别：<br />隔离级别决定了一个session中的事务可能对另一个session的影响、并发session对数据库的操作、一个session中所见数据的一致性<br />ANSI标准定义了4个隔离级别，MySQL的InnoDB都支持：<br /><pre name="code" class="java">
READ UNCOMMITTED：最低级别的隔离，通常又称为dirty read，它允许一个事务读取还没commit的数据，这样可能会提高性能，但是dirty read可能不是我们想要的
READ COMMITTED：在一个事务中只允许已经commit的记录可见，如果session中select还在查询中，另一session此时insert一条记录，则新添加的数据不可见
REPEATABLE READ：在一个事务开始后，其他session对数据库的修改在本事务中不可见，直到本事务commit或rollback。在一个事务中重复select的结果一样，除非本事务中update数据库。
SERIALIZABLE：最高级别的隔离，只允许事务串行执行。为了达到此目的，数据库会锁住每行已经读取的记录，其他session不能修改数据直到前一事务结束，事务commit或取消时才释放锁。
</pre><br /><br />可以使用如下语句设置MySQL的session隔离级别：<br /><pre name="code" class="java">
SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
</pre><br /><br />MySQL默认的隔离级别是REPEATABLE READ，在设置隔离级别为READ UNCOMMITTED或SERIALIZABLE时要小心，READ UNCOMMITTED会导致数据完整性的严重问题，而SERIALIZABLE会导致性能问题并增加死锁的机率<br /><br />事务管理语句：<br /><pre name="code" class="java">
START TRANSACTION：开始事务，autocommit设为0，如果已经有一个事务在运行，则会触发一个隐藏的COMMIT
COMMIT：提交事务，保存更改，释放锁
ROLLBACK：回滚本事务对数据库的所有更改，然后结束事务，释放锁
SAVEPOINT savepoint_name：创建一个savepoint识别符来ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT savepoint_name：回滚到从savepoint_name开始对数据库的所有更改，这样就允许回滚事务中的一部分，保证更改的一个子集被提交
SET TRANSACTION：允许设置事务的隔离级别
LOCK TABLES：允许显式的锁住一个或多个table，会隐式的关闭当前打开的事务，建议在执行LOCK TABLES语句之前显式的commit或rollback。我们一般所以一般在事务代码里不会使用LOCK TABLES
</pre><br /><br /><strong>2，定义事务</strong><br />MySQL默认的行为是在每条SQL语句执行后执行一个COMMIT语句，从而有效的将每条语句独立为一个事务。<br />在复杂的应用场景下这种方式就不能满足需求了。<br />为了打开事务，允许在COMMIT和ROLLBACK之前多条语句被执行，我们需要做以下两步：<br />1, 设置MySQL的autocommit属性为0，默认为1<br />2，使用START TRANSACTION语句显式的打开一个事务<br /><br />如果已经打开一个事务，则SET autocommit=0不会起作用，因为START TRANSACTION会隐式的提交session中所有当前的更改，结束已有的事务，并打开一个新的事务。<br /><br />使用SET AUTOCOMMIT语句的存储过程例子：<br /><pre name="code" class="java">
CREATE PROCEDURE tfer_funds
    (from_account int, to_account int, tfer_amount numeric(10,2))
BEGIN
    SET autocommit=0;

    UPDATE account_balance SET balance=balance-tfer_amount WHERE account_id=from_account;

    UPDATE account_balance SET balance=balance+tfer_amount WHERE account_id=to_account;

    COMMIT;
END;
</pre><br />使用START TRANSACITON打开事务的例子：<br /><pre name="code" class="java">
CREATE PROCEDURE tfer_funds
    (from_account int, to_account int, tfer_amount numeric(10,2))
BEGIN
    START TRANSACTION;

    UPDATE account_balance SET balance=balance-tfer_amount WHERE account_id=from_account;

    UPDATE account_balance SET balance=balance+tfer_amount WHERE account_id=to_account;

    COMMIT;
END;
</pre><br /><br />通常COMMIT或ROLLBACK语句执行时才完成一个事务，但是有些DDL语句等会隐式触发COMMIT，所以应该在事务中尽可能少用或注意一下：<br /><pre name="code" class="java">
ALTER FUNCTION
ALTER PROCEDURE
ALTER TABLE
BEGIN
CREATE DATABASE
CREATE FUNCTION
CREATE INDEX
CREATE PROCEDURE
CREATE TABLE
DROP DATABASE
DROP FUNCTION
DROP INDEX
DROP PROCEDURE
DROP TABLE
UNLOCK TABLES
LOAD MASTER DATA
LOCK TABLES
RENAME TABLE
TRUNCATE TABLE
SET AUTOCOMMIT=1
START TRANSACTION
</pre><br /><br /><strong>3，使用Savepoint</strong><br />使用savepoint回滚难免有些性能消耗，一般可以用IF改写<br />savepoint的良好使用的场景之一是“嵌套事务”，你可能希望程序执行一个小的事务，但是不希望回滚外面更大的事务：<br /><pre name="code" class="java">
CREATE PROCEDURE nested_tfer_funds
    (in_from_acct   INTEGER,
     in_to_acct     INTEGER,
     in_tfer_amount DECIMAL(8,2))
BEGIN
    DECLARE txn_error INTEGER DEFAULT 0;

    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
        SET txn_error=1;
    END

    SAVEPINT savepint_tfer;

    UPDATE account_balance
       SET balance=balance-in_tfer_amount
     WHERE account_id=in_from_acct;

    IF txn_error THEN
        ROLLBACK TO savepoint_tfer;
        SELECT 'Transfer aborted';
    ELSE
        UPDATE account_balance
           SET balance=balance+in_tfer_amount
         WHERE account_id=in_to_acct;

        IF txn_error THEN
            ROLLBACK TO savepoint_tfer;
            SELECT 'Transfer aborted';

        END IF:
    END IF;
END;
</pre><br /><br /><strong>4，事务和锁</strong><br />事务的ACID属性只能通过限制数据库的同步更改来实现，从而通过对修改数据加锁来实现。<br />直到事务触发COMMIT或ROLLBACK语句时锁才释放。<br />缺点是后面的事务必须等前面的事务完成才能开始执行，吞吐量随着等待锁释放的时间增长而递减。<br />MySQL/InnoDB通过行级锁来最小化锁竞争。这样修改同一table里其他行的数据没有限制，而且读数据可以始终没有等待。<br />可以在SELECT语句里使用FOR UPDATE或LOCK IN SHARE MODE语句来加上行级锁<br /><pre name="code" class="java">
SELECT select_statement options [FOR UPDATE|LOCK IN SHARE MODE]
</pre><br />FOR UPDATE会锁住该SELECT语句返回的行，其他SELECT和DML语句必须等待该SELECT语句所在的事务完成<br />LOCK IN SHARE MODE同FOR UPDATE，但是允许其他session的SELECT语句执行并允许获取SHARE MODE锁<br /><br />死锁：<br />死锁发生于两个事务相互等待彼此释放锁的情景<br />当MySQL/InnoDB检查到死锁时，它会强制一个事务rollback并触发一条错误消息<br />对InnoDB而言，所选择的rollback的事务是完成工作最少的事务（所修改的行最少）<br /><pre name="code" class="java">
mysql > CALL tfer_funds(1,2,300);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
</pre><br />死锁在任何数据库系统里都可能发生，但是对MySQL/InnoDB这种行级锁数据库而言可能性相对较少。<br />可以通过使用一致的顺序来锁row或table以及让事务保持尽可能短来减少死锁的频率。<br />如果死锁不容易debug，你可以向你的程序中添加一些逻辑来处理死锁并重试事务，但这部分代码多了以后很难维护<br />所以，比较好的避免死锁的方式是在做任何修改之前按一定的顺序添加行级锁，这样就能避免死锁:<br /><pre name="code" class="java">
CREATE PROCEDURE tfer_funds3
    (from_account INT, to_account INT, tfer_amount NUMERIC(10,2))
BEGIN
    DECLARE local_account_id INT;
    DECLARE lock_cursor CURSOR FOR
        SELECT account_id
          FROM account_balance
         WHERE account_id IN (from_account, to_account)
         ORDER BY account_id
           FOR UPDATE;

    START TRANSACTION;

    OPEN lock_cursor;
    FETCH lock_cursor INTO local_account_id;

    UPDATE account_balance
       SET balance=balance-tfer_amount
     WHERE account_id=from_account;

    UPDATE account_balance
       SET balance=balance+tfer_amount
     WHERE account_id=to_account;

    CLOSE lock_cursor;

    COMMIT;
END;
</pre><br /><br />设置死锁ttl: innodb_lock_wait_timeout，默认为50秒<br />如果你在一个事务中混合使用InnoDB和非InnoDB表，则MySQL不能检测到死锁，此时会抛出“lock wait timeuot”1205错误<br /><br />乐观所和悲观锁策略：<br />悲观锁：在读取数据时锁住那几行，其他对这几行的更新需要等到悲观锁结束时才能继续<br />乐观所：读取数据时不锁，更新时检查是否数据已经被更新过，如果是则取消当前更新<br />一般在悲观锁的等待时间过长而不能接受时我们才会选择乐观锁<br />悲观锁的例子：<br /><pre name="code" class="java">
CREATE PROCEDURE tfer_funds
       (from_account INT, to_account INT,tfer_amount NUMERIC(10,2),
        OUT status INT, OUT message VARCHAR(30))
BEGIN
    DECLARE from_account_balance NUMERIC(10,2);

    START TRANSACTION;


    SELECT balance
      INTO from_account_balance
      FROM account_balance
     WHERE account_id=from_account
       FOR UPDATE;

    IF from_account_balance>=tfer_amount THEN

         UPDATE account_balance
            SET balance=balance-tfer_amount
          WHERE account_id=from_account;

         UPDATE account_balance
            SET balance=balance+tfer_amount
          WHERE account_id=to_account;
         COMMIT;

         SET status=0;
         SET message='OK';
    ELSE
         ROLLBACK;
         SET status=-1;
         SET message='Insufficient funds';
    END IF;
END;
</pre><br />乐观锁的例子：<br /><pre name="code" class="java">
CREATE PROCEDURE tfer_funds
    (from_account INT, to_account INT, tfer_amount NUMERIC(10,2),
        OUT status INT, OUT message VARCHAR(30) )

BEGIN

    DECLARE from_account_balance    NUMERIC(8,2);
    DECLARE from_account_balance2   NUMERIC(8,2);
    DECLARE from_account_timestamp1 TIMESTAMP;
    DECLARE from_account_timestamp2 TIMESTAMP;

    SELECT account_timestamp,balance
        INTO from_account_timestamp1,from_account_balance
            FROM account_balance
            WHERE account_id=from_account;

    IF (from_account_balance>=tfer_amount) THEN

        -- Here we perform some long running validation that
        -- might take a few minutes */
        CALL long_running_validation(from_account);

        START TRANSACTION;

        -- Make sure the account row has not been updated since
        -- our initial check
        SELECT account_timestamp, balance
            INTO from_account_timestamp2,from_account_balance2
            FROM account_balance
            WHERE account_id=from_account
            FOR UPDATE;

        IF (from_account_timestamp1 &lt;> from_account_timestamp2 OR
            from_account_balance    &lt;> from_account_balance2)  THEN
            ROLLBACK;
            SET status=-1;
            SET message=CONCAT("Transaction cancelled due to concurrent update",
                " of account"  ,from_account);
        ELSE
            UPDATE account_balance
                SET balance=balance-tfer_amount
                WHERE account_id=from_account;

            UPDATE account_balance
                SET balance=balance+tfer_amount
                WHERE account_id=to_account;

            COMMIT;

            SET status=0;
            SET message="OK";
        END IF;

    ELSE
        ROLLBACK;
        SET status=-1;
        SET message="Insufficient funds";
    END IF;
END$$
</pre><br /><br /><strong>5，事务设计指南</strong><br /><pre name="code" class="java">
1，保持事务短小
2，尽量避免事务中rollback
3，尽量避免savepoint
4，默认情况下，依赖于悲观锁
5，为吞吐量要求苛刻的事务考虑乐观锁
6，显示声明打开事务
7，锁的行越少越好，锁的时间越短越好
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/195275#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</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>Wed, 21 May 2008 14:36:26 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/195275</link>
        <guid>http://hideto.javaeye.com/blog/195275</guid>
      </item>
      <item>
        <title>MySQL存储程序之创建和维护存储程序</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/194983" style="color:red;">http://hideto.javaeye.com/blog/194983</a>&nbsp;
          发表时间: 2008年05月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          创建和维护存储程序<br /><br /><strong>1，创建和维护存储程序语法</strong><br />1)CREATE PROCEDURE<br /><pre name="code" class="java">
CREATE PROCEDURE procedure_name ([parameter[,...])
  [LANGUAGE SQL]
  [ [NOT] DETERMINISTIC]
  [ {CONTAINS SQL|MODIFIES SQL DATA|READS SQL DATA|NO SQL} ]
  [SQL SECURITY {DEFINER|INVOKER}]
  [COMMENT comment_string]
  procedure_statements

/* parameters */
[{IN|OUT|INOUT}] parameter_name datatype
</pre><br />2)CREATE FUNCTION<br /><pre name="code" class="java">
CREATE FUNCTION function_name ([parameter[,...])
    RETURNS datatype
    [LANGUAGE SQL]
    [ [NOT] DETERMINISTIC]
    [ {CONTAINS SQL|NO SQL|MODIFIES SQL DATA|READS SQL DATA} ]
    [SQL SECURITY {DEFINER|INVOKER}]
    [COMMENT comment_string]
    function_statements
</pre><br />3)CREATE TRIGGER<br /><pre name="code" class="java">
CREATE [DEFINER = {user|CURRENT_USER}] TRIGGER trigger_name
    {BEFORE|AFTER}
    {UPDATE|INSERT|DELETE}
    ON table_name
    FOR EACH ROW
    trigger_statements
</pre><br />4)ALTER PROCEDURE/FUNCTION<br /><pre name="code" class="java">
ALTER {PROCEDURE|FUNCTION} procedure_or_function_name
    [SQL SECURITY {DEFINER|INVOKER}]
    [COMMENT comment_string]
</pre><br />5)DROP PROCEDURE/FUNCTION/TRIGGER<br /><pre name="code" class="java">
DROP {PROCEDURE|FUNCTION|TRIGGER} {IF EXISTS} program_name
</pre><br /><br /><strong>2，存储程序的分号问题</strong><br />MySQL使用分号“;”作为SQL语句的结束，但是存储程序的代码块里面经常也需要用到分号，这样就会导致错误：<br /><pre name="code" class="java">
mysql > CREATE PROCEDURE HelloWorld()
     -> BEGIN
     ->   SELECT 'Hello World';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near 'SELECT 'Hello
World'' at line 3
mysql >
</pre><br />为了避免此错误，可以使用DELIMITER语句修改分隔符：<br /><pre name="code" class="java">
mysql > DELIMITER $$

mysql > CREATE PROCEDURE HelloWorld()
     -> BEGIN
     ->    SELECT 'Hello World';
     -> END$$
Query OK, 0rows affected (0.00 sec)
</pre><br /><br /><strong>3，显示存储程序信息</strong><br />显示PROCEDURE/FUNCTION STATUS<br /><pre name="code" class="java">
SHOW {PROCEDURE|FUNCTION} STATUS [LIKE pattern]
</pre><br />显示存储过程和存储function信息<br /><pre name="code" class="java">
select * from information_schema.routtines;
</pre><br />显示trigger信息<br /><pre name="code" class="java">
select * from information_schema.triggers;
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/194983#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/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 May 2008 18:48:19 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/194983</link>
        <guid>http://hideto.javaeye.com/blog/194983</guid>
      </item>
      <item>
        <title>再谈ActiveRecord、MySQL和transaction</title>
        <author>hideto</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hideto.javaeye.com">hideto</a>&nbsp;
          链接：<a href="http://hideto.javaeye.com/blog/194865" style="color:red;">http://hideto.javaeye.com/blog/194865</a>&nbsp;
          发表时间: 2008年05月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          再谈ActiveRecord、MySQL和transaction<br /><br /><strong>第一篇：ActiveRecord如何与MySQL交互</strong><br /><br />我们自定义的MyModel继承于ActiveRecord::Base类<br /><pre name="code" class="java">
MyModel &lt; ActiveRecord::Base
MyModel.find_xxx -> find_by_sql -> connection.select_all(sql)
MyModel.create_xxx   ->  connection.insert(sql)
MyModel.update_xxx   ->  connection.update(sql)
MyModel.destroy      ->  connection.delete(sql)
</pre><br />这个connection是什么东西？让我们一步步往下看<br /><br />可以看到MyModel的一些方法都在base.rb里实现为connection对象相关的方法<br />如果我们使用的是MySQL数据库，则这里的connection为ActiveRecord::ConnectionAdapters::MysqlAdapter对象<br />这是在Rails初始化时做的：<br /><pre name="code" class="java">
environment.rb的Rails::Initializer.run -> initializer.rb的process方法 -> initializer.rb的initialize_database方法
-> ActiveRecord::Base.establish_connection（connenction_specification.rb里对ActiveRecord::Base添加该方法和connection方法）
-> mysql_adapter.rb的mysql_connection方法 -> mysql.rb的real_connect方法 -> TCPSocket::new/UNIXSocket::new
</pre><br /><br />而MysqlAdapter继承于AbstractAdapter，AbstractAdapter又include了DatabaseStatements所以：<br /><pre name="code" class="java">
MyModel &lt; ActiveRecord::Base
MyModel.find_xxx -> find_by_sql -> DatabaseStatements.select_all(sql) -> MysqlAdapter.select(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql)
MyModel.create_xxx   ->  MysqlAdapter.insert(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql)
MyModel.update_xxx   ->  MysqlAdapter.update(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql)
MyModel.destroy      ->  DatabaseStatements.delete(sql) -> MysqlAdapter.execute(sql) -> MysqlAdapter.@connection.query(sql)
</pre><br /><br />这里的MysqlAdapter.@connection在mysql_adapter.rb的mysql_connection方法里初始化了：<br /><pre name="code" class="java">
require_mysql
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]

ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
</pre><br /><br />继续追踪：<br /><pre name="code" class="java">
@connection.query(sql) -> real_query(sql) -> command(sql) -> Net.read/write -> socket.read/write
</pre><br />至此，Rails自带的MySQL Adapter了解清楚了。<br /><br /><strong>第二篇：ActiveRecord对MySQL的事务封装</strong><br />首先看看MySQL的事务控制(摘自MySQL5.1参考手册)：<br /><div class="quote_title">引用</div><div class="quote_div"><br />START TRANSACTION或BEGIN语句可以开始一项新的事务<br />COMMIT可以提交当前事务，使变更成为永久变更<br />ROLLBACK可以回滚当前事务，取消其变更<br />SET AUTOCOMMIT语句可以禁用或启用默认的autocommit模式，用于当前连接<br />默认情况下，MySQL采用autocommit模式运行<br />这意味着，当您执行一个用于更新（修改）表的语句之后，MySQL立刻把更新存储到磁盘中<br />如果您正在使用一个事务安全型的存储引擎（如InnoDB, BDB或NDB簇），则您可以使用以下语句禁用autocommit模式：SET AUTOCOMMIT=0<br />通过把AUTOCOMMIT变量设置为零，禁用autocommit模式之后，您必须使用COMMIT把变更存储到磁盘中，或着如果您想要忽略从事务开始进行以来做出的变更，使用ROLLBACK<br />使用START TRANSACTION，autocommit仍然被禁用，直到您使用COMMIT或ROLLBACK结束事务为止。然后autocommit模式恢复到原来的状态<br /></div><br /><br />开始追踪:<br /><pre name="code" class="java">
# transactions.rb

def self.included(base)
  base.extend(ClassMethods)

  base.class_eval do
    [:destroy, :save, :save!].each do |method|
      alias_method_chain method, :transactions
    end
  end
end
</pre><br />这就是Rails里所谓的“隐式事务”，给你安排好了<br />这表示ActiveRecord对save，save!和destroy方法都包上了事务，而create、update、delete等方法最终都是调用save、save!和destroy方法<br /><br />追踪:<br /><pre name="code" class="java">
transactions.rb的transaction -> connection.transaction -> MysqlAdapter.transaction(没有) -> AbstractAdapter.transaciton(没有) -> DatabaseStatements.transaction
</pre><br /><br />看下database_statements.rb里的transaciton方法：<br /><pre name="code" class="java">
def transaction(start_db_transaction = true)
  transaction_open = false
  begin
    if block_given?
      if start_db_transaction
        begin_db_transaction 
        transaction_open = true
      end
      yield
    end
  rescue Exception => database_transaction_rollback
    if transaction_open
      transaction_open = false
      rollback_db_transaction
    end
    raise
  end
ensure
  commit_db_transaction if transaction_open
end
</pre><br /><br />马上又回到mysql_adapter.rb里对transaction打开、提交、回滚的实现：<br /><pre name="code" class="java">
def begin_db_transaction #:nodoc:
  execute "BEGIN"
rescue Exception
  # Transactions aren't supported
end

def commit_db_transaction #:nodoc:
  execute "COMMIT"
rescue Exception
  # Transactions aren't supported
end

def rollback_db_transaction #:nodoc:
  execute "ROLLBACK"
rescue Exception
  # Transactions aren't supported
end
</pre><br />execute即Mysql.query，上面提到了。<br /><br />那么为啥ActiveRecord的transaction不支持嵌套呢？<br />还是看transaction.rb:<br /><pre name="code" class="java">
def transaction(*objects, &block)
  previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
  increment_open_transactions

  begin
    unless objects.empty?
      ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0.  See http://www.rubyonrails.org/deprecation for details.", caller
      objects.each { |o| o.extend(Transaction::Simple) }
      objects.each { |o| o.start_transaction }
    end

    result = connection.transaction(Thread.current['start_db_transaction'], &block)

    objects.each { |o| o.commit_transaction }
    return result
  rescue Exception => object_transaction_rollback
    objects.each { |o| o.abort_transaction }
    raise
  ensure
    decrement_open_transactions
    trap('TERM', previous_handler)
  end
end

private
  def increment_open_transactions #:nodoc:
    open = Thread.current['open_transactions'] ||= 0
    Thread.current['start_db_transaction'] = open.zero?
    Thread.current['open_transactions'] = open + 1
  end

  def decrement_open_transactions #:nodoc:
    Thread.current['open_transactions'] -= 1
  end
</pre><br />原来，Rails每次遇到调用transaction方法时，看看当前线程里open transaction的次数:<br />1, 如果有多次，则调用connection.transaction(false, &block)<br />2, 如果为零次，则调用connection.transaction(true, &block)<br />而database_statements.rb里的transaciton方法根据true or false参数来判断是否重复open transaction<br />这样，Rails遇到最外层的transaction打开后，就不会再打开内部的transaction了，这也是手动写多层嵌套transaction时只有最外层的起作用的原因<br /><br />至此，Rails对transaction的封装了解清楚。
          <br/>
          <span style="color:red;">
            <a href="http://hideto.javaeye.com/blog/194865#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><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><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 May 2008 16:08:32 +0800</pubDate>
        <link>http://hideto.javaeye.com/blog/194865</link>
        <guid>http://hideto.javaeye.com/blog/194865</guid>
      </item>
  </channel>
</rss>