Hibernate初始了解笔记汇集

一个User 属性id的类型定义错(定义为String)出项了错误,应为int,在many2one的工程中出项了异常,且数据库操作也发生了异常。
String做主键用uuid整数做主键用native

下面介绍一下hibernate的配置问题:
第一个hibernate项目

1、新建java项目

2、创建User Library,加入如下jar
 * HIBERNATE_HOME/hibernate3.jar
 * HIBERNATE_HOME/lib/*.jar
 * MySql jdbc驱动
 
3、创建hibernate配置文件hibernate.cfg.xml,为了便于调试最好加入log4j配置文件
如果不加入log4j的话,会出现异常。
下面是hibernate.cfg.xml文件的内容。

<!DOCTYPE hibernate-configuration PUBLIC
 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
 <property name="hibernate.connection.url">
  jdbc:mysql://localhost/hibernate_first
 </property>
 <property name="hibernate.connection.driver_class">
  com.mysql.jdbc.Driver
 </property>
 <property name="hibernate.connection.username">root</property>
 <property name="hibernate.connection.password">sunyuan</property>
 <property name="hibernate.dialect">
  org.hibernate.dialect.MySQLDialect
 </property>
 <property name="hibernate.show_sql">true</property>
 

 <mapping resource="com/bjsxt/hibernate/User.hbm.xml" />
</session-factory>
</hibernate-configuration>

4、定义实体类

5、定义User类的映射文件User.hbm.xml

6、将User.hbml.xml文件加入到
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.sy.hibernate.User">
  <id name="id">
   <generator class="uuid"/><!-- 主键设置方式,String类型的为uuid Int的是native-->
  </id>
  <property name="name"/>
  <property name="password"/>
  <property name="createTime"/>
  <property name="expireTime"/>
 </class>
</hibernate-mapping>hibernate.cfg.xml文件中其实就是<mapping resource="com/sy/hibernate/User.hbm.xml" />

7、编写hbm2ddl工具类,将实体类生成数据库表
主要代码:

  //读取hibernate.cfg.xml文件
  Configuration cfg = new Configuration().configure();
  
  SchemaExport export = new SchemaExport(cfg);
  
  export.create(true, true);

8、开发客户端
 
为了方便跟踪sql执行,在hibernate.cfg.xml文件中加入<property name="hibernate.show_sql">true</property>

 

测试实体对象的生命周期

junit简介:
 * 编写测试类xxxTest,需要继承TestCase即,junit.framework.TestCase
 * 编写单元测试方法,测试方法必须以test开头,测试方法不能含有参数和返回值,如:
   public void testHello1() {}
 * 最好单元测试的代码单独建立一个目录,解决这个问题可以采用新建一个SourceFolder,然后再建一个和源文件一样的名字的包即可。

 
了解Hibernate中CRUD操作

了解get和load的区别?
 * get不支持lazy,load支持lazy
就是说,//马上发出查询sql,加载User对象
   User user = (User)session.get(User.class, "402880d01b9bf210011b9bf2a2ff0001");

//不会发出查询sql,因为load方法实现了lazy(懒加载或延迟加载)
   //延迟加载:只有真正使用这个对象的时候,才加载(发出sql语句)如:System.out.println("user.name=" + user.getName());才加载。
   //hibernate延迟加载实现原理是代理方式
   User user = (User)session.load(User.class, "402880d01b9bf210011b9bf2a2ff0001");
 * 采用get加载数据,如果没有匹配的数据,返回null,而load则抛出异常
 
transient状态的特征?
 * 在数据库中没有与之匹配的数据
 * 没有纳入session的管理
 
persistent状态的特征?
 * persistent状态的对象在数据库中有与之匹配的数据
 * 纳入了session的管理
 * 在清理缓存(脏数据检查)的时候,会和数据库同步
 
detached状态的特征?
 * 在数据库中有与之匹配的数据
 * 没有纳入session的管理    
//手动构造的detached状态的对象即将id设为数据库中存在的即可。
User user = new User();
user.setId("43553545");//这里的id必须在数据库中存在。否则抛出org.hibernate.StaleObjectStateException

 
hibernate基本映射

实体类---表
实体类中的普通属性---表字段

采用<class>标签映射成数据库表,通过<property>标签将普通属性映射成表字段
所谓普通属性指不包括自定义类、集合和数组等

注意:如果实体类和实体类中的属性和sql中的关键字重复,必须采用table或column重新命名

实体类的设计原则:
 * 实现一个默认的(即无参数的)构造方法(constructor)
 * 提供一个标识属性(identifier property)(可选)
 * 使用非final的类 (可选)
 * 为持久化字段声明访问器(accessors) 

主键生成策略:
 uuid、native和assigned


hibernate一对一主键关联映射(单向关联Person---->IdCard)

一对一主键关联映射:让两个实体对象的id保持相同,这样可以避免多余的字段被创建

具体映射:

 <id name="id">
  <!-- person的主键来源idCard,也就是共享idCard的主键 -->
  <generator class="foreign">
   <param name="property">idCard</param>
  </generator>
 </id>
 <property name="name"/>
 <!-- one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,
 constrained="true", 表明当前主键上存在一个约束,person的主键作为外键参照了idCard 
  -->
 <one-to-one name="idCard" constrained="true"/>


hibernate一对一主键关联映射(双向关联Person<---->IdCard)

需要在idcard映射文件中加入<one-to-one>标签指向person,指示hibernate如何加载person
默认根据主键加载
如:IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.IdCard" table="t_idcard">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="cardNo"/>
  <one-to-one name="person"/>
 </class>
</hibernate-mapping>

Person.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Person" table="t_person">
  <id name="id">
   <generator class="foreign">
    <param name="property">idCard</param>
   </generator>
  </id>
  <property name="name"/>
  <one-to-one name="idCard" constrained="true"/>
 </class>
</hibernate-mapping>
如果为双向的映射,其实就是在原来单项映射的基础上,在idcard映射文件中加入<one-to-one>标签指向person,指示hibernate如何加载即:<one-to-one name="person"/>,其实两个相关联的实体相关的.hbm.xml文件,是相互的,无论哪一个写的具体,哪一个写的简单,都是可以的。就是所谓的此消彼长吧。


hibernate一对一唯一外键关联映射(单向关联Person---->IdCard)

一对唯一外键关联映射是多对一关联映射的特例

可以采用<many-to-one>标签,指定多的一端的unique=true,这样就限制了多的一端的

多重性为一。
<class name="Person" table="t_person">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="idCard" unique="true"/>
</class>

主要是unique属性的设置。

其中IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.IdCard" table="t_idcard">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="cardNo"/>
 </class>
</hibernate-mapping>

Person.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Person" table="t_person">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <many-to-one name="idCard" unique="true" column="asd"/>
 </class>
</hibernate-mapping>


hibernate一对一唯一外键关联映射(双向关联Person<---->IdCard)

一对一唯一外键关联双向,需要在另一端(idcard),添加<one-to-one>标签,指示hibernate如何加载
其关联对象,默认根据主键加载person,外键关联映射中,因为两个实体采用的是person的外键维护的关系,
所以不能指定主键加载person,而要根据person的外键加载,所以采用如下映射方式:
<one-to-one name="person" property-ref="idCard"/>

Person.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Person" table="t_person">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <many-to-one name="idCard" unique="true"/>
 </class>
</hibernate-mapping>

IdCard.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.IdCard" table="t_idcard">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="cardNo"/>
  <one-to-one name="person" property-ref="idCard"/>
 </class>
</hibernate-mapping>

 

 

session flush测试:

session flush方法主要做了两件事:
 * 清理缓存
 * 执行sql
 
session在什么情况下执行flush
 * 默认在事务提交时
 * 显示的调用flush
 * 在执行查询前,如:iterate
 
hibernate按照save(insert),update、delete顺序提交相关操作 

 


   //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
   //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
   session.save(user);
   
   //调用flush,hibernate会清理缓存,执行sql
   //如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
   //并且session中existsInDatebase状态为true
   session.flush();
   
   //提交事务
   //默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
   //commit后数据是无法回滚的
   tx.commit();

 

//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
   //纳入了session的管理,修改了session中existsInDatebase状态为true
   //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
   session.save(user);
   tx.commit();


//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
   //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
   session.save(user);
   
   //将user对象从session中逐出,即session的EntityEntries属性中逐出
   session.evict(user);
   
   //无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后
   //需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries
   //中逐出了,所以找不到相关数据,无法更新,抛出异常
   tx.commit();

 

//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
   //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
   session.save(user);
   
   //flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象
   //清除,并且设置session中existsInDatebase的状态为true
   session.flush();
   
   //将user对象从session中逐出,即session的EntityEntries属性中逐出
   session.evict(user);
   
   //可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
   //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
   tx.commit();

 

   //因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
   //纳入了session的管理,修改了session中existsInDatebase状态为true
   //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
   session.save(user);
   
   //将user对象从session中逐出,即session的EntityEntries属性中逐出
   session.evict(user);
   
   //可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
   //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
   tx.commit();

 User3 user = new User3();
   user.setId("001");
   user.setName("张三");
   
   session.save(user);
   
   user.setName("王五");
   session.update(user);
   
   User3 user3 = new User3();
   user3.setId("002");
   user3.setName("李四");
   session.save(user3);
//   
   //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
   //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
   //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?
   //hibernate按照save(insert),update、delete顺序提交相关操作,意思是无论上述的顺序怎么样,即使update在save前面,始终hibernate按照save(insert) ,update,delete的顺序执行。最后是delete


///

   User3 user = new User3();
   user.setId("003");
   user.setName("张三");
   
   session.save(user);
   
   user.setName("王五");
   session.update(user);
   
   session.flush();
   
   User3 user3 = new User3();
   user3.setId("004");
   user3.setName("李四");
   session.save(user3);
   
   //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
   //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?
   //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
   //因为我们在session.udpate(user)后执行了flush,所以在清理缓存时执行flush前的sql不会生成
   //sql会按照我们的意愿执行
   tx.commit();
这时,会执行按顺序。主要在于flush方法的执行。

 


hihernate一对多关联映射(单向Classes----->Student)
/
public class Classes {
 
 private int id;
 
 private String name;
 
 private Set students;
//
public class Student {
 
 private int id;
 
 private String name;

//
Classes.hbm.xml
///
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.hibernate">
 <class name="Classes" table="t_classes">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="students">
   <key column="classesid"/>
   <one-to-many class="Student"/>
  </set>
 </class>
</hibernate-mapping>


Student.hbm.xml
/
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Student" table="t_student">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
 </class>
</hibernate-mapping>


生成的表为Student和Classes两个表,其中Student表中包含属性有name id classid
Classes表中包含属性有name id
这是怎么做到的映射呢?下面主要介绍一下文件的配置。在Classes.hbm.xml中怎么将class的id映射到Student表中呢?
主要用到<set name="students">
<key column="classesid"/>
<one-to-many class="Student">
</set>这里的students是Classes类的属性。
而在Student.hbm.xml中实际要三个属性标签,而只写了两个属性,而classesid在Classes.hbm.xml中已经关联了。
改天再把hibernate映射的源码看看。

//
一对多关联映射利用了多对一关联映射原理

多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一
一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多

也就是说一对多和多对一的映射策略是一样的,只是站的角度不同。


在一一端维护关系的缺点:
 * 如果将t_student表里的classesid字段设置为非空,则无法保存

 * 因为不是在student这一端维护关系,所以student不知道是哪个班的,

   所以需要发出多余的update语句来更新关系

 

 

hihernate一对多关联映射(双向Classes<----->Student)

一对多双向关联映射:
 * 在一一端的集合上使用<key>,在对方表中加入一个外键指向一一端
 * 在多一端采用<many-to-one>
 
注意:<key>标签指定的外键字段必须和<many-to-one>指定的外键字段一致,否则引用字段的错误
 
如果在”一“一端维护一对多关联关系,hibernate会发出多余的udpate语句,所以我们一般在多
的一端来维护关联关系

inverse的值有两种,“true”和“false”。inverse="false"是默认的值
inverse的作用:在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的。

我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBC exception的throw。这是我们在建立实体类关系时必须需要关注的地方。一般来说,inverse=true是推荐使用,双向关联中双方都设置 inverse=false的话,必会导致双方都重复更新同一个关系。但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是 不行的,好在一对多中的一端:many-to-one默认是inverse=false,避免了这种错误的产生。但是多对多就没有这个默认设置了,所以很 多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。所以说,双向关联中最 好的设置是一端为inverse=true,一端为inverse=false。一般inverse=false会放在多的一端,那么有人提问了, many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。

 

关于inverse属性:
 inverse主要用在一对多和多对多双向关联上,inverse可以被设置到集合标签<set>上,
 默认inverse为false,所以我们可以从”一“一端和”多“一端维护关联关系,
 如果设置成inverse为true,则我们只能从多一端来维护关联关系
 
 注意:inverse属性,只影响数据的存储,也就是持久化
  
inverse和cascade
 * inverse是关联关系的控制方向


 * cascade操作上的连锁反应


双向关联:

Student.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Student" table="t_student">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <many-to-one name="classes" column="classesid"/>
 </class>
</hibernate-mapping>

Class.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.hibernate">
 <class name="Classes" table="t_classes">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="students" inverse="true" cascade="all">
   <key column="classesid"/>
   <one-to-many class="Student"/>
  </set>
 </class>
</hibernate-mapping>



hibernate多对多关联映射(单向User---->Role)
///
User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.User" table="t_user">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="roles" table="t_user_role">
   <key column="userid"/>
   <many-to-many class="com.bjsxt.hibernate.Role" column="roleid"/>
  </set>
 </class>
</hibernate-mapping>
Role.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Role" table="t_role">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
 </class>
</hibernate-mapping>

hibernate多对多关联映射(双向User<---->Role)

User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.User" table="t_user">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="roles" table="t_user_role">
   <key column="userid"/>
   <many-to-many class="com.bjsxt.hibernate.Role" column="roleid"/>
  </set>
 </class>
</hibernate-mapping>

Role.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.Role" table="t_role">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <set name="users" table="t_user_role" order-by="userid">
   <key column="roleid"/>
   <many-to-many class="com.bjsxt.hibernate.User" column="userid"/>
  </set>
 </class>
</hibernate-mapping>


映射方法:
  <set name="roles" table="t_user_role">
   <key column="userid"/>
   <many-to-many class="com.bjsxt.hibernate.Role" column="roleid"/>
  </set>
table属性值必须和单向关联中的table属性值一致
<key>中column属性值要与单向关联中的<many-to-many>标签中的column属性值一致
在<many-to-many>中的column属性值要与单向关联中<key>标签的column属性值一致


hibernate lazy策略可以使用在:
 * <class>标签上,可以取值:true/false
 * <property>标签上,可以取值:true/false需要类增强工具
 * <set><list>标签上,可以取值:true/false/extra
 * <one-to-one><many-to-one>单端关联上,可以取值:false/proxy/noproxy
 
lazy概念:只有真正使用该对象时,才会创建,对于hibernate而言,正真使用的时候才会发出sql

hibernate支持lazy策略只有在session打开状态下有效
例如:
Session session = null;
  Group group = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   group = (Group)session.load(Group.class, 1);
   
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
  
  //不能正确输出,抛出LazyInitializationException 异常,因为session已经关闭
  //hibernate支持lazy策略只有在session打开状态下有效
  System.out.println("group.name=" + group.getName());

<class>标签上的lazy特性只对普通属性起作用


class Classes
int id;String name;Set students;

class Student
int id;String name;Classes classes;

class.hbf.xml
<hibernate-mapping package="com.bjsxt.hibernate">
<class name="Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true" cascade="all" >
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>


设置<class>标签上的lazy=true,或者默认TRUE。其它默认
<class name="Classes" table="t_classes" lazy="true">

test1  
//不发sql
Classes classes = (Classes)session.load(Classes.class, 1);
//发sql,查询class
System.out.println("classes.name=" + classes.getName());
//不发sql
Set students = classes.getStudents();
//发sql,查询全部符合条件student的语句
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}

test2
//不发sql
Classes classes = (Classes)session.load(Classes.class, 1);
//发sql,查询class
System.out.println("classes.name=" + classes.getName());
//不发sql
Set students = classes.getStudents();
//发sql,查询全部符合条件student的语句
System.out.println("student.count=" + students.size());

设置<class>标签上的lazy=false,集合的lazy为默认(true)
<class name="Classes" table="t_classes" lazy="false">

test3
//发sql,查询student
Classes classes = (Classes)session.load(Classes.class, 1);
//不发sql
System.out.println("classes.name=" + classes.getName());
//不发sql
Set students = classes.getStudents();
//发sql,查询符合条件的student语句
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}

设置集合上的lazy=false,其它默认
<set name="students" inverse="true" cascade="all" lazy="false">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>

test4
//不发sql
Classes classes = (Classes)session.load(Classes.class, 1);
//不发sql
System.out.println("classes.name=" + classes.getName());
//发sql,分别查询出classes和student
Set students = classes.getStudents();
//不发sql
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}

test5
Classes classes = (Classes)session.load(Classes.class, 1);
//不发sql
System.out.println("classes.name=" + classes.getName());
//发sql,分别查询出classes和student
Set students = classes.getStudents();
//不发sql
System.out.println("student.count=" + students.size());


设置集合上的lazy=extra,其它默认
<set name="students" inverse="true" cascade="all" lazy="extra">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>

test6
//不发sql
Classes classes = (Classes)session.load(Classes.class, 1);
//发sql,查询出classes
System.out.println("classes.name=" + classes.getName());
//不发sql
Set students = classes.getStudents();
//发sql,查询出student
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}


test7  
//不发sql
Classes classes = (Classes)session.load(Classes.class, 1);
//发sql,查询出classes
System.out.println("classes.name=" + classes.getName());
//不发sql
Set students = classes.getStudents();
//发sql,智能的sql语句,查询出数目
System.out.println("student.count=" + students.size());

 
 
hibernate在单端关联上的lazy策略,可以取值:false/proxy/noproxy

<class>标签上的lazy不会影响到单端关联上的lazy特性

下面的User.hbm.xml
/.//
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.User" table="t_user" lazy="false">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <!--
  <many-to-one name="group" column="groupid" cascade="all"/>
   -->
   <many-to-one name="group" column="groupid" />
 </class>
</hibernate-mapping>
则这里的 <many-to-one name="group" column="groupid" lazy=""/>中lazy不受class中lazy的关联,就是说class中lazy的属性值与many-to-one中的lazy无关。如果,lazy设为false,那么在执行//不会发出sql
   User user = (User)session.load(User.class, 1);
   
   //会发出sql,发出两条sql分别加载User和Group
   System.out.println("user.name=" + user.getName());
时,会发出两条sql语句。lazy的延迟机制在这里不做解释了。
 原先看到在Hibernate3之后可以在Many to one 的关系映射中设置lazy属性,
  <many-to-one name="brother" cascade="none" lazy="no-proxy" insert="true" column="brotherId" outer-join="false" update="false">
  还以为和one to many一样,只要将lazy设为true就可以在不加载关联的对象呢,结果这个lazy的属性值只有proxy no-proxy 和false,试了其中任何一个值,结果都还是加载了关联的对象,后来想起class可以设置lazy的属性,就把关联的brother对象的类的lazy属性设为true,然后many to one中的lazy设为no-proxy后就实现了延迟加载,如果将many to one中的lazy属性设为false,系统还是会自动初始化brother对象。
  </many-to-one><class name="com.demo.bean.Brother" lazy="true" table="brother">
//
总之,class中的lazy只会对普通属性相关联,与集合,many-to-one one-to-many 都无关。//


每棵继承树映射成一张表

1、理解如何映射
  因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,必须有某种机制来区分哪些记录是属于哪个类的。
 这种机制就是,在表中添加一个字段,用这个字段的值来进行区分。用hibernate实现这种策略的时候,有如下步骤:
 父类用普通的<class>标签定义
 在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
 如:<discriminator column=”XXX” type=”string”/>
 子类使用<subclass>标签定义,在定义subclass的时候,需要注意如下几点:
 Subclass标签的name属性是子类的全路径名
 在Subclass标签中,用discriminator-value属性来标明本子类的discriminator字段(用来区分不同类的字段)
 的值Subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标
 签平行。 当subclass标签的定义与class标签平行的时候,需要在subclass标签中,添加extends属性,里面的值
 是父类的全路径名称。子类的其它属性,像普通类一样,定义在subclass标签的内部。

2、理解如何存储
 存储的时候hibernate会自动将鉴别字段值插入到数据库中,在加载数据的时候,hibernate能根据这个鉴别值
 正确的加载对象
 
多态查询:在hibernate加载数据的时候能鉴别出正真的类型(instanceOf)

get支持多态查询
load只有在lazy=false,才支持多态查询
hql支持多态查询 

在这个试验中,有三个类Animal、Bird、Pig,它们的关系是,Animal是基类,其他两个是子类,Brid除了Animal的属性外,还有一个属性height,Pig有weight,在hibernate中生成一个表t_animal
对应的xml文件为:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.hibernate">
 <class name="Animal" table="t_animal" lazy="false">
  <id name="id">
   <generator class="native"/>
  </id>
  <discriminator column="type" type="string"/><!--这里type="string"是hibernate自带的,与Java中的String没有关系。且该标签必须紧跟在标签<id>后-->
  <property name="name"/>
  <property name="sex"/>
  <subclass name="Pig" discriminator-value="P">
   <property name="weight"/>
  </subclass>
  <subclass name="Bird" discriminator-value="B">
   <property name="height"/>
  </subclass>
 </class>
</hibernate-mapping>
 
部分代码:

session = HibernateUtils.getSession();
   session.beginTransaction();
   
   Animal animal = (Animal)session.load(Animal.class, 1);
   
   //因为load默认只是lazy,因为我们看到的是Animal的代理对象
   //所以通过instanceof是反应不出正真的对象类型的
   //因此load在默认情况下是不支持多态查询的
   if (animal instanceof Pig) {
    System.out.println(animal.getName());
   }else {
    System.out.println("不是猪");
   }
   session.getTransaction().commit();


上述中,介绍了怎么生成一张表,但是每个类都生成一个表,包括Animal类对应的表。类的关联和上述一样。
对应的xml文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.hibernate">
 <class name="Animal" table="t_animal">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <property name="sex"/>
  <joined-subclass name="Pig" table="t_pig">
   <key column="pid"/>
   <property name="weight"/>
  </joined-subclass>
  <joined-subclass name="Bird" table="t_bird">
   <key column="bid"/>
   <property name="height"/>
  </joined-subclass>
 </class>
</hibernate-mapping>

这和生成一张表的区别是:前者用到<joined-subclass>而一张表的是<subclass>其他的细节这里不再叙述。

get支持多态查询,load不支持多态查询。只有将lazy设置为false,才支持多态查询,否则实行的是代理proxy

那么,将每个具体类都映射成一张表,即生成t_bird和t_pig表分别含有字段
id name sex height
id name sex weight
而父类Animal不映射成表。
///
对应的xml文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.hibernate">

 <class name="Animal" abstract="true">
  <id>
   <generator class="assigned"/>
  </id>
  <property name="name"/>
  <property name="sex"/>
  <union-subclass  name="Pig" table="t_pig">
   <property name="weight"/>
  </union-subclass>
  <union-subclass name="Bird" table="t_bird">
   <property name="height"/>
  </union-subclass>
 </class>
</hibernate-mapping>

component映射

在hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,
component可以成为是值对象(DDD)

采用component映射的好处:它实现了对象模型的细粒度划分,层次会更分明,复用率会更高

User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.bjsxt.hibernate.User" table="t_user">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>
  <component name="contact">
   <property name="email"/>
   <property name="address"/>
   <property name="zipCode"/>
   <property name="contactTel"/>
  </component>
 </class>
</hibernate-mapping>

类的关系:
public class Contact {
 
 private String email;
 
 private String address;
 
 private String zipCode;
 
 private String contactTel;

public class User {
 
 private int id;
 
 private String name;
 
 private Contact contact;
生成表t_user的字段:
id name email address zipCode contactTel


下面介绍:
 复合(联合)主键映射

通常将复合主键相关的属性,单独放到一个类中
 * 此类必须实现序列化接口
 * 覆写hashcode和equals方法
///

复合主键映射
这里假设一种情况:一个公司财务核算,核算日期由两个数字确定:年份和月份
这个时候我们就遇到一个问题:一个表有两个主键联合约束和上面的component映射是有着质的区别。component映射,映射的身体的部分零件,但,不是标准性的零件。意思就是映射的涉及不到主键(个人现阶段的观点)。component映射
对于这种符合主键,我们在设计实体类的时候把复合主键单独放在一个类中,由对应的实体类去引用它。但是这个保存主键的类在设计的时候需要注意一下两点
此类必须实现序列化接口(Serializable)。
因为当一个类可能会跨jvm使用的时候,就必须实现序列化接口
实现Serializable以后这个类的对象就可以远程传输或者保存到硬盘上。
覆写hashcode和equals方法。

下面是这个类的具体设计
首先是保存主键的类
注意这里实现Serializable
public class FiscalYearPeriodPK implements Serializable {
 //核算年
 private int fiscalYear;
 //核算月
 private int fiscalPeriod;
注意要覆盖hashCode和equals方法
这两个方法,系统会各产生两种不同的方法,自己只有身临其境,才会用感觉。
@Override
 public int hashCode() {

 }

 @Override
 public boolean equals(Object obj) {

 }
}

@Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + fiscalPeriod;
  result = prime * result + fiscalYear;
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  final FiscalYearPeriodPKCopy other = (FiscalYearPeriodPKCopy) obj;
  if (fiscalPeriod != other.fiscalPeriod)
   return false;
  if (fiscalYear != other.fiscalYear)
   return false;
  return true;
 }
下面是实体类的设计
public class FiscalYearPeriod {
在实体类中引用了刚才定义的主键类
 private FiscalYearPeriodPK fiscalYearPeriodPK;
 //开始日期
 private Date beginDate;
 //结束日期
 private Date endDate;
}

下面是映射文件的写法

<hibernate-mapping>
 <class name="com.itcast.hibernate.FiscalYearPeriod" table="t_fiscal_year_period">
  <composite-id name="fiscalYearPeriodPK">
   <key-property name="fiscalYear"/>
   <key-property name="fiscalPeriod"/>
  </composite-id>
  <property name="beginDate"/>
  <property name="endDate"/>
  <property name="periodSts"/>
 </class>
</hibernate-mapping>
这样做,我们在对象模型上看到一个两个类,但是在数据库表还是一个表


各种集合关系的映射
对下面四种集合进行分析
1.set
2.list
3.array
4.map

实体类实际如下
public class CollectionMapping {
 private int id;
 private String name;
 private Set setValue;
 private List listValue;
 private String[] arrayValue;
 private Map mapValue;
}
对于集合类的映射,都是通过附加一个关联表来进行的

 

 

 

 

 

主表:
CollectionMapping
id name

Set表
由于set内部是无序的,所以这里只要其主键是以主表主键为外键就可以了
setid setValue

List表
由于List内部是有序的,所以表生成的时候需要加上一个index序号标记
listId listValue index
Map表
由于其内部是key-value结构,所以表中也要同时保存key和value
mapId mapKey mapValue

其具体一身方式
Set
<hibernate-mapping>
 <class name="com.itcast.hibernate.CollectionMapping" table="t_CollectionMapping">
  <id name="id">
   <generator class="native"/>
  </id>
  <property name="name"/>

  <set name="setValue" table="t_set_value">
这个key 表示t_set_value的主键是以当前父表的主键为外键的
   <key column="set_id"/>
string是数据库中属性,会自动保存为varchar
   <element type="string" column="set_value"/>
  </set>

  <list name="listValue" table="t_list_value">
   <key column="list_id"/>
   <list-index column="list_index"/>
   <element type="string" column="list_value"/>
  </list>

  <array name="arrayValue" table="t_array_value">
   <key column="array_id"/>
   <list-index column="array_index"/>
   <element type="string" column="array_value"/>
  </array>

  <map name="mapValue" table="t_map_value">
   <key column="map_id"/>
   <map-key type="string" column="map_key"/>
   <element type="string" column="map_value"/>
  </map>
 </class>
</hibernate-mapping>

其中各个表中XX_id都和表t_CollectionMapping 中的id关联。即一样(我的感觉,呵呵)

悲观锁:
悲观锁的实现,通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改。

下面介绍一下悲观锁:
public class Inventory {

 private int itemNo;
 
 private String itemName;
 
 private int quantity;


Inventory.hbm.xml

<class name="com.bjsxt.hibernate.Inventory" table="t_inventory">
  <id name="itemNo">
   <generator class="native"/>
  </id>
  <property name="itemName"/>
  <property name="quantity"/>
 </class>


关于悲观锁的测试核心代码:
public void testLoad1() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   Inventory inv = (Inventory)session.load(Inventory.class, 2, LockMode.UPGRADE);
//主要是该方法:中参数LockMode.UPGRADE的设置。
   System.out.println("itemName=" + inv.getItemName());
   System.out.println("quantity=" + inv.getQuantity());
   inv.setQuantity(inv.getQuantity() - 200);
   session.update(inv);
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  } 
 }
//
 public void testLoad2() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   Inventory inv = (Inventory)session.load(Inventory.class, 2, LockMode.UPGRADE);
   System.out.println("itemName=" + inv.getItemName());
   System.out.println("quantity=" + inv.getQuantity());
   inv.setQuantity(inv.getQuantity() - 200);
   session.update(inv);
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  } 
 }

乐观锁

大多数基于数据版本记录机制(version)实现,一般是在数据库表中加入一个version字段
读取数据时将版本号一同读出,之后更新数据时版本号加一,如果提交数据时版本号小于或等于数据表中
的版本号,则认为数据是过期的,否则给予更新


下面介绍一下乐观锁:
public class Inventory {

 private int itemNo;
 
 private String itemName;
 
 private int quantity;
 
 private int version;

hbm.xml
<class name="com.bjsxt.hibernate.Inventory" table="t_inventory" optimistic-lock="version">
  <id name="itemNo">
   <generator class="native"/>
  </id>
  <version name="version"/>
  <property name="itemName"/>
  <property name="quantity"/>
 </class>

public void testLoad1() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   Inventory inv = (Inventory)session.load(Inventory.class, 1);
   System.out.println("itemName=" + inv.getItemName());
   System.out.println("version=" + inv.getVersion());
   System.out.println("quantity=" + inv.getQuantity());
   inv.setQuantity(inv.getQuantity() - 200);
   session.update(inv);
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  } 
 }
执行一次version会增加1,如果某操作的version的值小于当前的,那么它就不会执行该操作。每执行一次该对应的对象的操作,该操作的version的值就会增加1操作,就是自加。意思应该明白。(这里定义的version为int类型的)


下面介绍一下:
、、、、、、、、、、、、、、、、、、、、
public class Node {

 // 标识符
 private int id;

 // 节点名称
 private String name;

 // 层次
 private int level;

 // 是否叶子节点
 private boolean leaf;

 // 父节点 * --- 1
 private Node parent;

 // 子节点 1 --- *
 private Set children;
、、、、、、、、、、、、、、、、、、、、
 <many-to-one name="parent" column="pid"/>
<set name="children" lazy="extra" inverse="true">
         <key column="pid"/>
         <one-to-many class="com.bjsxt.hibernate.Node"/>
        </set>

<set>默认值是 true可选值是 true|false|extraLazy = false 一次性查询所需要的所有信息的sql语句,可能包含多条sql语句Lazy = true根据需要,发出相应的查询语句Lazy = extra, 和true一样,但是如果你只想查询大小,只会发出 select count()语句建议: true 或者 extra

一对多的维护(inverse="true"):表示只能在多的一段维护。如果new 一个Node ,那么就会先调用setParent

 

如果例如,像一个对象的id等于另一个的对象的pid时,如果父类加载成功,那么它对应的子类也会跟着加载。通过pid来维护的。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

 

 

 

 

 

 

 


hibernate查询语言hql

在hql中关键字不区分大小写,但是属性和类名区分大小写

1、简单属性查询【重要】
 * 单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致
 * 多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致
   数组的长度取决与select中属性的个数
 * 如果认为返回数组不够对象化,可以采用hql动态实例化Student对象
 参见:public class SimplePropertyQueryTest extends TestCase {
 
 /**
  * 单一属性查询
  */
 public void testQuery1() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   //返回结果集属性列表,元素类型和实体类中相应的属性类型一致
   List students = session.createQuery("select name from Student").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    String name = (String)iter.next();
    System.out.println(name);
   }
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
 }

 /**
  * 多个属性查询
  */
 public void testQuery2() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   //查询多个属性,其集合元素是对象数组
   //数组元素的类型和对应的属性在实体类中的类型一致
   //数组的长度取决与select中属性的个数
   List students = session.createQuery("select id, name, createTime from Student").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]+ "," + obj[2]);
   }
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
 }

 /**
  * 返回Student实体对象
  */
 public void testQuery3() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   //如果认为返回数组不够对象化,可以采用hql动态实例化Student对象
   //此时list中为Student对象集合
   List students = session.createQuery("select new Student(id, name) from Student").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    Student student = (Student)iter.next();
    System.out.println(student.getId() + "," + student.getName());
   }
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
 }

 /**
  * 使用别名
  */
 public void testQuery4() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   //可以使用别名
   List students = session.createQuery("select s.id, s.name from Student s").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]);
   }
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
 }

 /**
  * 使用别名
  */
 public void testQuery5() {
  Session session = null;
  try {
   session = HibernateUtils.getSession();
   session.beginTransaction();
   
   //可以使用as命名别名
   List students = session.createQuery("select s.id, s.name from Student as s").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]);
   }
   session.getTransaction().commit();
  }catch(Exception e) {
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally {
   HibernateUtils.closeSession(session);
  }
 }
 
}


