<<深入潜出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配置文件:
[code]
<?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>
[/code]
2.properties文件:
[code]
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
[/code]
mapping映射得写到.java文件中:
[code]
Configuration config = new Configuration();
config.addClass(Tuser.class);
[/code]

.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只能由一个线程使用.

[code]

//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;
}
}
[/code]

[code]
主键的生成方式:
<id name="id" column="id" type="java.lang.Integer">
<generator class="native">
</id>
[/code]
1.[color=red]Assigned[/color]:由应用逻辑产生,无需Hibernate干涉。
2.[color=red]hilo[/color]:通过hi/lo算法实现的主健生成机制,需要额外的数据库表保存主键生成历史状态。
3.[color=red]seqhilo[/color]:与2类似,只是主键历史状态保存在Sequence中,适用于Sequence的数据库(eg,Oracle)。
4.[color=red]increment[/color]:主键按数值顺序递增。缺点:如果当前有多个实例访问同一个数据库时,不同实例会产生同样的主键,从而造成
主键重复异常。
5.[color=red]identity[/color]:采用DB提供的主键生成机制。如SQL SERVER,MySql中的自增主键生成机制。
6.[color=red]sequence[/color]:采用DB提供的Sequence机制生成主键。如Oracle Sequence。缺点:插入一条新数据之前,Hibernate必须向数据库发起一条Select Sequence操作以获取主键值。
7.[color=red]native[/color]:由Hibernate根据Dialect(数据库适配器)的定义,自动采用identity,hilo,sequence的其中一种生成方式。
8.[color=red]uuid.hex[/color]:由Hibernate基于128位唯一值产生算法,根据当前设备IP,时间,JVM启动时间,内部自增量等4个参数生成16进制(编码后以长度32位的字符串表示)数值。
优点:在最大程度上保证产生ID的唯一性。在多实例运行并发下重复概率仍然存在,只是机率非常小。
9.[color=red]uuid.string[/color]:与8类似,只是生成的主键未进行编码(长度16位),在某些数据库中可能出现问题(如PostgreSQL)。
10.[color=red]foreign[/color]:使用外部表的字段作为主键。
11.[color=red]select[/color]:Hibernate3中新引入的主键获取机制,主要针对遗留系统的改造工程。如某些系统主键依赖触发器生成, 当insert时通过触发器捕获
这一操作,此时我们必须再次通过某一识别字段读取已插入的数据,获取其主键数据。
select类型生成器需要指定一个唯一标识字段用于二次读取,以获取触发器生成的主键值。
[code]
<generator class="select">
<param name="key">key_field</param>
</generator>
[/code]
4.3.3高级映射技术
1.自定义数据类型:
eg.为表TUser增加Email字段,但未知要加多少Email字段合适,通常做法:
A.增加Email1,Email2,.....;
B.增加一个Email表;
C.增加一个Email字段,每个Email以";"隔开。
当采用C方案时,可将Email字段映射成一个List,Hibernate不支持,所以需实现自己的UserType:
[code]
//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();
}
[/code]
接下来写EmailList的实现:
[code]
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;
}
}
[/code]
POJO类以及映射文件:
[code]
public class TUser implements Serializable{
private Integer id;
private List email;
......seter/geter;
}
[/code]
TUser.hbm.xml:
[code]
<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>
[/code]
运行测试代码:
[code]
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();
[/code]
结论:
这么作可以在model层进行String<==>List之间的转换,Service层只需操纵List对象即可。
CompositeUserType 包含了 UserType的接口中的方法定义,并提供了更多的方法:
[code]
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;
}
[/code]
4.34复合主键
eg.T_User表包含(id,name,age)属性,T_User2表:将name属性拆成(firstName,lastName,age)以这两个字段作为符合主键.
1>基于实体类属性的复合主键:复合主键由实体类的属性组成
[code]
<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>
[/code]
对应的POJO类TUser2.java
[code]
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();
)
}
[/code]
我们知道通过Session.load(Class theClass,Serializable id)来加载数据,但上面的复合主键可直接用对象名来load:
[code]
TUser2 user = new TUser2();
user.setFirstName("aaa");
user.setLastName("bbb");
user = (TUser2)session.load(TUser2.class,user);
[/code]
2>基于主键类的复合主键:
将firstName,lastName单独提到一个独立的TUserPK表中:
[code]
public class TUserPK implements Serilizable{
private String firstName;
private String lastName;
...//setter and getter
...//equals and hashCode
}
[/code]
TUser2映射文件:
[code]
<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>
[/code]
对应的POJO类TUser2.java:
[code]
public class TUser implements Serializable{
private Integer age;
private TUserPK userpk;
...//setter and getter
}
[/code]
之后再测试:
[code]
TUser2 user = new TUser2();
user.setFirstName("aaa");
user.setLastName("bbb");
user = (TUser2)session.load(TUser2.class,userpk);
[/code]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值