Hibernate总结

来源:互联网 发布:c语言fopen文件路径 编辑:程序博客网 时间:2024/06/06 01:48

1.Hibernate框架作用,优点

(1)、什么是Hibernate

       Hibernate是一个数据访问框架(持久层框架),在项目中利用Hibernate框

       架可以实现对数据库的增删改查操作,为业务层构建一个持久层。

(2)、Hibernate框架主要用于对数据库的操作。

    使用该框架可以简化数据操作代码,程序员可以将更多地精力放在业务

       编写上。Hibernate经典总结

     Hibernate本质上是对JDBC技术的封装。

    Hibernate和JDBC的关系类似于JQuery和JavaScript的关系。

   (3)、原有JDBC方式访问数据库,有以下几点不足:

   a.需要编写大量复杂的SQL语句

   b.需要做大量的对象和记录的转换

       c.数据库移植时,需要修改SQL语句。(非标准SQL语句:如分页语句,

使用数据库函数的语句)

使用Hibernate框架后,可以解决上述问题

   (4)、Hibernate框架的优点:

a.无需编写大量复杂的SQL语句

b.程序中的实体对象和数据库中的数据实现自动映射转换

c.方便数据库的移植

2.Hibernate设计原理

   Hibernate框架是一款ORM工具。基于ORM设计思想开发出来的。

   ORMObject--Relation--Mapping对象关系映射

         对象指的就是Java的实体对象;

         关系指的是关系型数据库。(Oracle、DB2、MySql、SqlServer)

         ORM的主要思想就是将程序中的对象和数据库中的数据实现自动映射

         转换。利用ORM工具,在查询时,可以自动将记录封装成Java对象返

         回。在更新、插入操作时,可以将对象自动写入数据表。对于中间的

         SQL+JDBC操作细节,完全封装在工具底层

  基于ORM思想设计的框架有很多,例如Hibernate,iBATIS,JPA等.

*3.Hibernate框架主要结构

    Hibernate主要由以下几部分构成:

   a.Java实体类(1-n个)

     与数据表对应,用于封装数据表的一行记录。

   b.hibernate.cfg.xml(1个)

      Hibernate主配置文件,里面主要定义连接数据库的参数、框架参数等。

<hibernate-configuration>

<session-factory>

<!-- 方言设置,hibernate根据方言类型自动生成相应数据库的sql语句 

<property name="dialect">org.hibernate.dialect.OracleDialect</property>

<property name="connection.url">jdbc:oracle:thin:@localhost:1522:xe

        </property>

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

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

<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver

</property>

<!-- 将sql语句从控制台打印出来 -->

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

<property name="format_sql">true</property><!-- 格式化sql语句 -->

<!-- 加载映射描述文件 -->

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

</session-factory>

</hibernate-configuration>

   c. 文件名.hbm.xml1-n个)

      Hibernate映射描述文件,里面定义了实体类和数据库之间的对应关

      系。例如定义了java实体类和数据表,属性和表字段之间的对应关系。

<hibernate-mapping>

<!-- Hibernate映射描述文件定义了实体类和数据库之间的对应关 系。例

         如定义了java类和数据表, 属性和表字段之间的对应关系。

 -->

<class name="entity.Cost" table="Cost">

<!-- 定义主键映射 -->Hibernate经典总结

<id name="id" column="ID" type="integer">

            <!-- 也可写成type="java.lang.Integer"-->

<!-- 指定主键的生成方式为序列方式 -->

<generator class="sequence">

<param name="sequence">cost_seq</param>

</generator>

</id>

<!-- 非主键映射 -->

<property name="name" column="NAME" type="string">

        </property>

<property name="baseDuration" column="BASE_DURATION" 

           type="integer"></property>

<property name="baseCost" column="BASE_COST" 

           type="double"></property>

<property name="unitCost" column="UNIT_COST" 

           type="double"></property>

</class>

</hibernate-mapping>

   d.Hibernate API

   在使用时,需要使用Hibernate提供的API, 它们将SQL+JDBC操作细节

        封装起来了。

*4.Hibernate主要API

 Configuration:用于加载主配置文件(hibernte.cfg.xml)和映射文件。

 SessionFactory:用于创建Session对象。

 Session:将原Connection对象进行了封装,代表Hibernate与数据库之

       间的一次连接,负责执行增删改查操作。

       save(),update(),delete(),load(),get()方法.

 Transaction:用于进行事务管理。

             注意:由于关闭了JDBC中的自动提交功能

                   (setAutoCommit(false)),所以使用时,必须显示地

                   执行commit 操作。

Query:负责执行各种查询。

 

注:此处的Session区别于JavaWeb中的Session。前者表示Hibernate与数据库之间的

    连接,后者表示客户端与服务器端的一次会话。

*5.Hibernate使用步骤

   a.创建工程,引入hibernate和驱动开发包

   b.在src下追加hibernate.cfg.xml主配置

   c.根据数据表创建Entity实体类

   d.编写实体类和数据表的映射文件xxx.hbm.xml

   e.利用Hibernate API实现增删改查操作

6.Hibernate中的增改删查操作:

/**

 * 测试添加操作

 */

public static void testSave() {

Cost cost = new Cost();

// cost.setId();//由hbm.xml定义的序列负责

cost.setName("400元套餐");

cost.setBaseDuration(200);

cost.setBaseCost(400.0);

cost.setUnitCost(2.0);

cost.setCostType("1");

cost.setDescr("400特惠大套餐");

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

cost.setStatus("0");

Session session = HibernateUtil.getSession();

// 开启事务

Transaction tx = session.beginTransaction();

session.save(cost);// 保存

tx.commit();// 提交事务

session.close();// 是否session

}

 Hibernate经典总结

/**

 * 测试更新操作

 */

public static void testUpdate() {

// 开启资费处理,更新status=1,startTime时间

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

// 按ID=189主键做条件查询

Cost cost = (Cost) session.load(Cost.class, 189);

cost.setStatus("1");// 设置为暂停状态

cost.setStartTime(new Date(System.currentTimeMillis()));// 设置为开

                    启时间

session.update(cost);

tx.commit();

session.close();

}

 

/**

 * 测试删除操作

 */

public static void testDelete() {

Cost cost = new Cost();

cost.setId(189);

// Cost cost = (Cost)session.get(Cost.class, 189);

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.delete(cost);// 执行一个delete语句,按ID做条件删除

tx.commit();

session.close();

}

 

/**

 * 测试查询操作

 */

public static void testFindAll() {

Session session = HibernateUtil.getSession();

// 利用Query执行一个HQL查询语句

Query query = session.createQuery("from Cost");

List<Cost> list = query.list();

for (Cost c : list) {

System.out.println(c.getId() + "  " + c.getName() + "  " + 

              c.getBaseDuration());

}

session.close();

}

 

7、Hibernate封装JDBC+SQL语句过程:

Java对象—>Cost—>SQL+JDBC—>DB

insert into cost (NAME, BASE_DURATION, BASE_COST, UNIT_COST, STATUS.) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

//java反射技术

Class costClass = Cost.class;

控制台输出的SQL语句:

Hibernate: 

    select cost_seq.nextval from dual

Hibernate: 

    insert into Cost

        (NAME, BASE_DURATION, BASE_COST, UNIT_COST, STATUS........) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

 

8、注意:

  (1).Hibernate在底层对JDBC语句进行了封装,用rs.getObject(“some”)方式

获取数据。

eg:"BASE_DURATION"为数据库中表的字段名,类型为Number(5)。如果在表中该字段有null值,那么对于如下两种获取方式将出现不同的结果:

对于rs.getObject("BASE_DURATION")方式,得到的值为null

对于rs.getInt("BASE_DURATION")方式,得到的值为0

所以在Hibernate中,根据数据表创建的实体类中不能有基本数据类型,如果为基本类型,rs.getObject(“some”)得到的null值赋给基本类型会发生异常,所以要定义成相应的包装类。

  (2).创建Session对象耗费时间比较多,session对象里面绑定了大量的预编译

     的SQL语句。所以不能每次使用都新建一个对象。

9、Hibernate数据映射类型:

  在hbm.xml中,描述属性和字段之间映射的时候,可以使用type属性指定数据 

  映射类型。

  type属性可以指定Java类型和Hibernate类型。主要作用是指定属性值和字段

  值之间转换时(即在底层执行rs.getXXX(),stmt.setXXX()时XXX的类型),采用的

  转换类型。(建议采用Hibernate映射类型)

  Hibernate数据映射类型主要有以下几种:

  a.整数类型

     byte,short,integer,long

  b.浮点数类型

     float,double

  c.字符串类型

     string

  d.日期时间类型

     date,time,timestamp

其中:

date只取年月日,

time只取小时分钟秒,

timestamp取完整的时间年月日小时分钟秒

  e.boolean类型

     该类型可以实现boolean属性和字符之间的转换。

     yes_no:将true|false转换成Y|N

    true_false:将true|false转换成T|F

    底层实现方法:

stmt.setChar(cost.getBoolean().toString())  

cost.setBoolean(Boolean.parseBoolean(rs.getChar("***")))

  f.其他big_decimal,big_integer,clob,blob

     

-----------案例-----------------

create table t_foo(

  t_id number primary key,

  t_name varchar2(50),

  t_salary number(8,2),

  t_marry char(1),

  t_hiredate date,

  t_last_login_time date

)

-------------mysql----------------

create table t_foo(

  t_id int auto_increment primary key,

  t_name varchar(50),

  t_salary double,

  t_marry char(1),

  t_hiredate date,

  t_last_login_time timestamp

) default charset=utf8;

10.Hibernate主键生成方式

  在hbm.xml中,可以为主键指定生成方式。具体如下:

  *a. sequence :采用指定序列生成,适用于Oracle数据库。使用格式

<generator class="sequence">

    <param name="sequence">foo_seq</param>

</generator>

  

 *b.identity : 采用数据库自增长机制生成。适用于MySQL,SQLServer,DB2数

              据库。

   <generator class="identity">

   </generator>

   

 *c.native : 由hibernate决定,hibernate会根据配置文件hibernate.cfg.xml中

             方言<property name="dialect">决定,如果方言是Mysql,相当

             于identity,如果方言是Oracle,相当于是sequence

 

   <generator class="native">

   </generator>

  

 *d.increment : 首先获取最大主键值,然后加1,再执行插入操作。适用于各

                 种数据库。

 先执行select max(id) from t_foo;

 再执行insert into...

    <generator class="increment">

   </generator>

  

 e.assigned : Hibernate忽略主键生成,不负责管理。需要程序员在程序中指定

             主键值,不常用

   <generator class="assigned">

   </generator>

 

  f.其他

 uuid:采用UUID算法生成一个字符串主键值

 hilo:采用高地位算法生成一个数值主键值

   

 

 

11.Hibernate框架基本特性

   1)一级缓存(Session级别缓存,默认开启)

    a.每次创建一个Session对象,会为这个Session对象提供一个缓存区,用于

      缓存该Session查询出来的单个对象。当使用该Session再次查询同一个对

      象时,从缓存取出,避免对数据库的查询。

   *b.一级缓存区管理的方法:

    session.evict(obj):将obj从一级缓存移除

    session.clear():清除一级缓存所有对象

    c.一级缓存好处

     当利用同一个Session对象多次查询同一个数据对象时,仅第一次从数据库

     查询,后续几次从缓存获取。

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

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

for(int i=1;i<100000;i++){ 

     User user = new User();

     //......

     session.save(user);

     //20条调用一次flush

     if(i%20==0){

        session.flush();//同步到数据库

        session.clear();//清空缓存

      }

}

tx.commit;

HibernateUtil.close();

对于上面列子,如果不及时清理缓存,将会发生内存溢出异常。

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

每个session只能访问自己的缓存区,在session1中缓存了一个对象,

   在session2中查询相同的对象,仍然要从数据库中重写查找

/**

 * 使用两个不同的Session查询同一个对象 结果:去数据库查询2两次

 */

