每日一得

不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速开发
最近关心的内容:SSH,seam,flex,敏捷,TDD
本站的官方站点是:颠覆软件

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  220 随笔 :: 9 文章 :: 421 评论 :: 0 Trackbacks

#

越来越发现其实掌握 hibernate并不容易,Spring用起来其实简单多了,但是在用hibernate的时候真的是需要一定的时间积累,对一个项目组来说如果采用hibernate最好有一个对hibernate比较清楚的人否则碰到问题就会成为项目的风险。
我想告诉各位的是,掌握hibernate可能比你预期的难多了,当你轻松的告诉我,hibernate很简单的时候该是你自己多反省了. (只有一种情况例外,你是一个牛人)

好了,一个引子废话那么多,其实今天只是想先说一说hibernate里的Fetch的作用.

大家都知道,在hibernate里为了性能考虑,引进了lazy的概念,这里我们以Parent和Child为模型来说明,

public class Parent implements Serializable {

    
/** identifier field */
    
private Long id;

    
/** persistent field */
    
private List childs;

    
//skip all getter/setter method

  
}  



public class Child implements Serializable {

    
/** identifier field */
    
private Long id;

    
/** persistent field */
    
private net.foxlog.model.Parent parent;

    //skip all getter/setter method

}

在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy="false"的话才同时取出关联的所有childs内容.

问题是我既想要hibernate默认的性能又想要临时的灵活性该怎么办?  这就是fetch的功能。我们可以把fetch与lazy="true"的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。

总值,fetch就是在代码这一层给你一个主动抓取得机会.

Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {
            
public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query q 
= session.createQuery(
                        
"from Parent as parent "+
                                
" left outer join fetch parent.childs " +
                                
" where parent.id = :id"
                );
                q.setParameter(
"id",new Long(15));
                
return (Parent)q.uniqueResult();
            }

        });

        Assert.assertTrue(parent.getChilds().size() 
> 0);


你可以在lazy="true"的情况下把fetch去掉,就会报异常. 当然,如果lazy="false"就不需要fetch了


有一个问题,使用Fetch会有重复记录的现象发生,我们可以理解为Fetch实际上不是为Parent服务的,而是为Child服务的.所以直接取Parent会有不匹配的问题.



参考一下下面的这篇文章
Hibernate集合初始化

======================================================================

update:以上有些结论错误,实际上在hibernate3.2.1版本下测试,可以不出现重复记录,

public void testNPlusOne() throws Exception{
        List list 
= (List)hibernateTemplate.execute(new HibernateCallback() {
            
public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query q 
= session.createQuery(
                        
"select distinct p from net.foxlog.model.Parent p inner join fetch p.childs"
                );
                
return q.list();
            }

        });

        
//((Parent)(list.get(0))).getChilds();
        System.out.println("list size = " + list.size());
        
for(int i=0;i<list.size();i++){
            Parent p 
= (Parent)list.get(i);
            System.out.println(
"===parent = " + p);
            System.out.println(
"===parent's child's length = " + p.getChilds().size());
        }

    }


打印结果如下:
Hibernate: select distinct parent0_.id as id2_0_, childs1_.id as id0_1_, childs1_.parent_id as parent2_0_1_, childs1_.parent_id as parent2_0__, childs1_.id as id0__ from parent parent0_ inner join child childs1_ on parent0_.id=childs1_.parent_id
list size 
= 3
===parent = net.foxlog.model.Parent@1401d28[id=14]
===parent's child's length = 1
===parent = net.foxlog.model.Parent@14e0e90[id=15]
===parent's child's length = 2
===parent = net.foxlog.model.Parent@62610b[id=17]
===parent's child's length = 3

另外,如果用open session in view模式的话一般不用fetch,但首先推荐fetch,如果非用的话因为有N+1的现象,所以可以结合batch模式来改善下性能.


posted @ 2006-12-01 13:01 Alex 阅读(19358) | 评论 (7)编辑 收藏

LDAPChina序言:这篇文章是互联网上最流行的一篇介绍LDAP基础知识的文章,本文作者Michael DonnellyLDAPMAN.org的站长。由于本文译文较长,我们将其分为两部分,第一部分介绍LDAP概论,第二部分介绍LDAP基础知识。感谢网友Brimmer翻译此文。

如 果你在计算机行业工作,那么对LDAP可能早有耳闻了。想深入地了解LDAP吗?那么可以好好地读一下这篇文章。这篇介绍性的文章是一系列介绍如何在企业 中设计、实现和集成LDAP环境的文章的头一篇。主要是先让你熟悉一下LDAP的基本概念,那些比较困难的细节问题将放到以后讨论。在这篇文章中我们将要 介绍:

什么是LDAP?
什么时候该用LDAP存储数据?

