Hibernate框架总结

目  录

一、 Hibernate的概述 1

1.1 Hibernate框架的作用 1

1.2 Hibernate访问数据库的优点 1

1.3 JDBC访问数据库的缺点 1

1.4 Hibernate的设计思想 1

二、 Hibernate的基本使用 2

2.1 Hibernate的主要结构 2

2.2 Hibernate主要的API 2

2.3 Hibernate使用步骤 2

2.4 HQL语句(简要介绍) 6

扫描二维码关注公众号,回复: 2229868 查看本文章

三、 数据映射类型 7

3.1映射类型的作用 7

3.2 type映射类型的两种写法 7

四、 Hibernate主键生成方式 8

4.1五种生成方式 8

五、 Hibernate基本特性 9

5.1对象持久性 9

5.2处于持久状态的对象具有的特点 9

5.3三种状态下的对象的转换 9

5.4批量操作:注意及时清除缓存 9

5.5案例:三种状态下的对象使用 10

5.6一级缓存机制(默认开启) 10

5.7一级缓存的好处 10

5.8管理一级缓存的方法 10

5.9延迟加载机制 11

5.10具有延迟加载机制的操作 11

5.11常犯的错误 12

5.12延迟加载的原理 12

5.13 Session的get和load方法的区别 12

5.14延迟加载的好处 12

5.15案例:测试延迟加载 12

5.16案例:重构NetCTOSS资费管理模块 13

5.17 Java Web程序中如何用延迟加载操作(OpenSessionInView) 15

六、 关联映射 18

6.1一对多关系one-to-many 18

6.2多对一关系many-to-one 19

6.3多对多关联映射many-to-many 19

6.4关联操作(查询join fetch/级联cascade) 21

6.5继承关系映射 24

七、 Hibernate查询方法 27

7.1 HQL查询 27

7.2 HQL和SQL的相同点 27

7.3 HQL和SQL的不同点 27

7.4 HQL典型案例 27

7.5 Criteria查询 30

7.6 Native SQL原生SQL查询 31

八、 Hibernate高级特性 32

8.1二级缓存 32

8.2二级缓存开启方法及测试 32

8.3二级缓存管理方法 33

8.4二级缓存的使用环境 33

8.5查询缓存 33

8.6查询缓存开启方法及测试 33

8.7查询缓存的使用环境 33

九、 Hibernate锁机制 34

9.1悲观锁 34

9.2悲观锁的实现原理 34

9.3悲观锁使用步骤及测试 34

9.4乐观锁 35

9.5乐观锁的实现原理 35

9.6乐观锁使用步骤及测试 35

十、 其他注意事项 36

10.1源码服务器管理工具 36

10.2利用MyEclipse根据数据表自动生成实体类、hbm.xml 36

10.3根据实体类和hbm.xml生成数据表 37

10.4 Hibernate中分页查询使用join fatch的缺点 37



一、Hibernate的概述

1.1 Hibernate框架的作用

Hibernate框架是一个数据访问框架(也叫持久层框架,可将实体对象变成持久对象,详见第5章)。通过Hibernate框架可以对数据库进行增删改查操作,为业务层构建一个持久层。可以使用它替代以前的JDBC访问数据。

1.2 Hibernate访问数据库的优点

1)简单,可以简化数据库操作代码。

2)Hibernate可以自动生成SQL,可以将ResultSet中的记录和实体类自动的映射(转化)。

3)Hibernate不和数据库关联,是一种通用的数据库框架(支持30多种数据库),可以方便数据库移植。任何数据库都可以执行它的API。因为Hibernate的API中是不涉及SQL语句的,它会根据Hibernate的配置文件,自动生成相应数据库的SQL语句。

1.3 JDBC访问数据库的缺点

1)需要编写大量的复杂的SQL语句、表字段多时SQL也繁琐、设置各个问号值。

2)需要编写实体对象和记录之间的代码,较为繁琐。

3)数据库移植时需要修改大量的SQL语句。

1.4 Hibernate的设计思想

Hibernate是基于ORM(Object Relation Mapping)思想设计的,称为对象关系映射。负责Java对象和数据库表数据之间的映射。

Hibernate是一款主流的ORM工具,还有其他很多ORM工具,如:MyBatis(以前叫iBatis)、JPA。Hibernate功能比MyBatis强大些,属于全自动类型,MyBatis属于半自动。但全自动会有些不可控因素,因此有些公司会用MyBatis。

ORM工具在完成Java对象和数据库之间的映射后:

1)在查询时,直接利用工具取出“对象”(不论是查询一条记录还是多条记录,取出的都是一个个对象,我们不用再去转化实体了)。

2)在增删改操作时,直接利用工具将“对象”更新到数据库表中(我们不用再去把对象转成数据了)。