2、实体对象查询【重要】
 * N + 1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
   所谓的N+1是在查询的时候发出了N+1条sql语句
   1: 首先发出一条查询对象id列表的sql
   N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句

 /**
    * 出现N+1问题
    *
    * 1:发出查询id列表的sql
    *   Hibernate: select student0_.id as col_0_0_ from t_student student0_
    *
    * N:在依次发出根据id查询Student对象的sql
    * Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_,
    * student0_.createTime as createTime1_0_, student0_.classesid as classesid1_0_
    * from t_student student0_ where student0_.id=?
    * 
    */
   Iterator iter = session.createQuery("from Student").iterate();

/**
    * 采用list查询发出一条查询语句,取得Student对象数据、
    *
    * Hibernate: select student0_.id as id1_, student0_.name as name1_,
    * student0_.createTime as createTime1_, student0_.classesid as classesid1_
    * from t_student student0_
    *
    */
   List students = session.createQuery("from Student").list();

List students = session.createQuery("from Student").list();

Iterator iter = session.createQuery("from Student").iterate();

   /**
    * 不会出现N+1问题
    *
    * 因为list操作已经将Student对象放到了一级缓存中,所以再次使用iterate操作的时候
    * 它首先发出一条查询id列表的sql,在根据id到缓存中去数据,只有在缓存中找不到相应的
    * 数据时,才会发出sql到数据库中查询
    *
    */