public static void test3() {

Session session = HibernateUtil.getSession();

// 第一次查询

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

session.close();

session = HibernateUtil.getSession();

// 第二次查询

Foo foo1 = (Foo) session.get(Foo.class, 1);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

/**

 * 同一个session两次查询不同对象 结果:去DB查两次

 */

public static void test2() {

Session session = HibernateUtil.getSession();

// 第一次查询

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

// 第二次查询

Foo foo1 = (Foo) session.get(Foo.class, 22);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

/**

 * 同一个session两次查询相同对象 结果:去DB查一次

 */

public static void test1() {

Session session = HibernateUtil.getSession();

// 第一次查询

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

// session.evict(foo);//从一级缓存移除foo对象

session.clear();

// 第二次查询

Foo foo1 = (Foo) session.get(Foo.class, 1);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

 

   2)对象持久性

     Hibernate框架用于实现对数据库的操作,为应用程序构建一个持久层。

    (由持久对象组成)

     Hibernate使用中,实体对象可以具有以下3种状态。

 

     a.临时状态(暂时态,Transient

        采用new方式构建的对象的状态是暂时的,如果没有跟数据库表相关联

        的行为,只要应用程序不再引用这些对象,他们的状态将会丢失,并由

        垃圾回收机制回收。

     *b.持久状态Persistent

如果内存中的对象和数据库的记录有对应关系,即和session对象相关,

则此对象处于Persistent状态,在当事务提交时它们的状态和数据库进行

同步。

采用Session对象查询出来的,受Session对象管理的对象。例如调

load,get,save,update方法后的对象。

        处于持久性的对象特点如下:

      --当事务提交(commit())时,或执行session.flush()方法时,对象的数

           据状态可以更新到数据库。

           commit()前会自动调用session.flush(),

 commit()=session.flush()+commit()

         --对象不能被垃圾回收器回收

        --对象存储在一级缓存中,由Session负责管理

     c.游离/脱管状态Detached

Session关闭(session.close())之后,持久化对象就变为detached对象。表

示这个对象不能再与数据库保持同步,它们不再受Hibernate(确切地说

是Session)管理。另外,调用了session.evict(),session.clear()方法后,

一级缓存中的对象就变成了游离状态。

session.close()前会自动调用session.clear(),close()=clear()+close()。

 

public static void test1() {

Foo foo = new Foo();

foo.setName("scott");

foo.setSalary(2000);

foo.setMarry(true);

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.save(foo);// foo变为持久对象

foo.setName("tom");// 修改持久对象name属性

foo.setSalary(3000);// 修改持久对象salary属性

// session.flush();//将缓存中对象数据与数据库同步

tx.commit();// 默认调用session.flush,然后提交事务

session.close();

      }

  *3)延迟加载(默认启用)

     Hibernate提供一些方法,利用这些方法返回的对象,并没有立刻加载数据

    库的数据。而是在调用对象的getXxx()方法时才触发数据库查询,加载数据

    记录。

其一,如果通过session查询某对象,session将先到缓存中查找是否有查询

的对象,找到则直接取出,否则才查询数据库。

其二,session需要负责实时维护在缓存中的数据,保护缓存中的数据与数据

库中数据的一致性,一旦用户对缓存中的数据做了修改,session负责将数据

更新到数据库中(前提是调用了commit()或flush()方法)。

*a)延迟加载机制的基本原理:

  当访问实体对象时,并不是立即到数据库中查找。而是在真正要使用实体

 对象的时候,才去数据库查询数据。有部分方法具备这种功能,比如

 session.load(),query.iterator()。

注意:这些方法返回的对象,只有id属性有值,其他属性数据在使用

时候(调用getXxx()方法时)才去获取。

b)延迟加载优点:

使用延迟加载,可以降低用户并发量,减少服务器资源占用。

*c)哪些方法具有延迟加载

     session.load()

     query.iterator()

     关联属性

   *d)使用延迟加载方法时,避免出现下面异常LazyInitializationException: 

       could not initialize proxy - no Session

     原因:session关闭过早

  session.close()要放在查询之后,否则出现异常

   *e)get()和load()区别

      相同点:按照主键ID做条件查询某个对象

      不同点如下:

       --load采用了延迟加载机制,get为立刻加载。

       --load如果没有符合记录,会抛出ObjectNotFoundException; 而get方法

         返回的是null,不会出现异常。

         session.load(Cost.class,11235)如果id不存在,则抛出异常

         session.get(Cost.class,11235)为null

       --load返回的是一个动态生成一个类型,get方法返回的是实体类型。

     f)延迟加载实现原理(动态代理技术)

      --采用延迟加载方法后,Hibernate会在底层动态的创建一个新的实体类,

        动态编译成class。比如load方法返回的是动态生成的一个类型(如Cost

        的子类),在这个类里面重写了Cost类的getter方法

  public class Cost$$CGLIB... extends Cost{

      //重写Cost中属性的getter方法

      public String getName(){

           //判断是否有name值,没有查询DB

           return name;

       }

   }

   --Hibernate采用了cglib.jar和asm.jar两个开发包。实现了动态生成新类和编

    译操作。

public static void test3() {

Session session = HibernateUtil.getSession();

Foo foo = (Foo) session.load(Foo.class, 29);

System.out.println(foo.getClass().getName());

 

session.close();

}

 Hibernate经典总结

public static void test2() {

Session session = HibernateUtil.getSession();

// 延迟加载查询

Foo foo = (Foo) session.load(Foo.class, 29);

session.close();// session关闭过早导致异常

System.out.println("-------------");

System.out.println(foo.getName());// 触发查询

System.out.println(foo.getSalary());

}

 

public static void test1() {

Session session = HibernateUtil.getSession();

// 延迟加载查询

Foo foo = (Foo) session.load(Foo.class, 1);

System.out.println("-------------");

System.out.println(foo.getName());// 触发查询

System.out.println(foo.getSalary());

session.close();

}

12、OpenSessionInView与ThreadLocal:

(1)、OpenSessionInView

如果通过Hibernate查询到了一条记录(此时并未执行getter方法,由于

延迟加载原因,此时的记录除了主键并不包含任何数据,即没有真正执行查

询),该记录须在Jsp页面显示(本质上是间接执行了getter方法),那么此时

就需要一种技术实现把Session的关闭延迟到View组件运行完之后。

OpenSessionInView技术可以把Session的关闭延迟到View组件运行完之

  后。

 如果用延迟加载必须使用OpenSessionInView技术,否则在取数据时,

session已经关闭了。

实现OpenSessionInView可以采用的技术如下:

Servlet------过滤器

Struts2------拦截器

Spring--------AOP

(2)、ThreadLocal:使用OpenSessionInView必须满足Session的线程单例。

一个线程分配一个Session,在该线程的方法中可以获得该Session,具体使用ThreadLoad,ThreadLocal是一个Map结构,线程ID作为key,拿要存的对象当做value,这样就可以将要存储的数据和线程绑定。

(3)、如何实现OpenSessionInView

方案一:利用ThreadLocal机制手动实现Session与当前线程绑定

  public class HibernateUtil1 {

private static SessionFactory sf;

private static ThreadLocal<Session> sessionLocal = new 

ThreadLocal<Session>();

static {

// 创建配置对象

Configuration conf = new Configuration();

// 加载指定的hibernate.cfg.xml配置

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

// 获取SessionFactory

sf = conf.buildSessionFactory();

}

public static Session getSession() {

Session session = sessionLocal.get();

if (session == null) {

session = sf.openSession();

sessionLocal.set(session);

}

return session;

}

public static void closeSession() {

Session session = sessionLocal.get();

sessionLocal.set(null);

if (session != null && session.isOpen()) {

session.close();

}

}

   }

public class OpenSessionInViewInterceptor extends AbstractInterceptor {

@Override

public String intercept(ActionInvocation in) throws Exception {

// 开启事务

Session session = HibernateUtil1.getSession();

Transaction tx = session.beginTransaction();

try {

System.out.println("----开启事务---");

in.invoke();// 执行Action,DAO和Result-->JSP

// 提交事务

tx.commit();

System.out.println("----提交事务---");

return null;

catch (Exception ex) {

// 回滚事务

tx.rollback();

System.out.println("----回滚事务---");

throw ex;

finally {

// 关闭session

 HibernateUtil1.closeSession();

System.out.println("----关闭事务---");

}

}

  }

 方案二:利用Hibernate框架封装的机制实现Session与当前线程绑定

public class HibernateUtil2 {

private static SessionFactory sf;

static {

//创建配置对象

Configuration conf = new Configuration();

//加载指定的hibernate.cfg.xml配置

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

//获取SessionFactory

sf = conf.buildSessionFactory();

}

public static Session getSession(){

//返回当前线程绑定的session,需要在hibernate.cfg.xml增加配置

//如果没有的新建一个,然后和线程绑定

//该Session对象在事务结束自动关闭

//该Session对象必须在一个事务中使用

return sf.getCurrentSession();

//创建一个新的Session对象,

//必须手动关闭

//return sf.openSession();

}

}

public class OpenSessionInViewInterceptor extends AbstractInterceptor {

@Override

public String intercept(ActionInvocation in) throws Exception {

// 开启事务

Session session = HibernateUtil1.getSession();

Transaction tx = session.beginTransaction();

try {

System.out.println("----开启事务---");

in.invoke();// 执行Action,DAO和Result-->JSP

// 提交事务

tx.commit();

System.out.println("----提交事务---");

return null;

catch (Exception ex) {

// 回滚事务

tx.rollback();

System.out.println("----回滚事务---");

throw ex;

finally {

// Hibernate会自动关闭session

System.out.println("----关闭事务---");

}

}

}

另外还需要在hibernate.cfg.xml中加入如下配置:

<!-- 支持sf.getCurrentSession()方法,将session与线程绑定 -->

<property name="current_session_context_class">thread</property>

 

:对于sf.openSession()和sf.getCurrentSession(),sf.getCurrentSession()

获得的session对象实现了和ThreaLocal的自动绑定以及在事务结束后的自

动关闭功能。而sf.openSession()则要显示地绑定和关闭,该

sf.getCurrentSession()对象必须在一个事务中使用。

 

=========案例练习==========

1.重构资费列表显示示例

为了支持延迟加载方法,需要在项目中采用OpenSessionInView思想。可以利用Filter和Interceptor技术实现。

*.action-->拦截器前期(无)

-->Action-->DAO-->Result

-->JSP(通过标签和EL获取对象数据)

-->拦截器后期(关闭Session)

-->将HTML结果响应

 

13.Hibernate关系映射的作用

   利用关联映射,Hibernate可以帮助查询加载对象相关的数据,可以简化查询

   代码。

   此外还可以基于关系进行增删改操作。

   Hibernate关系映射分类: 一对一、一对多、多对一、多对多、继承

注:Hibernate关系映射区别于Hibernate数据映射。

14.一对多关系映射

   a.确定两个对象哪个是1方和n方

   b.需求:由1方对象查询n方记录,可以采用一对多关系映射

   c.首先在1方实体类中添加集合属性Set

   d.然后再1方的hbm.xml中定义一对多关系映射的描述

    <set name="关系属性">

        <key column="关联条件的外键字段"/>

        <one-to-many class="关联的另一方类型,即n方类型"/>

    </set>

 

   e.使用时,通过1方对象.关系属性获取n方数据

    eg:测试一对多关系:account--->service

public class TestOneToMany {

/**

 * 测试一对多

 */

public static void main(String[] args) {

test2();

test2();

}

/**

 * 显示帐务帐号信息(关联操作)

 */

public static void test2() {

int id = 1011;

Session session = HibernateUtil.getSession();

Account account = (Account) session.load(Account.class, id);

System.out.println("--------显示帐务帐号基本信息--------");

System.out.println("登录名:" + account.getLoginName());

System.out.println("身份证号:" + account.getIdcardNo());

System.out.println("状态:" + account.getStatus());

System.out.println("创建日期" + account.getCreateDate());

System.out.println("------包含的业务帐号信息-------");

Set<Service> list = account.getServices();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getUnixHost() + " "

+ s.getOsUsername() + "" + s.getStatus());

}

HibernateUtil.closeSession();

}

 

/**

 * 显示帐务帐号信息(单表操作)

        如果用单表操作的话,要手动的编写两次查询语句,而关联操作只需一

         次

 */

public static void test1() {

int id = 1011;

Session session = HibernateUtil.getSession();

Account account = (Account) session.load(Account.class, id);

System.out.println("--------显示帐务帐号基本信息--------");

System.out.println("登录名:" + account.getLoginName());

System.out.println("身份证号:" + account.getIdcardNo());

System.out.println("状态:" + account.getStatus());

System.out.println("创建日期" + account.getCreateDate());

System.out.println("------包含的业务帐号信息-------");

String hql = "from Service where accountId=?";

Query query = session.createQuery(hql);

query.setInteger(0, id);

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getUnixHost() + " "

+ s.getOsUsername() + "" + s.getStatus());

}

HibernateUtil.closeSession();

}

}

<!-- 描述services属性,采用一对多关系加载Service记录 -->

       <set name="services">

        <!-- 指定关联条件,写外键字段 -->

        <key column="ACCOUNT_ID"></key>

        <!-- 指定采用一对多关系, class指定关联的另一方-->

        <one-to-many class="org.tarena.netctoss.entity.Service"/>

 </set>

test2()输出:

--------显示帐务帐号基本信息--------

Hibernate: 

    select

        account0_.ID as ID0_0_,

        account0_.RECOMMENDER_ID as RECOMMEN2_0_0_,

        ......................................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

登录名:dgbf70

身份证号:330902197108270429

状态:1

创建日期2009-03-01

------包含的业务帐号信息-------

Hibernate: 

    select

        services0_.ACCOUNT_ID as ACCOUNT4_1_,

        ...........................................

        services0_.CLOSE_DATE as CLOSE10_1_0_ 

    from

        SERVICE services0_ 

    where

        services0_.ACCOUNT_ID=?

2002 192.168.0.26 huangr2