3)中间的SQL+JDBC细节,都被封装在了工具底层,不需要程序员参与。

u 注意事项:

v Java程序想访问数据库,只能通过JDBC的方式,而Hibernate框架也就是基于ORM思想对JDBC的封装。

v Hibernate是以“对象”为单位进行数据库的操作。

二、Hibernate的基本使用

2.1 Hibernate的主要结构

1)hibernate.cfg.xml(仅1个):Hibernate的主配置文件,主要定义数据连接参数和框架设置参数。

u 注意事项:就是个xml文件,只是名字比较奇葩!

2)Entity实体类(n个,一个表一个):主要用于封装数据库数据。

3)hbm.xml映射文件(n个):主要描述实体类和数据表之间的映射信息。描述表与类,字段与属性的对应关系。

u 注意事项:hbm.xml是个后缀,如:命名可写Cost.hbm.xml。

2.2 Hibernate主要的API

1)Configuration:用于加载hibernate.cfg.xml配置信息。用于创建SessionFactory。

2)SessionFactory:存储了hbm.xml中描述的信息,内置了一些预编译的SQL,可以创建Session对象。

3)Session:负责对数据表执行增删改查操作。表示Java程序与数据库的一次连接会话,是对以前的Connection对象的封装。和JSP中的session不是一回事,就是名字一样而已。

4)Query:负责对数据表执行特殊查询操作。

5)Transaction:负责Hibernate操作的事务管理。默认情况下Hibernate事务关闭了自动提交功能,需要显式的追加事务管理(如调用Transaction对象中的commit();提交事务)!

u 注意事项:

v 这些API都是在Hibernate包下的,导包别导错!

v 第一次访问数据库比较慢,比较耗资源,因为加载的信息多。

2.3 Hibernate使用步骤

step1:建立数据库表。

step2:建立Java工程(Web工程也可),引入Hibernate开发包和数据库驱动包。必须引入的包:hibernate3.jar、cglib.jar、dom4j.jar、commons-collections.jar、commons-logging.jar…等

step3:添加hibernate.cfg.xml配置文件,文件内容如下:

<?xml version='1.0' encoding='UTF-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

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

<hibernate-configuration>

<session-factory>

<property name="dialect"><!-- 指定方言,决定Hibernate生成哪种SQL -->

org.hibernate.dialect.OracleDialect<!-- 不知道数据库版本就写OracleDialect -->

</property><!-- 可在hibernate3.jar中org.hibernate.dialect包下查看名字 -->

<property name="connection.url">

jdbc:oracle:thin:@localhost:1521:dbchang

</property>

<property name="connection.username">system</property>

<property name="connection.password">chang</property>

<property name="connection.driver_class">

oracle.jdbc.driver.OracleDriver

</property>

<!-- 框架参数,将hibernate底层执行的SQL语句从控制台显示 -->

<property name="show_sql">true</property>

<!-- 格式化显示的SQL -->

 <property name="format_sql">true</property>

<!-- 指定映射描述文件 -->

<mapping resource="org/tarena/entity/Cost.hbm.xml" />

</session-factory>

</hibernate-configuration>

u 注意事项:应该放在源文件的src目录下,默认为hibernate.cfg.xml。文件内容是Hibernate工作时必须用到的基础信息。

step4:编写Entity实体类(也叫POJO类),例如:资费实体类Cost

  private Integer id; //资费 ID      private String feeName; //资费名称

    private Integer baseDuration; //基本时长  private Float baseCost; //基本定费

    private Float unitCost; //单位费用      private String status; //0:开通;1:暂停;

    private String descr; //资费信息说明      private Date createTime; //创建日期

    private Date startTime; //启用日期      private String costType; //资费类型

……getter/setter方法

u 注意事项:POJO类表示普通类(Plain Ordinary Old Object),没有格式的类,只有属性和对应的getter/setter方法,而没有任何业务逻辑方法的类。这种类最多再加入equals()、hashCode()、toString()等重写父类Object的方法。不承担任何实现业务逻辑的责任。

step5:编写hbm.xml映射(文件)描述信息:映射文件用于指明POJO类和表之间的映射关系(xx属性对应xx字段),一个类对应一个映射文件。例如:Cost.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">

<!-- 定义COST_CHANG表和Cost类型之间的映射信息   -->

<hibernate-mapping><!-- <hibernate-mapping package="包名写这也行"> -->

<!-- name:包名.类名,指定是哪个类;table:数据库中哪个表;catalog:对Oracle而言为哪个数据库,对MySQl而言为某个用户(MySQl是在用户下建表,Oracle是在库中建表),