List students = session.createQuery("from Student").list();

   /**
    * 再次发出查询sql
    *
    * 在默认情况下list每次都会向数据库发出查询对象的sql,除非配置查询缓存,所以下面的list操作
    * 虽然在一级缓存中已经有了对象数据,但list默认情况下不会利用缓存,而再次发出sql
    *
    * 默认情况下,list会向缓存中放入数据,但不会利用数据
    *
    */
   students = session.createQuery("from Student").list();


 * list和iterate的区别?
  * list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
  * iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题

 参见:
///
//返回Student对象的集合
   //可以忽略select
   List students = session.createQuery("from Student").list();
/返回Student对象的集合
//可以忽略select,表可以使用别名
   List students = session.createQuery("from Student s").list();
///

//返回Student对象的集合
   //可以忽略select,表可以使用as命名别名
   List students = session.createQuery("from Student as s").list();
///
//返回Student对象的集合
   //使用select查询实体对象,必须采用别名
   List students = session.createQuery("select s from Student as s").list();

//不支持select * from .....这样的查询语句
   List students = session.createQuery("select * from Student").list();


 
3、条件查询【重要】 

 * 可以采用拼字符串的方式传递参数
List students = session.createQuery("select s.id, s.name from Student s where s.name like '%1%'").list();

 * 可以采用 ?来传递参数(索引从0开始)