2004 192.168.0.23 huangr0

2003 192.168.0.20 huangr0

 

test1()输出:

--------显示帐务帐号基本信息--------

Hibernate: 

    select

        account0_.ID as ID0_0_,

        ...........................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

登录名:dgbf70

身份证号:330902197108270429

状态:1

创建日期2009-03-01

------包含的业务帐号信息-------

Hibernate: 

    select

        service0_.ID as ID1_,

        .....................

        service0_.CLOSE_DATE as CLOSE10_1_ 

    from

        SERVICE service0_ 

    where

        accountId=?

2002 192.168.0.26 huangr2

2004 192.168.0.23 huangr0

2003 192.168.0.20 huangr0

 

15.多对一关系映射

   a.需要由n方对象查询1方对象信息

   b.在n方实体类中添加属性,属性类为1方类型

   c.在n方hbm.xml文件中,添加属性的映射描述信息

<many-to-one name="关系属性"

  column="关联条件的外键字段" class="关联的另一方类型,即1方类型"/>

   d.清除n方实体类中外键字段描述信息和属性

   e.使用时,通过n方对象.关联属性获取相关的1方记录信息

测试多对一关系:service-->account

public class TestManyToOne {

/**

 * 测试多对一:

 */

public static void main(String[] args) {

Session session = HibernateUtil.getSession();

Service service = (Service) session.load(Service.class, 2002);

System.out.println("业务帐号:" + service.getId());

System.out.println("业务服务器:" + service.getUnixHost());

System.out.println("业务用户名" + service.getOsUsername());

System.out.println("所属帐务帐号:" + service.getAccount().getId());

System.out.println("开通身份证:" + service.getAccount().getIdcardNo());

System.out.println("真实姓名:" + service.getAccount().getRealName());

HibernateUtil.closeSession();

}

     }

输出:

业务帐号:2002

Hibernate: 

    select

        service0_.ID as ID1_0_,

        service0_.UNIX_HOST as UNIX2_1_0_,

        ........................

        service0_.CLOSE_DATE as CLOSE10_1_0_ 

    from

        SERVICE service0_ 

    where

        service0_.ID=?

业务服务器:192.168.0.26

业务用户名huangr

所属帐务帐号:1011

Hibernate: 

    select

        account0_.ID as ID0_0_,

        account0_.RECOMMENDER_ID as RECOMMEN2_0_0_,

        ........................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

开通身份证:330902197108270429

  真实姓名:huangrong

16.关联操作注意事项

 级联操作也即关联操作。  

 注意区分:关联属性和主对象的关系

 不要忘了在hbm.xml的关联属性定义中添加cascade属性,具体参见下面

        的”*2)级联添加”

 *1)关联(级联)查询

     默认情况下,关联属性数据的加载采用了延迟加载机制,当调用属性getter

    方法才出发查询。在使用时,如果需要将关联属性和主对象一起加载,可以

    通过以下方法改变加载机制

     a.(1)在hbm.xml中为该属性添加lazy="false" (关联属性数据在主对象

        加载时加载)

       为该属性追加fetch="join"

       (2).(指定关联属性加载方式,可以指定为select,join,subselect值)

<many-to-one name="account" column="ACCOUNT_ID"  

lazy="false" fetch="join"  

class="org.tarena.netctoss.entity.Account">

  </many-to-one>

     b.写一个HQL,采用join fetch关键字加载关联属性。

    (注意:a方案会影响所有Service对象操作,不推荐,如果需要一起加载关

     联属性,建议采用b方案)

public class TestJoinFetch {

/**

 * 用join fetch关键字加载关联属性,取消延迟加载

 */

public static void main(String[] args) {

test2();

}

/**

 * 实例化Account对象时一起实例化关联属性services

 */

public static void test2(){

String hql = "from Account a " +

"join fetch a.services " +

"where a.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

Account account = 

(Account)query.uniqueResult();

System.out.println("--------显示帐务帐号基本信息--------");

System.out.println("登录名:"+account.getLoginName());

System.out.println("身份证号:"+account.getIdcardNo());

System.out.println("状态:"+account.getStatus());

System.out.println("创建日期"+account.getCreateDate());

System.out.println("------包含的业务帐号信息-------");

Set<Service> list = account.getServices();

for(Service s : list){

System.out.println(s.getId()+" "

+s.getUnixHost() +" "

+s.getOsUsername() +""

+ s.getStatus());

}

HibernateUtil.closeSession();

}

/**

 * 实例化Service对象时一起实例化关联属性account

 */

public static void test1(){

String hql = "from Service s " +

"join fetch s.account " +

"where s.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 2002);

Service service  = (Service)query.uniqueResult();

System.out.println("业务帐号:"+service.getId());

System.out.println("业务服务器:"+service.getUnixHost());

System.out.println("业务用户名"+service.getOsUsername());

System.out.println("所属帐务帐号:"+service.getAccount().getId());

System.out.println("开通身份

          证:"+service.getAccount().getIdcardNo());

System.out.println("真实姓名:"+service.getAccount().getRealName());

HibernateUtil.closeSession();

}

}

   2)级联添加

     对主对象做添加操作时,关联属性的数据也做相应的添加操作。

     a.需要在hbm.xml的关联属性定义中添加cascade属性,属性值可以为

       none(默认),all,delete,save-update等。

       其中:none为默认,表示不进行级联操作。

            save-update:级联添加或者级联更新。

            delete:级联删除

               all:包括级联添加、删除、更新

     b.在执行session.save()之前,将关联数据对象添加到关联属性中。

<!-- 描述services属性,采用一对多关系加载Service记录 -->

       <set name="services" cascade="all" inverse="true">

/**

 * 级联添加操作 同时向数据库添加一个Account和两个Service记录

 */

public static void testAdd() {

// 创建一个Account

Account account = new Account();

account.setLoginName("tiggy41");

account.setLoginPasswd("123456");

account.setRealName("老虎");

account.setIdcardNo("33033234324393");

account.setTelephone("13234232");

// 创建两个Service

Service service1 = new Service();

service1.setUnixHost("192.168.0.23");

service1.setOsUsername("hooqt1");

service1.setLoginPasswd("111111");

service1.setCostId(1);

service1.setAccount(account);

// --

Service service2 = new Service();

service2.setUnixHost("192.168.0.20");

service2.setOsUsername("hilot");

service2.setLoginPasswd("222222");

service2.setCostId(3);

service2.setAccount(account);

// 将service1和service2给account对象的services赋值

account.getServices().add(service1);

account.getServices().add(service2);

// 添加

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.save(account);

tx.commit();

HibernateUtil.closeSession();

}

   3)级联删除

     对主对象做删除操作时,关联属性的数据也做相应的删除操作。

      a.需要在hbm.xml中的关联属性开启级联删除操作。

     在执行session.delete(obj)操作时,删除的obj对象时利用session查询出

        来的。注意不要使用new方式,因为不具有关联数据

      (级联删除采用n+1个delete清除数据,因此关联数据对象n过多时,不推

       荐使用,而是采用HQL语句进行删除)

Hibernate在做级联删除操作时,比如删除service表中相关5个

accountId=1010相关字段时,其在底层执行的sql语句并不是delete from 

service where accountId=1010 而是5条delete from service where  

id=?,所以。在做批量删除时效率较低

 //批量清除Service关系表记录

delete from Service where account.id=?

Query query = session.createQuery(hql);

     //...设置参数

query.executeUpdate();

 

/**

 * 级联删除操作 删除account记录的同时, 也删除services属性中关联

   *servcie记录关联数据对象n过多时,不推荐使用

 */

public static void testDelete() {

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

Account account = (Account) session.load(Account.class, 2);

session.delete(account);

tx.commit();

HibernateUtil.closeSession();

}

   4)inverse属性

     a.inverse可以控制关系字段值维护的操作由哪一方负责。默认情况下由具

      有关系的对象双方负责。

    

     b.Hibernate在做增删改操作时默认分成对(非关联字段)的增删改和关联

      字段更新两个步骤

     c.Hibernate中对关系的维护默认为有关系的多表共同维护,在一对多关系

     中,通常在多方中设置inverse="true",意思要多方放弃关系维护

      操作,当程序对1方对象做级联操作时,不会再出现update维护关系字段

      的语句。这样可以提高执行效率。

 inverse="true"在添加的时候可以省略Hibernate对关联字段的更新

(account_Id)步骤,这样就可以解除一方解除关系维护操作

eg:

inverse=true时的添加操作:

Hibernate: 

    insert into SERVICE

     (UNIX_HOST, COST_ID, ACCOUNT_ID, OS_USERNAME, LOGIN_PASSWD, STATUS, CREATE_DATE, PAUSE_DATE, CLOSE_DATE, ID) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

默认情况下的添加操作(inverse=false):

insert  into SERVICE

     (UNIX_HOST, COST_ID, ACCOUNT_ID, OS_USERNAME, LOGIN_PASSWD,        STATUS, CREATE_DATE, PAUSE_DATE, CLOSE_DATE, ID) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

   *** update SERVICE  set ACCOUNT_ID=? where ID=?

 

--------------------------案例-----------------------------------

ACCOUNT : 账务账号表(1方)

SERVICE :业务账号表(n方)

案例1:查看账务账号及其包含的业务账号信息

案例2:查看某一个业务账号及其账务账号信息

 

 

17.如何利用MyEclipse根据数据库生成实体类和映射描述文件

 1)在DB Browser中建立一个与数据库的连接

 2)新建一个Web Project工程

 3)为工程添加Hibernate开发框架

   (导入包,添加主配置及其参数设置)

     选中工程-->右键-->MyEclipse-->Add Hibernate Capabilities...

  4)按MyEclipse向导添加Hibernate框架开发包,添加主配置文件,设置连接参

    数,创建一个HibernateUtil工具类  .    

    ----------生成实体类和hbm.xml-------------

  5)进入DB Browser,选中要操作的数据表,右键-->Hibernate Reverse 

     Engineering.按向导生成实体类和hbm.xml.

  6)向导界面1:选择存放实体类和hbm文件的工程和package。

     选择要生成文件:hbm.xml,pojo,dao

  7)向导界面2: 将Type Mapping选中为Hibernate Types

  8)向导界面3:点击Finish完成

 

18.多对多关系映射

   多对多关系在数据库中需要3张表表示。

  例如AdminInfo-->Admin_Role<--Role

  如果需要根据Admin查找Role,可以建立Admin到Role的多对多关系映射。具

  体过程如下:

   a.在Admin实体类中追加一个集合属性,用于存储相关联的Role对象信息

   b.在Admin的hbm.xml中描述集合属性映射

<set name="关系属性" table="关系表">

   <key column="与当前类型联的关系表字段">

   </key>

   <many-to-many  class="关联的另一方类型"  

      column="与另一方类型关联的关系表字段">

   </many-to-many>

 </set>

eg:

    <!-- 采用多对多关系加载roles信息 -->

        <set name="roles" order-by="ROLE_ID" table="ADMIN_ROLE">

         <!-- 指定ADMIN_ROLE表的ADMIN_ID与当前AdminInfo主键关联 -->

         <key column="ADMIN_ID"></key>

         <!-- 指定ADMIN_ROLE表的ROLE_ID与Role主键关联 -->

         <many-to-many class="org.tarena.entity.Role" column="ROLE_ID">

         </many-to-many>

    </set>

 