不写也行(若用工具则会自动生成)。例如,select * from cost_chang则会在hibernate.cfg配置文件中定义的库(或用户)下去找表。若写了则为select * from system.cost_chang  -->

    <class name="org.tarena.entity.Cost" table="COST_CHANG" catalog="system">

     <!--  <id></id>表明此为主键列,且必须写否则xml报错,主键映射 -->

        <id name="id" type="java.lang.Integer">

            <column name="ID" /><!-- 或双标签<column name="ID"></column> -->

   <!-- 指定主键值生成方式,采用序列方式生成主键,仅对添加操作有效-->

        <generator class="sequence">

           <param name="sequence">COST_SEQ_CHANG</param> <!--指定序列名-->

            </generator>

        </id>

        <property name="name" type="java.lang.String"><!-- 以下为非主键映射 -->

            <column name="NAME" /><!--可有length、not-null属性,如:length="20" -->

        </property>

        <property name="baseDuration" type="java.lang.Integer"><!-- 映射顺序没关系 -->

            <column name="BASE_DURATION" />

        </property>

        <property name="baseCost" type="java.lang.Float"><!-- 类型要和实体定义的相同 -->

            <column name="BASE_COST" />

        </property>

        <property name="startTime" type="java.sql.Date"><!--列名写错则报错读不到实体-->

            <column name="STARTIME" /><!--junit测试右键点Copy Trace查看错误列-->

        </property>

    <!--也可写成<property name=" " type=" " column=" "></property> ,主键列同理!-->

    …………其他省略…………

    </class>

</hibernate-mapping>

u 注意事项:

v 映射文件默认与POJO类放在一起;命名规则为:类名.hbm.xml。

v hbm.xml中已写出的属性与字段的映射要一一对应,若表中没有某个字段,却写了映射关系,则报错:找不到实体类。

step6:利用Hibernate API实现DAO

1)新建HibernateUtil类,用于封装创建Session的方法。如下:每个用户会对应一个Session,但是SessionFactory是共享的。

public class HibernateUtil {

private static SessionFactory sf;

static{//不用每次都加载配置信息,所以放static块中,否则每次都加载会耗费资源

Configuration conf=new Configuration();//加载主配置hibernate.cfg.xml

conf.configure("/hibernate.cfg.xml");

sf=conf.buildSessionFactory();//获取SessionFactory }

public static Session getSession(){//获取Session

Session session =sf.openSession(); return session; }     }

2)新建CostDAO接口

    public Cost findById(int id); public void save(Cost cost);

public void delete(int id); public void update(Cost cost);

  public List<Cost> findAll();

3)新建CostDAOImpl类,用于实现CostDAO接口

public class CostDAOImpl implements CostDAO {

private Session session;

public CostDAOImpl () {//不想老写获得session的方法,就写在构造器中

session=HibernateUtil.getSession(); }

/** get方法执行查询,按主键当条件查询,如何判断是主键,是根据写的描述文件来定,get方法就是findById,就是按主键去查,需指定:操作哪个类和id(主键)条件值即可,其他条件查询做不了 */

  public Cost findById(int id) {

//Session session=HibernateUtil.getSession();

Cost cost=(Cost)session.get(Cost.class,id); session.close();  return cost;   }

/** save方法执行增加操作,注意1:获取事务并开启,增删改要注意,查询可以不管事务,因为没对数据库进行修改;注意2:主键值根据hbm.xml中的<generator>定义生成,执行后,会先获取序列值,再去做insert操作。

即先:select COST_SEQ_CHANG.nextval from dual; 然后:insert into ……  */

public void save(Cost cost) {

//Session session=HibernateUtil.getSession();

Transaction tx=session.beginTransaction();//打开事务 session.save(cost);

tx.commit();//提交事务 session.close();//释放   }

/** delete方法执行删除操作,由于Hibernate以“对象”为单位进行数据库操作,所以这里要传进去一个对象,虽然是个对象,但还是按主键做条件删除,只要把主键值设置上就行,其他非主键值不用管。也可先通过id查再删 */

  public void delete(int id) {

//Session session=HibernateUtil.getSession();

Transaction tx=session.beginTransaction(); Cost cost=new Cost();

cost.setId(id);   session.delete(cost);   tx.commit();   session.close();   }

/** update方法执行修改操作, */

public void update(Cost cost) {

//Session session=HibernateUtil.getSession();

Transaction tx=session.beginTransaction();

session.update(cost);//将cost对象更新到数据库 tx.commit();

session.close();       }

/** 特殊查询,SQL语句:String sql="select * from COST_CHANG"; 

HQL语句:String hql="from Cost"; (Hibernate Query Language)是面向对象的查询语句。

from后写映射的类名,它是Hibernate中特有的查询语句,根据映射的类去查询。 */

public List<Cost> findAll() {

//Session session=HibernateUtil.getSession();

String hql="from Cost";//HQL语句

Query query=session.createQuery(hql);

List<Cost> list=query.list();//执行查询,返回List集合

session.close(); return list;    }   }

4)新建TestCostDAO类,使用junit测试

  @Test