现 在LDAP技术不仅发展得很快而且也是激动人心的。在企业范围内实现LDAP可以让运行在几乎所有计算机平台上的所有的应用程序从LDAP目录中获取信 息。LDAP目录中可以存储各种类型的数据:电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表,等等。通过把LDAP目录作为系统集成中 的一个重要环节,可以简化员工在企业内部查询信息的步骤,甚至连主要的数据源都可以放在任何地方。如果Oracle、Sybase、Informix或 Microsoft SQL数据库中已经存储了类似的数据,那么LDAP和这些数据库到底有什么不同呢?是什么让它更具优势?请继续读下去吧!


什么是LDAP?

LDAP 的英文全称是Lightweight Directory Access Protocol,一般都简称为LDAP。它是基于X.500标准的,但是简单多了并且可以根据需要定制。与X.500不同,LDAP支持TCP/IP, 这对访问Internet是必须的。LDAP的核心规范在RFC中都有定义,所有与LDAP相关的RFC都可以在LDAPChina.com RFC专栏中找到。

怎么使用LDAP这个术语呢?

在 日常交谈中,你可能会听到有些人这么说:“我们要把那些东西存在LDAP中吗?”,或者“从LDAP数据库中取出那些数据!”,又或者“我们怎么把 LDAP和关系型数据库集成在一起?”。严格地说,LDAP根本不是数据库而是用来访问存储在信息目录(也就是LDAP目录)中的信息的协议。更为确切和正式的说法应该是象这样的:“通过使用LDAP,可以在信息目录的正确位置读取(或存储)数据”。但是,也没有必要吹毛求疵,尽管表达得不够准确,我们也都知道对方在说什么。

LDAP目录是数据库吗?

就 象Sybase、Oracle、Informix或Microsoft的数据库管理系统(DBMS)是用于处理查询和更新关系型数据库那样,LDAP服务 器也是用来处理查询和更新LDAP目录的。换句话来说LDAP目录也是一种类型的数据库,但是不是关系型数据库。不象被设计成每分钟需要处理成百上千条数 据变化的数据库,例如:在电子商务中经常用到的在线交易处理(OLTP)系统,LDAP主要是优化数据读取的性能。

LDAP目录的优势

现在该说说LDAP目录到底有些什么优势了。现在LDAP的流行是很多因素共同作用的结果。我在这里说的不过是一些基本的原因,请你注意一下这不过是一小部分原因。

可能LDAP最大的优势是:可以在任何计算机平台上,用很容易获得的而且数目不断增加的LDAP的客户端程序访问LDAP目录。而且也很容易定制应用程序为它加上LDAP的支持。

LDAP 协议是跨平台的和标准的协议,因此应用程序就不用为LDAP目录放在什么样的服务器上操心了。实际上,LDAP得到了业界的广泛认可,因为它是 Internet的标准。产商都很愿意在产品中加入对LDAP的支持,因为他们根本不用考虑另一端(客户端或服务端)是怎么样的。LDAP服务器可以是任 何一个开发源代码或商用的LDAP目录服务器(或者还可能是具有LDAP界面的关系型数据库),因为可以用同样的协议、客户端连接软件包和查询命令与 LDAP服务器进行交互。与LDAP不同的是,如果软件产商想在软件产品中集成对DBMS的支持,那么通常都要对每一个数据库服务器单独定制。

不象很多商用的关系型数据库,你不必为LDAP的每一个客户端连接或许可协议付费。

大多数的LDAP服务器安装起来很简单,也容易维护和优化。

LDAP服务器可以用“推”或“拉”的方法复制部分或全部数据,例如:可以把数据“推”到远程的办公室,以增加数据的安全性。复制技术是内置在LDAP服务器中的而且很容易配置。如果要在DBMS中使用相同的复制功能,数据库产商就会要你支付额外的费用,而且也很难管理。

LDAP 允许你根据需要使用ACI(一般都称为ACL或者访问控制列表)控制对数据读和写的权限。例如,设备管理员可以有权改变员工的工作地点和办公室号码,但是 不允许改变记录中其它的域。ACI可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由LDAP目录服务器完 成的,所以不用担心在客户端的应用程序上是否要进行安全检查。

LDAP对于存储下面这样的信息最为有用,也就是数据需要从不同的地点读取,但是不需要经常更新。例如,这些信息存储在LDAP目录中是十分有效的:

  • 公司员工的电话号码簿和组织结构图
  • 客户的联系信息
  • 计算机管理需要的信息,包括NIS映射、email假名,等等
  • 软件包的配置信息
  • 公用证书和安全密匙

什么时候该用LDAP存储数据?

大 多数的LDAP服务器都为读密集型的操作进行专门的优化。因此,当从LDAP服务器中读取数据的时候会比从专门为OLTP优化的关系型数据库中读取数据快 一个数量级。也是因为专门为读的性能进行优化,大多数的LDAP目录服务器并不适合存储需要经常改变的数据。例如,用LDAP服务器来存储电话号码是一个 很好的选择,但是它不能作为电子商务站点的数据库服务器。