可以这样写:
Query query = session.createQuery("select s.id, s.name from Student s where s.name like ?");
query.setParameter(0, "%1%");
List students = query.list();

也可以这样写:
//可以使用?方式传递参数
//参数的索引从0开始
//传递的参数值,不用单引号引起来
//注意方法链编程
List students = session.createQuery("select s.id, s.name from Student s where s.name like ?").setParameter(0, "%1%").list();

 

 * 可以采用 :参数名 来传递参数
List students = session.createQuery("select s.id, s.name from Student s where s.name like:myname")
.setParameter("myname", "%1%")
.list();

 * 如果传递多个参数,可以采用setParamterList方法

List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname and s.id=:myid")
  .setParameter("myname", "%1%")
               .setParameter("myid", 12)
               .list();

 *支持in,需要使setParameterList进行参数传递
List students = session.createQuery("select s.id, s.name from Student s where s.id in(:myids)")
  .setParameterList("myids", new Object[]{1, 2, 3, 4, 5})
  .list();

生成的Hibernate Hql:Hibernate: select student0_.id as col_0_0_, student0_.name as col_1_0_ from t_student student0_ where student0_.id in (? , ? , ? , ? , ?)

        *查询2008年2月创建的学生
List students = session.createQuery("select s.id, s.name from Student s where date_format(s.createTime, '%Y-%m')=?")
 .setParameter(0, "2008-02")
 .list();

 *查询2008-01-10到2008-02-15创建的学生

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