public class TestManyToMany {

/**

 *基于多对多关系映射的查询、添加、删除

 */

public static void main(String[] args) {

 testFindAdmin();

// testAdminAddRole();

// testAdminRemoveRole();

//testFindRole();

 testAddAdmin();

}

/**查询具有某个角色的用户 */

public static void testFindRole() {

Session session = HibernateSessionFactory.getSession();

Role role = (Role) session.get(Role.class, 1);

System.out.println(role.getName());

System.out.println("-----具有该角色的用户----");

for (AdminInfo admin : role.getAdmins()) {

System.out.println(admin.getId() + " " + admin.getAdminCode());

}

HibernateSessionFactory.closeSession();

}

/**给管理员删除某个角色*/

public static void testAdminRemoveRole() {

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

// 分配角色

// 获取管理员

AdminInfo admin = (AdminInfo) session.get(AdminInfo.class, 1);

// 获取角色

Role role1 = (Role) session.get(Role.class, 10);

// 将角色对象在管理员对象的roles中移除

admin.getRoles().remove(role1);

// 更新admin

session.update(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

/** 利用多对多查询管理员和角色信息*/

public static void testFindAdmin() {

Session session = HibernateSessionFactory.getSession();

AdminInfo admin = (AdminInfo) session.load(AdminInfo.class, 1);

System.out.println(admin.getAdminCode() + " " + admin.getName());

System.out.println("---具有以下角色---");

for (Role role : admin.getRoles()) {

System.out.println(role.getId() + " " + role.getName());

}

HibernateSessionFactory.closeSession();

}

/**给管理员追加角色*/

public static void testAdminAddRole() {

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

// 分配角色

// 获取管理员

AdminInfo admin = (AdminInfo) session.get(AdminInfo.class, 1);

// 获取角色

Role role = (Role) session.get(Role.class, 9);

Role role1 = (Role) session.get(Role.class, 10);

// 将角色对象给管理员对象的roles赋值

admin.getRoles().add(role);

admin.getRoles().add(role1);

// 更新admin对象

session.update(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

/**给管理员添加角色*/

public static void testAddAdmin(){

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加管理员

AdminInfo admin = new AdminInfo();

admin.setAdminCode("Scofield");

admin.setName("斯科菲尔德");

admin.setPassword("12364");

admin.setEnrolldate(new Date(System.currentTimeMillis()));

//获取角色

Role role1 = (Role) session.get(Role.class, 2);

Role role2 = (Role) session.get(Role.class, 7);

// 将角色对象给管理员对象的roles赋值

admin.getRoles().add(role1);

admin.getRoles().add(role2);

//给管理员添加角色

session.save(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

}

 Hibernate经典总结

19.继承关系映射

   a.父类一张表,每个子类一个表,主键对等

   b.可以采用<joined-subclass>进行继承关系映射,具体如下

    --在子类追加extends 父类

    --在子类hbm.xml中定义

 <joined-subclass name="子类类型" extends="父类类型" table="子类表">

   <key column="子类哪个字段与父类关联">

   </key>

   //子类中属性的property映射

</joined-subclass>

     eg:参见Hibernate05_1

hbm.xml:

<hibernate-mapping>

    <joined-subclass name="org.tarena.entity.Book" table="BOOK"  

        extends="org.tarena.entity.Product" schema="SYSTEM">        

         <!-- 指定父子之间的关联条件,写子类的关联字段 -->

         <key column="ID"></key>     

         <!-- 由于映射类型为继承类型,所以把子类的主键映射描述去掉 -->

        <!-- 

        <id name="id" type="integer">

            <column name="ID" precision="5" scale="0" />

            <generator class="assigned" />

        </id>

         -->       

        <property name="author" type="string">

            <column name="AUTHOR" length="20" />

        </property>

        <property name="publishing" type="string">

            <column name="PUBLISHING" length="50" />

        </property>

        <property name="wordNumber" type="string">

            <column name="WORD_NUMBER" length="20" />

        </property>

        <property name="totalPage" type="string">

            <column name="TOTAL_PAGE" length="20" />

        </property>

    </joined-subclass>

</hibernate-mapping>

test.java:

public class TestExtends {

/**

 * 多对多关系映射:

 */

public static void main(String[] args) {

addBook1();

//addBook2();

//findBook();

//deleteBook();

//addCar();

//findAllBook();

}

public static void findAllBook(){

String hql = "from Book";

Session session = 

HibernateSessionFactory.getSession();

Query query = session.createQuery(hql);

List<Product> list = query.list();

for(Product p:list){

System.out.println(p.getId()+" "+p.getName()+" "+p.getPrice());

}

HibernateSessionFactory.closeSession();

}

public static void addCar(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加汽车

Car car = new Car();

car.setName("Q5");

car.setPrice(150000);

car.setProductPic("4.jpg");

car.setType("J");

car.setBrand("宝马");

car.setColor("红色");

car.setDisplacement("2.0排量");

session.save(car);//保存

tx.commit();

HibernateSessionFactory.closeSession();

}

public static void deleteBook(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

Book book = (Book)session.load(Book.class, 3);

session.delete(book);

tx.commit();

HibernateSessionFactory.closeSession();

}

public static void findBook(){

Session session = 

HibernateSessionFactory.getSession();

Book book = (Book)session.load(Book.class, 1);

System.out.println(book.getName()+" "+book.getPrice());

System.out.println(book.getAuthor()+" "+book.getPublishing());

HibernateSessionFactory.closeSession();

}

/** 基于继承关系映射添加*/

public static void addBook2(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加图书

Book book = new Book();

book.setName("Struts2框架使用");

book.setPrice(90);

book.setProductPic("2.jpg");

book.setAuthor("张三的书");

book.setPublishing("菜鸟出版社");

book.setTotalPage("100");

book.setWordNumber("100个字");

session.save(book);//添加图书

tx.commit();

HibernateSessionFactory.closeSession();

}

/** 没有关系映射添加图书*/

public static void addBook1(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加图书

//product

Product pro = new Product();

pro.setName("Java语言基础");

pro.setPrice(100);

pro.setProductPic("1.jpg");

session.save(pro);//保存product信息

//book

Book book = new Book();

book.setId(pro.getId());

book.setAuthor("我写的");

book.setPublishing("我家出版社");

book.setWordNumber("10个字");

book.setTotalPage("2");

session.save(book);//保存book信息

tx.commit();

HibernateSessionFactory.closeSession();

}

}

 

######示例表Oracle######

CREATE TABLE PRODUCT

(

ID  NUMBER(5) CONSTRAINT PRODUCT_ID_PK PRIMARY KEY,

NAME  VARCHAR2(20), 

PRICE  NUMBER(15,2), 

PRODUCT_PIC  VARCHAR2(100)

);

 

CREATE SEQUENCE product_seq;

 

CREATE TABLE BOOK

(

ID  NUMBER(5) CONSTRAINT BOOK_ID_PK PRIMARY KEY,

AUTHOR  VARCHAR2(20), 

PUBLISHING  VARCHAR2(50), 

WORD_NUMBER VARCHAR2(20), 

TOTAL_PAGE VARCHAR2(20)

);

 

CREATE TABLE CAR

(

ID  NUMBER(5) CONSTRAINT CAR_ID_PK PRIMARY KEY,

BRAND VARCHAR2(20), 

TYPE VARCHAR2(1),

COLOR  VARCHAR2(50), 

DISPLACEMENT VARCHAR2(20)

);

##############################

20、Hibernate查询

  *a.HQL查询

    Hibernate Query Language

    HQL与SQL语句结构相似,SQL语句是面向数据表和字段进行查询,而HQL

    语句是面向Hibernate映射过来的对象进行查询,因此HQL被称为是

    一种面向对象查询语言

    HQL和SQL共同点:

      --都支持select,from,where,order by,having,group by等子句。

      --都支持运算符表达式,例如+,-,*,/,>,<等

      --都支持in,not in,between and,like等过滤条件关键字

      --都支持分组函max,min,sum,avg,count

    HQL和SQL不同点:

      --HQL是大小写敏感的,类名和属性名严格区分大小写

      --HQL不支持select * 写法

      --HQL不支持join...on...中的on子句,因为join...on发生在多表操作,而

         Hibernate中对于有关系的多张表已将将关联映射写在了xxx.hbm.xml

         中,在查询时会自动加上join..on

      --HQL不支持表名和字段名

 

 HQL案例:

--查询所有:

from Account

/**查询所有的Account select * from account */

public static void test1() {

 String hql = "from Account"//也可写成:String hql = "select a from 

                                  Account a";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

                            a.getIdcardNo());

}

HibernateUtil.closeSession();

     }

--参数查询:

from Account where status=?

    也可以写成:from Account where status=:s

   eg1:使用默认参数:?

   /** 测试带参数的查询*/

public static void test3() {

// 查询所有status=0的account记录

// String hql = "from Account where status=?";

// 查询status=1 并且 realnameguo

String hql = "from Account " + "where status=? and realName like ?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 设置?参数

query.setString(0, "1");

query.setString(1, "guo%");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

 }

HibernateUtil.closeSession();

}

       eg2:使用Hibernate中的参数: :Xxx

/**测试带参数的查询*/

public static void test4() {

// 查询status=1 并且 realnameguo

String hql = "from Account " + "where status=:s and realName like :r";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 设置:s和:r参数

query.setString("r""guo%");

query.setString("s""1");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

}

HibernateUtil.closeSession();

}

--查询部分字段:

    select id,osUsername from Service

    返回List<Object[]>

    select new Service(id,osUsername) from Service

    返回List<Service>

注意:查询部分字段,也包括select id,osUsername,.......(Account中的

      所有字段),情况;非部分字段查询只限于”from account ”。

    eg1:返回List<Object[]>

   /** 测试查询部分字段值 查询部分字段,hibernate会利用Object[]封装一条记

       录信息*/

public static void test5() {

String hql = "select id,osUsername,unixHost "

"from Service where account.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

// 部分字段用Object[]封装一行结果

List<Object[]> list = query.list();

for (Object[] objs : list) {

System.out.println(objs[0] + " " + objs[1] + " " + objs[2]);

}

HibernateUtil.closeSession();

}

 

  eg2:返回List<Service>

     要先在Service实体类中添加如下构造器:

public Service(){}

public Service(int id,String osUsername,String unixHost){

this.id = id;

this.osUsername = osUsername;

this.unixHost = unixHost;

}

   /** 测试查询部分字段值查询部分字段,通过追加构造器使hibernate利用

 * List<Service>封装一条记录信息*/

public static void test6() {

// 需要在Service类中添加相应的构造方法

String hql = "select " + "new Service(id,osUsername,unixHost) "

"from Service where account.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

// 部分字段用Service封装一行结果

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername() + " "

+ s.getUnixHost());

}

HibernateUtil.closeSession();

}

--在hbm.xml中定义hql

   Query query =  session.getNamedQuery("标识符")

eg:

  先在Service.hbm.xml中加入如下配置:(与class平级)

  <!-- 在TestHQL.java中被读取用于查询语句 -->

          <query name="findAll"><![CDATA[ from Account  ]]></query>

 

   /**在hbm.xml中定义hql*/

public static void test2() {

// String hql = "from Account";

Session session = HibernateUtil.getSession();

// Query query = session.createQuery(hql);

// 获取hbm.xml定义的hql

Query query = session.getNamedQuery("findAll");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

 }

HibernateUtil.closeSession();

}

 

--分页查询用法

    query.setFirstResult(抓取起点从0开始计算)

    query.setMaxResult(抓取最大数量);

    List list = query.list();

/** 分页查询 */

public static void test7() {

Session session = HibernateUtil.getSession();

Query query = session.getNamedQuery("findAll");

// 设置分页抓取参数,从第1条抓,最多抓取3个

query.setFirstResult(0);// 设置抓取起点(从0开始)

query.setMaxResults(3);// 设置最大抓取数量

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

 }

HibernateUtil.closeSession();

}

 

  b.Criteria查询QBC

只需了解,具体参考示例和Hibernate帮助文档

public static void test2() {

// 按status=0并且accountId=1011查询Service

Session session = HibernateUtil.getSession();

Criteria c = // from Service

session.createCriteria(Service.class);

// 添加查询条件

c.add(Restrictions.and(Restrictions.eq("status""0"), Restrictions.eq(

"account.id", 1011)));

// 执行查询

List<Service> list = c.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername());

}

HibernateUtil.closeSession();

}

 

public static void test1() {

Session session = HibernateUtil.getSession();

// 设置查询的数据源

Criteria c = session.createCriteria(Account.class);

// 执行查询

c.setFirstResult(0);

c.setMaxResults(5);

List<Account> list = c.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

}

HibernateUtil.closeSession();

}

  c.Native SQL查询

只需了解,具体参考示例和Hibernate帮助文档

/**

 * 默认采用Object[]封装一行结果

 */

public static void test1() {

String sql = "select id,unix_host,os_username from SERVICE";

Session session = HibernateUtil.getSession();

SQLQuery query = session.createSQLQuery(sql);

// 默认采用Object[]封装一行记录

List<Object[]> list = query.list();

for (Object[] objs : list) {

System.out.println(objs[0] + " " + objs[1] + " " + objs[2]);

}

HibernateUtil.closeSession();

}

/**

 * 利用指定的类型封装记录结果

 */

public static void test2() {

String sql = "select * from SERVICE";

Session session = HibernateUtil.getSession();

SQLQuery query = session.createSQLQuery(sql);

// 指定采用哪种类型将一行记录封装,仅限于单表使用

query.addEntity(Service.class);

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername());

}

HibernateUtil.closeSession();

}

 

21.Hibernate高级特性

 1)二级缓存技术

    SessionFactory级别的缓存,受SessionFactory管理,可以被不同

    Session访问和操作。默认是关闭。一般在使用时需要利用

    SessionFactory.evict()等方法显式的管理该缓存。

二级缓存和一级缓存的区别:

Hibernate提供了二级缓存机制。

首先,Hibernate中的一级缓存机制(也叫做事务内的缓存)是与Session绑定在一起的。当一个Session开启,一级缓存创 建;当一个Session关闭,一级缓存销毁。若使用一级缓存机制(Session的缓存,每个用户线程对应一块Session缓存)现在有5个用户(5 个线程)访问Hibernate,那么Hibernate会为5个用户创建5个不同的Session(一个线程分配一个Session)。


假设用户1调用getId("1")方法查找id=1的Emp对象,Session会首先查找内部有没有id=1的Emp对象,如果有,则返回给用户;没有则去数据库中查找,并保存到该Session中,当用户第二次访问时,就不用去数据库中取数据了。