如果下面每一个问题的答案都是“是”,那么把数据存在LDAP中就是一个好主意。

  • 需要在任何平台上都能读取数据吗?
  • 每一个单独的记录项是不是每一天都只有很少的改变?
  • 可以把数据保存在平面数据库(flat database)而不是关系型数据库中吗?换句话来说,也就是不管什么范式不范式的,把所有东西都存在一个记录中(差不多只要满足第一范式)。

最 后一个问题可能会唬住一些人,其实用平面数据库去存储一些关系型的数据也是很一般的。例如,一条公司员工的记录就可以包含经理的登录名。用LDAP来存储 这类信息是很方便的。一个简单的判断方法:如果可以把数据保存在一张张的卡片里,就可以很容易地把它存在LDAP目录里。


接上文

LDAP目录树的结构
单条LDAP记录
定制目录的对象类
一个LDAP单个条目的例子
LDAP复制
安全和访问控制

LDAP目录树的结构

LDAP 目录以树状的层次结构来存储数据。如果你对自顶向下的DNS树或UNIX文件的目录树比较熟悉,也就很容易掌握LDAP目录树这个概念了。就象DNS的主 机名那样,LDAP目录记录的分辨名(Distinguished Name,简称DN)是用来读取单条记录,以及回溯到树的顶部。后面会做详细地介绍。

为什么要用层次结构来组织数据呢?原因是多方面的。下面是可能遇到的一些情况:

  • 如果你想把所有的美国客户的联系信息都“推”到位于到西雅图办公室(负责营销)的LDAP服务器上, 但是你不想把公司的资产管理信息“推”到那里。
  • 你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里,资产管理组对“asset-mgmt”部分有完全的访问权限,但是不能访问其它地方。
  • 把LDAP存储和复制功能结合起来,可以定制目录树的结构以降低对WAN带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息,但是欧洲的销售情况就只要每小时更新一次就行了。

刨根问底:基准DN

LDAP目录树的最顶部就是根,也就是所谓的“基准DN”。基准DN通常使用下面列出的三种格式之一。假定我在名为FooBar的电子商务公司工作,这家公司在Internet上的名字是foobar.com。

o="FooBar, Inc.", c=US

(以X.500格式表示的基准DN)

在 这个例子中,o=FooBar, Inc. 表示组织名,在这里就是公司名的同义词。c=US 表示公司的总部在美国。以前,一般都用这种方式来表示基准DN。但是事物总是在不断变化的,现在所有的公司都已经(或计划)上Internet上。随着 Internet的全球化,在基准DN中使用国家代码很容易让人产生混淆。现在,X.500格式发展成下面列出的两种格式。

o=foobar.com

(用公司的Internet地址表示的基准DN)

这种格式很直观,用公司的域名作为基准DN。这也是现在最常用的格式。

dc=foobar, dc=com

(用DNS域名的不同部分组成的基准DN)

就 象上面那一种格式,这种格式也是以DNS域名为基础的,但是上面那种格式不改变域名(也就更易读),而这种格式把域名:foobar.com分成两部分 dc=foobar, dc=com。在理论上,这种格式可能会更灵活一点,但是对于最终用户来说也更难记忆一点。考虑一下foobar.com这个例子。当 foobar.com和gizmo.com合并之后,可以简单的把“dc=com”当作基准DN。把新的记录放到已经存在的dc=gizmo, dc=com目录下,这样就简化了很多工作(当然,如果foobar.com和wocket.edu合并,这个方法就不能用了)。如果LDAP服务器是新 安装的,我建议你使用这种格式。再请注意一下,如果你打算使用活动目录(Actrive Directory),Microsoft已经限制你必须使用这种格式。

更上一层楼:在目录树中怎么组织数据

在UNIX文件系统中,最顶层是根目录(root)。在根目录的下面有很多的文件和目录。象上面介绍的那样,LDAP目录也是用

posted @ 2006-11-30 20:45 Alex| 编辑 收藏

key words:jotspot,wiki,项目管理

最近听说了jotspot,这个东西确实很好,原来一直打算自己建立一个wiki,作一些公司项目资源的管理,进度管理,以及提供其他团队成员了解项目信息的窗口,原来打算用jspwiki,可是建在哪呢? 在我的笔记本上还是公司的服务器上,似乎都不是很好,还是建在jotspot上比较好。zheng
给我看了一个截图:

jotspot_project.gif


功能还挺强大,我想做我的日常项目管理应该够了


posted @ 2006-11-09 22:03 Alex 阅读(704) | 评论 (2)编辑 收藏


key words:google搜索技巧,in site,inurl,filetype

最常用的是下面这个

IDEA in site:blogjava.net inurl:Alex filetype:ppt


下面转录一篇
玩转GOOGLE的十九招秘技


第一招:

  在输入多个词的时候,Google默认的是并且式的查询,如果想使用或者式的查询,使用OR,例如:java OR c++

