Hibernate学习

作者在 2010-07-17 19:29:47 发布以下内容
1.概念:ORM 对象关系映射 它是一个框架,所谓框架就是前人对相关问题处理方案的总结,将某类问题的解决方案,细节等组合在一起形成框架,后人在使用的时候就可以直接使用这个框架里所定义好的API,并遵循框架里的规则进行开发,达到自己想要的目的。
2.先来个例子,熟悉一下hibernate
创建数据库。create table......
创建一个POJO类。属性名对应表中的列名。添加getter/setter方法
创建映射文件:XXXX.hbm.xml (XXXX是POJO类的名称).如:BasicCar.hbm.xml:
.......(DTD配置信息)
<hibernate-mapping>
 <class name="basicCar.bean.BasicCar" table="basiccar" lazy="false"> //类名(含包),表名,是否延迟加载
  <id name="id" column="id" type="long"> //表中的id表名
   <generator class="increment"></generator> //自增类型
  </id>
  <property name="name" column="name" type="string"></property> //表中的name列名
  <property name="factory" column="factory" type="string"></property>//表中的factory列名
  <property name="date" column="date" type="date"></property>  //表中的date列名
 </class>
</hibernate-mapping>
创建一个配置文件:hibernate.cfg.xml
<hibernate-configuration>
 <session-factory>
  <!-- 定义数据库连接的用户名,密码,所用的语言,驱动,连接URL等 -->
  <property name="connection.username">root</property>
  <property name="connection.url">
   jdbc:mysql://localhost:3306/test
  </property>
  <property name="dialect">
   org.hibernate.dialect.MySQLDialect
  </property>
  <property name="myeclipse.connection.profile">MySQL</property>
  <property name="connection.password">sa</property>
  <property name="connection.driver_class">
   com.mysql.jdbc.Driver
  </property>
  <mapping resource="basicCar/bean/Basiccar.hbm.xml" />  
 </session-factory>
</hibernate-configuration>
 
