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

<<深入潜出Hibernate>>读书笔记

阅读更多
第一章:面向应用的持久化设计
1.1
持久(persistance):把数据保存到可掉电式存储设备中供之后使用
持久层:讲数据库使用者与数据实体相关联。持久层介于上层应用与DB之间。在系统逻辑层面上,专注于数据持久化的一个相对独立的领域(Domain)。
持久化:讲内存中的数据保存到数据库(或其他媒介中)的过程。
1.2
解耦合:采用一切手段降低关系的紧密程度。
解耦合设计目标:
1.应用层解耦合---应用逻辑与数据逻辑相分离。
2.资源层解耦合---逻辑结构与物理结构相分离。


DAO(Data Access Object)模式 = Data Accessor模式 + Active Domain Object模式
Data Accessor模式:数据访问和业务逻辑的分离。
Active Domain Object模式:业务数据的对象化封装。
1.数据存储逻辑的分离
2.数据访问底层实现的分离
3.资源管理和调度的分离
4.数据抽象

Connection Pool原理:在内部对象池中维护一定数量得得数据库连接,并对外暴露数据库连接获取和返回方法。
优点:
1.资源重用。
2.更快的系统相应速度。
3.新的资源分配手册。
4.统一的连接管理,避免数据库连接泄漏。
在一个系统中如果连接无法重用,那么连接池机制就形同虚设。为了解决这个问题特引入两种设计模式:
1.Decorator模式:利用一个对象透明地为另一个对象增加新的功能。简单说就是通过一个Decorator对原有对象进行封装,同时实现与原有对象相同的接口,从而得到一个基于原有对象的,对既有接口的增强性实现。
2.Dynamic Proxy模式:JDK1.3中引入的一种代理机制。通过实现
java.lang.reflect.InvocationHandler接口,可以实现拦截需要改写的方法。
O/R Mapping:一种设计思想或者实现机制。
O/R Mapper:根据O/R原理设计的持久化框架。
持久层实现类型:
1.混杂模式:在业务类中写JDBC。优点:小型系统开发迅速,但维护性和扩展性较差。
2.基于DATA CLASS的持久层实现模式:介于Business Classes与数据库之间,实现了底层与业务逻辑结构之间的分离。缺点:编码亮增大。
3.基于现有持久层框架的实现模式:在第二种持久化实现模式基础上,在DATA CLASSES后与数据库之间再封装一层持久化组件。

第三章:快速起步
HIBERNATE的配置文件:1.可以用xml文件写;2.可以用properties文件写
1.典型的hibernate.cfg.xml配置文件:
<?xml version='1.0'?>

<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <!-- SessionFactory 配置 -->
  <session-factory>
      <!-- 数据库URL -->
      <property name="hibernate.connection.url">dbc:oracle:thin:@192.168.0.1:1521:scgldb</property>
      <!-- 数据库JDBC驱动(以ORACLE为例) -->
      <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
      <!-- 数据库用户名 -->
      <property name="hibernate.connection.username">User</property>
      <!-- 数据库用户密码 -->
      <property name="hibernate.connection.password">password</property>
      <!-- dialect, 每个数据库都有对应的Dialect对应其平台特性(以ORACLE为例) -->
      <property name="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</property>
      <!-- 是否将运行期生成的SQL输出到日志以供调试 -->
      <property name="hibernate.show_sql">True</property>
      <!-- 是否使用数据库外链接 -->
      <property name="hibernate.user_outer_join">True</property>
      <!-- 事务管理类型,这里我们使用JDBC Transaction -->
      <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactroy</property>
      <!-- 映射文件配置,配置文件名必须包含其相对于根的全路径 -->
      <mapping resource="com/aaa/model/Tuser.hbm.xml"> 
  </seesion-factory> 
</hibernate-configuration>

2.properties文件:
hibernate.dialect org.hibernate.dialect.Oracle9Dialect
hibernate.connection.driver_class oracle.jdbc.driver.OracleDriver
hibernate.connection.url jdbc:oracle:thin:@192.168.0.1:1521:scgldb
hibernate.connection.username user
hibernate.connection.password password

mapping映射得写到.java文件中:
Configuration config = new Configuration();
config.addClass(Tuser.class);


.properties与XML相比:1.xml更具有可读性。2.xml中可以配置mapping文件