一级缓存提高了效率,减少了访问数据库的压力。如果5个用户都调用getId("1")方法查找id=1的Emp对象,那么在这5个session中就分别保存着5个id=1的Emp对象,这样显然重复。


由此,我们引入了二级缓存机制(SessionFactory中的缓存,同一个项目中只有一份,所有用户共用)当用户1第一次调用getId("1")方法时,会到数据库中查找出Emp对象,保存到一级缓存中的同时,也在二级缓存中保存一份。这样,当其他用户也需要id=1的Emp对象时,只需要到二级缓存中查找即可,就不用连接到数据库了。


一级缓存是用户线程专用的,二级缓存是大家共用的。

我们通过配置一些现成的缓存组件(如ehcache)来实现,同时我们还可以控制哪些对象需要放入二级缓存,哪些对象不需要做二级缓存。

 

   a.什么情况可以考虑使用二级缓存

     --该对象被多个不同用户频繁使用

     --该对象更新操作不频繁

   b.如何使用二级缓存

     --添加ehcache.jar开发包和src/ehcache.xml配置

     --在hibernate.cfg.xml中开启二级缓存,指定采用哪种二级

       缓存组件

    hibernate.cfg.xml:

<!-- 指定二级缓存组件 -->

  <property name="hibernate.cache.provider_class">

net.sf.ehcache.hibernate.EhCacheProvider

  </property>

       ehcache.xml:

     <!-- 

b.eternal表示是否设置这些放入二级缓存的数据对象为永久的

         (即放入即保存,不再清除)一般都为false

c.timeToIdleSeconds=120表示如果120秒内,放入的对象没有被

          再次访问到,就清除出去

d.timeToLiveSeconds=120表示对象在缓存中存活的时间,一个

           对象进入到本缓存中120秒后,就会自动被清除(一般 设置

           的时间会比timeToIdleSeconds时间长),设置此属性是为了

           让更多活跃的对象进入到缓存中来。

e.overflowToDisk="true"表示如果活跃对象已经超出

          maxElementInMemory设置的最大值时,

  超出的对象要被写入到硬盘上保存下来,用于缓解活跃用户较

          多的情况。

        -->

    <defaultCache

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="30"

        timeToLiveSeconds="240"

        overflowToDisk="true"

                      />Hibernate经典总结

<cache name="sampleCache1"

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="300"

        timeToLiveSeconds="600"

        overflowToDisk="true"

  />

     --需要缓存哪个对象,就在hbm.xml中添加<cache>元素配

       置。

      <cache usage="read-only或read-write"

        region="采用ehcache.xml哪组参数缓存该对象"/>

Account.hbm.xml:

<!-- 指定采用二级缓存缓存Account对象 -->

    <cache usage="read-only" region="sampleCache1"/>

    注:上面的配置要写在配置文件的顶端

其中:

region属性表示指定使用哪个二级缓存。

usage属性表示二级缓存的使用方式。有两种:read-only

和read-write、read-only 表示如果值为read-only,那么就

不能修改,这样ehcache就不用考虑修改和更新的操作。

read-write 设置为read-write,ehcache还需要考虑更新和

修改,这样会降低效率。

所以,设置usage属性是很重要的,需要根据实际情况判

断存入的对象使用二级缓存的方式。

     c.二级缓存管理

         sessionFactory.evict方法

 

eg:

public class TestSecondCache {

/**

 * 二级缓存

 */

public static void main(String[] args) {

findAccount();

findAccount();

}

/**

 * 为使用二级缓存时,两次调用findAccount()方法,会执行两次查询

 * 使用二级缓存后,两次调用findAccount()方法,只执行一次查询

 * */

public static void findAccount() {

Session session = HibernateUtil.getSession();

Account account = (Account) session.get(Account.class, 1011);

System.out.println(account.getRealName() + " " + 

          account.getIdcardNo());

HibernateUtil.closeSession();

}

}

 2)查询缓存技术

查询缓存基于二级缓存

二级缓存只保存单个对象,而不会管诸如取字符串、取数组、取集合等的

操作,但是查询缓存支持这些。

    a.查询缓存的使用

       --要对查询的目标对象开启二级缓存

  <!-- 设置开启二级缓存 -->

<property name="hibernate.cache.use_second_level_cache">true

      </property>

<!-- 指定二级缓存组件 -->

<property name="hibernate.cache.provider_class">

net.sf.ehcache.hibernate.EhCacheProvider

 </property>

       --在hibernate.cfg.xml中开启查询缓存设置

<!-- 开启查询缓存 -->

<property name="hibernate.cache.use_query_cache">

true

</property>

       --在执行query.list()查询之前,

         调用query.setCacheable(true);

    b.适合使用查询缓存的情况

       --不同用户都执行相同的SQL查询和相同结果

       --查询结果集不发生改变

    

   注意:在二级缓存和查询缓存使用关联映射时,关系属性数据

         默认不参与缓存,如果需要缓存关联属性数据,需要

         对关联属性和hbm.xml(Account.hbm.xml和

         Service.hbm.xml)都设置<cache>元素

    查询缓存是在二级缓存的基础之上的,所以关于二级缓

    存的在hibernate.cfg.xml中的配置要保留。

   在Account.hbm.xml和Service.hbm.xml中均添加:

 <!--指定该关联属性也参与缓存操作,

  在需要查询缓存时才需要添加-->

 <cache usage="read-only" region="sampleCache1"/>

  

   eg:

public class TestQueryCache {

/**

 * 查询缓存

 */

public static void main(String[] args) {

findAll();

System.out.println("-----------");

findAll();

}

public static void findAll() {

String hql = "select distinct a " + "from Account a "

"left outer join fetch a.services where a.realName 

                     like ?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 设置采用查询缓存机制

query.setCacheable(true);

query.setString(0, "guo%");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

// 业务账号信息

for (Service s : a.getServices()) {

System.out.println("---" + s.getId() + " " + 

                s.getOsUsername());

}

}

HibernateUtil.closeSession();

}

}

 

             hibernate缓存机制总结:

Hibernate中的一级缓存机制(也叫做事务内的缓存)是与Session绑定在一起的。

当一个Session开启,一级缓存创建;当一个Session关闭,一级缓存销毁。

Hibernate中的二级缓存特点如下:

a.二级缓存被称为SessionFactory级别缓存. 生命周期与SessionFactory对象相关

b. 二级缓存空间可以被不同的Session对象访问 共享.

c. 二级缓存默认是关闭状态.如果遇到某个数据对象被多个不同的Session频繁访 问,可以开启.

Hibernate中的查询缓存:

前面的一级和二级缓存,缓存的时load,get出来的数据对象.不能缓存一个结果集.查询缓存可以缓存查询语句和结果集, 当重复执, 同一个查询语句时,只取数据库查询一次,后续都是将缓存中的结果集取出。适用于频繁的执行同一个查询语句,而且查询结果集很少发生变化的情况。

 

3)悲观锁和乐观锁

 当出现多个用户同时执行更新等操作时,会出现事务交叉更新操作的冲突,会破

 坏业务和数据的完整性。可以使用悲观锁和乐观锁解决这类问题。

 悲观锁:

 a.悲观锁机制:在进行数据查询时追加一个锁机制,进行业务操作,此时其他用

               户不能进行增删改操作,在事务结束时会自动将锁释放,其他用

               户可以继续执行此类操作。

悲观锁的处理方式是当前线程的事务没有结束前,其它事务都要

等着(事务串行化)。

  悲观锁特点:将用户操作一个一个处理,可以解决更新并发问题,缺点是处理效

              率比较低。

   Hibernate悲观锁机制一般是借助于数据库锁机制。

   eg:

 Train train = 

(Train)session.load(Train.class, 1,LockMode.UPGRADE);

 

  乐观锁:

     b.乐观锁机制:多个不同用户都可以同时对数据库记录进行查看和更新操作,

       但是最先commit提交的用户会执行成功,后续用户会以异常形  式提示失败。

         乐观锁是借助于一个版本字段进行控制,当并发操作中一个用  户成功提交了,版本字段值会自动加1,后续提交的对象版本信

                 息小于数据库版本字段值会被hibernate阻止掉。

     乐观锁实现原理:

使用乐观锁,我们需要做数据库更改,为数据库增加一个字段version(版本号),当用户读取数据时,会将版本号version 一同读出,如果该用户修改了数据,会先将取出的版本号与数据库中的版本号做对比,如果相同,才能修改;修改完成后,会将版本号version+1如果不相 同,则不能修改,会抛出异常。

如果不使用框架技术,那么我们需要手工做对比,使用Hibernate框架后,Hibernate可以帮助我们做version对比的操作。

 

       乐观锁特点:允许多个用户同时操作,处理效率相对较高。

      乐观所使用步骤:

        --将原有数据表追加一列版本字段,初始值0

        --在实体类中添加版本属性

        --在映射描述文件中采用<version>元素定义版本属性和版本字段的映射

<!--optimistic-lock 指定采用版本字段形式实现乐观锁控制-->

<hibernate-mapping>

<class name="org.tarena.netctoss.entity.Train" 

      optimistic-lock="version" table="TRAIN">

<id name="id" type="integer">

<column name="T_ID" precision="22" scale="0" />

<generator class="assigned" />

</id>

<!-- 描述版本字段的映射 -->

<version name="version" type="integer">

<column name="T_VERSION"></column>

</version>

<property name="value" type="integer">

<column name="T_VALUE" precision="22" scale="0" />

</property>

</class>

</hibernate-mapping>

        --当发生多个事务并行交叉执行时,第一个提交的成功,后续提交的会抛

          出异常org.hibernate.StaleObjectStateException。可以异常捕

         获给用户一个友善的提示。

   

==============示例表=================

create table train(

t_id number primary key,

t_value number,

t_version number);

insert into train values (1,100,0);



HIbernate技术详解


一:Hibernate的基本用法:
1,Hibernate的数据库操作:
        在所有的ORM框架中有一个非常重要的媒介:PO(Persistence Object,持久化对象),持久化对象的作用是完成持久化操作,简单的说,通过该对象可以对数据进行增删改查的操作。
        Hibernate的PO是非常简单的,Hibernate是低侵入式设计,完全采用普通的Java对象来作为持久化对象。看下面的 一个普通的Java对象:
public class News
{
    //消息类的标识属性
    private int id;
    //消息标题
    private String title;
    //消息内容
    private String content;
    //构造器
    public News()
    {
    }
    //标识属性的setter和getter方法
    public void setId(int id) 
    {
        this.id = id; 
    }
    public int getId()
    {
        return (this.id); 
    }
    //消息标题的setter方法和getter方法
    public void setTitle(String title) 
    {
        this.title = title; 
    }
    public String getTitle() 
    {
        return (this.title); 
    }

    //消息内容的setter方法和getter方法
    public void setContent(String content)
    {
        this.content = content; 
    }
    public String getContent()
    {
        return (this.content); 
    }
}
News.hbm.xml:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="cn.huaxia.domain"
default-access="field">
<classname="News"table="table_news">
<idname="id">
<generatorclass="native"/>
</id>
<propertyname="name"column="uname"/>
<propertyname="sex"/>
<propertyname="age"/>
</class>
</hibernate-mapping>
这个普通的javaBean目前还不具备持久化操作的能力,为了使其具备持久化操作的能力,Hibernate采用XML映射文件,如:
hibernate.cfg.xml:
<!DOCTYPEhibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factoryname="foo">
<propertyname="show_sql">true</property>
<propertyname="hibernate.connection.username">sa</property>
<propertyname="hibernate.connection.password">123</property>
<propertyname="ibernate.connection.pool_size">1000</property>
<propertyname="ibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<propertyname="hibernate.connection.url">jdbc:sqlserver://localhost:1433;DataBaseName=WebExma</property>
<propertyname="hibernate.c3p0.min_size">5</property>
<propertyname="hibernate.c3p0.max_size">20</property>
<propertyname="hibernate.c3p0.timeout">1800</property>
<propertyname="hibernate.c3p0.max_statements">50</property>
<propertyname="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<propertyname="hibernate.hbm2ddl.auto">update</property>
<mappingresource="cn/huaxia/domain/News.hbm.xml"/>
</session-factory>
</hibernate-configuration>
hibernate-mapping元素下的class子元素,每一个class映射一个PO,所以可以这样说:
                PO=POJO+映射文件
Hibernate的配置文件可以是一个properties文件也可以是一个xml文件,在实际应用中,通常使用xml文件,例如:
 
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
    <session-factory>
        <!-- 指定连接数据库所用的驱动 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <!-- 指定连接数据库的url,hibernate连接的数据库名 -->
        <property name="connection.url">jdbc:mysql://localhost/hibernate</property>
        <!-- 指定连接数据库的用户名 -->
        <property name="connection.username">root</property>
        <!-- 指定连接数据库的密码 -->
        <property name="connection.password">32147</property>
        <!-- 指定连接池里最大连接数 -->
        <property name="hibernate.c3p0.max_size">20</property>
        <!-- 指定连接池里最小连接数 -->
        <property name="hibernate.c3p0.min_size">1</property>
        <!-- 指定连接池里连接的超时时长 -->
        <property name="hibernate.c3p0.timeout">5000</property>
        <!-- 指定连接池里最大缓存多少个Statement对象 -->
        <property name="hibernate.c3p0.max_statements">100</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>
        <property name="hibernate.c3p0.validate">true</property>
        <!-- 指定数据库方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
        <!-- 根据需要自动创建数据库 -->
        <property name="hbm2ddl.auto">create</property>
        <!-- 罗列所有的映射文件 -->
        <mapping resource="News.hbm.xml"/>
    </session-factory>