List students = session.createQuery("select s.id, s.name from Student s where s.createTime between ? and ?")
.setParameter(0, sdf.parse("2008-01-10 00:00:00"))
.setParameter(1, sdf.parse("2008-02-15 23:59:59"))
.list();

 *查询2008-01-10到2008-02-15创建的学生


SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

List students = session.createQuery("select s.id, s.name from Student s where s.createTime between ? and ?")
         .setParameter(0, sdf.parse("2008-01-10 00:00:00"))
         .setParameter(1, sdf.parse("2008-02-15 23:59:59"))
               .list();
对应的hql:
Hibernate: select student0_.id as col_0_0_, student0_.name as col_1_0_ from t_student student0_ where student0_.createTime between ? and ?
///

 * 在hql中可以使用数据库的函数,如:date_format
 参见:


 *查询2008-01-10到2008-02-15创建的学生


SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

List students = session.createQuery("select s.id, s.name from Student s where s.createTime between ? and ?")
         .setParameter(0, sdf.parse("2008-01-10 00:00:00"))
         .setParameter(1, sdf.parse("2008-02-15 23:59:59"))
               .list();


   
4、hibernate也支持直接使用sql进行查询
 *原生sql测试
List students = session.createSQLQuery("select * from t_student").list();
次方法可以,但是session.createQuery()不可以执行带*的语句。
///