hibernate2代码初解:
1.Configuration config = new Configuration().configure();
2.SessionFactory sessionFactory = config.buildSessionFactory();
3.Session session = sessionFactory.openSession();

//1.读取.classpath文件根据导入的*.hbm.xml或.properties文件
(或者可以通过Configuration config = new Configuration().config("hibernate.cfg.xml"))
//来NEW一个Configuration类的实例Configuration保存了数据库的配置;如果选用的是.properties文件:
//Configuration config = new Configuration().addFile("TUser.hbm.xml").addClass(com.xxx.model.TGroup.class);
//2.SessionFactory中保存了当前数据库的配置的所有的映射关系,同时维护
//当前的二级缓存和Statment Pool;SessionFactory采用了线程安全的设计,一般在一个应用中针对一个数据库
//共享一个SessionFactory实例即可,多个太麻烦,代价太大。
//3.通过这个Session可以对对象进行持久化操作,Session是非线程安全的.也就是说一个Session只能由一个线程使用.


//POJO类:
public class TUser implemenst Serializable{//实现Serializable序列化:1.在HttpSession中保存TUser对象,或将其通过RMI传输
	private Integer Id;//建议不要用final类型,导致Hibernate的代理机制难以进行。
	private String name;//而代理机制恰是Hibernate中提高性能的重要途径之一。
         
        public TUser(){} //Hibernage通过Constructor.newInstance()来构造实体对象。
	
	//尽量是set()和get()都为public,如果设为private,protected,Hibernate将无法对属性的存取
        //进行优化,只能转为传统的反射机制进行操作,这将导致大量的性能开销。
	public Integer getId(){
		return this.id;
	}
        public void setId(Integer id){
		this.id = id;		
        }
        public String getName(){
        	return this.name = name;
        }
        public void setName(String name){
		this.name = name;
	}
}


主键的生成方式:
<id name="id" column="id" type="java.lang.Integer">
	<generator class="native">
</id>

1.Assigned:由应用逻辑产生,无需Hibernate干涉。
2.hilo:通过hi/lo算法实现的主健生成机制,需要额外的数据库表保存主键生成历史状态。
3.seqhilo:与2类似,只是主键历史状态保存在Sequence中,适用于Sequence的数据库(eg,Oracle)。
4.increment:主键按数值顺序递增。缺点:如果当前有多个实例访问同一个数据库时,不同实例会产生同样的主键,从而造成
            主键重复异常。
5.identity:采用DB提供的主键生成机制。如SQL SERVER,MySql中的自增主键生成机制。
6.sequence:采用DB提供的Sequence机制生成主键。如Oracle Sequence。缺点:插入一条新数据之前,Hibernate必须向数据库发起一条Select Sequence操作以获取主键值。
7.native:由Hibernate根据Dialect(数据库适配器)的定义,自动采用identity,hilo,sequence的其中一种生成方式。
8.uuid.hex:由Hibernate基于128位唯一值产生算法,根据当前设备IP,时间,JVM启动时间,内部自增量等4个参数生成16进制(编码后以长度32位的字符串表示)数值。
           优点:在最大程度上保证产生ID的唯一性。在多实例运行并发下重复概率仍然存在,只是机率非常小。
9.uuid.string:与8类似,只是生成的主键未进行编码(长度16位),在某些数据库中可能出现问题(如PostgreSQL)。
10.foreign:使用外部表的字段作为主键。
11.select:Hibernate3中新引入的主键获取机制,主要针对遗留系统的改造工程。如某些系统主键依赖触发器生成, 当insert时通过触发器捕获
          这一操作,此时我们必须再次通过某一识别字段读取已插入的数据,获取其主键数据。
          select类型生成器需要指定一个唯一标识字段用于二次读取,以获取触发器生成的主键值。
<generator class="select">
   <param name="key">key_field</param>  
</generator>

4.3.3高级映射技术
1.自定义数据类型:
eg.为表TUser增加Email字段,但未知要加多少Email字段合适,通常做法:
A.增加Email1,Email2,.....;
B.增加一个Email表;
C.增加一个Email字段,每个Email以";"隔开。
当采用C方案时,可将Email字段映射成一个List,Hibernate不支持,所以需实现自己的UserType:
//UserType可映射一个或多个字段
public interface UserType{
    
    //返回映射(Email)字段的SQL类型			
    public int[] sqlTypes{};