编写测试类:导入包
public class Test { 
 Configuration cfg;
 SessionFactory sf;
 Session session; 
 public static void main(String[] args) {
  Test test = new Test();
  test.doConfiguration();
  test.openSession();
  test.saveEntity();
  test.closeSession();
 } 
 void doConfiguration(){
  cfg = new Configuration();
  cfg.configure("/hibernate.cfg.xml");//读取配置文件
  sf = cfg.buildSessionFactory();
 }
 void openSession(){
  session = sf.openSession();
 }
 void closeSession(){
  session.close();
 } 
 void saveEntity(){
  Transaction savetx = session.beginTransaction();
  BasicCar bc1 = new BasicCar();
  bc1.setId(4);
  bc1.setName("BJCar");
  bc1.setFactory("BeiJing");
  bc1.setDate(Date.valueOf("2010-3-5"));
  session.save(bc1);  
  savetx.commit();
  System.out.println("保存成功!!");
 } 
保存成功后可以看到在数据库中有了记录!
3.理解hibernate
Configuration类负责管理Hibernate的配置信息。是连接底层的数据库入口。包括几个关键的属性:URL,用户名,密码,JDBC驱动类以及数据库所用的语言。要使用Hibernate必须先提供这些信息以完成初始化工作。为后继操作做好准备,当创建该类的实例并调用configure()后,Hibernate会在当前的classpath中搜索hibernate.cfg.xml文件并将其读取到内存中作为后继操作的基础配置(它是借助于dom4j的XML解析器进行解析)。用它的对象可以创建SessionFactory,SessionFactory相当于JDBC的DrieverManager,接着就可以创建Session了。如:
Configuration config = new Configuration().configure();
SessionFactory sf = config.buildSessionFactory();
Session session = sf.openSession();
Session 是用户进行持久化对象操作的接口。相当于JDBC中的Connection.同时也是一个必须的缓存持久化对象的一级缓存。常用的函数是:
与查询相关:
createQuery()
createCriteria()
createSQlQuery()
与对象操作有关:
save();
update();
delete();
saveorupdate(); 如果是一个新对象则执行保存操作,如果这个对象在数据库中已经存在,则执行更新操作。
get();根据主键返回对象,直接从数据库中查询出,相当于JDBC中的ResultSet。如果没有则返回null
load();同上,但是它先多缓存中查找,如果有直接返回,如果二级缓存都没有,则从数据库中查询。如果还是没有,则抛出异常
evict(); 把某个对象清除出session
clear();清空session缓存
close();关闭session缓存
flush();同步刷新session缓存。
disconnect();关闭数据库连接
reconnect();重新连接数据库
和事务有关的:
beginTransaction();创建并开始一个与这个session相关的事务
getTransaction();获得关联这个session的事务对象

4.对象关系映射:
JAVA通过对象的内存地址来区别不同的对象,而hibernate则通过主键来区别。所以Hibernate对每个对象都在用一个主键来区别它们。设置主键有不同的方法,主要有三种:
4.1 映射代理主键:主键没有任何业务意义,在POJO中可以设为private类型,避免被其他程序更改。在映射文件中这样写:
<id column="id" name="id" type="long">
<generator class="increment" ></generator> //设为自增类型
</id>
4.2 映射单个自然主键:用数据表中唯一的,不变的,可以区别不同对象实体的字段来充当主键。这时hibernate不参与,由外部应用程序负责。映射文件这样写:
<id column="name" name="name" type="string">
<generator class="assigned"></generator>
</id>
这时就要在应用程序中设置相应的值才能保存到数据库,并且当设置的两个name值相同时会出错,因为主键不可以相同,保存不成功!
4.3 映射复合主键:primary(name,date)当需要多个字段结合来区分实体对象时,就要映射复合自然主键。这可以分为两种:一种是不单独定义主键类:跟前面的POJO一样,映射文件这样写
<composite-id>
<key-property name="name" column="name" type="string">
<key-property name="date" column="date" type="date">
</composite-id>
另一种是单独定义主键类。也就是把上面的name与date字段单独写成一个POJO并写上相应的get/setter方法,而这个类被作为实体类中的一个属性处理,映射文件写成:
<composite-id name="carID" class="com.bean.CarID">//主键类
<key-property name="name" column="name" type="string" >
<key-property name="date" column="date" type="date">
</composite-id>
5.继承关系映射类型:
策略有三个:
每个子类对应一张表,把父类属性添加到子类中。但是这样不能识别继承关系,所以不支持多态;
每个类对应一个数据表,子类中的主键同时也是外键,参考父类表中的字段。在配置文件中这样写:
<joined-subclass name="com.bean.Lorry" table="lorry">
<key column="LorryID"> //LorryID参考父类的主键
....(其他属性字段)
</joined-subclass>
<joined-subclass name="com.bean.car" table="car">
<key column="carID"> //carID参考父类表的主键
.......
</joined-subclass>
共享一个数据表:把所有的属性都写在同一个POJO类中,再添加一个TYPE类型,用于区别不同的子类。
映射文件中这样写:
<class name="类名" table="表名">
.......(其他字段)
<!--定义discriminator元素,用于指明表中的type字段来区分子类的类型-->
<discriminator column="type" type="string"></discriminator>
<subclass name="com.bean.Lorry" discriminator-value="lorry">
......(子类属性)
</subclass>
<subclass name="com.bean.Car" discriminator-value="car">
.......(子类属性)
</subclass>
</class>
这样,当用List lorrylist = session.find("from Lorry")就相当于“select * from tablename where type='lorry'”;找出所有Lorry的数据。
BasicCar bc = new Lorry("",""....."lorry");
session.save(bc);就保存了一个Lorry对象!
6.关联关系映射:
一对一:类A----->类B    若A类中设外键参照B类,则A类的映射文件为:
<id name="aid" column="aid" type="long">
<generator class="foreign">
<param name="property">B类属性</param>
<generator>
......
<one-to-one name="B类对应表" class="B类" constrained="true"
cascade="none">
</one-to-one>
/////主键生成方式为foreign,说明这个主键同时也是外键,依赖于另一个表的主键,所以需要param元素来说明所关联的实体。另外,constrained设为true,说明这个主键参照另一个表salesname
一对多/多对一:如:类A(1)----->类B(*)那么在类B对应的映射文件中添加
<many-to-one name="类A中的属性名" >
<column=“外键名">
<class="类A">
<not-null="true">
</many-to-one>
多对多:用得不多,要注意几点,建关联表的时候,可以只设两个外键,其主键由SQL自动生成;也可以是两个外键同时也是复合主键;但是不能自己设一个主键外加两个外键。因为关联表的主键是不会显示地管理的,当插入关联表时报主键为空的错误信息。
在两个关联类中设集合属性,用于存储对方关联类。
在映射文件中对集合属性:
<set name="变量名" table="关联表名" inverse="true" cascade="save-update">
<key column="关联表中的外键">
<many-to-many class="对方关联类" column="对方表主键">
inverse 说明保存时根据另一个对象的状态来同步
cascade代表级连更新。
在双方的映射文件中都应该设cascade="save-update"
还要设置一方的inverse=true;另一方设为inverse=false;(默认不写就是false)
7.JAVA对象的三种状态:
临时态(一个对象创建之后或者在session中用delete()之后就是临时态,若程序后面没有对他的引用,则会被垃圾回收机制回收)
持久态(被关联在某个session中的对象就是持久态了。临时态对象被save()/saveorupdate()/get()/load()/find()/iterate())
游离态(对象被存入数据库中并且没有与session关联了)
8.HQL与SQL的区别,前者是面向对象的,而后者是面向数据库字段的。前者仅用于查询数据,不支持insert,update,delete语句,要更新则使用session的update或者使用query.executeUpdate()
9.查询对象 Query
     普通查询如上所示 
     带参数的查询(相当于 PreparedStatement)
     q = session.createQuery("from User u where u.name= :name");
     q.setString("name", "张三"); //按名字
     q = session.createQuery("from User u where u.name= ?");
     q.setString(0, "张三");    //按位置
     Native SQL查询方式(用于特定的数据库的查询)
     SQLQuery sqlQuery=session.createSQLQuery("select name,factory from basiccar where id < ?");
 sqlQuery.setInteger(0,10); 返回的是一个Object对象
10.事务:(工作中的基本逻辑单元,可能包括数据库的一系列操作,当其中的某个操作出问题时,通过事务回滚,可以恢复到事务起始处,避免发生数据不完整或者错误)
Hibernate是JDBC的轻量级封装,本身不具备事务管理能力,在事务管理层中将它委托级底层的JDBC或者JTA,以实现事务管理和调度功能。一般情况下都默认用前者,如:
Transaction tx = session.beginTransaction();
.......
tx.commit();
11.数据库并发带来的问题:更新丢失,脏读,不可重复读,幻读。
 避免并发冲突的三种方案:设置事务隔离级别(在session-factory中设置<property name="hibernate.connection.isolation">4</property> ),乐观锁与悲观锁
 事务隔离级别:读未提交(不能避免脏读现象),读已提交(可能出现不可重复读问题),可重复读(可能发生幻读),串行化(资料开销大),级别由低到高(1.2.3.4)
 乐观锁(事务更新记录时对事务进行检查,看自从上次读取这条记录后,它是否被其他事务修改)
 悲观锁(在数据更新时把记录锁住,防止其他事务读写这个记录)
12.使用动态类:可以不写具体的POJO类,直接在XML文件中用<entity-name>写出与表对应的类名就可以了,它是由xml解析器生成动态类。如:
<class entity-name="com.bean.BasicCar" table="basiccar">
......(属性映射)
</class>
13.连接查询
HQL进行连接查询:from 表名 c inner join c.salesname //通过salesname连接
Criteria进行连接查询:
Criteria crit=session.createCriteria(Salesman.class)
crit.add(Restrictions.like("salesname","F%"))
Criteria crit2=crit.createCriteria("carorders")//关联到Salesman的carorders集合属性
crit2.add(Restrictions.like("carname","c%"))
List result=crit2.list();
另外,这两种查询方式都支持抓取模式,使用fetch与不使用的区别在于返回的对象不同:
from Person p inner join p.basiccar(POJO类Person中通过basiccar属性关联到Basiccar对象),返回的是两个Object[0]=Person对象,Object[1]=Basiccar对象
from Person p inner join fetch p.basiccar。则返回的是主类Person对象,如果需要得到关联的Basiccar对象,调用getBasiccar()方法
14.检索策略:
什么时候载入:1.类级别(立即检索,代理检索,非代理检索,属性延迟检索)2.关联级别(延迟集合检索,进一步延迟集合检索)
如何检索:(主要针对存在关联关系的对象的检索方式)
 连接检索(采用outer join的select来进行关联查询,即连接查询)
 查询检索(先检索主对象,根据主对象连接关联对象的外键来作为检索关联对象的条件,然后检索出关联对象)
 子查询检索(另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合,与查询检索不同的是这里检索查询到的所有实体的关联集合而前者只检索载入实体的关联集合)
 批量检索(对查询抓取的优化方案,通过指定一个主键或外键列表,使用单条的select语句获取一批对象实例或集合)
15.二级缓存:
结构:包括缓存并发控制策略和查询缓存及缓存提供者,
选择二级缓存策略:
1.读策略(只读不修改,最简单性能最高,多个应用程序同时并发读取缓存中的数据)
2.读写策略(read-write)
3.不严格读写策略(nonstrict-read-write 不常用)
4.事务策略(transactional 隔离级别最高,隔离级别越高,并发性能就越低)
使用二级缓存:
可以对类对象或者集合使用二级缓存,要显示地对使用缓存对象在映射文件中设置并发控制策略,即设置其读写策略。不同的读写策略会导致不同的隔离级别。
1.配置文件中:
<hibernate-mapping>
<session-factory>
....
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
</property>//设置缓存提供者
<property name="hibernate.cache.use_query_cache">true</property>//查询缓存时用到
.....
2.映射文件中:
<class name="eg.Immutable" mutable="false">
<cache usage="read-only" //必须
region="RegionName"  ///可选,默认是使用类和集合名字。指名对象存放的二级缓存区域
include="all|not lazy"/>//可选,默认是All,说明映射文件中设置lazy="true"的持久化类,当使用延迟载入策略时,其属性不会被缓存
....
</class>
默认分类 | 阅读 1024 次
文章评论,共0条
游客请输入验证码
最新评论