5、外置命名查询
 * 在映射文件中采用<query>标签来定义hql,不限制哪一个映射文件。其中的query 的name名不要重复了,这一个不要重的范围是,所有的映射文件。
<query name="searchStudents">
  <![CDATA[
   SELECT s FROM Student s where s.id<?
  ]]>
 </query>

 * 在程序中采用session.getNamedQuery()方法得到hql查询串

List students = session.getNamedQuery("searchStudents")
  .setParameter(0, 10)
  .list();

 参见:Student.hbm.xml、NameQueryTest.java
 
6、查询过滤器 
 * 在映射文件中定义过滤器参数
<filter-def name="filtertest">
  <filter-param name="myid" type="integer"/>
 </filter-def> 
在映射文件中定义过滤器参数,最好不要重名,它可以被多个类的映射中使用。

 * 在类的映射中使用这些参数
即,在class标签中使用,可以说是引用吧,
<filter name="filtertest" condition="id &lt; :myid"/>

 * 在程序中启用过滤器
session.enableFilter("filtertest")
 .setParameter("myid", 10);
 
List students = session.createQuery("from Student").list();

 
7、分页查询【重要】 
 * setFirstResult(),从0开始
 * setMaxResults,每页显示多少条数据
 参见:List students = session.createQuery("from Student")
 .setFirstResult(1)
 .setMaxResults(2)
 .list();

    