    //UserType.nullSafeGet()所返回的自定义数据类型
    public Class returnedClass{};

    //脏数据检查,x,y为同一实体bean的两个副本
    public boolean equals(Object x,Object y) throws HibernateException;

    /**从ResultSet读取数据,将其映射为自定义数据类型返回,
     *(此方法要求对可能出现的Null值进行处理),names包含
     *当前自定义类型(Email)类型的映射名称
     */
    public Object nullSafeGet(ResultSet rs,String[] names,Object owner)
                              throws HibernateException,SQLException;

    //在Hibernate保存数据时调用,可以通过preparedStatement
    //将自定义数据写进对应的库表字段.
    public void nullSafeSet(PreparedStatement st,Object value,int index)
                             throws HibernateException,SQLException;
    
    /**当调用nullSafeGet()可获得自定义数据对象,接着调用该方法拷贝刚才
     *得到的自定义数据对象,此时就有两个自定义对象:第一个由Hibernate来
     *负责维护,第二个由用户来使用。第一个要做脏数据检查,检查过程中调
     *equals()来判断是否发生变化,变化了则return false;
     */
    public Object deepCopy(Object value)throws HibernateException;

    //本类型实例是否可变
    public boolean isMutable();
}

接下来写EmailList的实现:
public class EMailList implements UserType{

    private List emails;
    private static final char SPLITTER = ";";
    private static final int[] TYPES = new int[] { java.sql.Types.VARCHAR };

    public boolean isMutable(){
           return false;
    }

    public int[] sqlTypes(){
           return TYPES; 
    }

    public Class returnedClass(){
           return List.Class;
    }
    
    //创建一个新的List包含原有List实例中的所有元素  
    public Object deepCopy(Object value)throws HibernateException{
           List sourceList = (List)value;
           List targetList = new ArrayList();
           targetList.addAll(sourceList);
           return targetList;
    } 

    //判断email list是否发生变化
    public boolean equals(Object x,Object y)throws HibernateException,SQLException{
          if(x==y) return true;
          if(null!=x && null!=y){
             List xList = (List)x;
             List yList = (List)y;
             if(xList.size() != yList.size()) return false;
             for(int i=0;i<xList.size();i++){
                 String str1 = (String)xList.get(i);
                 String str2 = (String)yList.get(i);
                 if(!str1.equals(str2)) return false;  
             }
             return true;
          }
          return false;
   }

   //从resultSet中取出Email字段,并将其解释为List类型后返回
   public Object nullSafeGet(ResultSet rs,String[] names,Object owner)
                             throws HibernateException,SQLException{
          String value = (String)Hibernate.STRING.nullSafeGet(rs,name[0]);
          if(value != null){
               return parse(value);  
          }else{
               return null;             
          }

   }

   //将List型的Email信息组装成字符串之后保存到email字段         
   public void nullSafeSet(PreparedStatement st,Ojbect value,int index)
                           throws HibernateException,SQLException{
          if(value != null){
               String str = assemble((List)value);
               Hibernate.STRING.nullSafeSet(st,str,index); 
          }else{
               Hibernate.STRING.nullSafeSet(st,value,index);
          }
    } 

   //将String拼成一个字符串,以";"分割
   public String assemble(List emailList){
           StringBuffer strBuf = new StringBuffer();
           for(int i=0;i<emailList.size()-1;i++){
              strBuf.append(emailList.get(i)).apend(SPLITTER);
           }
           strBuf.apend(emailList.get(emailList.size()-1));
           return strBuf.toString();
   } 

   //将以";"分割的字符串解析为一个字符串数组
   private List parse(String value){
           String[] str = org.apache.commons.lang.StringUtils.split(value,SPLITTER);
           List emailList = new ArrayList();
           for(int i=0;i<str.length;i++){
               emailList.add(strs[i]);
           }
           return emailList;
   }
}

POJO类以及映射文件:
public class TUser implements Serializable{
  private Integer id;
  private List email;
  ......seter/geter;
}

TUser.hbm.xml:
<hibernate-mapping>
    <class name="com.xxx.hibernate.entity.TUser" table="T_USER">
           <id...></id>     
           <property name="email" column="EMAIL" 
            type="com.xxx.hibernate.entity.EMailList"/> 
    </class>
</hibernate-mapping>

