<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>tianping</title>
    <description>勇敢，自信，不屈，团队 ——狼的精神</description>
    <link>http://tianping.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>如何让你的SQL运行得更快  </title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/146287" style="color:red;">http://tianping.javaeye.com/blog/146287</a>&nbsp;
          发表时间: 2007年12月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　人们在使用SQL时往往会陷入一个误区，即太关注于所得的结果是否正确，而忽略了不同的实现方法之间可能存在的性能差异，这种性能差异在大型的或是复杂的数据库环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。笔者在工作实践中发现，不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的where子句。在对它们进行适当的优化后，其运行速度有了明显地提高!下面我将从这三个方面分别进行总结:<br /><br />　　为了更直观地说明问题，所有实例中的SQL运行时间均经过测试，不超过1秒的均表示为(&lt; 1秒)。<br /><br />　　测试环境--<br /><br />　　主机:HP LH II<br /><br />　　主频:330MHZ<br /><br />　　内存:128兆<br /><br />　　操作系统:Operserver5.0.4<br /><br />　　数据库:Sybase11.0.3<br /><br />　　一、不合理的索引设计<br /><br />　　例:表record有620000行，试看在不同的索引下，下面几个 SQL的运行情况:<br /><br />　　1.在date上建有一非个群集索引<br /><br /> <br /><br />select count(*) from record where date ><br />'19991201' and date &lt; '19991214'and amount ><br />2000 (25秒)<br />select date,sum(amount) from record group by date<br />(55秒)<br />select count(*) from record where date ><br />'19990901' and place in ('BJ','SH') (27秒)<br /><br />　　分析:<br /><br />　　date上有大量的重复值，在非群集索引下，数据在物理上随机存放在数据页上，在范围查找时，必须执行一次表扫描才能找到这一范围内的全部行。<br /><br />　　2.在date上的一个群集索引<br /><br /> <br /><br />select count(*) from record where date ><br />'19991201' and date &lt; '19991214' and amount ><br />2000 （14秒）<br />select date,sum(amount) from record group by date<br />（28秒）<br />select count(*) from record where date ><br />'19990901' and place in ('BJ','SH')（14秒）<br /><br />　　分析:<br /><br />　　在群集索引下，数据在物理上按顺序在数据页上，重复值也排列在一起，因而在范围查找时，可以先找到这个范围的起末点，且只在这个范围内扫描数据页，避免了大范围扫描，提高了查询速度。<br /><br />　　3.在place，date，amount上的组合索引<br /><br /> <br /><br />select count(*) from record where date ><br />'19991201' and date &lt; '19991214' and amount ><br />2000 （26秒）<br />select date,sum(amount) from record group by date<br />（27秒）<br />select count(*) from record where date ><br />'19990901' and place in ('BJ', 'SH')（&lt; 1秒）<br /><br />　　分析:<br /><br />　　这是一个不很合理的组合索引，因为它的前导列是place，第一和第二条SQL没有引用place，因此也没有利用上索引;第三个SQL使用了place，且引用的所有列都包含在组合索引中，形成了索引覆盖，所以它的速度是非常快的。<br /><br />　　4.在date，place，amount上的组合索引<br /><br /> <br /><br />select count(*) from record where date ><br />'19991201' and date &lt; '19991214' and amount ><br />2000(&lt; 1秒)<br />select date,sum(amount) from record group by date<br />（11秒）<br />select count(*) from record where date ><br />'19990901' and place in ('BJ','SH')（&lt; 1秒）<br /><br />　　分析:<br /><br />　　这是一个合理的组合索引。它将date作为前导列，使每个SQL都可以利用索引，并且在第一和第三个SQL中形成了索引覆盖，因而性能达到了最优。<br /><br />　　5.总结:<br /><br />　　缺省情况下建立的索引是非群集索引，但有时它并不是最佳的;合理的索引设计要建立在对各种查询的分析和预测上。一般来说:<br /><br />　　①.有大量重复值、且经常有范围查询(between, >,&lt; ，>=,&lt; =)和order by、group by发生的列，可考虑建立群集索引;<br /><br />　　②.经常同时存取多列，且每列都含有重复值可考虑建立组合索引;<br /><br />　　③.组合索引要尽量使关键查询形成索引覆盖，其前导列一定是使用最频繁的列。<br /><br />　　二、不充份的连接条件:<br /><br />　　例:表card有7896行，在card_no上有一个非聚集索引，表account有191122行，在account_no上有一个非聚集索引，试看在不同的表连接条件下，两个SQL的执行情况:<br /><br /> <br /><br />select sum(a.amount) from account a,<br />card b where a.card_no = b.card_no（20秒）<br /><br />　　将SQL改为:<br /><br /> <br /><br />select sum(a.amount) from account a,<br />card b where a.card_no = b.card_no and a.<br />account_no=b.account_no（&lt; 1秒）<br /><br />　　分析:<br /><br />　　在第一个连接条件下，最佳查询方案是将account作外层表，card作内层表，利用card上的索引，其I/O次数可由以下公式估算为:外层表account上的22541页+(外层表account的191122行*内层表card上对应外层表第一行所要查找的3页)=595907次 I/O<br /><br />      在第二个连接条件下，最佳查询方案是将card作外层表，account作内层表，利用account上的索引，其I/O次数可由以下公式估算为:<br /><br />　　外层表card上的1944页+(外层表card的7896行*内层表account上对应外层表每一行所要查找的4页)= 33528次I/O<br /><br />　　可见，只有充份的连接条件，真正的最佳方案才会被执行。<br /><br />　　总结:<br /><br />　　1.多表操作在被实际执行前，查询优化器会根据连接条件，列出几组可能的连接方案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定，乘积最小为最佳方案。<br /><br />　　2.查看执行方案的方法-- 用set showplanon，打开showplan选项，就可以看到连接顺序、使用何种索引的信息;想看更详细的信息，需用sa角色执行dbcc(3604,310,302)。<br /><br />　　三、不可优化的where子句<br /><br />　　1.例:下列SQL条件语句中的列都建有恰当的索引，但执行速度却非常慢:<br /><br /> <br /><br />select * from record where<br />substring(card_no,1,4)='5378'(13秒)<br />select * from record where<br />amount/30&lt; 1000（11秒）<br />select * from record where<br />convert(char(10),date,112)='19991201'（10秒）<br /><br />　　分析:<br /><br />　　where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的，因此它不得不进行表搜索，而没有使用该列上面的索引;如果这些结果在查询编译时就能得到，那么就可以被SQL优化器优化，使用索引，避免表搜索，因此将SQL重写成下面这样:<br /><br /> <br /><br />select * from record where card_no like<br />'5378%'（&lt; 1秒）<br />select * from record where amount<br />&lt; 1000*30（&lt; 1秒）<br />select * from record where date= '1999/12/01'<br />（&lt; 1秒）<br /><br />　　你会发现SQL明显快起来!<br /><br />　　2.例:表stuff有200000行，id_no上有非群集索引，请看下面这个SQL:<br /><br /> <br /><br />select count(*) from stuff where id_no in('0','1')<br />（23秒）<br /><br />　　分析:<br /><br />　　where条件中的'in'在逻辑上相当于'or'，所以语法分析器会将in ('0','1')转化为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找，再将结果相加，这样可以利用id_no上的索引;但实际上(根据showplan), 它却采用了"OR策略"，即先取出满足每个or子句的行，存入临时数据库的工作表中，再建立唯一索引以去掉重复行，最后从这个临时表中计算结果。因此，实际过程没有利用id_no上索引，并且完成时间还要受tempdb数据库性能的影响。<br /><br />　　实践证明，表的行数越多，工作表的性能就越差，当stuff有620000行时，执行时间竟达到220秒!还不如将or子句分开:<br /><br /> <br /><br />select count(*) from stuff where id_no='0'<br />select count(*) from stuff where id_no='1'<br /><br />　　得到两个结果，再作一次加法合算。因为每句都使用了索引，执行时间只有3秒，在620000行下，时间也只有4秒。或者，用更好的方法，写一个简单的存储过程:<br /><br /> <br /><br />create proc count_stuff as<br />declare @a int<br />declare @b int<br />declare @c int<br />declare @d char(10)<br />begin<br />select @a=count(*) from stuff where id_no='0'<br />select @b=count(*) from stuff where id_no='1'<br />end<br />select @c=@a+@b<br />select @d=convert(char(10),@c)<br />print @d<br /><br />　　直接算出结果，执行时间同上面一样快!<br /><br />　　总结:<br /><br />　　可见，所谓优化即where子句利用了索引，不可优化即发生了表扫描或额外开销。<br /><br />　　1.任何对列的操作都将导致表扫描，它包括数据库函数、计算表达式等等，查询时要尽可能将操作移至等号右边。<br /><br />　　2.in、or子句常会使用工作表，使索引失效;如果不产生大量重复值，可以考虑把子句拆开;拆开的子句中应该包含索引。<br /><br />　　3.要善于使用存储过程，它使SQL变得更加灵活和高效。<br /><br />　　从以上这些例子可以看出，SQL优化的实质就是在结果正确的前提下，用优化器可以识别的语句，充份利用索引，减少表扫描的I/O次数，尽量避免表搜索的发生。其实SQL的性能优化是一个复杂的过程，上述这些只是在应用层次的一种体现，深入研究还会涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。<br /><br />　　1.合理使用索引<br /><br />　　索引是数据库中重要的数据结构，它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处，其使用原则如下:<br /><br />　　●在经常进行连接，但是没有指定为外键的列上建立索引，而不经常连接的字段则由优化器自动生成索引。<br /><br />　　●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。<br /><br />　　●在条件表达式中经常用到的不同值较多的列上建立检索，在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值，因此就无必要建立索引。如果建立索引不但不会提高查询效率，反而会严重降低更新速度。<br /><br />　　●如果待排序的列有多个，可以在这些列上建立复合索引(compound index)。<br /><br />　　●使用系统工具。如Informix数据库有一个tbcheck工具，可以在可疑的索引上进行检查。在一些数据库服务器上，索引可能失效或者因为频繁操作而使得读取效率降低，如果一个使用索引的查询不明不白地慢下来，可以试着用tbcheck工具检查索引的完整性，必要时进行修复。另外，当数据库表更新大量数据后，删除并重建索引可以提高查询速度。<br /><br />　　2.避免或简化排序<br /><br />　　应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时，优化器就避免了排序的步骤。以下是一些影响因素:<br /><br />　　●索引中不包括一个或几个待排序的列;<br /><br />　　●group by或order by子句中列的次序与索引的次序不一样;<br /><br />　　●排序的列来自不同的表。<br /><br />　　为了避免不必要的排序，就要正确地增建索引，合理地合并数据库表(尽管有时可能影响表的规范化，但相对于效率的提高是值得的)。如果排序不可避免，那么应当试图简化它，如缩小排序的列的范围等。<br /><br />　　3.消除对大型表行数据的顺序存取<br /><br />　　在嵌套查询中，对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略，一个嵌套3层的查询，如果每层都查询1000行，那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如，两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接，就要在“学号”这个连接字段上建立索引。<br /><br />　　还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引，但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:<br /><br /> <br /><br />SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008<br /><br />　　虽然在customer_num和order_num上建有索引，但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合，所以应该改为如下语句:<br /><br /> <br /><br />SELECT ＊ FROM orders WHERE customer_num=104 AND order_num>1001 <br />UNION <br />SELECT ＊ FROM orders WHERE order_num=1008 <br /><br />　　这样就能利用索引路径处理查询。<br /><br />　　4.避免相关子查询<br /><br />　　一个列的标签同时在主查询和where子句中的查询中出现，那么很可能当主查询中的列值改变之后，子查询必须重新查询一次。查询嵌套层次越多，效率越低，因此应当尽量避免子查询。如果子查询不可避免，那么要在子查询中过滤掉尽可能多的行。<br /><br />　　5.避免困难的正规表达式<br /><br />　　MATCHES和LIKE关键字支持通配符匹配，技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”<br /><br />　　即使在zipcode字段上建立了索引，在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT * FROM customer WHERE zipcode >“98000”，在执行查询时就会利用索引来查询，显然会大大提高速度。<br /><br />　　另外，还要避免非开始的子串。例如语句:SELECT * FROM customer WHERE zipcode[2，3] >“80”，在where子句中采用了非开始子串，因而这个语句也不会使用索引。<br /><br />　　6.使用临时表加速查询<br /><br />　　把表的一个子集进行排序并创建临时表，有时能加速查询。它有助于避免多重排序操作，而且在其他方面还能简化优化器的工作。例如:<br /><br /> <br /><br />SELECT cust.name，rcvbles.balance，……other columns <br />FROM cust，rcvbles <br />WHERE cust.customer_id = rcvlbes.customer_id <br />AND rcvblls.balance>0 <br />AND cust.postcode>“98000” <br />ORDER BY cust.name<br /><br />　　如果这个查询要被执行多次而不止一次，可以把所有未付款的客户找出来放在一个临时文件中，并按客户的名字进行排序:<br /><br /> <br /><br />SELECT cust.name，rcvbles.balance，……other columns <br />FROM cust，rcvbles <br />WHERE cust.customer_id = rcvlbes.customer_id <br />AND rcvblls.balance>0 <br />ORDER BY cust.name <br />INTO TEMP cust_with_balance <br /><br />　　然后以下面的方式在临时表中查询:<br /><br /> <br /><br />SELECT ＊ FROM cust_with_balance <br />WHERE postcode>“98000”<br /><br />　　临时表中的行要比主表中的行少，而且物理顺序就是所要求的顺序，减少了磁盘I/O，所以查询工作量可以得到大幅减少。<br /><br />　　注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下，注意不要丢失数据。<br /><br />　　7.用排序来取代非顺序存取<br /><br />　　非顺序磁盘存取是最慢的操作，表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况，使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。<br /><br />　　有些时候，用数据库的排序能力来替代非顺序的存取能改进查询。<br /><br />　　3.优化 tempdb 性能<br /><br />　　对 tempdb 数据库的物理位置和数据库选项设置的一般建议包括:<br /><br />　　使 tempdb 数据库得以按需自动扩展。这确保在执行完成前不终止查询，该查询所生成的存储在 tempdb 数据库内的中间结果集比预期大得多。<br /><br />　　将 tempdb 数据库文件的初始大小设置为合理的大小，以避免当需要更多空间时文件自动扩展。如果 tempdb 数据库扩展得过于频繁，性能会受不良影响。<br /><br />　　将文件增长增量百分比设置为合理的大小，以避免 tempdb 数据库文件按太小的值增长。如果文件增长幅度与写入 tempdb 数据库的数据量相比太小，则 tempdb 数据库可能需要始终扩展，因而将妨害性能。<br /><br />　　将 tempdb 数据库放在快速 I/O 子系统上以确保好的性能。在多个磁盘上条带化 tempdb 数据库以获得更好的性能。将 tempdb 数据库放在除用户数据库所使用的磁盘之外的磁盘上。有关更多信息，请参见扩充数据库。<br /><br />　　4.优化服务器:<br /><br />　　使用内存配置选项优化服务器性能<br /><br />　　Microsoft® SQL Server™ 2000 的内存管理组件消除了对 SQL Server 可用的内存进行手工管理的需要。SQL Server 在启动时根据操作系统和其它应用程序当前正在使用的内存量，动态确定应分配的内存量。当计算机和SQL Server 上的负荷更改时，分配的内存也随之更改。有关更多信息，请参见内存构架。<br /><br />　　下列服务器配置选项可用于配置内存使用并影响服务器性能:<br /><br />　　min server memory<br /><br />　　max server memory<br /><br />　　max worker threads<br /><br />　　index create memory<br /><br />　　min memory per query<br /><br />　　min server memory 服务器配置选项可用于确保 SQL Server 在达到该值后不会释放内存。可以基于 SQL Server 的大小及活动将该配置选项设置为特定的值。如果选择设置此选项，必须为操作系统和其他程序留出足够的内存。如果操作系统没有足够的内存，会向 SQL Server 请求内存，从而导致影响 SQL Server 性能。<br /><br />　　max server memory 服务器配置选项可用于:在 SQL Server 启动及运行时，指定 SQL Server 可以分配的最大内存量。如果知道有多个应用程序与 SQL Server 同时运行，而且想保障这些应用程序有足够的内存运行，可以将该配置选项设置为特定的值。如果这些其它应用程序(如 Web 服务器或电子邮件服务器)只根据需要请求内存，则 SQL Server 将根据需要给它们释放内存，因此不要设置 max server memory 服务器配置选项。然而，应用程序通常在启动时不假选择地使用可用内存，而如果需要更多内存也不请求。如果有这种行为方式的应用程序与 SQL Server 同时运行在相同的计算机上，则将 max server memory 服务器配置选项设置为特定的值，以保障应用程序所需的内存不由 SQL Server 分配出。<br /><br />　　不要将 min server memory 和 max server memory 服务器配置选项设置为相同的值，这样做会使分配给 SQL Server 的内存量固定。动态内存分配可以随时间提供最佳的总体性能。有关更多信息，请参见服务器内存选项。<br /><br />　　max worker threads 服务器配置选项可用于指定为用户连接到 SQL Server 提供支持的线程数。255 这一默认设置对一些配置可能稍微偏高，这要具体取决于并发用户数。由于每个工作线程都已分配，因此即使线程没有正在使用(因为并发连接比分配的工作线程少)，可由其它操作(如高速缓冲存储器)更好地利用的内存资源也可能是未使用的。一般情况下，应将该配置值设置为并发连接数，但不能超过 32727。并发连接与用户登录连接不同。SQL Server 实例的工作线程池只需要足够大，以便为同时正在该实例中执行批处理的用户连接提供服务。如果增加工作线程的数量超过默认值，会降低服务器性能。有关更多信息，请参见max worker threads 选项。<br /><br />　　说明 当 SQL Server 运行在 Microsoft Windows® 98 上时，最大工作线程服务器配置选项不起作用。<br /><br />　　index create memory 服务器配置选项控制创建索引时排序操作所使用的内存量。在生产系统上创建索引通常是不常执行的任务，通常调度为在非峰值时间执行的作业。因此，不常创建索引且在非峰值时间时，增加该值可提高索引创建的性能。不过，最好将 min memory per query 配置选项保持在一个较低的值，这样即使所有请求的内存都不可用，索引创建作业仍能开始。有关更多信息，请参见 index create memory 选项。<br /><br />　　min memory per query 服务器配置选项可用于指定分配给查询执行的最小内存量。当系统内有许多查询并发执行时，增大 min memory per query 的值有助于提高消耗大量内存的查询(如大型排序和哈希操作)的性能。不过，不要将 min memory per query 服务器配置选项设置得太高，尤其是在很忙的系统上，因为查询将不得不等到能确保占有请求的最小内存、或等到超过 query wait 服务器配置选项内所指定的值。如果可用内存比执行查询所需的指定最小内存多，则只要查询能对多出的内存加以有效的利用，就可以使用多出的内存。有关更多信息，请参见 min memory per query 选项和 query wait 选项。<br /><br />　　使用 I/O 配置选项优化服务器性能<br /><br />　　下列服务器配置选项可用于配置 I/O 的使用并影响服务器性能:<br /><br />　　recovery interval<br /><br />　　recovery interval 服务器配置选项控制 Microsoft® SQL Server™ 2000 在每个数据库内发出检查点的时间。默认情况下，SQL Server 确定执行检查点操作的最佳时间。然而，若要确定这是否为适当的设置，需要使用 Windows NT 性能监视器监视数据库文件上的磁盘写入活动。导致磁盘利用率达到 100% 的活动尖峰值会妨害性能。若更改该参数以使检查点进程较少出现，通常可以提高这种情况下的总体性能。但仍须继续监视性能以确定新值是否已对性能产生正面影响。有关更多信息，请参见recovery interval 选项。
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/146287#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 05 Dec 2007 20:41:58 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/146287</link>
        <guid>http://tianping.javaeye.com/blog/146287</guid>
      </item>
      <item>
        <title>struts+spring+hibernate是怎样的架构</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/141955" style="color:red;">http://tianping.javaeye.com/blog/141955</a>&nbsp;
          发表时间: 2007年11月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          以下为引用内容:举一个小例子回答楼主的问题。<br />    加入我要盖一件小平房（做一个网站），我需要做的是：<br />    1：买一块地。(租一台服务器)--1天<br />    2：申请一个门牌号码（买一个域名）--1天<br />    3：买材料，请2、3个工人，开始盖房（准备所需要的开发工具IDE什么的，搭建测试环境，找2，3个程序员--刚毕业的大学生就可以了）。--1周<br />    4：盖房（敲代码编程）--1到3个月。<br />    5：房子盖好了，东敲敲西敲敲看牢不牢固（自己测试一下功能都齐全了没）--1周。<br />    6:如果有问题，比方说卧室不够透气，把卧室的墙多开一扇窗户(哪个页面有问题，删除重新写一个)。比方说墙里面的电线发现有问题，把墙凿开，换一根电线。<br />    6：完工。<br /><br />    总共耗时2-4个月左右。<br />    现在我要盖一栋大楼（开发一个企业级项目），本质上还是盖房子，但是如果就像盖小平房一样的去做。结果是怎样的呢？<br />    盖到18楼了，突然说5楼内测的电线堵住了，5楼以上都没有点，于是得把5楼以上的全拆了，拆到5楼，把墙凿开，把电线换一根，再继续往上面盖。<br />    相信没有哪栋大厦是这样盖出来的。<br /><br />    于是有了建筑师（软件架构师）这个职位，有了建筑设计学（软件架构学）这么个说法。<br />    盖大楼，一开始的步骤是这样的。<br />    地还是要买，不过得做土地勘察，样本采集，看地质如何。<br />    然后得画图纸，这图纸一画就是很久，要考虑到整栋大楼的方方面面，哪些墙可以供拆除，就在里面放电线水管什么的。哪些是主力墙不能拆除。这里卖弄的学问太多，笔者也无法弄清楚。<br /><br />    然后就是打地基。这也是相当重要的环节，地基没打好，整个房子都不稳固。然后就是用钢筋水泥等，打造整栋大楼的框架。让房子非常坚固。<br /><br />    再后才是逐步完善大楼内部，一层层的砌砖，一间间地粉刷，装修。就算某一块出了问题。去掉或者修复那一块就行了，整栋大楼屹立不倒，稳稳当当。<br /><br />    企业级项目中，也是同安的过程。<br />    先用UML建模，建立数据库模型，项目的分层架构设计，使用框架让整个项目健壮起来，动一处而不会牵动全身。<br /><br />    struts, spring,hibernate都是为了让项目更健壮而产生的。如果只是要做一个小网站，我觉得很没有必要使用这些东西，就 ASP，PHP一定能做得更快，就像盖小平房一样。就算要用Java,就用JSP+JavaBean就行了。如果是开发企业级项目，ASP和PHP是绝对不能跟J2EE相提并论的。<br /><br />    Struts是MVC框架，它的作用不是让人能干什么（它能做的事JSP都能做），它的作用是不能让人干什么。就好像，设计师要砌墙工人在哪里砌砖，他就不能去别出砌一样。给程序语言严格的规范，让它按照规范走，不会随心所欲地去编代码。这样可以保证项目的高度一致性。<br /><br />    Spring是J2EE轻量级框架。它的做法好比把项目分成一个个地组件，哪一块出了问题，换掉那一块就是了。别的地方根本不用动。就好像我刚刚举的5楼电线出了问题，就在5楼把墙凿开，换一个电线即可，不用担心5楼的墙凿开上面的会塌下来。<br /><br />    Hibernate是ORM工具，不能称之为框架，它至少至少有一个好处，在项目做移植数据库的时候，不用切换数据库层的代码。当然它的好处很多。笔者在所有的开源技术中，也最钟情对Hibernate的研究。<br /><br />    不过这些问题，都只有在很大的项目开发中才会用到。所以如是是小项目开发，不建议使用J2EE.<br /><br /><br />    JSP是动态网页；<br /><br />    struts是一种MVC构架，MVC构架包括模型（如javaBean),显示(如jsp),控制(如servlet);<br /><br />    hibernate是数据库映射工具，使得在j2ee编程中更方便对数据库操作；
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/141955#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 Nov 2007 10:36:35 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/141955</link>
        <guid>http://tianping.javaeye.com/blog/141955</guid>
      </item>
      <item>
        <title>如何在三个月掌握三年的经验</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/141921" style="color:red;">http://tianping.javaeye.com/blog/141921</a>&nbsp;
          发表时间: 2007年11月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          很多职场新人都谈到了工作经验的问题，似乎招聘公司不给你机会，你就没办法获得必要的工作经验，其实并不一定。<br /><br />　　很多资料在网上都是可以找到的，只是看你具备不具备足够的信息收集与处理能力，而这个收集与处理信息的过程，也能极大的提升你的职业能力。<br /><br />　　我一直有个感觉，在“模仿中成长，在创新中成功”，其实在真正的职业工作中，大多数的工作都是模仿重复，强调的是工作效率，而不是创新。对于企业而言，过度的创新必然导致过多的失败，以及效率的低下。<br /><br />　　以下方式是我的成长中曾经做过的，也是我用来训练新员工的方案。你们也可以试试。<br /><br />　　看到很多谈应聘技巧的帖子，其实并不实用，有菜谱并不代表能做出好菜，能不能做出好菜仍要看你天天炒，日日炒，炒出来的本事。<br /><br />　　所以，我这里要强调的一点是，你收集到的任何资料都不能只是看看，而必须自己手把手，动手去整理、去归类，去建立新的结构，这个信息收集与处理的过程甚至比你最后总结成文的文字更重要。<br /><br />　　何谓“学习”？学习学习，学而习，习而成习惯。光学不习，那知识还只是书上的，老师教的，不是你自己的，只有你重复练习了，经过量变，才会有质变，当你形成条件反射时，你就真正掌握这个东西了。<br /><br />　　这个过程需要维持两至三个月的时间，一定要坚持下去，你会看到自己的变化的。否则，你会用你最青春的两三年来慢慢沉淀出这些你两三个月就能掌握的东西。<br /><br />　　一切一切，其实，你们比的不是其它的东西，只是比的速度。<br /><br />　　这也是为什么我那么强调基本功的原因。<br />　　　　<br />　　　　<br />　　　　1. 职业分析：<br />　　　　A. 分析性格——分析长处和短处——分析大家都有的长处——确定自己最终发展的专业<br />　　　　B. 确定兴趣——分析竞争的激烈程度和发展的空间大小——寻找相对优势—确定自己最终进入的行业<br />　　　　C. 确定行业内自己的专业方向，继续保持自身的专业优势。<br />　　　　<br />　　　　2. 编写行业报告——着重对行业全面性的把握。<br />　　　　A. 通过上网查询和购买行业报刊，收集不少于三十万字的行业、重点企业的有效资料，在电脑中进行资料分析、分类、汇总。<br />　　　　B. 参考同类行业书籍，确定写作提纲，确定文章结构和逻辑方向，培养文字表达能力和逻辑能力，以及熟练的电脑使用技能。<br />　　　　C. 将三十万字资料浓缩成十至十五万字，写成一本符合出版行文格式要求的行业报告。如果选题好，还真的有出版的可能性。如果有一定的独特见解，也可以写成文章争取在专业刊物上发表，树立个人专业形象。<br />　　　　<br />　　　　3. 编写讲座报告——着重对专业系统性的把握。<br />　　　　A. 根据你希望从事的专业岗位，从报告中选择两到三个重点，将书稿压缩成两万字的讲座稿（按每分钟150字的演讲速度，即两个小时）。<br />　　　　B. 将演讲稿再浓缩成两千字的提纲和重要内容，使用PPT软件编成演讲用演示文件，并根据相关内容配以精彩图片。<br />　　　　C. 培养职业化的公众表达能力和表达方式，练习普通话，使用讲座稿进行互动讲座和演讲练习，只到脱口而出。<br />　　　　<br /><br />　　告诉大家两个名人是这么成长的．<br />　　<br />　　一个是教英语的李阳，他读大学时成绩不好，英语不及格，然后他做什么去了？他跑到没人的地方大声喊英语去了．<br />　　<br />　　一个是做广告的叶茂中，他卖广告卖不出去了，他跑回家写书．别人看到的和他自己说的是拿着书出版出了名，发达了．其实做过这个事的人才会知道，当他把这本书写出来时，能不能出版已经不重要的，因为他知道他变化了．<br />　　<br />　　我当时也是没办法了，把所有的钱买了台电脑，在家里做了三个月这个事，三个月后的变化是惊人的，我的父母、我兼职的公司的老总，最重要的是我自己，都感觉到了自己的变化。<br />　　<br />　　完全不同了。<br /><br />　　其实我写的已经不是理论了，其实什么都没有技巧的，只是多看书，然后多做，硬磕，坚持下去，刚开始觉得没变化，没感觉，很累，坚持不下去，然后做着做着，就越来越快了，然后慢慢的有变化．<br /><br />　　而且有意思的是，我在家呆了三个月，做的事其实根本与我所从事的工作没有一点关系．只是这三个月的训练，对于我的逻辑、结构、全局性、文字表达能力、口头表达能力有了极大的提升。<br /><br />　　至于收入翻５翻，当年一个月也就八百块钱，然后做完这个训练后整个人的状态都变了，有自信了，然后写了一个方案去应聘，结果进了一家大公司，当然，开始我还不想去，因为对方只给我800/月，还要自己租房子，吃饭，觉得不好，但是对方连续四个月三次打电话找我，于是我去了，结果去了就后悔了，真正好的公司根本不在乎工资的，重要的是你自己的能力。第一个月，我就挣了八千块，我以前想都不敢想的。然后两个月就转了正，而有一个有关系的同事，呆了一年还没能转正。然后每个月的收入超过工资几倍，还有年终奖两万，出国旅游，其实也不累，我到这个家公司的同时，还到另一家广告公司兼职，呵呵，很回忆的过去。<br /><br />　　现在看到太多的人谈工资，我确实不喜欢，我这几年都不和老板谈工资的，因为说出来好笑，帐面工资高了，还要多扣税．<br /><br />　　我只在意公司的分配方式，怎么样算提成和奖金，年薪．<br />　　<br />　　上个月有一个和我同龄的名牌大学MBA来我现在所在的小公司应聘,不愿意和人事小姐谈,老板不在,我就来谈了,我说好呀,以你的资历我不能和你谈给谁做副手的问题了,我跟你谈谈公司的分配方式吧,其实我们公司普通员工的收入都不高的,长沙平均水平,只是不忙,周末休两天,工作满一年还有一个星期的年休假.<br />　　<br />　　但是公司几个部门负责人还是有钱的,象我三十岁,一年18万左右的年薪,其它的我就不清楚了,有几个我一个星期才见一次的,比我还小,只怕拿得比我还多.你应该也是这样的吧.<br />　　<br />　　他要求6千一个月的月薪,我说这倒不重要,重要的是公司不会给你安排业务的,你自己找业务回来,公司给你平台,给你配团队,能挣多少钱是你的本事.<br />　　<br />　　我说完了,问,你有什么想法吗?他说没想法,起身走人.<br />　　<br />　　太有意思了,你在长沙想拿六千一个月,你等别人找事给你做,你为什么不能自己找到项目呀?六千是底薪呀,差不多7万2千的底薪,如果是这样的,那我自己算我应该拿到二十五万以上的年薪了.<br />　　<br />　　从来拿底薪和拿年薪的人就是不一样的.<br />　　<br />　　如果你不敢拿年薪,你就不要想着谈什么老板给你少了.<br />　　<br />　　企业是要盈利的,资本家是要剥削的.问题是,如果你是一个真正能创造价值的人,你自己所创造的价值你是可以拿到手的.<br />　　<br />　　大学毕业生,如果什么经验也没有,只有知识,没有技能,能找到一个给你几百块钱,让你在这里呆着学东西的企业就应该感谢了,如果你觉得这种企业不是你所向往的,你在上大学时就老老实实努力学,少玩,多练.<br />　　<br />　　我工作有一个总结,钱永远不会是目标,但是它会是结果.<br /><br />　　谈到职业规划，有人说过职业可以规划的，我也相信未来可以计划的，问题是，你是不是这个能不能计划出你未来的人，以及，你身边有没有熟悉你的，如果没有，那你自己都不会明白你自己的未来是什么的，就象象你去做所谓的性向测试，说不定是你自己在自欺欺人了，这种事多了，没人会把自己算成一个坏人的。<br /><br />　　所以重要的还是那一句话，复杂的生活简单过，简单的事情重复做。<br />　　<br />　　你是中文系的，如果你的年纪还不是很大，建议你凭你自己的能力，哪怕是工资少点，你都要进最好的广告公司，去呆上一年半载，按我说的方法偷师，基本能力提升了，慢慢的你会遇到一些贵人的，还有你会涉及一些行业，慢慢的，你会发觉你内心深处喜欢的行业。<br />　　<br />　　呵呵，特别是哦，女孩子，只有努力才能进大公司，只有进了大公司才能遇到优秀的男生。好男生都关在写字楼里上班下班加班的，呵呵。生活圈子都小的，你选择的工作圈在你努力的阶段就是你的生活圈。<br /><br />　　在你的成长过程中，有五个人非常重要。<br />　　　　<br />　　第一个，导师，教练。<br />　　他教给你实用的技巧、一定的工作经验，而不是知识。他可以给你指明方向。<br />　　这个人可能是你的上司、前辈、学长。<br />　　　　<br />　　第二个，陪练，同路人。<br />　　任何人的成长都不是学出来的，而是学而习，习而成习惯，练出来的。在这个练的过程中，是一件很苦的过程，是一系列简单动作的重复重复再重复，由量变到质变的过程，在这个过程中，一个人很难坚持下来，这时你需要一个同路人。<br />　　他可以是和你共同兴趣，共同目标的朋友，最好是你生命中所爱的人。<br />　　　　<br />　　第三个，榜样，他是你人生的标杆。<br />　　在你一生中，在不同阶段，会有不同的标杆，你向他学习，受他鼓舞，一步一步向他靠扰。<br />　　最重要的是那个你看得到摸得着的人，你知道，不需要通过机遇，只需要通过努力就可以达到的榜样。<br />　　　　<br />　　第四个，敌人，看不起你的人，拒绝过你的人。<br />　　人不到绝境是不会有斗志的，你要证明他是错的，他会给你真正的动力。<br />　　　　<br />　　第五个，最重要的是第五个，你们觉得第五个人是你自己。<br />　　世界上没有救世主，任何希望当别人救世主的人不是疯子就是傻子，只有自己才可以救自己。<br />　　这个世界上，失败的人除了天分太差之外，只有以下几点，懒，方向不对，方法不对，没有坚持。<br />　　如果你自己做不到，你不要怪别人。<br /><br />　　基本功是你自己的，细节所积累下来的，能让你迅速融入新环境．<br />　　<br />　　<br />　　不知道怎么跟大家谈基本功这个问题．<br /><br />　　很多东西大家都没把它当基本功了．<br /><br />　　比如说，我想要的人，他打字很快，他很少很少写错别字，有丰富的词汇量，逻辑很清晰，用词很准确，这些看上去难不难？<br />　　<br />　　但是在我这两年见过的应聘的策划文案来看，只有两个人做到了．一个是做了三年文案的女孩子，慢慢磨的．一个是中文硕士生，还没毕业．<br />　　<br />　　其实大学到底教给大家什么了？<br />　　知识？<br />　　<br />　　大学阶段必须打好你的基本功，这些决定了你就业后的学习能力，阶层简单工作的工作效率．<br />　　<br />　　如果谁还说打字、排版是文员做的事，那只能说他是真正不明白真正的职场需要。<br />　　<br />　　你们在大学所学到的知识，都是同质化的了，如果将知识变为通用的、标准化的技能才是重要的。<br />　　<br />　　既然学的东西没用，那在大学还要不要认真学习呢？<br />　　<br />　　当然要，因为这些东西是系统性的，这个学习过程能培养你的学习能力。<br />　　<br />　　知识不能改变你的命运了，但是它可以改变你的气质。<br />　　<br />　　如果你读个四年大学出来，你的气质还不能好一点，那你的大学就真的白读了。<br />　　<br />　　经常有人在问面试穿什么衣服呀？<br />　　<br />　　穿什么衣服重要吗？<br />　　<br />　　重要的是什么人在穿这些衣服。<br />　　<br />　　重要的是你的精气神，你的气质。<br />　　<br />　　有一天有一个应聘文案的来了，我叫设计总监先和他聊聊。<br />　　<br />　　聊完了，我说这个人不行吧，设计总监说为什么？<br />　　<br />　　我说我们调性不符，我们多少都有点书卷气，而他是一脸的江湖气。<br />　　<br />　　果然，呵呵。<br />　　<br />　　招聘方当然是要看应聘者的外形条件的，但并不是丑的就不招，重要的是能力和你的气质，是不是符合公司要求的。<br /><br />　　重要的是兴趣。<br />　　<br />　　然后是狂练基本功，简单重复积累。<br />　　<br />　　学打拳，你先站三个月桩再说。<br />　　<br />　　面对新人，我说很多东西，你会发现，每个字你都认识，每句话你都看得懂，但是你理解吗？<br />　　<br />　　领悟，是教不了的。<br />　　<br />　　自己努力吧，自己重复做，再会明白自己最想要的是什么。<br />　　<br />　　你考公员员如果死活考不上，那你应该去想想，这种机械性的考试你都过不了，那是不是学习方法，或者兴趣不对呀？<br />　　<br />　　做销售，同样的，从基阶做起吧。<br />　　<br />　　你的财政学对你有没有帮助？<br />　　<br />　　当然有，你对销售的认识会不同的。<br />　　<br />　　象十年前我卖保险，人人都跟银行比，算利息，都算得没有银行高，只能说死了人有赔了。<br />　　<br />　　而我是怎么算呢？我用递增，还是增减年金公式算，呵呵，比银行高呢。<br />　　<br />　　另外，别人说死了人有赔，最多是说得婉转点。<br />　　<br />　　我可没把它当死人卖呀，我把它当礼物卖，当成父母送给孩子的礼物卖，卖得可好了，呵呵。<br />　　<br />　　现在哪个做人寿险的人敢说他一年做两百多单？<br />　　<br />　　呵呵，我好象一年做了二百四十单左右，全是年缴哦。<br /><br />　　这个世界上最穷的和最富的人都在做销售．<br />　　<br />　　做销售的人底薪很低的，大多数人拼的只是体力罢了，如果你想做好，你多花心思就可以了．多想多跑，还是在一个行业里多坚持，找到高手做师父带你．<br /><br />　　我说说当年我混日子的时候怎么过来的．<br />　　<br />　　那年头电脑还紧俏，我只要一有机会就到别人电脑上练东西，终于练成了今天的电脑基本功，一方面要多学，一方面要多用心．<br />　　<br />　　然后，我每天做记录，记下工作的流程，记下别人说过的工作中重要的话，其实什么叫行业经验，很多老手随便说的话，都是行话了，有它的意思的，听了就要想，就要去查，很多东西就知道了．<br />　　<br />　　为什么要记录，因为什么叫职业化？职业化就是标准化、流程化，模式化，你多看多记多想就能明白了，这些东西在很多地方都是通用的。<br />　　<br />　　有一点，如果这里收入还可以的话，你好好学吧，任何工作都要呆一两年，你才会有认识的，跳来跳去的对你不好，真的，你还在磨性情的时候，只要你保持学习的能力，别下班玩去了就可以了，有压力才有动力，好好留心心仪的公司招聘的要求，按那个要求去做一个一年的训练与学习计划，一年后，那个公司在等你。
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/141921#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 20 Nov 2007 09:33:26 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/141921</link>
        <guid>http://tianping.javaeye.com/blog/141921</guid>
      </item>
      <item>
        <title>左连接和内联的区别</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/139939" style="color:red;">http://tianping.javaeye.com/blog/139939</a>&nbsp;
          发表时间: 2007年11月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          INNER<br />    指定返回所有相匹配的行对。废弃两个表中不匹配的行。如果未指定联接类型，则这是默认设置。<br />LEFT [OUTER]<br />    指定除所有由内联接返回的行外，所有来自左表的不符合指定条件的行也包含在结果集内。来自左表的输出列设置为 NULL。<br />RIGHT [OUTER]<br />    指定除所有由内联接返回的行外，所有来自右表的不符合指定条件的行也包含在结果集内。来自右表的输出列设置为 NULL。<br />FULL [OUTER]<br />    如果来自左表或右表的某行与选择准则不匹配，则指定在结果集内包含该行，并且将与另一个表对应的输出列设置为 NULL。除此之外，结果集中还包含通常由内联接返回的所有行。<br />&lt;join_hint><br />    指定联接提示或执行算法。如果指定了 &lt;join_hint>，也必须明确指定 INNER、LEFT、RIGHT 或 FULL。有关联接提示的更多信息，请参见 FROM。<br />JOIN<br />    表示联接所指定的表或视图。
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/139939#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 Nov 2007 15:01:27 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/139939</link>
        <guid>http://tianping.javaeye.com/blog/139939</guid>
      </item>
      <item>
        <title>Hibernate fetch lazy cascade inverse</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/139938" style="color:red;">http://tianping.javaeye.com/blog/139938</a>&nbsp;
          发表时间: 2007年11月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Hibernate最让人头大的就是对集合的加载形式。<br />书看了N次了，还是没有真正理解Hibernate。所以下午专门做了下测试，对配置文件的意思加深了认识。<br /><br />假设有两个表，Photos(一）  ---  picture(多）Photo包含picture集合<br /><br />结论1： HQL代码 > fetch（配置） > lazy （配置）<br />结论2： 默认 lazy="true"<br />结论3： fetch 和 lazy 主要是用来级联查询的，   而 cascade 和 inverse 主要是用来级联插入和修改的<br />结论4： 如果你是用spring来帮你管理你的session, 并且是自动提交，延迟加载就等于没加载~_~(当然<br />                除非你手动重新打开session然后手动Hibernate.initialize(set);然后关闭session.<br />结论5:     cascade主要是简化了在代码中的级联更新和删除。<br />j结论6：老爸可以有多个孩子，一个孩子不能有多个老爸，而且老爸说的算, 孩子围着老爸转。<br />               所以Photos老爸要有权力所以 cascade 这个关键子都是送给老爸的， 也就是级联更新，<br />               老爸改姓了，儿子也得跟着改，呵呵。“不然，就没有零花钱咯”。<br />                而Picture儿子整体挨骂，但是还是要维护父子之间良好的关系，对老爸百依百顺，所<br />               以老爸就说，儿子，“关系，由你来维护（inverse="true") ，不然就不给零花钱。呵。”。<br />               &lt;set name="pictures" inverse="true" cascade="all"><br />                    &lt;key><br />                       &lt;column name="photosid" not-null="true" /><br />                    &lt;/key><br />                 &lt;one-to-many class="girl.domain.Picture" /><br />             &lt;/set><br />               <br />测试代码：<br /><br />   Photos p = ps.getById(1);<br />  Set&lt;Picture> set = p.getPictures();<br />  for(Picture pic : set){<br />     System.out.println(pic.getId());<br />  }<br /><br />  配置文件的一部分：<br />       &lt;set name="pictures" inverse="true" cascade="all" ><br />            &lt;key><br />                &lt;column name="photosid" not-null="true" /><br />            &lt;/key><br />            &lt;one-to-many class="girl.domain.Picture" /><br />        &lt;/set><br /><br />测试过程会对配置文件不断修改：并且从来不曾手动重新打开session<br /><br />测试结构：<br /><br />当配置条件为 lazy=true 一句查询 测试代码中没有调用getPicture()  正常<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br />lazy=true 一句查询 有getPicture()<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br /><br />lazy=true 一句查询  有getPicture() 并且访问了里面的元数Picture 且有异常抛出<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br /><br />lazy="false" 两句查询  肯定没问题，因为全部数据都个查了出来 所以怎么调用都正常<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br />Hibernate: select pictures0_.photosid as photosid1_, pictures0_.id as id1_, pictures0_.id as id2_0_, pictures0_.photosid as photosid2_0_, pictures0_.name as name2_0_, pictures0_.clicked as clicked2_0_, pictures0_.uploaddate as uploaddate2_0_, pictures0_.size as size2_0_, pictures0_.description as descript7_2_0_, pictures0_.uri as uri2_0_ from super.picture pictures0_ where pictures0_.photosid=?<br /><br /><br />fetch="join"  一句查询  效果 ＝＝ lazy="false" 呵呵，哪个效率高，我就不知道了。。。。。。。。。。。<br />Hibernate: select photos0_.id as id0_1_, photos0_.userid as userid0_1_, photos0_.typeid as typeid0_1_, photos0_.name as name0_1_, photos0_.createtime as createtime0_1_, photos0_.description as descript6_0_1_, photos0_.faceid as faceid0_1_, photos0_.uri as uri0_1_, pictures1_.photosid as photosid3_, pictures1_.id as id3_, pictures1_.id as id2_0_, pictures1_.photosid as photosid2_0_, pictures1_.name as name2_0_, pictures1_.clicked as clicked2_0_, pictures1_.uploaddate as uploaddate2_0_, pictures1_.size as size2_0_, pictures1_.description as descript7_2_0_, pictures1_.uri as uri2_0_ from super.photos photos0_ left outer join super.picture pictures1_ on photos0_.id=pictures1_.photosid where photos0_.id=?<br /><br />不加fetch＝"join" 一句查询  没有getPicture() 正常<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br />不加fetch＝"join" 一句查询  有getPicture() 正常<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br />不加fetch＝"join" 一句查询 有getPicture() 并且访问里面的元素Picture的ID 有异常抛出<br />Hibernate: select photos0_.id as id0_0_, photos0_.userid as userid0_0_, photos0_.typeid as typeid0_0_, photos0_.name as name0_0_, photos0_.createtime as createtime0_0_, photos0_.description as descript6_0_0_, photos0_.faceid as faceid0_0_, photos0_.uri as uri0_0_ from super.photos photos0_ where photos0_.id=?<br /><br />来个两兵交战 fetch="join" lazy="true"  呵呵 结果，一句查询， 结构正常 所以就当lazy不存在好了。 看来fetch 是老大。、、、、、、、、、、、、、<br />Hibernate: select photos0_.id as id0_1_, photos0_.userid as userid0_1_, photos0_.typeid as typeid0_1_, photos0_.name as name0_1_, photos0_.createtime as createtime0_1_, photos0_.description as descript6_0_1_, photos0_.faceid as faceid0_1_, photos0_.uri as uri0_1_, pictures1_.photosid as photosid3_, pictures1_.id as id3_, pictures1_.id as id2_0_, pictures1_.photosid as photosid2_0_, pictures1_.name as name2_0_, pictures1_.clicked as clicked2_0_, pictures1_.uploaddate as uploaddate2_0_, pictures1_.size as size2_0_, pictures1_.description as descript7_2_0_, pictures1_.uri as uri2_0_ from super.photos photos0_ left outer join super.picture pictures1_ on photos0_.id=pictures1_.photosid where photos0_.id=?
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/139938#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 Nov 2007 15:00:27 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/139938</link>
        <guid>http://tianping.javaeye.com/blog/139938</guid>
      </item>
      <item>
        <title>外国一家公司的一道超难推理面试题,如果你做对了，直接录取拿5K以上的工资不成问题!!!!!!</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/139072" style="color:red;">http://tianping.javaeye.com/blog/139072</a>&nbsp;
          发表时间: 2007年11月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          问题如下:有100个犯人，头天晚上被通知第二天一早要带着一顶帽子（总共有100顶黑的和100顶白的,帽子是随机带的，而且不知道自己头上的帽子是什 么颜色），排成一列直线队伍，后面的人能看到前面的所有人带的帽子的颜色，前面的看不到后面的人的帽子颜色，现在警官让犯人们先讨论下，等明天排队时，警 官从最后一个人问起直到第一个，&ldquo;你头上带的帽子颜色是黑还是白？&rdquo;犯人只许说一个字&ldquo;黑或白&rdquo;，（说话时没有任何提示，都是标准的一个音，而且没有眼神 什么提示，有的只是头天晚上想出的方法）犯人说错直接杀，说对了马上放了，问讨论出一个怎样的方法使被杀的人数确定最少？<br />
<br />
<br />
感觉最接近正确的答案：<br />
犯人们先商量好,等排好队后，每个人都先记下在自己前面人的黑帽子的个数和白帽子的个数．<br />
排在最后面的人的答案是关键的，他掌控着所有人的生死大权哦，这样,他前面所有的人都要记下他的答案，而且要记下他后面每一个人的答案．<br />
比如说：<br />
倒数第一个人，他前面９９个人中白色帽子是奇数个数,那他就说自己的帽子白色，这是事先协商好的．<br />
倒数第二个人，他就知道白是奇数，这时如果他前面看到的９８个人中白色是偶数的话，那他自己一定就是白色的了，他就要说是白．<br />
倒数第三个人，如果他前面９７个人中白色偶数的话，而他后面的人是白色，所以他可以马上知道自己也是黑色了．<br />
倒数第Ｎ个人，以此类推啦．．．．<br />
运气好的话，一个都不用死哦<br />
<br />
奇偶校验法
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/139072#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 08 Nov 2007 13:11:04 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/139072</link>
        <guid>http://tianping.javaeye.com/blog/139072</guid>
      </item>
      <item>
        <title>hibernate集合映射inverse和cascade详解</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/138083" style="color:red;">http://tianping.javaeye.com/blog/138083</a>&nbsp;
          发表时间: 2007年11月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <font size="2">4. hibernate如何根据pojo来更新数据库<br />
<br />
4.0 在commit/flush之前，hibernate不会对pojo对象作神秘的处理。<br />
4.0.1 在select查询出pojo时，hibernate根据&ldquo;字段--属性&rdquo;的对应关系，用字段的值填充pojo的属性；<br />
然后根据&ldquo;关系标记&rdquo;生成sql语句从relationTable中查询出满足条件的relationPojo，并把这些relatinPojo<br />
放到&ldquo;关系属性&rdquo;中。这个过程是机械的。<br />
<br />
4.0.2 在pojo对象被查出来后，到commit(或flush)之前，它将是一个普通的java对象，hibernate不会做额外的手脚。<br />
比如，不会限制你设置一个属性的值为null或其它任何值<br />
在集合类Set的add(object)操作时， 不会改变object的值，不会检查参数object是否是一个pojo对象<br />
设置mainPojo的一个&ldquo;桥属性&rdquo;的值，不会自动设置relationPojo的对应的&ldquo;桥属性&rdquo;的值。<br />
执行session.delete(pojo)时，pojo本身没有变化，他的属性值也没有变化。<br />
执行session.save(pojo)时，如果pojo的id不是hibernate或数据库生成,则它的值没有变化。<br />
如果pojo的id是hibernate或数据库生成，则hibernate会把id给pojo设上去。<br />
<br />
extend: 对lazy=true的set，hibernate在进行set的操作(调用java.util.Set中声明的方法)时<br />
会先inialize这个set，仅此而已。而inialize仅仅是从数据库中捞出set的数据。 <br />
如果一个set已经被inialize了，那么对它进行的操作就是java.util.Set接口中定义的语义。<br />
<br />
另外，如果id由hibernate来生成，那么在save(pojo)时，hibernate会改变该pojo，会设置它的id，这<br />
可能改变该pojo的hashCode，详细地讨论见帖《》<br />
<br />
mapping文件中标记的某些属性及pojo对象的操作会对数据库操作产生影响，这些影响都是在commit时才会起作用。<br />
而在commit前pojo的状态不受它们的影响。<br />
<br />
不过，待commit之时，将由hibernate完全掌控，它好像知道pojo对象从创建到commit这中间的所有变化。<br />
<br />
<br />
4.01. 关联更新<br />
&quot;关系标记&quot;对应的属性是一个pojo或一个pojo的集合，修改&ldquo;关系属性&rdquo;的值能会导致更新mainTable表，也可能会更新relationTable表。<br />
<br />
这种更新暂叫&ldquo;关联更新&rdquo;。<br />
<br />
<br />
4.1.inverse属性的作用（假定没有设置cascade属性） <br />
4.1.1 &ldquo;只有集合标记（set/map/list/array/bag）才有inverse属性&rdquo;。<br />
&mdash;&mdash;&mdash;&mdash;不妨以标记set为例，具体为&ldquo;一个地区（Address表）的学校（School表）&rdquo; -- address.schoolSet。<br />
<br />
4.1.2 &ldquo;set的inverse属性决定是否把对set的改动反映到数据库中去。<br />
inverse=false&mdash;&mdash;&mdash;&mdash;反映；inverse=true&mdash;&mdash;&mdash;&mdash;不反映&rdquo;<br />
inverse属性默认为false<br />
<br />
对&lt;one-to-many&gt;和&lt;many-to-many&gt;子标记，这两条都适用。<br />
不管是对set做什么操作，4.1.2都适用。<br />
<br />
4.1.3当inverse=false时，hibernate如何将对set的改动反映到数据库中：<br />
<br />
对set的操作主要有：（1）新增元素 address.getSchoolSet().add(oneSchool);<br />
（2）删除元素 address.getSchoolSet().remove(oneSchool);<br />
（3）删除set address.setSchoolSet(null);<br />
（4）设新set address.setSchoolSet( newSchoolSet);<br />
（5）转移set otherSchoolSet = otherAddress.getSchoolSet();<br />
otherAddress.setSchoolSet(null);<br />
address.setSchoolSet(otherSchoolSet);<br />
（6）改变set中元素的属性的值 如果是改变key属性，这会导致异常<br />
如果改变的是普通的属性，则hibernate认为set没有变化（在后面可以看出缘由）。<br />
所以这种情形不予考虑。<br />
<br />
改变set后，hibernate对数据库的操作根据是&lt;one-to-many&gt;关系还是&lt;many-to-many&gt;关系而有不同。<br />
<br />
对one-to-many，对school set的改动，会改变表SCHOOL中的数据:<br />
#SCHOOL_ID是school表的主键，SCHOOL_ADDRESS是school表中的地址栏位<br />
#表School的外键为SCHOOL_ADDRESS，它对应表Address的主键ADDRESS_ID<br />
（11）insert oneSchool&mdash;&mdash;&mdash;&mdash; sqlInsertRowString: <br />
update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=? <br />
(仅仅update foreign-key的值。)<br />
（22）delete oneSchool&mdash;&mdash;&mdash;&mdash; sqlDeleteRowString: <br />
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?<br />
（很奇怪，把foreign-key设置为null不知道有什么实际意义？）<br />
（33）delete 属于某一address的所有school &mdash;&mdash;&mdash;&mdash;sqlDeleteString：<br />
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?<br />
（44）update &mdash;&mdash;&mdash;&mdash;sqlUpdateRowString：&quot;&quot;， no need<br />
<br />
对many-to-many，对school set的改动，会改变关系表ADDRESS_SCHOOL中的数据:<br />
#&ldquo;地区&mdash;&mdash;&mdash;&mdash;学校&rdquo;的关系为多对多的关系有点牵强，只是为了方便与上面的one-to-many作比较<br />
#假设有一个关系表ADDRESS_SCHOOL，有两个字段ADDRESS_ID, SCHOOL_ID，<br />
#这两个字段分别对应ADDRESS和SCHOOL两表的key<br />
（11）insert的SQL语句为： insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID) <br />
values(?,?)<br />
（22）delete的SQL语句为： delete from ADDRESS_SCHOOL <br />
where ADDRESS_ID=? AND SCHOOL_ID=?<br />
（33）delete all的SQL语句为： delete from ADDRESS_SCHOOL<br />
where ADDRESS_ID=?<br />
（44）update的sql语句为 &mdash;&mdash;&mdash;&mdash;sqlUpdateRowString：<br />
update ADDRESS_SCHOOL set ADDRESS_ID=?<br />
where ADDRESS_ID=? AND SCHOOL_ID=?<br />
<br />
对set的操作(1),hibernate会执行(11)sqlInsertRowString<br />
对set的操作(2),hibernate会执行(22)sqlDeleteRowString<br />
对set的操作(3),hibernate会执行(33)sqlDeleteString<br />
对set的操作(4),老的schoolSet因为没有所属的address,所以被全部delete掉，即先执行(33)sqlDeleteString<br />
然后新增新的schoolSet,即再执行sqlInsertRowString<br />
对set的操作(5)，实际上就是将set从一个pojo转移到另一pojo：<br />
首先，执行sqlDeleteString，删除掉otherAddress所属的school<br />
然后，执行sqlDeleteString，删除掉address原先的school<br />
最后，执行sqlInsertRowString，将otherSchoolSet新增给address<br />
<br />
总结：（1）对one-to-many而言，改变set，会让hibernate执行一系列的update语句， 不会delete/insert数据<br />
（2）对many-to-many而言，改变set,只修改关系表的数据，不会影响many-to-many的另一方。<br />
（3）虽然one-to-many和many-to-many的数据库操作不一样，但目的都是一个：维护数据的一致性。执行的sql都<br />
只涉及到&ldquo;桥字段&rdquo;，不会考虑或改变其他的字段，所以对set的操作(6)是没有效果地。<br />
extend:对list,可能还会维护index字段。<br />
<br />
4.1.4 &ldquo;inverse与cascade没有什么关系，互无牵扯。&rdquo;<br />
commit后，这两个属性发挥作用的时机不同，hibernate会根据对pojo对象的改动，及cascade属性的设置，<br />
生成一系列的Action，比如UpdateAction,DeleteAction,InsertAction等，每个Action都有execute方法以执行对应的sql语句。<br />
待所有这些Action都生成好了后，hibernate再一起执行它们，在执行sql前，inverse属性起作用，<br />
当inverse=true时，不执行sql；当inverse=false时，执行sql。<br />
<br />
4.1.5 inverse的默认值为false，所以inverse属性默认会进行&ldquo;关联更新&rdquo;。<br />
<br />
4.1.6 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性。<br />
&nbsp; 糟糕的是，不设置inverse属性时，inverse默认为false。<br />
<br />
4.2. 级联（cascade）属性的作用： <br />
4.2.1 只有&ldquo;关系标记&rdquo;才有cascade属性：many-to-one，one-to-one ，any, <br />
set(map, bag, idbag, list, array) + one-to-many(many-to-many)<br />
<br />
4.2.2 级联指的是当主控方执行操作时，关联对象（被动方）是否同步执行同一操作。<br />
pojo和它的关系属性的关系就是&ldquo;主控方 -- 被动方&rdquo;的关系，如果关系属性是一个set，那么被动方就是set中的一个一个元素，。<br />
比如：学校（School）有三个属性：地区(Address),校长（TheMaster）和学生(Set， 元素为Student)<br />
执行session.delete(school)时，级联决定是否执行session.delete(Address),session.delete(theMaster)，<br />
是否对每个aStudent执行session.delete(aStudent)。<br />
<br />
extend:这点和inverse属性是有区别的。见4.3.<br />
<br />
4.2.3 一个操作因级联cascade可能触发多个关联操作。前一个操作叫&ldquo;主控操作&rdquo;，后一个操作叫&ldquo;关联操作&rdquo;。<br />
cascade属性的可选值：<br />
all : 所有情况下均进行关联操作。<br />
none：所有情况下均不进行关联操作。这是默认值。<br />
save-update:在执行save/update/saveOrUpdate时进行关联操作。<br />
delete：在执行delete时进行关联操作。 <br />
<br />
具体执行什么&ldquo;关联操作&rdquo;是根据&ldquo;主控操作&rdquo;来的：<br />
&ldquo;主控操作&rdquo; &nbsp; &nbsp; &nbsp; &ldquo;关联操作&rdquo;<br />
session.saveOrUpdate --&gt; session.saveOrUpdate (执行saveOrUpdate实际上会执行save或者update)<br />
session.save ----&gt; session.saveOrUpdate<br />
session.udpate --&gt; session.saveOrUpdate<br />
session.delete --&gt; session.delete<br />
<br />
4.2.4 主控操作和关联操作的先后顺序是&ldquo;先保存one，再保存many；先删除many，再删除one；先update主控方，再update被动方&rdquo;<br />
对于one-to-one，当其属性constrained=&quot;false&quot;（默认值）时，它可看作one-to-many关系；<br />
&nbsp; 当其属性constrained=&quot;true&quot;时，它可看作many-to-one关系；<br />
对many-to-many，它可看作one-to-many。<br />
<br />
比如：学校（School）有三个属性：地区(Address),校长（TheMaster，其constrained=&quot;false&quot;）和学生(Set， 元素为Student) <br />
当执行session.save(school)时，<br />
实际的执行顺序为：session.save(Address);<br />
session.save(school);<br />
session.save(theMaster);<br />
for( 对每一个student ){<br />
session.save(aStudent);<br />
}<br />
<br />
当执行session.delete(school)时，<br />
实际的执行顺序为：session.delete(theMaster);<br />
for( 对每一个student ){<br />
session.delete(aStudent);<br />
}<br />
session.delete(school);<br />
session.delete(Address);<br />
<br />
当执行session.update(school)时，<br />
实际的执行顺序为：session.update(school);<br />
session.saveOrUpdate(Address);<br />
session.saveOrUpdate(theMaster);<br />
for( 对每一个student ){<br />
session.saveOrUpdate(aStudent);<br />
}<br />
注意：update操作因级联引发的关联操作为saveOrUpdate操作，而不是update操作。<br />
saveOrUpdate与update的区别是：前者根据操作对象是保存了还是没有保存，而决定执行update还是save<br />
<br />
extends: 实际中，删除学校不会删除地区，即地区的cascade一般设为false<br />
另外，many-to-many关系很少设置cascade=true，而是设置inverse=false。这个反映了cascade和inverse的区别。见4.3<br />
<br />
4.2.6 cascade的默认值为false，所以inverse属性默认会进行&ldquo;关联更新&rdquo;。<br />
<br />
4.2.7 总结：级联（cascade）就是操作一个对象时，对它的属性（其cascade=true）也进行这个操作。<br />
<br />
<br />
4.3 inverse和cascade的比较<br />
这两个属性本身互不影响，但起的作用有些类似，都能引发对关系表的更新。<br />
<br />
4.3.1 inverse只对set+one-to-many(或many-to-many)有效，对many-to-one, one-to-one无效。<br />
cascade对关系标记都有效。<br />
<br />
4.3.2 inverse对集合对象整体起作用，cascade对集合对象中的一个一个元素起作用，如果集合为空，那么cascade不会引发关联操作。<br />
比如将集合对象置为null， school.setStudentSet(null)<br />
inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?<br />
cascade则不会执行对STUDENT表的关联更新， 因为集合中没有元素。<br />
<br />
再比新增一个school, session.save(school)<br />
inverse导致hibernate执行：<br />
for( 对(school的每一个student ){<br />
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id<br />
}<br />
cascade导致hibernate执行：<br />
for( 对school的每一个student ){<br />
session.save(aStudent); //对学生执行save操作<br />
}<br />
<br />
extends:如果改变集合中的部分元素（比如新增一个元素），<br />
inverse: hibernate先判断哪些元素改变了，对改变的元素执行相应的sql<br />
cascade: 它总是对集合中的每个元素执行关联操作。<br />
（在关联操作中，hibernate会判断操作的对象是否改变）<br />
<br />
4.3.2 两个起作用的时机不同：<br />
cascade：在对主控方操作时，级联发生。<br />
inverse: 在flush时（commit会自动执行flush)，对session中的所有set，hibernate判断每个set是否有变化，<br />
对有变化的set执行相应的sql，执行之前，会有个判断：if( inverse == true ) return;<br />
<br />
可以看出cascade在先，inverse在后。<br />
<br />
4.3.3 inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。<br />
对one-to-many，hibernate对many方的数据库表执行update语句。<br />
对many-to-many, hibernate对关系表执行insert/update/delte语句，注意不是对many方的数据库表而是关系表。<br />
<br />
cascase 对set都是一致的，不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many<br />
方的数据库表。<br />
<br />
4.3.4 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性，都设为inverse=true。<br />
&nbsp; <br />
&nbsp; 对cascade，一般对many-to-one，many-to-many，constrained=true的one-to-one 不设置级联删除。</font>
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/138083#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 05 Nov 2007 14:35:11 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/138083</link>
        <guid>http://tianping.javaeye.com/blog/138083</guid>
      </item>
      <item>
        <title>开放式基金，封闭式基金分别是什么意思？</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/138047" style="color:red;">http://tianping.javaeye.com/blog/138047</a>&nbsp;
          发表时间: 2007年11月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>开放式基金，封闭式基金分别是什么意思？</strong> <strong>　　</strong>基金分为开放式基金与封闭式基金,&quot;开放&quot;是相对于&quot;封闭&quot;而言 把握升浪起点 现在机构底部建仓名单 <br />
外汇市场直通车 没有必赚只有稳赚 。简单地说,开放式基金的&quot;开放&quot;有两重意思。 <br />
一、封闭式基金,其发行的规模是固定的,比如我们可以说某基金规模为20亿。发行结束后,基金公司就不接受新的申购,投资者手里的基金不想要了,也不能 退给基金公司。投资者只能从二级市场其他的投资者那里买,而卖也只能卖给其他的投资者。这样,封闭式基金的总分额是不变的。<br />
<br />
<p>封闭式基金知识问答（3）</p>
<p>22、封基是投资还是投机<br />
答：封基是基金，当然首要的是投资，但因为它不可以赎回，只可以交易，所以有很大的投机成分，比如以前净值1.00的基金炒到4元，就是投机。而近年来，纯粹的投机封基很少，今年6.19的高点就算是一个小的投机操作。</p>
<p><br />
23、有没有投资封基赚大钱的例子？<br />
（本帖转自&ldquo;双色杜梋&rdquo;的&ldquo;乱弹封基5&rdquo;）原文如下：</p>
<p>&ldquo;买封基近二年只买进不卖出，收益当然可观。近日闲来无事，回顾一下投资封基的过程，算一下哪笔钱是比较成功，一算还真吓一跳。在2005年中期觉 得大盘跌无可跌时说服老公将准备提前还房贷的一笔钱买了科汇，就用其中的一万元来计算，当时是以0.9元买入，共买11100份，到2006年四月科汇分 红0.23元，共得红利2550元，第二天用红利买入除权后1.25元2000份，于是有了共13100份科汇。到06年10月份，根据坛子里朋友的指 点，以1.7元的左右的价格全部卖出科汇共得22000多元，同日换入0.88元的泰和25400份。到07年4 月泰和分红0.48元共得红利12000多元，红利到账日以1.7元又买入7200份共计32600份，07年八月泰和分红0.655元，共得红利 21000多元，红利到账日以2.4元又买入8900份共计41500份。到今天泰和价为2.42元，总市值为10万元。一万元变了十万元从不做波段，看 到这个结果吓一跳不知这样算对否？&rdquo;<br />
两年十倍！这样的结果很正确！<br />
其实，资本市场真正的大赢家，往往是寂寞的长跑者而非花心的油头小光棍,更非异想天开的算命先生。在这个残酷博弈游戏中，颠覆传统思维我们应该习以为常。拙能胜勤，朴亦笑华！此所谓：大道至简，大智若愚。感谢基老太省了我不少文字，祝福她老人家秋分快乐,万寿无疆。<br />
杜梋观点：<br />
对于做股票的短线高手，两年10倍的收益，也许能达到或超过！<br />
持有一个好股票！两年10倍的收益！也许能达到或超过！<br />
但是，大多数人都很难把握短线的机会，而在一千多只股票中寻找到价值股并长期持有，也难度相当大，只有极少数人能做到。</p>
<p><br />
封基的缺点:<br />
24分红意愿远落后开鸡</p>
<p>25二级市场的价格涨跌考验持有人的耐心,引诱持有人丢掉筹码,因为随时可以买卖交易，手痒的同志容易养成追涨杀跌的坏毛病，长此以往，收益打折扣;封基经常会磨你的耐心，心态不稳，想一口吃成胖子的人最好不要买。</p>
<p>26不能每天公布净值</p>
<p>27垃圾公司存在利益输送的现象</p>
<p>28封基里好坏差别很大，同样是养鸡人，可能你养的鸡总是不下蛋或者不长肉，看着别人的肥鸡容易眼红</p>
<p>29机构控盘,散户被作弄.</p>
<p>30折价率低于你买入时的折价率时，持有人的利益受损；</p>
<p>31持有的股票价格偏高，因为集中持股，交易风险大。</p>
<p>32基金公司经理不作为，但仍收取基金管理费。不排除基金黑幕的存在。</p>
<p>&nbsp;</p>
<p>封基的优点<br />
33封基的投资者主要是机构和散户中的高手，跟着高手有肉吃</p>
<p>34封基的仓位较开基而言更低，适合对点数有恐惧症的投资者</p>
<p>35封基打折卖，就算你看不懂打折的优势，还是喜欢开基，那我建议你买马上封转开的小基，一直持有到转开，即享受折价，还享受净值增长，说不定还有意想不到的利益输送可以蹭到。</p>
<p>36封基的分红不同于开基的分红，打折买全价比例分红可以让你享受到超值的价值投资优势</p>
<p>37封基的交易模式类似于股票，通过封基买卖可以体验下股票交易的瘾</p>
<p>38封基时不时让你享受到涨停板的极度快感，开基永远不可能有</p>
<p>39价格透明，不像开基，申购和赎回价格都是事先不知道的。</p>
<p>40到帐快。封基买入份额立即到帐，明天就可以出售。卖出时资金立即到帐，马上可以买股票债券权证或者别的封基开基，马上打新股也行。不像开基那样T+2、T+3（LOF、ETF除外）。只是和股票一样，提现金要到明天。</p>
<p>41佣金低。网上买卖，一般0.15%--0.3最多了。我与券商谈的结果是0.1%。到银行买开基至少1%多，这可是相差9倍哦。赎回又要0.35%到0.5%不等。</p>
<p>42到银行买开基，累了自己，累了银行的打工妺，累了其他到银行办事的人，酷暑严冬，其苦自知道。封基就可以坐在自己家里在买卖，空调或者电扇总有 的吧，冬天空得再多也没人闲话。还可以上网聊天，甚至打个瞌睡、睡个回笼觉也是可以的。工作场所如果领导不是管得很紧，也是可以买卖封基的。现在还有炒股 用的手机，只要可以炒股，就可以用手机买卖封基。</p>
<p>&nbsp;</p>
<p><br />
43、投资封基的感受<br />
封基投资感受最深的是收益的确定性（确定是正收益，并非指收益大小）和风险的最小化的确定性。<br />
即使是现在开始的折价如此高的封基长期投资要发生亏损那是要遭遇多大的股灾，我是风险的厌恶者，这就是我长期持有封基的最大动力。</p>
<p><br />
44、封基投资的两种方法之-------1.买入并长期持有！<br />
就是选择诚信的基金公司和优秀的基金经理，在相对合适的时间购买基金，或者做定投，绝大多数时间可以不管它。</p>
<p><br />
45、封基投资的两种方法之-------2.做做波段操作<br />
当市场有风险时，保存本金是第一位的，可降低仓位，或调为比较抗跌的封基品种；市场上升时，要选择净值增加快的品种，这时候当然要满仓操作。<br />
分红再投资，充分利用复利的时间价值。</p>
<p>&nbsp;</p>
<br />
<br />
而开放式基金,基金规模是不固定的。基金发行期结束后,经过一定的封闭期,就进入正常申购赎回期,投资者如果想买这个基金,基金公司继续以当时的基金净值卖给投资者,而投资者如果不想持有基金了,也可以按照赎回日的净值退给基金公司(赎回业务),基金规模是不固定的。
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/138047#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 05 Nov 2007 13:14:31 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/138047</link>
        <guid>http://tianping.javaeye.com/blog/138047</guid>
      </item>
      <item>
        <title>Eclipse 运行命令行参数大全</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/137619" style="color:red;">http://tianping.javaeye.com/blog/137619</a>&nbsp;
          发表时间: 2007年11月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font face="Arial"><a href="http://www.blogjava.net/beansoft/category/23349.html">http://www.blogjava.net/beansoft/category/23349.html</a></font></p>
<p>&nbsp;</p>
<p>Eclipse 运行命令行参数大全&nbsp;&nbsp;<br />
&nbsp;&nbsp;包括英文版本和中文版本两种的说明, 特别需要值得一提的是那个 -nl 参数, 可以指定程序启动时所使用的语言. 例如:<br />
eclipse -nl en_US<br />
将启动英文语言, 这个特性在安装了国际化语言包以后特别有用, 可以方便的切换各个语言的版本. 注意 IBM WSAD v5.1 也支持这个功能. <br />
<br />
运行 Eclipse<br />
将 Eclipse 驱动程序安装（解压缩）到某个目录（例如，c:\eclipse）中之后，通过运行顶级安装目录中的 Eclipse 可执行文件来启动&quot;工作台&quot;。在 Windows 系统上，该可执行文件称为 eclipse.exe，而在 Linux 系统上称为 eclipse。注意：下列讨论描述 Windows 系统上的设置。Linux 上的设置是相似的。<br />
<br />
如果您没有另行指定，则平台将缺省工作区目录创建为可执行文件的兄弟目录（例如 c:\eclipse\workspace）。此工作区目录用作项目的缺省内容区，还用于保存任何必需的元数据。要进行共享安装或多工作区安装，应明确指出工作区的位置而不是使用缺省值。有两种控制工作区位置的方法：使用当前工作目录或使用 -data 命令行自变量。<br />
<br />
将工作区位置设置为在当前工作目录内<br />
在此方案中，工作区位置将是当前工作目录中称为 workspace 的目录。<br />
<br />
实现此目的最容易的方法可能是使用下列步骤来创建快捷方式：<br />
<br />
导航到 Windows 资源管理器中的 eclipse.exe 并使用右键拖动来创建 eclipse.exe 的快捷方式。 <br />
编辑快捷方式的属性，以使启动位置：字段标识工作区位置的父目录（例如，c:\users\robert）。 <br />
关闭属性对话框并双击快捷方式（如果提供的目录为 c:\users\robert，则工作区位置将为 c:\users\robert\workspace）。 <br />
当然，您也可以使用命令提示符（通过将目录切换为工作区父目录然后运行 eclipse.exe）来获得同样的效果。<br />
<br />
使用 -data 设置工作区的特定位置<br />
要使用 -data 命令行自变量，只要将 -data your_workspace_location（例如，-data c:\users\robert\myworkspace）添加至快捷方式属性中的目标字段或显式地将它包括在命令行上。<br />
<br />
使用 -vm 设置 java VM<br />
建议显式指定在运行 Eclipse 时要使用哪个 Java VM。使用 -vm 命令行自变量（例如，-vm c:\jre\bin\javaw.exe）可以实现此目的。如果不使用 -vm，则 Eclipse 将使用在 O/S 路径上找到的一个 Java VM。当安装其它产品时，它们可更改您的路径，导致在下一次启动 Eclipse 时使用另一 Java VM。<br />
<br />
运行 Eclipse 中的高级主题<br />
Eclipse 可执行文件及平台本身提供了人们感兴趣的开发或调试 Eclipse 各部件的许多执行选项。运行 Eclipse 可执行文件的一般格式是：<br />
<br />
eclipse [platform options] [-vmargs&nbsp;&nbsp;<strong><a href="http://wiki.matrix.org.cn/Wiki.jsp?page=Java VM arguments" title="wiki link" class="wiki_link">Java VM arguments</a></strong> ]<br />
Eclipse 启动参数&nbsp;&nbsp;命令 描述&nbsp;&nbsp;原因 <br />
-arch architecture<br />
定义 Eclipse 平台在其上运行的处理器体系结构。Eclipse 平台通常使用 Java os.arch 属性的常用值来计算最佳设置。如果在此处指定该项，则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getOSArch() 用于插件。示例值有：&quot;x86&quot;、&quot;sparc&quot;、&quot;PA-RISC&quot;和&quot;ppc&quot;。 2.0 <br />
-application applicationId<br />
要运行的应用程序。应用程序由向 org.eclipse.core.runtime.applications 扩展点提供扩展的插件来声明。通常不需要此自变量。如果指定了此项，则该值会覆盖配置提供的值。如果不指定此项，则会运行&quot;Eclipse 工作台&quot;。 1.0 <br />
-boot bootJarURL<br />
（建议不使用；用 -configuration 代替；支持 1.0 兼容）。Eclipse 平台的引导插件代码（boot.jar）的位置，表示为 URL。如果指定此项，则会用它来为装入 Eclipse 平台引导程序类装入器的类装入器设置类路径。仅当更改 startup.jar 和 boot.jar 的相对位置时才需要它。注意，不允许使用相对 URL。&nbsp;&nbsp;*1.0 <br />
-classloaderproperties [file]<br />
如果指定的话，则使用给定位置处的类装入器属性文件来激活平台类类装入器增强。文件自变量可以是文件路径或 URL。注意，不允许使用相对 URL。单击此处以获得更多详细信息。 2.0.2 <br />
-configuration configurationFileURL<br />
Eclipse 平台配置文件的位置，表示为 URL。配置文件确定 Eclipse 平台、可用插件集和主要功能部件的位置。注意，不允许使用相对 URL。当安装或更新 Eclipse 平台时配置文件被写至此位置。&nbsp;&nbsp;2.0 <br />
-consolelog<br />
将 Eclipse 平台的错误日志镜像到用来运行 Eclipse 的控制台。与 -debug 组合时很方便使用。 1.0 <br />
-data workspacePath<br />
要运行 Eclipse 平台的工作区的路径。工作区位置也是项目的缺省位置。相对于从中启动 eclipse 的目录来解释相对路径。 1.0 <br />
-debug [optionsFile]<br />
将平台置于调试方式，并从给定位置处的文件装入调试选项（如果指定的话）。此文件指示哪些调试点可用于插件以及是否已启用它们。如果未给出文件位置，则平台在启动 eclipse 的目录中查找称为&quot;.options&quot;的文件。URL 和文件系统路径都可作为文件位置。 1.0 <br />
-dev [classpathEntries]<br />
将平台置于开发方式。将可选类路径条目（用逗号分隔的列表）添加至每个插件的运行时类路径。例如，当工作区包含要开发的插件时，指定 -dev bin 会为每个插件项目的名为 bin 的目录添加类路径条目，允许在其中存储最新生成的类文件。除去了冗余或不存在的类路径条目。 1.0 <br />
-endsplash params<br />
用于在 Eclipse 平台启动并运行时关闭闪屏的内部选项。此选项在闪屏处理链中不同的位置有不同的语法和语义。 2.0 <br />
-feature featureId<br />
主要功能部件的标识。主要功能部件为 Eclipse 的已启动实例提供了产品个性，并确定使用的产品定制信息。 2.0 <br />
-keyring keyringFilePath<br />
磁盘上授权数据库（或&quot;密钥环&quot;文件）的位置。此自变量必须与 -password 选项配合使用。相对于从中启动 eclipse 的目录来解释相对路径。 1.0 <br />
-nl locale<br />
定义 Eclipse 平台在其上运行的语言环境的名称。Eclipse 平台通常自动计算最佳设置。如果在此处指定该项，则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getNL() 用于插件。示例值有：&quot;en_US&quot;和&quot;fr_FR_EURO&quot;。 2.0 <br />
-nolazyregistrycacheloading<br />
取消激活装入优化的平台插件注册表高速缓存。缺省情况下，仅当需要时才从注册表高速缓存（可用时）中装入扩展的配置元素，以减少内存占用。此选项将在启动时强制完全装入注册表高速缓存。 2.1 <br />
-noregistrycache<br />
绕过读写内部插件注册表高速缓存文件。 2.0 <br />
-nosplash<br />
运行平台而不显示闪屏。 1.0 <br />
-os operatingSystem<br />
定义 Eclipse 平台在其上运行的操作系统。Eclipse 平台通常使用 Java os.name 属性的常用值来计算最佳设置。如果在此处指定该项，则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getOS() 用于插件，并用于解析插件清单文件中提及的路径中 $os$ 变量的出现。示例值有：&quot;win32&quot;、&quot;linux&quot;、&quot;hpux&quot;、&quot;solaris&quot;和&quot;aix&quot;。 1.0 <br />
-password password<br />
授权数据库的密码。与 -keyring 选项配合使用。 1.0 <br />
-perspective perspectiveId<br />
启动时要在活动工作台窗口中打开的透视图。如果没有指定该参数，则将打开关闭时活动的透视图。 1.0 <br />
-plugincustomization&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;propertiesFile<br />
包含插件首选项缺省设置的属性文件的位置。这些缺省设置覆盖在主要功能部件中指定的缺省设置。相对于从中启动 eclipse 的目录来解释相对路径。 2.0 <br />
-plugins pluginsFileURL<br />
（建议不使用；用 -configuration 代替；支持 1.0 兼容）。 指定 Eclipse 平台查找插件的文件的位置，表示为 URL。该文件为属性文件格式，其中键是任意用户定义名称，值是指向 plugin.xml 文件的显式路径或指向包含插件的目录的路径的用逗号分隔的列表。注意，不允许使用相对 URL。如果指定此项，则此选项会导致创建适当的临时配置。 *1.0 <br />
-refresh <br />
启动时执行工作区的全局刷新的选项。这将使从上次平台运行以来在文件系统中所做的任何更改一致。 1.0 <br />
-showlocation <br />
用于在窗口标题栏中显示工作区的位置的选项。在发行版 2.0 中，此选项仅与 -data 命令行自变量一起使用。 2.0 <br />
-showsplash params<br />
用于显示闪屏（由可执行的 Eclipse 平台启动器执行）的内部选项。此选项在闪屏处理链中不同的位置有不同的语法和语义。 2.0 <br />
-vm vmPath<br />
要用来运行 Eclipse 平台的&quot;Java 运行时环境&quot;（JRE）的位置。如果不指定此项，则 JRE 位于 jre（它是 Eclipse 可执行文件的兄弟目录）。相对于从中启动 eclipse 的目录来解释相对路径。 1.0 <br />
-ws windowSystem<br />
定义 Eclipse 平台在其上运行的 Windows 系统。Eclipse 平台通常使用 Java os.name 属性的常用值来计算最佳设置。如果在此处指定该项，则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getWS() 用于插件、用于配置 SWT 以及用于解析插件清单文件中提及的路径中 $ws$ 变量的出现。示例值有：&quot;win32&quot;、&quot;motif&quot;和&quot;gtk&quot;。 1.0 <br />
<br />
将 -vmargs 条目后面的所有自变量（但不包括 -vmargs）作为虚拟机自变量（即，在要运行的类的前面）直接传递到所指示的 Java VM。注意：如果 Eclipse 启动在 Java vm 自变量（-vmargs）之后提供的自变量（例如，-data），则 Eclipse 将不会启动并且您将接收到&quot;JVM 已终止。出口代码为 1&quot;的错误。<br />
<br />
在不同的 VM 上运行 <br />
在 J9 上运行 Eclipse<br />
当在 J9 版本 1.5 上运行 Eclipse 时，建议使用以下 VM 选项： <br />
<br />
eclipse.exe [eclipse arguments] -vm path_to_j9w.exe&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -vmargs -ms:32 -mm:2048 -mo:32768 -moi:32768 -mca:32 -mco:128 -mx:2000000<br />
当在 J9 版本 2.0 上运行 Eclipse 时，J9W 选择的缺省自变量应为合适的选项。但是，要覆盖 Eclipse 可执行文件以内部方式自动设置的参数，必须指定 -vmargs 不带任何参数，如下所示： <br />
<br />
eclipse.exe [eclipse arguments] -vm path_to_j9w.exe -vmargs<br />
有关进一步信息，参考 J9 VM 文档和帮助。<br />
<br />
在 IBM Developer Kit, Java(TM) Technology Edition VM 上运行 Eclipse<br />
IBM Developer Kit, Java(TM) Technology Edition 1.3 Linux 的缺省 VM 设置适合进行初期研究工作，但在进行大型开发时是不够的。对于大型开发，应修改 VM 自变量以使有更多的堆可用。例如，下列设置将允许 Java 堆增大为 256MB：<br />
<br />
-vmargs -Xmx256M </p>
<p><br />
安装 Language Packs 后如何切换语言<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;启动的时候加参数：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-nl &quot;en_US&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-nl &quot;zh_CN&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;等等 <br />
</p>
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/137619#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 02 Nov 2007 17:07:34 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/137619</link>
        <guid>http://tianping.javaeye.com/blog/137619</guid>
      </item>
      <item>
        <title>Eclipse 启动运行速度调优</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/137618" style="color:red;">http://tianping.javaeye.com/blog/137618</a>&nbsp;
          发表时间: 2007年11月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>JVM 提供了各种用于调整内存分配和垃圾回收行为的标准开关和非标准开关。其中一些设置可以提高 JAVA IDE 的性能。 <br />
注意，由于 -X （尤其是 -XX JVM）开关通常是 JVM 或 JVM 供应商特定的，本部分介绍的开关可用于 Sun Microsystems J2SE 1.4.2。</p>
<p>以下设置在大多数系统上将产生比工厂更好的设置性能。 <br />
-vmargs - 表示将后面的所有参数直接传递到所指示的 Java VM。</p>
<p>-Xverify:none - 此开关关闭Java字节码验证，从而加快了类装入的速度，并使得在仅为验证目的而启动的过程中无需装入类。此开关缩短了启动时间，因此没有理由不使用它。 </p>
<p>-Xms24m - 此设置指示 Java 虚拟机将其初始堆大小设置为 24 MB。通过指示 JVM 最初应分配给堆的内存数量，可以使 JVM 不必在 IDE 占用较多内存时增加堆大小。 </p>
<p>-Xmx96m - 此设置指定 Java 虚拟机应对堆使用的最大内存数量。为此数量设置上限表示 Java 进程消耗的内存数量不得超过可用的物理内存数量。对于具有更多内存的系统可以增加此限制，96 MB 设置有助于确保 IDE 在内存量为 128MB 到 256MB 的系统上能够可靠地执行操作。注意：不要将该值设置为接近或大于系统的物理内存量，否则将在主要回收过程中导致频繁的交换操作。 </p>
<p>-XX:PermSize=20m - 此 JVM 开关不仅功能更为强大，而且能够缩短启动时间。该设置用于调整内存&quot;永久区域&quot;（类保存在该区域中）的大小。因此我们向 JVM 提示它将需要的内存量。该设置消除了许多系统启动过程中的主要垃圾收集事件。SunONE Studio 或其它包含更多模块的 IDE 的用户可能希望将该数值设置得更高。 <br />
下面列出了其它一些可能对 ECLIPSE 在某些系统（不是所有系统）上的性能产生轻微或明显影响的 JVM 开关。尽管使用它们会产生一定的影响，但仍值得一试。 </p>
<p>-XX:CompileThreshold=100 - 此开关将降低启动速度，原因是与不使用此开关相比，HotSpot 能够更快地将更多的方法编译为本地代码。其结果是提高了 IDE 运行时的性能，这是因为更多的 UI 代码将被编译而不是被解释。该值表示方法在被编译前必须被调用的次数。 </p>
<p>-XX:+UseConcMarkSweepGC -XX:+UseParNewGC - 如果垃圾回收频繁中断，则请尝试使用这些开关。此开关导致 JVM 对主要垃圾回收事件（如果在多处理器工作站上运行，则也适用于次要回收事件）使用不同的算法，这些算法不会影响整个垃圾回收进程。注意：目前尚不确定此收集器是提高还是降低单处理器计算机的性能。 </p>
<p>-XX:+UseParallelGC - 某些测试表明，至少在内存配置相当良好的单处理器系统中，使用此回收算法可以将次要垃圾回收的持续时间减半。注意，这是一个矛盾的问题，事实上此回收器主要适用于具有千兆字节堆的多处理器。尚无可用数据表明它对主要垃圾回收的影响。注意：此回收器与 -XX:+UseConcMarkSweepGC 是互斥的。</p>
<p>我的机器是512MB的内存<br />
下面是我的eclipse启动参数：eclipse.exe -vmargs -Xverify:none -Xms64M -Xmx256M -XX:PermSize=20M&nbsp; -XX:+UseParallelGC</p>
<p>-----</p>
<p>我的电脑是1G内存, 有一次内存不足了... MyEclipse 就推荐我使用一个启动参数, 现在我的启动参数是:</p>
<p><br />
eclipse.exe -vmargs -Xverify:none -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M -XX:+UseParallelGC</p>
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/137618#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 02 Nov 2007 17:05:06 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/137618</link>
        <guid>http://tianping.javaeye.com/blog/137618</guid>
      </item>
      <item>
        <title>转： Web Services开发体会和项目教训</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/137082" style="color:red;">http://tianping.javaeye.com/blog/137082</a>&nbsp;
          发表时间: 2007年10月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>去年，在一个大型项目(1500w)中用到Web Services，现在项目进入了尾声，所以对以前的开发经历做一个总结。 <br />
我想大家一定会问？为什么你们项目中要用到Web Services，因为客户有如下需求： <br />
1、客户要求项目用C/S架构，并且服务器端是IBM那一套：WebSphere AppServer＋DB2＋AIX5.3＋RS/6000。 <br />
2、最终用户上报数据，因为网络原因，譬如Modem上网，可以离线操作，等填写了几十张报表后，可以一次提交。同时，在登录时，可以将服务端数据同步到本地Access或MSSQL数据库，这样提高客户端响应速度。 <br />
3、由于有些报表以后可能需要修改，或添加一些新报表，又不想重新开发，这样客户那边工作人员可以通过客户端自定义。</p>
<p>如果有以上需求，我想大家应该都比较认同这种异构分布式解决方案：客户端用C# .Net开发，通过Web Services调用服务器端Java组件。</p>
<p>其实，上面的解决方案太过于理想，最后我们不得不面对残酷的现实：三种客户端中的两种最后被迫改为B/S。 <br />
在项目中，我主要负责Web Services和服务器端组件开发中所遇到的种种问题，相当于技术支持吧，以及部分模块的开发。</p>
<p>以下是我们开发中遇到的实际问题，虽然最终都一一解决，但遇到了几个无法突破的瓶颈：客户端不稳定，客户端响应迟缓，后期测试和维护困难巨大。</p>
<p><strong>一、异构平台的Web Services兼容性</strong> <br />
开发过程中，我们用Axis做Web Services引擎，Tomcat做容器。因为我们只有IBM提供的RAD6.0的60天试用版。该工具超级占内存，用内置的WebSphere开发测 试极其缓慢，严重影响开发效率，经过我初期试用后，基本废弃了。推荐项目组二三十开发人员用Lomboz eclipse3.12开发，基本满意。</p>
<p>由于Axis是一个嵌入式引擎，所以可以将其打包到最终的WebSphere AppServer（WAS）上，也就是说，我们没有用到WAS提供的Web Services引擎，这引出了后面会谈到的一个问题：Web Services安全性怎么部署？</p>
<p>用Axis时，Axis一直都有一个bug或是说缺陷，官方文档也详细注明，只是我们当时没有发现而走了很多弯路：用Axis发布的Web Services给.net客户端调用时，必须用RPC风格，不能用Web Services标准的跨平台风格Document，而后者是Lomboz axis插件的默认方式。也就是说，我们发布的Web Services总是莫名其妙的不好用。我们用JBuilder2007自带的Axis插件发布，竟然非常顺利。</p>
<p><strong>二、Web Services开发中服务器端组件问题</strong> <br />
我们服务器端开发，是用Spring＋Hibernate，在Spring的Service层上再封装一层，也就是fa&ccedil;ade模式了，该 fa&ccedil;ade直接发布为Web Services，必须经过这个转换，一是因为性能，二是因为Hibernate的复杂Model对象，在wsdl描述后，被.net客户端识别有些问 题，List、Map也会有问题，总之这些对象太复杂了，我们包装成简单的VO对象。</p>
<p>另外一个问题是，我们的service方法，如果直接给WebWork这样的框架在服务端用的的话，是不会出问题，当提供给.net客户端用时，就 会出现lazy loading的错误，因为.net客户端不能接收Proxy对象，必须将数据全部load出来，但这时Hibernate的session已经关闭。项 目组很多人遇到这些问题，最后大家不约而同的全部用eager模式，导致了最后的恶果：严重的的性能问题。由于我不是leader，所以当时这个问题发现 了，也没法要求别人，毕竟很大的一个团队。 <br />
切身体会：一个团队，如果不熟悉Hibernate就随便上，技术风险非常大。Hibernate带来的开发效率，是以团队成员掌握它为前提。</p>
<p>当然，性能问题不只是由Hibernate引起，Web Services本身的性能也非常严重：XML的序列化和反序列化耗时，XML文件的膨胀导致的网络传输，HTTP的无状态导致网络IO性能。切身体会： 如果系统必须用分布式，而不是追求所谓的SOA架构，Web Services应该是下下策，因为还有很多协议和方式可以选择：IIOP、RMI、Hessian、burlap、RPC，另外，做系统集成还有 Message方式。</p>
<p>Web Services开发中其它问题比较少，因为Web Services本身不用编程，只是部署的事情，开发工具和服务器会自动为我们做，我们只需要理解SOAP引擎的原理和使用就够了，真的遇到问题，可以通过Axis的TcpMonitor监视SOAP数据包。 <br />
开发过程中，.net客户端那边，VSStudio做得很智能，它会根据wsdl文件生成我们所要的一切，当然，wsdl文件的变化，会导致VSStudio重新生成所有的类和接口，也很耗时，并且容易出问题。</p>
<p><strong>三、Web Services的安全问题</strong> <br />
当时解决Web Services安全问题，花了我将近一个月的时间，主要是学习和处理如下四个问题： <br />
XML和Web Services安全规范 <br />
WAS的 Web Services引擎的安全部署 <br />
Axis和参考的Xfire引擎的Web Services安全 <br />
.net客户端WSE3.0的安全以及和WAS的通讯</p>
<p>最后这些问题基本上都解决了，不过还是没有用上，因为在我们已经开发的几种客户端和服务器端部署上很麻烦，还要测试，另外，客户也没法验收这个啊。</p>
<p>当然，我们还回避了一个严肃的问题：我们的Web Services是发布在Axis引擎上，还没有移植到WAS的Web Services引擎，而发布在这个平台，必须有RAD这类开发工具支持，几乎没法手动做。WAS引擎的Web Services安全配置异常复杂：我们当时只配置了Authentication和Integration，没有做Encryption，但完全够用。 我当时用Sun的NetBean开发工具发布了一下Web Serivces，也是挺好用的，不过没有配置安全。顺便说一下，Sun的web Services引擎jwsdp2.0设计有点类似于EJB容器，很不好用，移植性特差。 <br />
我们当时用Axis引擎是1.3版本，而该版并不支持标准的OASIS的WS-Security，只有到2.0版才开始，而且几乎都是手写配置文件。</p>
<p>WAS的Web Services安全配置，对照IBM的红皮书，不是很难，但很复杂，安全相关的xml代码都好几百行，好几个文件。配置过程中，和.net客户端通讯时 遇到一个问题，怎么也不能互通，但.net和.net客户端可以互通，Java和Java客户端也可以互通,最后我通过拦截soap包,找到了解决办法: 必须手动更改http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token -profile-1.0#X509v3 后的v3，IBM工具生成的是v1，这是标准不兼容引起的，因为当时RAD是04年底,而微软的WSE3.0比较新。后来发现这篇文章有相似的经历：<a href="http://pluralsight.com/blogs/kirillg/archive/2005/04/13/7315.aspx" target="blank">http://pluralsight.com/blogs/kirillg/archive/2005/04/13/7315.aspx </a></p>
<p>在处理Web Services安全过程中，我通过emule和IBM网站下载了上10本这方面的书籍，我觉得以下资料对我帮助最大： <br />
</p>
<ul>Axis的若干文档：Reference、developers-guide、architecture-guide等，非常详细     <br />
    IBM的红皮书：《WebSphere Version 6 Web Services Handbook Development and     Deployment.pdf》、《WebSphere Application Server V6 Security     Handbook.pdf》，它专门讲述了Web Services安全的原理和具体配置，非常深入浅出。     <br />
    《Securing Web Services with WS-Security》：这本书很理论化，但我认为非常好，虽然Amazon排行不高。     <br />
    WSE3.0的MSDN文档。</ul>
    <p><strong>四、开发过程中的的沟通问题</strong>     <br />
    这应该不是一个技术问题，而是一个软件开发方法学的问题，但对整个软件开发过程影响极大。     <br />
    我们面对的现实：.net客户端开发人员不懂服务器端Java，服务器端Java开发人员不懂.net。     <br />
    除了技术壁垒外，还有业务衔接性的问题，因为我们不是纵向分模块开发，而横向开发的前提是我们服务器端开发人员很熟悉业务，知道客户端需要的接口，但实际上，业务主要由客户端推动。所以，两端的开发人员都遇到很大的沟通壁垒。</p>
    <p>从技术的角度表达就是：客户端开发人员需要的接口，服务器端开发人员不清楚；服务器端开发人员也不知道怎么把握粒度。譬如，有个     updateUser方法，但更新用户信息时，可能需要更新很多信息：用户信息、用户角色、用户所属组&hellip;.。loadUser时也有同样的按需加载问题。</p>
    <p>当然，从技术角度，开发Web     Serivces有Bottom-up和Top-down两种开发模式。我们选择了前者，也是最常见的方式，也许用后者更适合我们的项目：从定义的     wsdl文件开始，客户端和服务器端开发都遵循它。但问题是：我们怎么确定wsdl，也就是我们所要求的接口，因为我们自己对业务都不是很熟。</p>
    <p><strong>五、客户端和服务端开发测试方法</strong>     <br />
    我们当时做得很笨，也最直接：等服务器端组件发布完毕后，通知客户端开发人员，然后客户端开发人员通过VSStudio提供的Web Services生成工具，根据Axis发布的wsdl文件，生成所需的.net对象，然后像本地调用一样使用。</p>
    <p>但问题是：     <br />
    wsdl随时都在变，这意味着客户端生成的组件总在变化，经常出现编译错误。     <br />
    客户端开发过程中遇到的问题，一会是客户端自己，一会是服务器端组件：我要的方法包含的信息不够啊。</p>
    <p>服务器组件测试一次，起容器特慢，而且客户端调用也慢。     <br />
    我们的测试，最后走入了一个怎样的泥潭：譬如测试一张报表，都是在客户端手工填写，然后观察服务器端日志和响应。有人会问，用     LoadRunner或Function Tester这类自动测试工具不就ok了吗？我都用过，它们对Web     UI确实好用，后者对Swing客户端也好用，但对.net客户端，像是不太现实。     <br />
    另外，Debug非常困难，因为它要求两端开发人员必须在一起密切配合。</p>
    <p>我自己认为的解决方案，但未必真的好用：     <br />
    服务器端Service方法必须写单元测试TestCase，可能代码量非常大，测试好后方发布为Web Services。     <br />
    同时，服务器端提供同一套接口的Mock实现，供客户端开发测试，解决并行开发的问题。</p>
    <p><strong>六、其它问题</strong>     <br />
    当然，上面的几点，具体到细节，我都省略了，总之问题非常非常多：技术问题、管理问题、方法和过程问题。</p>
    <p>特别提的一点是，我们几乎开发了两套&ldquo;业务层＋持久化&rdquo;解决方案，因为离线客户端也用了NHibernate持久化，这样导致开发测试工作量巨大，     就说一点吧:两边同步是通过打包的sql语句，通过SOAP传输，但Access和DB2的sql有不兼容问题，如果要兼容，就会以牺牲性能和灵活性为代     价。</p>
    <p>另外，我们写项目建议书时很被动，但也没办法，因为有好几家公司竞争。对我们影响极大的几个问题：     <br />
    </p>
    <ul>1、IBM的WAS比起WebLogic Server易用性差远了，导致部署时极其耗时。而且还有一些bug，譬如连接池资源，当时不得不和IBM工程师咨询。实际上，我们只用到强大的WAS的一个非常小的部分：Web容器。         <br />
        2、我们没有针对WAS的开发工具RAD。但说实话，那试用版的RAD也是一个字：慢，而且安装时超级大，约4个G。而且和我们已经在用的版本控制工具VSS没法集成。         <br />
        3、项目的C/S架构不是很合理，就是原来客户的B/S架构，也运行挺好的，而且用asp，跑在一个pc server上。我们一定程度上为了技术而技术。最后也达不到客户需求：性能＋稳定。         <br />
        4、自定义报表最后没有投入使用，只是一个半成品。本来自定义报表就很难，要是容易，一个软件外行人员，就可以把表现层到持久化轻松搞定，那一般         MIS开发人员不要失业了，MDA也没那么强。很多OA平台一直在解决这个问题，也没有发现特别好用的。我们做技术调研期间试过MS的InfoPath和         Adobe Designer，以及Excel Server，都不能满足需求。</ul>
        <p>当然，这个子系统只是我们那个庞大系统的一个部分。上面也就算我做的一点点总结吧，也是教训啊！不过，从个人角度考虑，学到的东西还是很多的。</p>
        <p>这个子系统花去了我们将近200个人月，如果说那浪费的部分，估计至少是100个人月的工作量。是什么导致？从我这篇文章只能窥其一角，因为整个系统涉及CMS、OA、BI、E-commerce、GIS、IM、MIS。我自己总结一下，有以下原因：         <br />
        1、项目建议书空洞，不切实际：公司也很无奈，客户也不成熟。         <br />
        2、需求调研后的需求分析闭门造车：客户的合同是分阶段，我们上交需求说明书后付20％款，上交设计书后又付20％。全一个瀑布开发，虽然按RUP文档写。到半年后的实际开发时，发现很多需求都不合理。         <br />
        3、整个过程都没有和客户沟通，到最后开发完毕才让客户看，那时客户也懵了：这不是我要的产品啊。改呀，改呀，熬夜啊。         <br />
        4、项目团队整体技术实力薄弱，当时调来做Java开发的人员，只有少数几个以前做Java，大多数是临时学。想起那Hibernate使用，心寒啊。另外，WAS问题、AIX问题在产品环境下都出来了：系统不稳定、宕机。         <br />
        5、整个开发阶段流程没有把握好，像项目规范、测试方法、日志、版本控制，这些后期都出现了，而且非常严重。就说那日志吧，最后出问题都不知道怎么查，日志一遍混乱。         <br />
        6、缺乏做大项目经验，整个系统架构都比较松散，项目开始时很多都不知从何入手，也很仓促。         <br />
        7、项目持续一年多，人都换了几批了，工作交接很大问题。         <br />
        .....</p>
        <p>不过，说实话，项目团队，特别是进公司一、两年的员工都很努力，没有人抱怨什么，我和他（她）们一起合作，还是很开心的。</p>
          <br/>
          <span style="color:red;">
            <a href="http://tianping.javaeye.com/blog/137082#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 31 Oct 2007 18:13:39 +0800</pubDate>
        <link>http://tianping.javaeye.com/blog/137082</link>
        <guid>http://tianping.javaeye.com/blog/137082</guid>
      </item>
      <item>
        <title>转：说说大型高并发高负载网站的系统架构</title>
        <author>tianping</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tianping.javaeye.com">tianping</a>&nbsp;
          链接：<a href="http://tianping.javaeye.com/blog/137081" style="color:red;">http://tianping.javaeye.com/blog/137081</a>&nbsp;
          发表时间: 2007年10月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>　　我在CERNET做过拨号接入平台的搭建，而后在Yahoo&amp;3721从事过搜索引擎前端开发，又在MOP处理过大型社区猫扑大杂烩的 架构升级等工作，同时自己接触和开发过不少大中型网站的模块，因此在大型网站应对高负载和并发的解决方案上有一些积累和经验，可以和大家一起探讨一下。</p>
<p>　　一个小型的网站，比如个人网站，可以使用最简单的html静态页面就实现了，配合一些图片达到美化效果，所有的页面均存放在一个目录下，这样的 网站对系统架构、性能的要求都很简单，随着互联网业务的不断丰富，网站相关的技术经过这些年的发展，已经细分到很细的方方面面，尤其对于大型网站来说，所 采用的技术更是涉及面非常广，从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求，已经不是原来简单的html静态 网站所能比拟的。</p>
<p>　　大型网站，比如门户网站。在面对大量用户访问、高并发请求方面，基本的解决方案集中在这样几个环节：使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面，还没法根本解决大型网站面临的高负载和高并发问题。</p>
<p>　　上面提供的几个解决思路在一定程度上也意味着更大的投入，并且这样的解决思路具备瓶颈，没有很好的扩展性，下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。</p>
<p>1、HTML静态化 <br />
其实大家都知道，效率最高、消耗最小的就是纯静态化的html页面，所以我们尽可能使我们的网站上的页面采用静态页面来实现，这个最简单的方 法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站，我们无法全部手动去挨个实现，于是出现了我们常见的信息发布系统CMS，像我们常访问的各 个门户站点的新闻频道，甚至他们的其他频道，都是通过信息发布系统来管理和实现的，信息发布系统可以实现最简单的信息录入自动生成静态页面，还能具备频道 管理、权限管理、自动抓取等功能，对于一个大型网站来说，拥有一套高效、可管理的CMS是必不可少的。</p>
<p>　　除了门户和信息发布类型的网站，对于交互性要求很高的社区类型网站来说，尽可能的静态化也是提高性能的必要手段，将社区内的帖子、文章进行实时 的静态化，有更新的时候再重新静态化也是大量使用的策略，像Mop的大杂烩就是使用了这样的策略，网易社区等也是如此。目前很多博客也都实现了静态化，我 使用的这个Blog程序WordPress还没有静态化，所以如果面对高负载访问，www.toplee.com一定不能承受<img src="http://www.javaeye.com/images/forum/smiles/icon_smile.gif" alt="" /></p>
<p>　　同时，html静态化也是某些缓存策略使用的手段，对于系统中频繁使用数据库查询但是内容更新很小的应用，可以考虑使用html静态化来实现， 比如论坛中论坛的公用设置信息，这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中，这些信息其实大量被前台程序调用，但是更新频率很小，可以 考虑将这部分内容进行后台更新的时候进行静态化，这样避免了大量的数据库访问请求。</p>
<p>　　在进行html静态化的时候可以使用一种折中的方法，就是前端使用动态实现，在一定的策略下进行定时静态化和定时判断调用，这个能实现很多灵活 性的操