8、对象导航查询,在hql中采用 . 进行导航【重要】
 参见:
List students = session.createQuery("select s.name from Student s where s.classes.name like '%1%'").list();
其中classes是Student中的参数,其实就是关联的一个类。 
9、连接查询【重要】
 * 内连
List students = session.createQuery("select c.name, s.name from Student s inner join s.classes c").list();
产生的hql:
Hibernate: select classes1_.name as col_0_0_, student0_.name as col_1_0_ from t_student student0_ inner join t_classes classes1_ on student0_.classesid=classes1_.id

List students = session.createQuery("select c.name, s.name from Student s join s.classes c").list();
对应的hql:
Hibernate: select classes1_.name as col_0_0_, student0_.name as col_1_0_ from t_student student0_ inner join t_classes classes1_ on student0_.classesid=classes1_.id

 * 外连接(左连接/右连接)

List students = session.createQuery("select c.name, s.name from Classes c left join c.students s").list();

对应的Hql:
Hibernate: select classes0_.name as col_0_0_, students1_.name as col_1_0_ from t_classes classes0_ left outer join t_student students1_ on classes0_.id=students1_.classesid

List students = session.createQuery("select c.name, s.name from Classes c right join c.students s").list();

对应的Hql:
Hibernate: select classes0_.name as col_0_0_, students1_.name as col_1_0_ from t_classes classes0_ right outer join t_student students1_ on classes0_.id=students1_.classesid
 

 
10、统计查询【重要】
两种情况:
List students =session.createQuery("select count(*) from Student").list();
Long count = (Long)students.get(0); System.out.println(count);
输出:
Hibernate: select count(*) as col_0_0_ from t_student student0_
110