运行测试代码:
TUser user = (TUser)session.load(TUser.class,new Integer(2));
List list = user.getEmail();
for(int i=0;i<list.size();i++){
   System.out.println(list.get(i));
}
//添加一个Email后保存
list.add("aaa@123.com");
Transaction tx = session.getTransaction();
session.save(user);
tx.commit();

结论:
这么作可以在model层进行String<==>List之间的转换,Service层只需操纵List对象即可。
CompositeUserType 包含了 UserType的接口中的方法定义,并提供了更多的方法:
public interface CompositeUserType{
     ....此处忽略UserType接口已定义的方法
     //可用HQL获取属性名数组
     public String[] getPropertyNames();
  
     //获取对应属性的类型
     public Type[] getPropertyTypes();

     //获取属性值
     public Object getPropertyValue(Object component,int property)throws HibernateException;

     //设置属性值
     public Object setPropertyValue(Object component,int property,Object value)throws HibernateException;

     /**当对象被写入二级缓存之前调用
      *通过这个方法可将对象转换为易于在二级缓存中保存的形式,如对于某些无法
      *序列化的数据进行序列化,典型情况:当前对象对其它实体对象的引用,可以
      *将这种引用关系以id值的形式保存
      */
      public Serializable disassemble(Object value,SessionImplementor session)throws HibernateException;

      //与上面的方法相反,将二级缓存的数据重新转换为我们制定的对象类型
      public Object assemble(Serializable cached,SessionImplementor session,Object owner)throws HibernateException;
}

4.34复合主键
eg.T_User表包含(id,name,age)属性,T_User2表:将name属性拆成(firstName,lastName,age)以这两个字段作为符合主键.
1>基于实体类属性的复合主键:复合主键由实体类的属性组成
<hibernate-mapping>
   <class name="com.xxx.hibernate.entity.TUser2" table="T_User2">
       <composite-id>
           <key-property name="lastName" column="last_name" type="String"/>
           <key-property name="firstName" column="first_name" type="String"/>       
       </composite-id>
       <property column="age" name="age" type="Integer"/> 
   </class>
</hibernate-mapping>

对应的POJO类TUser2.java
public class TUser implements Serializable{
    private Integer age;
    private String lastName;
    private String firstName;
    .....//getter and setter
    
    //apache.commons.lang.EqualsBuilder()
    public boolean equals(){
        if(!(object instanceof TUser2)){
            return false;
        }
        TUser2 rhs = (TUser2)object;    
        return new EqualsBuilder().apendSuper(super.equals(object)).append(this.lastName,rhs.lastName).
                   append(this.firstName,rhs.firstName).isEquals();
    }    

    public int hashCode(
        return new HashCodeBuilder(-52825373,-475504089).appendSuper(super.hashCode()).append(this.lastName).        
                   append(this.firstName).toHashCode();
    )     
}

我们知道通过Session.load(Class theClass,Serializable id)来加载数据,但上面的复合主键可直接用对象名来load:
TUser2 user = new TUser2();
user.setFirstName("aaa");
user.setLastName("bbb");
user = (TUser2)session.load(TUser2.class,user);

2>基于主键类的复合主键:
将firstName,lastName单独提到一个独立的TUserPK表中:
public class TUserPK implements Serilizable{
    private String firstName;
    private String lastName;
    ...//setter and getter
    ...//equals and hashCode
}

TUser2映射文件:
<hibernate-mapping>
   <class name="com.xxx.hibernate.entity.TUser2" table="T_User2">
       <!-- name指定实体类的主键类属性名,class指定主键类类型 -->
       <composite-id name="userpk" class="TUserPK">
           <key-property name="lastName" column="last_name" type="String"/>
           <key-property name="firstName" column="first_name" type="String"/>       
       </composite-id>
       <property column="age" name="age" type="Integer"/> 
   </class>
</hibernate-mapping>

对应的POJO类TUser2.java:
public class TUser implements Serializable{
    private Integer age;
    private TUserPK userpk;
    ...//setter and getter         
}

之后再测试:
TUser2 user = new TUser2();
user.setFirstName("aaa");
user.setLastName("bbb");
user = (TUser2)session.load(TUser2.class,userpk);
分享到:
评论
1 楼 306781704 2009-02-20  
 

相关推荐

Global site tag (gtag.js) - Google Analytics