public void testFindById(){//当get方法没有记录时,返回null

CostDAO costDao = new CostDAOImpl();   Cost cost = costDao.findById(1);

System.out.println(cost.getName());System.out.println(cost.getBaseDuration());

System.out.println(cost.getBaseCost()); System.out.println(cost.getUnitCost());

System.out.println(cost.getDescr()); }

@Test

public void testSave(){//id主键列由Hibernate管理,这里不用设置

Cost cost=new Cost(); cost.setName("2013计时");

cost.setUnitCost(0.8f); cost.setDescr("2013-08-09计时,0.8元/小时。");

cost.setStatus("0"); cost.setCreaTime(new Date(System.currentTimeMillis()));

CostDAO costDao = new CostDAOImpl(); costDao.save(cost); }

@Test

public void testUpdate(){//开通某个资费,把状态由0变为1

CostDAO costDAO=new CostDAOImpl();

/** 注意事项:更新部分字段,不能和实现类中的删除那样,做一个对象出来!否则没设置的字段将被改为空! 即不能:Cost cost=new Cost();  cost.setId(90);  

   cost.setStatus("1");   cost.setStartTime(new Date(System.currentTimeMillis()));   */

Cost cost=costDAO.findById(90);//只能先通过id找到带有所有值的对象

cost.setStatus("1");//然后再对部分字段进行更新,才能避免把其他字段更新为空

cost.setStartTime(new Date(System.currentTimeMillis()));

costDAO.update(cost); }

@Test

public void testDelete(){

CostDAO costDAO=new CostDAOImpl();   costDAO.delete(90); }

@Test

public void testFindAll(){

CostDAO costDAO=new CostDAOImpl(); List<Cost> list=costDAO.findAll();

for(Cost c:list){ System.out.println(c.getName()); } }

2.4 HQL语句(简要介绍)

简要介绍见2.3节中step6中的3)特殊查询(本页最上)。详细介绍见第七章。



七、Hibernate查询方法

7.1 HQL查询

Hibernate Query Language简称HQL。

HQL语句是面向对象的一种查询语言。HQL针对Hibernate映射之后的实体类型和属性进行查询(若出现表名和字段名则为错误的!)。

7.2 HQL和SQL的相同点

1)都支持select、from、where、group by、order by、having子句……。

2)都支持+、-、*、/、>、<、>=、<=、<>等运算符和表达式。

3)都支持in、not in、between...and、is null、is not null、like、or等过滤条件。

4)都支持分组统计函数count、max、min、avg、sum。

7.3 HQL和SQL的不同点

1)HQL区分大小写(除了关键字外)。

2)HQL使用类名和属性名,不能使用表名和字段名。

3)HQL不能使用select * 写法。

4)HQL不能使用join...on中的on子句。

5)HQL不能使用数据库端的函数。

u 注意事项:HQL中select count(*)可以使用。

7.4 HQL典型案例

1)案例1:一个主键的情况

step1:借用之前的Account实体、Account.hbm.xml、hibernate.cfg.xml

step2:新建TestHQL类,用于测试HQL。查询操作可不写事务控制语句。

    ………………………………………………………………………………


八、Hibernate高级特性

8.1二级缓存

二级缓存是SessionFactory级别的。由SessionFactory对象负责管理。通过Factory创建的Session都可以访问二级缓存。

二级缓存默认是关闭的。如果二级缓存打开,则先去一级缓存找对象,找不到则去二级缓存,还找不到则访问数据库。

u 注意事项:

v 一级缓存和二级缓存都是存“单个对象”的!不能存集合、数组!

v Hibernate本身没有提供组件,需要第三方提供的组件。有很多第三方组件,这里用ehcache-1.2.3.jar。

8.2二级缓存开启方法及测试

step1:引入ehcache-1.2.3.jar,在src下添加ehcache.xml配置文件

ehcache.xml配置文件说明:

<diskStore path="java.io.tmpdir"/><!-- 磁盘存储路径 -->

<defaultCache <!--默认参数设置-->

        maxElementsInMemory="2000" <!--存储的最大对象个数-->

        eternal="false"   <!--缓存对象的有效期,true为永久存在-->

        timeToIdleSeconds="20"  <!--空闲时间:某个对象空闲超过20秒则清出二级缓存-->

        timeToLiveSeconds="120"  <!--某个对象生存了120秒,则自动清出二级缓存-->

        overflowToDisk="true"<!--当存储个数超出设置的最大值时,把超出对象存到磁盘-->

  />

step2:在hibernate.cfg.xml中添加开启配置参数,指定缓存类型

………………………………………………………


………………………………………

…………………………

………………

…………

……

猜你喜欢

转载自blog.csdn.net/too_cruel/article/details/17102213