Long count = (Long)session.createQuery("select count(*) from Student").uniqueResult();   System.out.println(count);
输出:
Hibernate: select count(*) as col_0_0_ from t_student student0_
110

List students =session.createQuery("select c.name, count(s) from Student s join s.classes c " +
     "group by c.name order by c.name").list();
   for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + ", " + obj[1]);
   }

输出结果:
Hibernate: select classes1_.name as col_0_0_, count(student0_.id) as col_1_0_ from t_student student0_ inner join t_classes classes1_ on student0_.classesid=classes1_.id group by classes1_.name order by classes1_.name
班级0, 10
班级1, 10
班级2, 10
班级3, 10
班级4, 10
班级5, 10
班级6, 10
班级7, 10
班级8, 10
班级9, 10

 


11、DML风格的操作(尽量少用,因为和缓存不同步)
 session.createQuery("update Student s set s.name=? where s.id < ?")
     .setParameter(0, "李四")
     .setParameter(1, 5)
     .executeUpdate();
 参见:DMLQueryTest.java


hibernate一级缓存
 
一级缓存很短和session的生命周期一致,一级缓存也叫session级的缓存或事务级缓存

那些方法支持一级缓存:
 * get()
 * load()
 * iterate(查询实体对象)
 
如何管理一级缓存:
 * session.clear(),session.evict()
 
如何避免一次性大量的实体数据入库导致内存溢出
 * 先flush,再clear

for (int i=0; i<1000; i++) {
    Student student = new Student();
    student.setName("s_" + i);
    session.save(student);
    //每20条数据就强制session将数据持久化
    //同时清除缓存,避免大量数据造成内存溢出
    if ( i % 20 == 0) {
     session.flush();
     //session.evict(student);只是清除某个对象。
     session.clear();
    }
   }

如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具     


hibernate二级缓存

二级缓存也称进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享
二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存

二级缓存的配置和使用:
 * 将echcache.xml文件拷贝到src下
echcache.xml
<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->

    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> -->

    <!-- Place configuration for your caches following -->

</ehcache>

 * 开启二级缓存,修改hibernate.cfg.xml文件
  <property name="hibernate.cache.use_second_level_cache">true</property>
 * 指定缓存产品提供商,修改hibernate.cfg.xml文件
  <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
 * 指定那些实体类使用二级缓存(两种方法)
  * 在映射文件中采用<cache>标签
  * 在hibernate.cfg.xml文件中,采用<class-cache>标签
  
二级缓存是缓存实体对象的

了解一级缓存和二级缓存的交互  
  

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值