第二招
  google是不区分大小写的,搜索Java和搜索JAVA或者java是完全一样的。

  第三招
  逻辑关系优先级使用圆括号,例如查找包含java和(JVM或者虚拟机):Java (JVM或者虚拟机)。

  第四招
  要搜索词组需要使用引号括起来,例如搜索Java虚拟机可以使用:"Java虚拟机"。

  第五招
  要想在搜索结果中不包含某些结果可以使用减号,例如搜索java又不包含培训可以使用:java -培训。

  第六招
  一般情况下是不能使用通配符的,通配符只能使用在词组中,例如:"使用*模式"。

  第七招
  只在网页的标题(即html的title指定的部分)内搜索指定的内容,例如:intitle:java。
第八招
  只在网页的url内搜索指定的内容,例如:inurl:java。

  第九招
  只在网页的正文内搜索指定的内容(忽略链接文字、标题和url),例如:intext:java。

  第十招
  只在链接文字(链接Java研究组织的链接文字就是Java研究组织)内搜索指定的内容,例如:inanchor:java。

  第十一招
  只在指定的网站内搜索指定的内容,可以是某个具体的网站或者是某个域名分类,例如:site:javaresearch.org或者site:org。

  第十二招
  只在指定的文件格式内搜索指定的内容,需要注意google只能支持有限的常用文本格式,包含一些诸如doc,xsl,ppt,pdf之类的富文本格式,例如:filetype:htm。

  第十三招
  google在检索的时候对于输入的内容的顺序是敏感的,如果找不到合适的结果可以试试改变一下搜索的关键字的顺序。


  第十四招
  在搜索多个关键字构成的内容时可以试试加引号和不加引号两者情况,结果可能有很大的差异,例如:java虚拟机和"java虚拟机"。

  第十五招
  搜索的策略可以采用先多后少,假设你想搜索Java的的command模式的例子,可以先试试java command pattern,然后试试java pattern或者java command,每次减少的那个关键字应该是你认为相对不重要的一个。
第十六招
  对于搜索中文而言,搜索“虚拟机”和搜索“虚拟机”是不同的,后者一般比前者的结果多并且相关性差一些,一般情况下你应该在各个词之间加空格,而在词内不加空格,这样得到的结果一般更好。

  第十七招
  一个关键字可以重复两次,对结果的排名和数量也会有影响,重复两次以上好像就没有什么影响了,例如搜索"internet"和搜索"internet internet"的结果是不同的。

  第十八招
  搜索内容最多只能包含十个单词,包括搜索intitle之类的选项,多出的部分被忽略。

  第十九招
  对于intitle,inurl,intext,inanchor和site之类的搜索选项一次不要使用多次,否则要么不是你想要的结果,要么根本就没有结果,混合使用这些选项是合法的,但是规则很复杂。

posted @ 2006-11-09 21:49 Alex 阅读(973) | 评论 (0)编辑 收藏

key words:mappingResources,通配符,mappingDirectoryLocations

平时写mapping的文件需要一个一个的放到配置文件里,比如
   1. <property name="mappingResources">  
   2.             
<list>  
   3.                 
<value>net/foxlog/model/Classes.hbm.xml</value>  
   4.                 
<value>net/foxlog/model/Parent.hbm.xml</value>  
   5.                 
<value>net/foxlog/model/Child.hbm.xml</value>  
   6.                 
<value>net/foxlog/model/User.hbm.xml</value>  
   7.             
</list>  
   8.         
</property>  


可以用更一步到位的方法:

   1. <property name="mappingDirectoryLocations">  
   2.             
<list>  
   3.                 
<value>classpath*:/org/springside/bookstore/commons/model/hbm</value>  
   4.             
</list>  
   5.         
</property>  

posted @ 2006-11-09 21:00 Alex 阅读(5023) | 评论 (0)编辑 收藏

key words:hibernate,复合主键,composite-id



基于业务需求,您会需要使用两个字段来作复合主键,例如在User数据表中,您也许会使用"name""phone"两个字段来定义复合主键。

假设您这么建立User表格:

CREATE TABLE user (

    name 
VARCHAR(100NOT NULL,

    phone 
VARCHAR(50NOT NULL,

    age 
INT,

    
PRIMARY KEY(name, phone)

);

在表格中,"name""age"被定义为复合主键,在映像时,您可以让User类别直接带有"name""age"这两个属性,而Hibernate要求复合主键类别要实作Serializable接口,并定义equals()hashCode()方法:

User.java

package onlyfun.caterpillar;

 

import java.io.Serializable;

import org.apache.commons.lang.builder.EqualsBuilder;

import org.apache.commons.lang.builder.HashCodeBuilder;

 

// 复合主键类的对应类别必须实作Serializable接口

public class User implements Serializable {

    
private String name;

    
private String phone;

    
private Integer age;

   

    
public User() {

    }

 

    
public Integer getAge() {

        
return age;

    }

 

    
public void setAge(Integer age) {

        
this.age = age;

    }

 

    
public String getName() {

        
return name;

    }

 

    
public void setName(String name) {

        
this.name = name;

    }

 

    
public String getPhone() {

        
return phone;

    }

 

    
public void setPhone(String phone) {

        
this.phone = phone;

    }

   

    
// 必须重新定义equals()与hashCode()

    
public boolean equals(Object obj) {

        
if(obj == this) {

            
return true;

        }

       

        
if(!(obj instanceof User)) {

            
return false;

        }

       

        User user 
= (User) obj;

        
return new EqualsBuilder()

                 .append(
this.name, user.getName())

                 .append(
this.phone, user.getPhone())

                 .isEquals();

       

    }

   

    
public int hashCode() {

        
return new HashCodeBuilder()

                 .append(
this.name)

                 .append(
this.phone)

                 .toHashCode();

    }

}

equals()hashCode()方法被用作两笔不同数据的识别依据;接着您可以使用<composite-id>在映射文件中定义复合主键与对象的属性对应:

User.hbm.xml

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

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

 

<hibernate-mapping>

 

    
<class name="onlyfun.caterpillar.User" table="user">

        
<composite-id>

            
<key-property name="name"

                          column
="name"

                          type
="java.lang.String"/>

            
<key-property name="phone"

                          column
="phone"

                          type
="java.lang.String"/>

        
</composite-id>

 

        
<property name="age" column="age" type="java.lang.Integer"/>

   

    
</class>

</hibernate-mapping>

在储存数据方面,复合主键的储存没什么区别,现在的问题在于如何依据复合主键来查询数据,例如使用load()方法,您可以创建一个User实例,并设定复合主键对应的属性,接着再透过load()查询对应的数据,例如:

User user = new User();

user.setName(
"bush");

user.setPhone(
"0970123456");

       

Session session 
= sessionFactory.openSession();

// 以实例设定复合主键并加载对应的数据

user 
= (User) session.load(User.class, user);

       

System.out.println(user.getAge() 
+ "\t" +

                                  user.getName() 
+ "\t" +

                                  user.getPhone());

session.close();

 

 

可以将主键的信息独立为一个类别,例如:

UserPK.java

package onlyfun.caterpillar;

 

import java.io.Serializable;

 

import org.apache.commons.lang.builder.EqualsBuilder;

import org.apache.commons.lang.builder.HashCodeBuilder;

 

public class UserPK implements Serializable {

    
private String name;

    
private String phone;

 

    
public String getName() {

        
return name;

    }

 

    
public void setName(String name) {

        
this.name = name;

    }

 

    
public String getPhone() {

        
return phone;

    }

 

    
public void setPhone(String phone) {

        
this.phone = phone;

    }

   

    
public boolean equals(Object obj) {

        
if(obj == this) {

            
return true;

        }

       

        
if(!(obj instanceof User)) {

            
return false;

        }

       

        UserPK pk 
= (UserPK) obj;

        
return new EqualsBuilder()

                 .append(
this.name, pk.getName())

                 .append(
this.phone, pk.getPhone())

                 .isEquals();

       

    }

   

    
public int hashCode() {

        
return new HashCodeBuilder()

                 .append(
this.name)

                 .append(
this.phone)

                 .toHashCode();

    }

}

现在User类别的主键信息被分离出来了,例如:

User.java

package onlyfun.caterpillar;

 

import java.io.Serializable;

 

public class User implements Serializable {

    
private UserPK userPK; // 主键

    
private Integer age;

   

    
public User() {

    }

 

    
public UserPK getUserPK() {

        
return userPK;

    }

 

    
public void setUserPK(UserPK userPK) {

        
this.userPK = userPK;

    }

 

    
public Integer getAge() {

        
return age;

    }

 

    
public void setAge(Integer age) {

        
this.age = age;

    }

}

在映像文件方面,需要指定主键类的信息,例如:

User.hbm.xml

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

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

 

<hibernate-mapping>

 

    
<class name="onlyfun.caterpillar.User" table="user">

        
<composite-id name="userPK"

                      class
="onlyfun.caterpillar.UserPK"

                      unsaved-value
="any">

            
<key-property name="name"

                          column
="name"

                          type
="java.lang.String"/>

            
<key-property name="phone"

                          column
="phone"

                          type
="java.lang.String"/>

        
</composite-id>

       

        
<property name="age" column="age" type="java.lang.Integer"/>

   

    
</class>

 

</hibernate-mapping>

在查询数据时,必须指定主键信息,例如:

UserPK pk = new UserPK();

pk.setName(
"bush");

pk.setPhone(
"0970123456");

      

Session session 
= sessionFactory.openSession();

// 以主键类实例设定复合主键并加载对应的数据

User user 
= (User) session.load(User.class, pk);

      

System.out.println(user.getAge() 
+ "\t" +

                                  user.getUserPK().getName() 
+ "\t" +

                                  user.getUserPK().getPhone());

session.close();

 


=================================================
再参考robbin的一篇文章
一个简单的复合主键的做关联类的例子  
posted @ 2006-11-09 19:22 Alex 阅读(12910) | 评论 (5)编辑 收藏

key words:jdk错误,Unsupported major.minor version

今天用一个工作流的产品,非要用jdk1.4的版本,没办法,只好切换回来,但是换回来后打开页面jsp出错,提示Unsupported major.minor version 49.0错误,到网上查了一下,49.0错误属于jdk1.5的错误,但是我的jdk1.5已经删除了啊?怎么回事呢?

最后想起来,可能是jboss中的1.5 版本产生临时文件class文件删除,删除后OK


资料:Unsupported major.minor version 49.0
posted @ 2006-11-08 10:13 Alex 阅读(573) | 评论 (2)编辑 收藏

key words:hibernate,load,session.get,session.load()

public class Parent implements Serializable {

    
/** identifier field */
    
private Long id;

    
/** persistent field */
    
private List childs;

    
/** full constructor */
    
public Parent(Long id, List childs) {
        
this.id = id;
        
this.childs = childs;
    }

    
/** default constructor */
    
public Parent() {
    }

    
/** 
     *            @hibernate.id
     *             generator-class="assigned"
     *             type="java.lang.Long"
     *             column="id"
     *
     
*/
    
public Long getId() {
        
return this.id;
    }

    
public void setId(Long id) {
        
this.id = id;
    }

    
/**
     *            @hibernate.set
     *             lazy="true"
     *             inverse="true"
     *             cascade="none"
     *            @hibernate.collection-key
     *             column="parent_id"
     *            @hibernate.collection-one-to-many
     *             class="net.foxlog.model.Child"
     *         
     
*/
    
public List getChilds() {
        
return this.childs;
    }

    
public void setChilds(List childs) {
        
this.childs = childs;
    }

    
public String toString() {
        
return new ToStringBuilder(this)
            .append(
"id", getId())
            .toString();
    }

    
public boolean equals(Object other) {
        
if ( !(other instanceof Parent) ) return false;
        Parent castOther 
= (Parent) other;
        
return new EqualsBuilder()
            .append(
this.getId(), castOther.getId())
            .isEquals();
    }

    
public int hashCode() {
        
return new HashCodeBuilder()
            .append(getId())
            .toHashCode();
    }

}


Parent parent = (Parent)session.load(Parent.class,new Integer(7));

运行提示出错,"can't get Entity.."

知道是什么低级错误么?  其实很弱智的东西,但是不注意还就会出现,呵呵,答案如下:


posted @ 2006-11-07 19:28 Alex 阅读(1643) | 评论 (3)编辑 收藏

key words: mysql,外键,fm150

这次在mysql中建立外键关联时发现问题:
alter table CHILD add constraint FK3D1FCFC74B18345 foreign key (PARENTID) references PARENT;      


提示..fm150错误,最后发现是mysql的版本问题,我用的是4.1的,对外键不知道是不支持还是支持的有问题,下载5.0的解决.
posted @ 2006-11-07 09:44 Alex 阅读(429) | 评论 (0)编辑 收藏

     摘要: 什么玩意  阅读全文
posted @ 2006-11-06 12:00 Alex 阅读(3447) | 评论 (9)编辑 收藏

     摘要:   阅读全文
posted @ 2006-10-31 12:32 Alex 阅读(8490) | 评论 (7)编辑 收藏

访问控制背景

    访问控制技术是由美国国防部(Department of Defense, DoD)资助的研究和开发成果演变而来的。这一研究导致两种基本类型访问控制的产生:自主访问控制(Discretionary Access Control, DAC)和强制访问控制(Mandatory Access Control, MAC)。最初的研究和应用主要是为了防止机密信息被未经授权者访问,近期的应用主要是把这些策略应用到为商业领域。

    自主访问控制,允许把访问控制权的授予和取消留给个体用户来判断。为没有访问控制权的个体用户授予和废除许可。自主访问控制机制允许用户被授权和
取 消访问其控制之下的任何客体(object),换句话说,用户就是他们控制下的客体的拥有者。然而,对于多数组织来说,最终用户对所访问的信息没有拥有 权。对于这些组织,公司或代理机构是事实上的系统客体和处理他们的程序的拥有者。访问优先权受组织控制,而且也常常基于雇员功能而不是数据所有权。

   强制访问控制,在美国国防部 Trusted Computer Security Evaluation Criteria (TCSEC) 中定义如下:“一种限制访问客体的手段,它以包含在这些客体中的信息敏感性和访问这些敏感性信息的主体的正式授权信息(如清除)为基础”。
  
   以上访问控制策略对于处理一些无需保密但又敏感的信息的政府和行业组织的需求并不是特别的适合。在这样的环境下,安全目标支持产生于现有法律、道德规范、 规章、或一般惯例的高端组织策略。这些环境通常需要控制个体行为的能力,而不仅仅是如何根据信息的敏感性为其设置标签从而访问这一信息的个人能力。
    


什么是基于角色访问控制(Role-Based Access Control, RBAC)?NIST 有如下定义。
  
   访问是一种利用计算机资源去做某件事情的的能力,访问控制是一种手段,通过它这种能力在某些情况下被允许或者受限制(通常是通过物理上和基于系统的控 制)。基于计算机的访问控制不仅可规定是“谁”或某个操作有权使用特定系统资源,而且也能规定被允许的访问类型。这些控制方式可在计算机系统或者外部设备 中实现。
  
    就基于角色访问控制而言,访问决策是基于角色的,个体用户是某个组织的一部分。用户具有指派的角色(比如医生、护士、出纳、经理)。定义角色的过程应该基于对组织运转的彻底分析,应该包括来自一个组织中更广范围用户的输入。
   
    访问权按角色名分组,资源的使用受限于授权给假定关联角色的个体。例如,在一个医院系统中,医生角色可能包括进行诊断、开据处方、指示实验室化验等;而研究员的角色则被限制在收集用于研究的匿名临床信息工作上。
   
    控制访问角色的运用可能是一种开发和加强企业特殊安全策略,进行安全管理过程流程化的有效手段。
     


用户(User)和角色(Role)

    用户指访问系统中的资源的主体,一般为人,也可为 Agent 等智能程序。角色指应用领域内一种权力和责任的语义综合体,可以是一个抽象概念,也可以是对应于实际系统中的特定语义体,比如组织内部的职务等。针对角色 属性的不同,某些模型中将角色进一步细分为普通角色和管理员角色(可理解为全局角色)。

 

许可(Permissions)和权限(Permission)

    许可描述了角色对计算机资源的访问和操作所具有的权限,其反映的是授权的结果。比如授予某个角色对计算机资源有读的权限,则代表了一个许可的存在,这个许 可表示:角色获取了对计算机资源的读许可。针对操作来说,其描述的是许可和操作之间的一种关联关系,而这层关系则表示了某一角色对某一操作所具有的权限及 权限状态。


     
角色和指派(Assignment)

    指派包含两个方面,用户指派和许可指派。用户指派表示的是,将用户指派给特定的角色。许可指派表示的是为角色指派计算机资源的访问和操作许可。

 

会话(session)

    会话表示的是用户和角色之间的关系。用户每次必须通过建立会话来激活角色,得到相应的访问权限。

 

角色和角色等级(Role Hierarchies)

    角色本身仅仅只是一个名词,其本身并不能代表权限的大小。比如,我们可以定一个“Director”的角色,也可以定一个“Project Leader”的角色。对于现实中我们来说,看到这样两个角色,就清楚 DIR 的权限要比一个 PL 的权限级别高。但是对计算机来说,这两个角色仅仅是两个“词语”,是等同的。可以采用分等级角色,在角色上实现层次化来解决这些问题。也可以采用复合角色 (其表示的就是一个角色组的概念),对角色实现一定的分组和复合,以便于权限指派。在一些 OA 产品中经常出现分等级角色。
   


限制(Constraints)
   
    模型中的职责分离关系(Separation of Duty),用于控制冲突(Conflict)。静态职责分离(Static SD)指定角色的互斥关系,用于用户指派阶段。避免同一用户拥有互斥的角色。实现简单,角色互斥语义关系清楚,便于管理不够灵活,不能处理某些实际情况。 动态职责分离(Dynamic SD)指定角色的互斥关系,用于角色激活阶段。允许同一用户拥有某些互斥的角色,但是不允许该用户同时激活互斥的角色。更灵活,直接与会话挂钩,适应实际 管理需要,实现复杂,不易管理。

             

 

 

参考文献

《AN INTRODUCTION TO ROLE-BASED ACCESS CONTROL》 NIST

《工作流授权控制模型》        胡长城

《基于角色的权限管理综述》 俞诗鹏

 

 

最后,感谢宏云博士对本文翻译提供的指导。   


请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen

posted @ 2006-10-26 21:31 Alex 阅读(476) | 评论 (0)编辑 收藏

     摘要: 前言 : 权限往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断“ Who 对 What(Which) ...  阅读全文
posted @ 2006-10-26 21:28 Alex 阅读(366) | 评论 (0)编辑 收藏

转自 javaEye


很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了。
我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化。

hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了。

二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache等,需要设置hibernate.cache.provider_class,我们这里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查询缓存,加上
hibernate.cache.use_query_cache=true

缓存可以简单的看成一个Map,通过key在缓存里面找value。

Class的缓存
对于一条记录,也就是一个PO来说,是根据ID来找的,缓存的key就是ID,value是POJO。无论list,load还是 iterate,只要读出一个对象,都会填充缓存。但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,如果在缓存里面有,就从缓存取,没有的话就去数据库load。假设是读写缓存,需要设置:
<cache usage="read-write"/>
如果你使用的二级缓存实现是ehcache的话,需要配置ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示缓存是不是永远不超时,timeToLiveSeconds是缓存中每个元素(这里也就是一个POJO)的超时时间,如 果eternal="false",超过指定的时间,这个元素就被移走了。timeToIdleSeconds是发呆时间,是可选的。当往缓存里面put 的元素超过500个时,如果overflowToDisk="true",就会把缓存中的部分数据保存在硬盘上的临时文件里面。
每个需要缓存的class都要这样配置。如果你没有配置,hibernate会在启动的时候警告你,然后使用defaultCache的配置,这样多个class会共享一个配置。
当某个ID通过hibernate修改时,hibernate会知道,于是移除缓存。
这样大家可能会想,同样的查询条件,第一次先list,第二次再iterate,就可以使用到缓存了。实际上这是很难的,因为你无法判断什么时候 是第一次,而且每次查询的条件通常是不一样的,假如数据库里面有100条记录,id从1到100,第一次list的时候出了前50个id,第二次 iterate的时候却查询到30至70号id,那么30-50是从缓存里面取的,51到70是从数据库取的,共发送1+20条sql。所以我一直认为 iterate没有什么用,总是会有1+N的问题。
(题外话:有说法说大型查询用list会把整个结果集装入内存,很慢,而iterate只select id比较好,但是大型查询总是要分页查的,谁也不会真的把整个结果集装进来,假如一页20条的话,iterate共需要执行21条语句,list虽然选择 若干字段,比iterate第一条select id语句慢一些,但只有一条语句,不装入整个结果集hibernate还会根据数据库方言做优化,比如使用mysql的limit,整体看来应该还是 list快。)
如果想要对list或者iterate查询的结果缓存,就要用到查询缓存了

查询缓存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然后
query.setCacheable(true);//激活查询缓存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可选
第二行指定要使用的cacheRegion是myCacheRegion,即你可以给每个查询缓存做一个单独的配置,使用setCacheRegion来做这个指定,需要在ehcache.xml里面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略第二行,不设置cacheRegion的话,那么会使用上面提到的标准查询缓存的配置,也就是net.sf.hibernate.cache.StandardQueryCache

对于查询缓存来说,缓存的key是根据hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
参数是"tiger%",那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了):
select * from cat c where c.name like ? , parameter:tiger%
这样,保证了同样的查询、同样的参数等条件下具有一样的key。
现在说说缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方 法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的 行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是 一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。
可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。
这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的 时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第 二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以 后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存 的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。

另外,如果hql查询包含select字句,那么查询缓存里面的value就是整个结果集了。

当hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢?
hibernate在一个地方维护每个表的最后更新时间,其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。
当通过hibernate更新的时候,hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时 间和这个缓存所查询的表,当hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这 些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。
可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低。

Collection缓存
需要在hbm的collection里面设置
<cache usage="read-write"/>
假如class是Cat,collection叫children,那么ehcache里面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。
这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新。

缓存策略
只读缓存(read-only):没有什么好说的
读/写缓存(read-write):程序可能要的更新数据
不严格的读/写缓存(nonstrict-read-write):需要更新数据,但是两个事务更新同一条记录的可能性很小,性能比读写缓存好
事务缓存(transactional):缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境,这个我没有怎么研究过

读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,然后就直接取数据库查询。
在hibernate2.1的ehcache实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到60秒后超时。
不严格读写缓存不锁定缓存中的数据。

使用二级缓存的前置条件
你的hibernate程序对数据库有独占的写访问权,其他的进程更新了数据库,hibernate是不可能知道的。你操作数据库必需直接通过 hibernate,如果你调用存储过程,或者自己使用jdbc更新数据库,hibernate也是不知道的。hibernate3.0的大批量更新和删 除是不更新二级缓存的,但是据说3.1已经解决了这个问题。
这个限制相当的棘手,有时候hibernate做批量更新、删除很慢,但是你却不能自己写jdbc来优化,很郁闷吧。
SessionFactory也提供了移除缓存的方法,你一定要自己写一些JDBC的话,可以调用这些方法移除缓存,这些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不过我不建议这样做,因为这样很难维护。比如你现在用JDBC批量更新了某个表,有3个查询缓存会用到这个表,用evictQueries (String cacheRegion)移除了3个查询缓存,然后用evict(Class persistentClass)移除了class缓存,看上去好像完整了。不过哪天你添加了一个相关查询缓存,可能会忘记更新这里的移除代码。如果你的 jdbc代码到处都是,在你添加一个查询缓存的时候,还知道其他什么地方也要做相应的改动吗?

----------------------------------------------------

总结:
不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。
如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。
在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为 底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。

终于写完了,好累……

posted @ 2006-10-23 14:30 Alex 阅读(622) | 评论 (1)编辑 收藏

在Struts或别的框架中集成Spring的时候,Spring向我们提供了获得context的方法 getApplicationContext,那在jsp中如何获得呢?

ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext
(
this.getServletConfig().getServletContext());

建议在项目中开发的时候提供一个singleton对外公布统一的applicationContext,毕竟不是每个人都一定能获得web环境或servlet.

posted @ 2006-10-23 11:03 Alex 阅读(2528) | 评论 (0)编辑 收藏

仅列出标题
共15页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last