</hibernate-configuration>
Hibernate不推荐使用DriverManager来连接数据库,推荐使用C3P0数据源。
随着PO与Session的关联关系,PO有如下3种状态:
瞬时状态:如果PO实例从未与Session关联过,该PO实例处于瞬时状态。
持久状态:如果PO实例域Session关联起来,且该实例对应数据库记录,则该实例处于持久化状态。
脱管状态:如果PO实例曾与Session关联过,但因为Session关闭等原因,PO实例脱离了Session的管理,这种状态称之为脱管状态。
        对PO的操作必须在Session管理下才能同步到数据库,Session由SessionFactory产生,SessionFactory是数据库编译后的内存镜像,通常一个应用对应一个SessionFactory对象。SessionFactory由Configuration对象产生,Configuration对象负责加载Hibernate配置文件。如:
publicclass NewsTest {
    publicstatic void add(Object obj) {
        Configuration conf =new Configuration().configure();
        SessionFactory sessionFactory = conf.buildSessionFactory();
        Session session = sessionFactory.openSession();
        Transaction tr = session.beginTransaction();
        session.save(obj);
        tr.commit();
    }
    publicstatic void main(String[] args) {
        News news =new News();
        news.setAge(20);
        news.setSex("男");
        news.setName("johnny");
        add(news);
    }
}
2,JNDI数据源的连接属性:
        如果无需使用hibernate自己管理数据源,而是直接访问容器管理数据源,hibernate可使用JNDI数据源的相关配置,下面是连接JNDI数据源的主要配置属性:
hibernate.connection.datasource:指定数据源JNDI名字;
hibernate.jndi.url:指定JNDI提供者的URL,该属性是看可选的,如果JNDI与hibernate持久访问的代码处于同一个应用中,则无需指定该属性;
hibernate.jndi.class:指定JNDI InitialContextFactory的实现类,该属性也是可选的,如果JNDI与hibernate持久访问的代码处于同一个应用中,则无需指定该属性;
hibernate.connection.username:指定连接数据库的用户名;
hibernate.connection.password:指定连接数据库的密码。
注意:即使使用jndi数据源,一样需要指定连接数据库的方言,虽然设置数据库方言不是必须的,但是对于优化持久访问层很有必要。
3,改变持久化对象的状态的方法:
        根据前面的介绍的内容,我们知道通过new新建一个持久化实例时,该实例处于瞬时状态,为了让瞬时对象转换为持久化状态,Hibernate Session提供如下几个方法:
Serializable save(Object obj)将Object 对象转变成持久化状态,该对象的属性值被保存到数据库中。
void persist(Object obj):将对象转化成持久化状态,该对象的属性保存到数据库中。
Serializable save(Object obj,Object pk):将obj对象保存到数据库中,保存到数据库时,指定主键值。
void persist(Object obj,Object  pk):将obj对象保存到数据库中,包存到数据库时,指定主键值。
save方法和persist方法的区别:
使用save方法保存持久化对象时,该方法返回该持久化对象的标识属性值(即对应的主键值),但使用persist方法保存持久化对象时,该方法没有任何返回值,因为save方法需要立即返回持久化对象的标识属性,所以执行save方法会立即把持久化对象插入数据库;而persist方法则保证当它在一个事务外部被调用时,并不立即转化成insert语句,这个功能很有用,当我们封装一个长长的会话流程的时候,persist方法显得尤其重要。
load()和get()方法:
也可以通过load方法来加载一个持久化实例,如:
session.load(News.class,new Integer(pk));
上面的pk就是持久化对象的标识属性,如果没有匹配的数据库记录,load方法可能会抛出异常;如果在类配置文件中指定了延迟加载,则load方法会返回一个未初始化的代理对象,这个代理对象没有装载数据记录。
get方法也用于根据主键装载持久化对象,但get()方法会立即访问数据库,如果没有对应的数据,会返回一个null,
        一旦加载该持久化对象后,在代码中对持久化实例做的修改如:
        n.setName("johnny");
这种修改将会保存到数据库中,程序对持久化实例做的修改会在session flush之前自动保存到数据库,无需调用其他方法,也就是说,修改对象的最简单的方法就是在session处于打开状态时load它,然后直接修改。
        这种做法,有极大的性能缺陷,当我们修改某条记录时,这种做法将会产生2条SQL语句,一条用于查询该记录,另一条用来修改该记录。
但是实际情况下无需考虑这种情况,对于一个Java EE而言,Hibernate的处理流程通常是从数据库里加载数据————>发给表现层以供用户修改————>将所做的修改保存到数据库里。这种情况下,应用本身就需要两条SQL语句。
        当我们使用load或get方法来加载持久化对象时,还可以指定一个“锁模式”参数,Hibernate使用LockMode,LockMode提供READ/UPDATE两个静态属性来代表共享修改锁,如果需要加载某个对象以提供修改,则可以用一下代码:
News n =  Session.get(News.class,new Integer(pk),LockMode.UPDATE);
    lock方法也可将某个脱管状态的对象重新持久化,但该脱管对象必须是一个没有被修改的。如:
sess.lock(news,LockMode.NONE);
Hibernate 的update()、merge()和updateOrSave():
        对于一个曾经持久化过的、但现在已经脱离了Session管理的持久化对象,把它称为脱管状态。
当我们修改脱管对象的状态后,程序应该使用新的Session来保存这些修改。如:
    News n=  session.load(News.class,new Integer(pk))
    session.close();
    n.setTitle("news1");
    //打开第二个Session
    Session session2 = .....;
    session2.update(n);
注意:merge方法也可以将程序对脱管对象所做的修改保存到数据库中,但是,merge与update方法最大的区别是merge方法不会持久化给定的对象,也就是对象依然不是持久化状态,仍然与Session没有关系。
 4,深入Hibernate的映射文件:
①映射主键:
标识属性通过<id../>元素指定,<id../>元素的name属性的值就是持久化类标识属性名,除此之外<id../>元素还可以指定如下属性:
type指定该标识属性的类型,该类型可以是 Hibernate内建类型,也可以是Java类型。通常建议设置该属性,提高性能。
column:略。
unsaved-value:在Hibernate3中,无需指定该属性。
access:指定Hibernate访问该标识属性的访问策略。
②映射集合属性:
        集合属性大致分为2种,第一种是单纯的机会属性,例如像:List、Set或数组等集合属性;还有一种是Map结构的集合属性,每个属性都有对应的key映射。
        Hibernate要求持久化集合字段必须声明为接口,实际的接口可以为,java.util.Set,java.util.Collection,java.util.List,java.util,MaP.........甚至是自定义类型
(只需要实现org.hibernae,usertype.userCollectionType接口即可)。
        因为集合属性需要保存到另一个数据库表中,所欲i保存集合属性的数据表需要包含一个外键列,用于参照主键列,该外键列通过在<set.../>,<list.../>等集合元素中使用<key../>元素来映射,如图
上图的集合属性数据表的外键列需要<key../>元素指定。
            ——————List集合属性:
List是有序集合因此持久化到数据库必须增加一列来表示集合元素的次序,如:
public class Person
{
    //标识属性
    private int id;
    //普通属性name
    private String name;
    //普通属性age
    private int age;
    //集合属性,保留该对象关联的学校
    private List schools = new ArrayList();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , String name , int age , List schools)
    {
        this.id = id;
        this.name = name;
        this.age = age;
        this.schools = schools;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //schools属性的setter和getter方法
    public void setSchools(List schools)
    {
        this.schools = schools;
    }
    public List getSchools()
    {
        return this.schools;
    }

}
映射文件:Person.hbm.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射List集合属性 -->
        <list name="schools" table="school">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="personid" not-null="true"/>
            <!-- 映射集合属性数据表的集合索引列 -->
            <list-index column="list_order"/>
            <!-- 映射保存集合元素的数据列 -->
            <element type="string" column="school_name"/>
        </list>
    </class>
</hibernate-mapping>
PersonManager.java:
public class PersonManager
{
    public static void main(String[] args)
    {
        PersonManager mgr = new PersonManager();
        mgr.createAndStorePerson();
        HibernateUtil.sessionFactory.close();
    }
    //创建并保存Person对象
    private void createAndStorePerson()
    {
        //打开线程安全的session对象
        Session session = HibernateUtil.currentSession();
        //打开事务
        Transaction tx = session.beginTransaction();
        //创建Person对象
        Person yeeku = new Person();
        //为Person对象设置属性
        yeeku.setAge(29);
        yeeku.setName("Yeeku.H.Lee");
        //创建List集合
        List schools = new ArrayList();
        schools.add("小学");
        schools.add("中学");
        //设置List集合属性
        yeeku.setSchools(schools);
        session.save(yeeku);
        tx.commit();
        HibernateUtil.closeSession();
    }
}
——————数组属性:
Hibernate对数组和List的处理方式非常相似,实际上,List和数组也非常像,尤其是jdk1.5增加了自动装箱和拆箱功能,它们用法的区别只是List的长度可以变化,而数组长度不可变而已。例如:
public class Person
{
    //标识属性
    private int id;
    //普通属性name
    private String name;
    //普通属性age
    private int age;
    //数组属性,保留该对象关联的学校
    private String[] schools;

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , String name , int age , String[] schools)
    {
        this.id = id;
        this.name = name;
        this.age = age;
        this.schools = schools;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //schools属性的setter和getter方法
    public void setSchools(String[] schools)
    {
        this.schools = schools;
    }
    public String[] getSchools()
    {
        return this.schools;
    }

}
数组集合属性应该 使用<array../>元素完成映射,而<array../>的子元素和<list../>元素一样,如:Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射数组属性 -->
        <array name="schools" table="school">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="personid" not-null="true"/>
            <!-- 映射集合属性数据表的集合索引列 -->
            <list-index column="list_order"/>
            <!-- 映射保存集合元素的数据列 -->
            <element type="string" column="school_name"/>
        </array>
    </class>
</hibernate-mapping>
由此可见Hibernate对List和数组的处理几乎完全一样。
            ————Set集合属性
Set集合与List集合有点不同,因为Set是无序的,不可重复的集合,因此<set../>元素无需使用<list-index../>元素来映射集合元素的索引列。
如:Person.class:
public class Person
{
    //标识属性
    private int id;
    //普通属性name
    private String name;
    //普通属性age
    private int age;
    //集合属性,保留该对象关联的学校
    private Set<String> schools = 
        new HashSet<String>();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , String name , int age , Set<String> schools)
    {
        this.id = id;
        this.name = name;
        this.age = age;
        this.schools = schools;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //schools属性的setter和getter方法
    public void setSchools(Set<String> schools)
    {
        this.schools = schools;
    }
    public Set<String> getSchools()
    {
        return this.schools;
    }

}
下面是Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射Set集合属性 -->
        <set name="schools" table="school">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="personid" not-null="true"/>
            <!-- 映射保存集合元素的数据列,增加非空约束 -->
            <element type="string" column="school_name"
                not-null="true"/>
        </set>
    </class>
</hibernate-mapping>
PersonManager.java:
public class PersonManager
{
    public static void main(String[] args)
    {
        PersonManager mgr = new PersonManager();
        mgr.createAndStorePerson();
        HibernateUtil.sessionFactory.close();
    }

    private void createAndStorePerson()
    {
        Session session = HibernateUtil.currentSession();
        Transaction tx = session.beginTransaction();
        //创建Person对象
        Person yeeku = new Person();
        //为Person对象设置属性
        yeeku.setAge(29);
        yeeku.setName("Yeeku.H.Lee");
        //创建Set集合
        Set<String> s = new HashSet<String>();
        s.add("小学");
        s.add("中学");
        //设置Set集合属性
        yeeku.setSchools(s);
        session.save(yeeku);
        tx.commit();
        HibernateUtil.closeSession();
    }
}
        对比发现List和Set两种集合属性,发现List集合属性的元素是有序的,而Set集合元素没有顺序,当集合属性在另一张表中存储时,List集合可以用关联持久化类的外键和集合索引列作为联合主键,但是Set集合没有索引列,则以关联持久化类的外键和元素列作为联合主键,前提是元素列不能为空。
 
                ————bag元素映射:
<bag../>元素即可映射List集合属性,也可以映射Set集合属性,甚至可以映射Connllection集合属性。不管是那种集合属性,使用<bag../>元素都被将映射成无序集合,集合属性对应的表没有主键。
 
                ————map集合属性:
map集合属性必须需要使用<map../>元素进行映射,当配置<map../>元素时需要使用<key..>元素映射外键列,除此之外,Map集合属性还需要映射Map key。映射Map集合的key的元素比较多,当Map的key是字符串、日期类型时,可以直接使用<map-key../>元素来映射Map key。
Hibernate以外键列和key作为联合主键。
示例:
public class Person
{
    //标识属性
    private int id;
    //普通属性name
    private String name;
    //普通属性age
    private int age;
    //集合属性,保留该对象关联的考试成绩
    private Map scores = new HashMap();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , String name , int age , Map scores)
    {
        this.id = id;
        this.name = name;
        this.age = age;
        this.scores = scores;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //scores属性的setter和getter方法
    public void setScores(Map scores)
    {
        this.scores = scores;
    }
    public Map getScores()
    {
        return this.scores;
    }
}
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射Map集合属性 -->
        <map name="scores" table="score">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="personid" not-null="true"/>
            <!-- 映射集合属性数据表的Map key列 -->
            <map-key column="subject" type="string"/>
            <!-- 映射保存集合元素的数据列 -->
            <element column="grade" type="float"/>
        </map>
    </class>
</hibernate-mapping>
映射scores属性时指定<map-key../>和<element../>两个子元素,由于程序定义Map集合时,没有使用泛型来限制Map集合。所以Hibernate无法通过反射来获取Map集合的key和value的数据类型,所以配置<map-key../>和<element.../>元素时必须指定type属性。
                    ————集合属性的性能分析:
略;
                    ————映射组件属性:
        
组件属性的意思是持久化对类的属性不是一个基本数据类型,也不是字符串、日期等类型的变量,而是一个被包含的对象,在持久化的过程中,它仅仅被当作值类型,而非一个实体引用。如:

public class Person
{
    //标识属性
    private int id;
    //普通属性age
    private int age;
    //组件属性name
    private Name name;

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , 
        int age , Name name)
    {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //name属性的setter和getter方法
    public void setName(Name name)
    {
        this.name = name;
    }
    public Name getName()
    {
        return this.name;
    }
}
Name.java:

public class Name
{
    //定义first属性
    private String first;
    //定义last属性
    private String last;
    //引用拥有该Name的Person对象
    private Person owner;

    //无参数的构造器
    public Name()
    {
    }
    //初始化first、last属性的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    //first属性的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    //last属性的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    //owner属性的setter和getter方法
    public void setOwner(Person owner)
    {
        this.owner = owner;
    }
    public Person getOwner()
    {
        return this.owner;
    }
}
        显然,一个普通的数据列无法存储Name对象,因此我们不能用<property../>元素进行映射,除此之外,上面的Name类包含一个owner属性,该owner属性指向包含该Name属性的容器实体(也就是Person对象)。Hibernate提供了>component../>元素,每一个component元素映射一个组件属性。
        使用<component.../>元素还需要如下几个属性:
class:指定组件类的名称,如果不指定该属性,Hibernate将通过反射来该组件的类型。
insert:指定被映射的字段是否出现在SQL的insert语句中。
update:指定被映射的字段是否出现在SQL的update语句中。
access:指定Hibernate访问该组件属性的策略,默认是property。
lazy:设置该组件是否在持久化对象第一次访问时启用延迟加载,默认为true。
optimistic-lock:设置更新该组件的属性是否获取乐观锁,如果设置为true,当修改该组件属性时,持久化对象的版本号会增加。
unique:指定是否在该组件映射的所有字段上添加唯一约束。
        除此之外,还可以在<comonent../>元素内插入一个<parent../>子元素,用于映射组件类内一个指向其容器实体的反向引用,定义一个<parent../>子元素时只需要指定一个name属性,其值为引用容器实体的属性名。
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
        <!-- 映射组件属性name,组件属性的类型为Name -->
        <component name="name" 
            class="Name" unique="true">
            <!-- 指定owner属性代表容器实体 -->
            <parent name="owner"/>
            <!-- 映射组件属性的first属性 -->
            <property name="first"/>
            <!-- 映射组件属性的last属性 -->
            <property name="last"/>
        </component>
    </class>
</hibernate-mapping>
 
public class PersonManager
{
    public static void main(String[] args)
    {
        PersonManager mgr = new PersonManager();
        mgr.createAndStorePerson();
        HibernateUtil.sessionFactory.close();
    }

    private void createAndStorePerson()
    {
        Session session = HibernateUtil.currentSession();
        Transaction tx = session.beginTransaction();
        //创建Person对象
        Person yeeku = new Person();
        //为Person对象设置属性
        yeeku.setAge(29);
        Name n = new Name("Yeeku" , "Lee");
        yeeku.setName(n);
        session.save(yeeku);
        tx.commit();
        HibernateUtil.closeSession();
    }
}
                    ————组件的属性为集合:
如果组件类再次包含了List、Set、Map等级和属性,则我们可以在<comonent../>元素里使用<list../><mao../><set../>子元素来映射这些集合属性。
如:

public class Name
{
    //定义first属性
    private String first;
    //定义last属性
    private String last;
    //引用拥有该Name的Person对象
    private Person owner;
    private Map power = new HashMap();


    //无参数的构造器
    public Name()
    {
    }
    //初始化全部属性的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    //first属性的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    //last属性的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    //owner属性的setter和getter方法
    public void setOwner(Person owner)
    {
        this.owner = owner;
    }
    public Person getOwner()
    {
        return this.owner;
    }

    //power属性的setter和getter方法
    public void setPower(Map power)
    {
        this.power = power;
    }
    public Map getPower()
    {
        return this.power;
    }
}
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
        <!-- 映射组件属性name,组件属性的类型为Name -->
        <component name="name" 
            class="Name" unique="true">
            <!-- 指定owner属性代表容器实体 -->
            <parent name="owner"/>
            <!-- 映射组件属性的first属性 -->
            <property name="first"/>
            <!-- 映射组件属性的last属性 -->
            <property name="last"/>
            <!-- 映射组件属性里的Map集合属性 -->
            <map name="power" table="power">
                <!-- 映射集合属性数据表的外键列 -->
                <key column="person_name_id" not-null="true"/>
                <!-- 映射集合属性数据表的Map key列 -->
                <map-key column="name_aspect" type="string"/>
                <!-- 映射保存集合元素的数据列 -->
                <element column="name_power" type="int"/>
            </map>
        </component>
    </class>
</hibernate-mapping>
从上面对配置文件可以看出,该<map../>元素配置方法与前面的映射集合属性时 所用的<map../>元素没有任何区别。只是<map../>成了<comonent../>子元素。
           ———— 集合属性的元素为组件:
例如:

public class Person
{
    //标识属性
    private int id;
    //普通属性age
    private int age;
    //组件属性name
    private Map<String , Name> nicks
        = new HashMap<String , Name>();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , int age , Map<String,Name> nicks)
    {
        this.id = id;
        this.age = age;
        this.nicks = nicks;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //nicks属性的setter和getter方法
    public void setNicks(Map<String , Name> nicks)
    {
        this.nicks = nicks;
    }
    public Map<String , Name> getNicks()
    {
        return this.nicks;
    }
}
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
        <!-- 映射nicks集合属性,集合属性对应的数据表为nick_table -->
        <map name="nicks" table="nick_table">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="person_id" not-null="true"/>
            <!-- 映射集合属性数据表的Map key列 -->
            <map-key column="phase" type="string"/>
            <!-- 映射保存集合里的组件元素 -->
            <composite-element class="lee.Name">
                <!-- 指定owner属性代表容器实体 -->
                <parent name="owner"/>
                <!-- 映射组件属性的first属性 -->
                <property name="first"/>
                <!-- 映射组件属性的last属性 -->
                <property name="last"/>
            </composite-element>
        </map>
    </class>
</hibernate-mapping>
组件为Map的索引:

public class Person
{
    //标识属性
    private int id;
    //普通属性age
    private int age;
    //组件属性name
    private Map<Name , Integer> nickPower
        = new HashMap<Name , Integer>();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int id , int age , 
        Map<Name , Integer> nickPower)
    {
        this.id = id;
        this.age = age;
        this.nickPower = nickPower;
    }

    //id属性的setter和getter方法
    public void setId(int id)
    {
        this.id = id;
    }
    public int getId()
    {
        return this.id;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //nickPower属性的setter和getter方法
    public void setNickPower(Map<Name , Integer> nickPower)
    {
        this.nickPower = nickPower;
    }
    public Map<Name , Integer> getNickPower()
    {
        return this.nickPower;
    }
}
 

public class Name
{
    //定义first属性
    private String first;
    //定义last属性
    private String last;
    //引用拥有该Name的Person对象
    private Person owner;

    //无参数的构造器
    public Name()
    {
    }
    //初始化全部属性的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    //first属性的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    //last属性的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    //owner属性的setter和getter方法
    public void setOwner(Person owner)
    {
        this.owner = owner;
    }
    public Person getOwner()
    {
        return this.owner;
    }

    //重写equals方法,根据first、last进行判断
    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj != null 
            && obj.getClass() == Name.class)
        {
            Name target = (Name)obj;
            if (target.getFirst().equals(getFirst())
                && target.getLast().equals(getLast()))
            {
                return true;
            }
        }
        return false;
    }
    //重写hashCode方法,根据first、last计算hashCode值
    public int hashCode()
    {
        return getFirst().hashCode() * 7 
            + getLast().hashCode();
    }
}
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射标识属性 -->
        <id name="id" column="personid">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
        <!-- 映射nickPower集合属性,集合属性对应的数据表为nick_power -->
        <map name="nickPower" table="nick_power">
            <!-- 映射集合属性数据表的外键列 -->
            <key column="person_id" not-null="true"/>
            <!-- 映射集合属性数据表的Map key列 ,
                因为Map key的数据类型是复合类型,所以使用如下元素-->
            <composite-map-key class="lee.Name">
                <!-- 映射复合组件的属性 -->
                <key-property name="first" type="string"/>
                <key-property name="last" type="string"/>
            </composite-map-key>
            <!-- 映射集合元素的数据列 -->
            <element column="nick_power" type="int"/>
        </map>
    </class>
</hibernate-mapping>
组件为复合主键:

public class Person
{
    //以Name组件作为标识属性
    private Name name;
    //普通属性age
    private int age;
    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(Name name , int age)
    {
        this.name = name;
        this.age = age;
    }
    //name属性的setter和getter方法
    public void setName(Name name)
    {
        this.name = name;
    }
    public Name getName()
    {
        return this.name;
    }
    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }
}
 

public class Name implements
    java.io.Serializable
{
    //定义first属性
    private String first;
    //定义last属性
    private String last;

    //无参数的构造器
    public Name()
    {
    }
    //初始化全部属性的构造器
    public Name(String first , String last)
    {
        this.first = first;
        this.last = last;
    }

    //first属性的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    //last属性的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    //重写equals方法,根据first、last进行判断
    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj != null 
            && obj.getClass() == Name.class)
        {
            Name target = (Name)obj;
            if (target.getFirst().equals(getFirst())
                && target.getLast().equals(getLast()))
            {
                return true;
            }
        }
        return false;
    }
    //重写hashCode方法,根据first、last计算hashCode值
    public int hashCode()
    {
        return getFirst().hashCode() * 7 
            + getLast().hashCode();
    }
}
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 映射组件类型的标识属性 -->
        <composite-id name="name" class="lee.Name">
            <!-- 映射组件主键里的各属性 -->
            <key-property name="first" type="string"/>
            <key-property name="last" type="string"/>
        </composite-id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
    </class>
</hibernate-mapping>
 
多列作为复合主键:

public class Person implements 
    java.io.Serializable
{
    //定义first属性,作为标识属性的成员
    private String first;
    //定义last属性,作为标识属性的成员
    private String last;
    //普通属性age
    private int age;

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(String first , String last , int age)
    {
        this.first = first;
        this.last = last;
        this.age = age;
    }

    //first属性的setter和getter方法
    public void setFirst(String first)
    {
        this.first = first;
    }
    public String getFirst()
    {
        return this.first;
    }

    //last属性的setter和getter方法
    public void setLast(String last)
    {
        this.last = last;
    }
    public String getLast()
    {
        return this.last;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //重写equals方法,根据first、last进行判断
    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj != null 
            && obj.getClass() == Person.class)
        {
            Person target = (Person)obj;
            if (target.getFirst().equals(getFirst())
                && target.getLast().equals(getLast()))
            {
                return true;
            }
        }
        return false;
    }
    //重写hashCode方法,根据first、last计算hashCode值
    public int hashCode()
    {
        return getFirst().hashCode() * 7 
            + getLast().hashCode();
    }
}
Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <class name="Person">
        <!-- 直接使用composite-id映射多列联合主键 -->
        <composite-id>
            <!-- 映射组件主键里的各属性 -->
            <key-property name="first" type="string"/>
            <key-property name="last" type="string"/>
        </composite-id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
    </class>
</hibernate-mapping>
        二:深入使用Hibernate:
1,hibernate的关联映射:
关联关系是面向对象分析、面向对象设计最重要的知识,Hibernate可以完全理解这种关系,如果映射得当,Hibernate的关联映射将可以大大简化持久层数据的访问,关联关系大致有如下分类:
单向关系:只能单向访问关联端,例如,只能通过老师访问学生,或者只能通过学生访问老师。
双向关心:关联的两端可以互相访问。例如:老师和学生之间可以互相访问。
单向关联可以分为:
单向1——1;
单向1——N;
单向N——1;
单向N——N;
双向关联可以分为:
双向1——1;
双向1——N;
双向N——N;
①无连接表  单向N——1:
N——1是非常常见的关联关系,最常见的父子关系也是N——1,单向的N——1关联只需从N可以访问1的一段。
例如:
Person.java:
publicclass Person {
privateint personid;
private Stringname;
privateint age;
private Addressaddress;
publicint getPersonid() {
returnpersonid;
}
publicvoid setPersonid(int personid) {
this.personid = personid;
}
public String getName() {
returnname;
}
publicvoid setName(String name) {
this.name = name;
}
publicint getAge() {
returnage;
}
publicvoid setAge(int age) {
this.age = age;
}
public Address getAddress() {
returnaddress;
}
publicvoid setAddress(Address address) {
this.address = address;
}
}
 
Address.java:
publicclass Address {
privateint addressid;
private Stringdetail;
publicint getAddressid() {
returnaddressid;
}
publicvoid setAddressid(int addressid) {
this.addressid = addressid;
}
public String getDetail() {
returndetail;
}
publicvoid setDetail(String detail) {
this.detail = detail;
}
}
Address.hbm.xml:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="cn.huaxia.domain"
default-access="field">
<classname="Address"table="table_address">
<idname="addressid">
<generatorclass="native"/>
</id>
<propertyname="detail"column="detail"/>
</class>
</hibernate-mapping>
 
Person.hbm.xml:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="cn.huaxia.domain"
default-access="field">
<classname="Person"table="table_person">
<idname="personid">
<generatorclass="native"/>
</id>
<propertyname="name"column="pname"/>
<propertyname="age"/>
<many-to-onename="address"column="addressid"cascade="all"class="Address"/>
</class>
</hibernate-mapping>
     以上程序创建了一个瞬时Address对像,当程序执行到session.persist(p),系统准备保存Person对象,系统将要向数据表中插入一条记录,但是该记录参照的主表记录还不曾被保存(Address对象还处于瞬时状态)这时有2中情况发生:
    系统抛出TransientObjectException异常:object references an unsaved transient instance,因为主表不曾插入,所以参照该记录的从表无法插入。
    系统自动级联插入主表,再插入从表记录。
也就是说,上面的配置文件中少了cascade="all",这以为着执行到session.persist(p)会抛出异常。
 
注意:在所有既有的基于外键约束的关联关系中,都必须牢记:要么总是先持久化主表对应实体,要么设置级联操作,否则当Hibernate试图向从表插入数据的时候,如果发现该从表记录参照的主表记录不存在就报异常。
 
②有连接表的N--1关联:
对于绝大多数单向 N--1关联而言,使用基于外键的关联映射已经足够了,但因为底层数据库建模时可以使用连接表来建立这种关系。
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" >
            <!-- 映射连接表中参照本表主键的外键列 -->
            <key column="personid"/>
            <!-- 映射连接表中参照关联实体的外键列 -->
            <many-to-one name="address" cascade="all"
                class="Address" column="addressId"/>
        </join>
    </class>
</hibernate-mapping>
③单向1--1关联:
        对于单向的1--1关联关系,需要在持久化类里为关联实体 的引用属性增加setter和getter方法,从持久化类上看,单向的1--1与单向N--1没有任何区别。因为N的一端,或者1的一端都是直接访问关联实体
        事实上,单向1--1和N--1的映射配置也非常相似,只需要原有的many-to-one元素增加unique="true"属性,用以表示N端必须唯一,那么就成了1--1了
基于外键的单向1--1:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 用于映射N-1关联实体,指定关联实体类为Address
            指定外键列名为addressId,并指定级联全部操作 -->
        <many-to-one name="address" cascade="all"
            unique="true" class="Address" 
            column="addressId"/>
    </class>
</hibernate-mapping>
            这意味着在person表的addressid外键列上增加唯一约束。
 
有连接表的单向1--1:
虽然这种情况少见,但是Hibernate也支持:
 
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" >
            <!-- 映射连接表中参照本表主键的外键列 -->
            <key column="personid"/>
            <!-- 映射连接表中参照关联实体的外键列 -->
            <many-to-one name="address" cascade="all"
                unique="true" class="Address" 
                column="addressId"/>
        </join>
    </class>
</hibernate-mapping>
            基于主键的单向1--1:
    1--1的关联可以基于主键关联,基于主键关联的持久化类不能拥有自己定的主键生成器,他的主键由关联类负责生成。
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" column="addressid">           
            <!-- 基于主键关联时,主键生成策略是foreign,
                表明根据关联类的主键来生成本表主键 -->
            <generator class="foreign">
                <!-- 指定引用关联实体的属性名 -->
                <param name="property">address</param>
            </generator>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 下面映射基于主键的1-1关联 -->
        <one-to-one name="address"/>
    </class>
</hibernate-mapping>
④无关联表的 单向1--N关联:

public class Person
{
    //标识属性
    private int personid;
    //Person的name属性
    private String name;
    //保留Person的age属性
    private int age;
    //1-N关联关系,使用Set来保存关联实体
    private Set<Address> addresses
        = new HashSet<Address>();

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(int personid , String name , int age)
    {
        this.personid = personid;
        this.name = name;
        this.age = age;
    }

    //personid属性的setter和getter方法
    public void setPersonid(int personid)
    {
        this.personid = personid;
    }
    public int getPersonid()
    {
        return this.personid;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //age属性的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }

    //addresses属性的setter和getter方法
    public void setAddresses(Set<Address> addresses)
    {
        this.addresses = addresses;
    }
    public Set<Address> getAddresses()
    {
        return this.addresses;
    }
}
 
 
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射集合属性,集合元素是其他持久化实体
            没有指定cascade属性 -->
        <set name="addresses">
            <!-- 指定关联的外键列 -->
            <key column="personId"/>
            <!-- 用以映射到关联类属性 -->
            <one-to-many class="Address"/>
        </set>
    </class>
</hibernate-mapping>
⑤ 有连接表的单向1--N:
使用<many-to-many>元素,但是为了保证当前实体是1的一端,因此需要为元素指定unique="true"。
<many-to-many>元素的unique属性:指定本持久化实体是否 增加唯一约束,默认是false。
同理:<many-to-one>元素的unique属性等于true的话,那么就相当于1--1。
如:Person.hbm.xml:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射集合属性,集合元素是其他持久化实体
            指定连接表的表名-->
        <set name="addresses" table="person_address">
            <!-- 指定连接表中参照本表记录的外键列名 -->
            <key column="person_id" />
             <!-- 使用many-to-many来映射1-N关联,
                增加unique="true" -->
            <many-to-many class="Address"
                unique="true"/>
        </set>
    </class>
</hibernate-mapping>
⑥单向N--N关联:
单向的N--N关联和1--N关联的持久化类代码完全相同,控制关系的一端需要增加一个set集合属性,被关联的持久化实体以集合的形式存在。
N--N必须使用链表。
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person">
        <!-- 映射标识属性personid -->
        <id name="personid" >
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射集合属性,集合元素是其他持久化实体
            指定连接表的表名-->
        <set name="addresses" table="person_address">
            <!-- 指定连接表中参照本表记录的外键列名 -->
            <key column="person_id" />
             <!-- 使用many-to-many来映射1-N关联,
                增加unique="true" -->
            <many-to-many class="Address"/>
        </set>
    </class>
</hibernate-mapping>
⑦双向一系列的关联略。
2,传播性持久化:
        每一个Hibernate session的基本操作,包括persist(),merge(),saveOrUpdate(),delete(),lock(),refesh(),evict(),replication()都有对应的级联风格。这些级联风格命名为create,merge,save-update,delete,lock,refesh,evict,replication。如果程序希望某个操作能被级联传播到关联实体,则可以在配置关联映射时通过cascade属性来指定。且级联风格是可以组合,如:<one-to-one name="person" cascade="create,delete,lock"/>
        Hibernate有一个特殊的级联策略:delete-orphan(删除孤儿),此级联策略只对one-to-many关联有效,表明delete()操作将被级联到所有从关联中删除对象。对于级联策略的设定,Hibernate有如下建议:
        <many-to-one>或者<many-to-many>关系中指定级联没有什么意义,级联通常在<one-to-one>和<one-to-many>关系中比较有用,因为级联操作应该是由主表记录传播到从表记录,而从表记录不能传播到主表记录。
        如果从表记录完全限制在主表记录之内(当主表记录被删除后,从表记录就没有存在的意义),则可以指定cascade="all,delete-orphan"级联策略,将从表实体的生命周期完全交给主表管理。
        如果经常在某个事务中同时使用主表实体和从表实体,则可以考虑指定cascade="create,merege,save-update"级联策略。
3,继承映射:
        对于面向对象的程序设计语言,继承和多态是两个最基本的概念,Hibernate几种继承映射策略,不管哪种继承映射策略,Hibernate的多态查询都可以运行良好。
        Peroson类是本应用继承树结构的父类:

public class Person
{
    //标识属性
    private long id;
    //定义该Person实体的名字属性
    private String name;
    //定义该Person实体的性别属性
    private char gender;
    //定义该Person实体的组件属性:address
    private Address address;

    //无参数的构造器
    public Person()
    {
    }
    //初始化全部属性的构造器
    public Person(long id , String name , char gender)
    {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

    //id属性的setter和getter方法
    public void setId(long id)
    {
        this.id = id;
    }
    public long getId()
    {
        return this.id;
    }

    //name属性的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    //gender属性的setter和getter方法
    public void setGender(char gender)
    {
        this.gender = gender;
    }
    public char getGender()
    {
        return this.gender;
    }

    //address属性的setter和getter方法
    public void setAddress(Address address)
    {
        this.address = address;
    }
    public Address getAddress()
    {
        return this.address;
    }
}
 

public class Address
{
    //定义该Address的详细信息
    private String detail;
    //定义该Address的邮编信息
    private String zip;
    //定义该Address的国家信息
    private String country;

    //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String detail , String zip , String country)
    {
        this.detail = detail;
        this.zip = zip;
        this.country = country;
    }

    //detail属性的setter和getter方法
    public void setDetail(String detail)
    {
        this.detail = detail;
    }
    public String getDetail()
    {
        return this.detail;
    }

    //zip属性的setter和getter方法
    public void setZip(String zip)
    {
        this.zip = zip;
    }
    public String getZip()
    {
        return this.zip;
    }

    //country属性的setter和getter方法
    public void setCountry(String country)
    {
        this.country = country;
    }
    public String getCountry()
    {
        return this.country;
    }
}
三种映射文件略。
4,Hibernate的批量处理:
        ————批量插入:
如果需要将100000条数据插入数据库,通过Hibernate可能会采用这样的做法:
for(int i=0;i<100000;i++){
    User user= new User(....);
    session.save(user);
}
    但随着这个程序的运行,总会在某个时候运行失败,并且抛出内存溢出,这是因为Hibernate的Session持有一个必选的一级缓存,所有的User实例都将在Session级别的缓存区进行缓存的缘故。
    解决这个问题,有一个非常简单的思路,定时将Session缓存的数据刷入数据库,而不是一直在Session级别缓存,可以考虑设计一个累加器,如:

public class UserManager
{
    public static void main(String[] args)throws Exception
    {
        UserManager mgr = new UserManager();
        mgr.testUser();
        HibernateUtil.sessionFactory.close();
    }

    private void testUser()throws Exception
    {
        //打开Session
        Session session = HibernateUtil.currentSession();
        //开始事务
        Transaction tx = session.beginTransaction();
        //循环100000次,插入100000条记录
        for (int i = 0 ; i < 100000 ; i++ )
        {
            //创建User实例
            User u1 = new User();
            u1.setName("xxxxx" + i);
            u1.setAge(i);
            u1.setNationality("china");
            //在Session级别缓存User实例
            session.save(u1);
            //每当累加器是20的倍数时,将Session中数据刷入数据库,
            //并清空Session缓存。
            if (i % 20 == 0)
            {
                session.flush();
                session.clear();
            }
        }
        //提交事务
        tx.commit();
        //关闭事务
        HibernateUtil.closeSession();
    }
}
上面的代码中当i%20==0时,手动将Session处缓存的数据邪途数据库,并且清空Session缓存里的数据,除了对Session级别缓存进行处理外,还应该通过如下配置关闭SessionFactory的二级缓存:
        hibernate.cache.use_second_level_cache false
原文章地址:http://blog.csdn.net/he90227/article/details/39577071
0 0
原创粉丝点击