Hibernate的基础

Hibernate基础语义
基础配置
Hibernate O/R映射
数据关联
Hibernate数据检索
HQL实用技术

Hibernate基础语义
Configuration
SessionFactory
Session

Configuration
正如其名,Configuration类负责管理Hibernate的配置信息。Hibernate运行时需要获取一些底层的基本信息,其中几个关键属性包括:
       
加图config

Configuration的使用:
//Hibernate框架自动加载CLASSPATH中的hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure();
//Hibernate框架加载指定的hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure(path);

SessionFactory
SessionFactory负责创建Session实例。我们可以通过Configuation实例构建SessionFactory:
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory=cfg.buildSessionFactory();
Configuration实例cfg会根据当前的数据库配置信息,构造SessionFactory实例并返回。SessionFactory一旦构造完毕,即被赋予特定的配置信息。
也就是说,之后cfg的任何变更将不会影响到已经创建的SessionFactory实例。如果需要使用基于改动后的cfg实例的SessionFactory,需要从cfg重新构建一个SessionFactory实例。同样,如果应用中需要访问多个数据库,那么针对每个数据库,应分别为其创建对应的SessionFactory实例。

Session
Session是Hibernate持久化操作的基础。注意这里Session的含义,它与传统意义上Web层的HttpSession并没有什么关系。Hibernate Session之与Hibernate,相当于JDBC Connection相对与JDBC。
Session作为贯穿Hibernate的持久化管理器核心,提供了众多持久化方法,如save、update、delete、load等。通过这些方法,我们即可透明地完成对象的增删改查(CRUD)。
同时,值得注意的是,Hibernate Session的设计是非线程安全的,也就是说,一个Session实例同时只可由一个线程使用,同一个Session实例的多线程并发调用将导致难以预知的错误。
Session实例由SessionFactory构建:
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory=cfg.buildSessionFactory();
Session session= sessionFactory.openSession();
之后我们就可以调用Session所提供的save、update、delete、load等方法完成持久层操作。

基础配置
在“快速起步”部分,我们已经涉及到Hibernate配置文件的相关内容。为了达到最简明的效果,之前的示例中我们仅仅引入了一些最基本的配置条目。而在此之外,Hibernate还提供了更加丰富多样的选项设定,我们可以结合实际情况,根据应用特点对这些选项进行调整,以获得更加符合需求的匹配模式和更好的性能表现。
运行期生成的SQL:
<property name="show_sql">true</property>
数据库连接池的配置:
<property name="connection.pool_size">10</property>
事务管理:
<property name="transaction.factory_class">
    org.hibernate.transaction.JDBCTransactionFactory
</property>

Hibernate O/R映射
O/R映射关系无疑是ORM框架中最为关键的组成部分,也是日常开发中我们必须时刻关注的内容。我们将就Hibernate日常开发中最为关键的映射关系进行探讨。

 加入图片RO图片

类/表映射配置
<class name="User1" table="user1" catalog="sample">
    ……
</class>

参数    描述
name    类名
table    类对应的表名
catalog    数据库目录

id映射配置
<id name="id" type="java.lang.Integer">
    <column name="id" />
    <generator class="assigned" />
</id>

参数    描述
name    映射类中对应主键的属性名
type    上述属性的数据类型
column    主键字段名
class    主健生成方式

assigned、increment、identity、sed.hex、foreign

属性/字段映射配置
<property name="name" type="java.lang.String">
    <column name="name" length="45" not-null="true" />
</property>

参数    描述
name    映射类属性名称
type    上述属性的数据类型
column    对应数据库表字段名
length    数据库类型长度
not-null    字段是否允许为空

复合主键
Hibernate中,通过composite-id节点对复合主键进行定义。
为了说明复合主键的使用,我们以user2表为蓝本,将其name属性拆分为两部分:firstname、lastname,以这两个字段作为复合主键,由此得到user3表。
CREATE TABLE `sample`.`user3` (
  `firstname` VARCHAR(45) NOT NULL DEFAULT '',
  `lastname` VARCHAR(45) NOT NULL DEFAULT '',
  `age` INTEGER NOT NULL DEFAULT 0,
  PRIMARY KEY(`firstname`, `lastname`)
)
举例:
主键类
package fire;

public class User3Id implements java.io.Serializable {
    private String firstname;
    private String lastname;
    public User3Id() {}
    public User3Id(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }
    ……
    public boolean equals(Object other) {
        ……
    }
    public int hashCode() {
        ……
    }
}

实体类
package fire;

public class User3 implements java.io.Serializable {
    private User3Id id;
    private Integer age;
    public User3() {
    }
    public User3(User3Id id, Integer age) {
        this.id = id;
        this.age = age;
    }
    ……
}

Hibernate映射文件
<hibernate-mapping>
    <class name="fire.User3" table="user3" catalog="sample">
        <composite-id name="id" class="fire.User3Id">
            <key-property name="firstname" type="java.lang.String">
                <column name="firstname" length="45" />
            </key-property>
            <key-property name="lastname" type="java.lang.String">
                <column name="lastname" length="45" />
            </key-property>
        </composite-id>
        <property name="age" type="java.lang.Integer">
            <column name="age" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

Hibernate配置文件
<hibernate-configuration>
     <session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
        jdbc:mysql://localhost:3306/sample
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="fire/User3.hbm.xml" />
     </session-factory>
</hibernate-configuration>

测试类
插入数据:
User3Id userid=new User3Id();
userid.setFirstname("a");
userid.setLastname("b");
User3 user=new User3();
user.setId(userid);
user.setAge(20);
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
s.save(user);
t.commit();
HibernateSessionFactory.closeSession();
查询数据:
User3Id userid=new User3Id();
userid.setFirstname("a");
userid.setLastname("b");
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
User3 user=(User3) s.load(User3.class,userid);
System.out.println(user.getAge());
t.commit();
HibernateSessionFactory.closeSession();


Blob、Clob字段的映射
在许多情况下,我们需要在库表中保存大型字符串,或者二进制数据。Hibernate中也提供了对Blob、Clob类型的内置支持。
Blob和Clob字段的区别在于,Blob字段采用字节存储,适合保存二进制数据,如图片文件,Clob字段采用多字节存储,适合保存大型文本数据。
对于user1表而言,假设我们需要为用户增加两个大型字段,其image字段用于保存照片(Blob),resume字段用于保存简历(Clob)。
经过改造的user4表结构如下:
CREATE TABLE  `sample`.`user4` (
  `id` varchar(32) NOT NULL default '',
  `name` varchar(45) NOT NULL default '',
  `image` blob,
  `resume` text,
  PRIMARY KEY  (`id`)
)
举例:
实体类
package fire;

import java.sql.Blob;
import java.sql.Clob;

public class User4 implements java.io.Serializable {
    private String id;
    private String name;
    private Blob image;
    private Clob resume;
    ……
}

Hibernate映射文件
<hibernate-mapping>
  <class name="fire.User4" table="user4" catalog="sample">
    <id name="id" type="java.lang.String">
      <column name="id" length="32" />
      <generator class="uuid.hex" />
    </id>
    <property name="name" type="java.lang.String">
      <column name="name" length="45" not-null="true" />
    </property>
    <property name="image" type="java.sql.Blob" column="image">
    </property>
    <property name="resume" type="java.sql.Clob" column="resume">
    </property>
  </class>
</hibernate-mapping>

Hibernate配置文件
<hibernate-configuration>
     <session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
        jdbc:mysql://localhost:3306/sample
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="fire/User4.hbm.xml" />
     </session-factory>
</hibernate-configuration>

测试类
插入数据:
User4 user = new User4();
user.setName("zs");
FileInputStream imgis = new FileInputStream("a.jpg");
Blob img = Hibernate.createBlob(imgis);
user.setImage(img);
Clob resume = Hibernate.createClob("This is Clob");
user.setResume(resume);
Session s = HibernateSessionFactory.getSession();
Transaction t = s.beginTransaction();
s.save(user);
t.commit();
HibernateSessionFactory.closeSession();
查询数据:
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
User4 user=(User4) s.load(User4.class,"297e421018bd28a80118bd28aac30001");
Clob resume=user.getResume();
//通过Clob.getSubString()方法获取Clob字段内容
System.out.println(resume.getSubString(1,(int)resume.length()));
Blob img=user.getImage();
//通过Blob.getBinaryStream()方法获取二进制流
InputStream is=img.getBinaryStream();
FileOutputStream fos=new FileOutputStream("b.jpg");
byte[] buf=new byte[102400];int len;
while((len=is.read(buf))!=-1){
    fos.write(buf,0,len);
}
fos.close();is.close();t.commit();
HibernateSessionFactory.closeSession();

数据关联
在前面的内容中,我们讨论了基于Hibernate的实体映射技术及其设计上的一些通用策略。对于ORM而言,另一个非常关键的特性,就是对实体之间关联关系的管理。
数据关联是ORM的一个重要特征,但往往也是导致系统性能低下的原因,不良的关联设计会对系统的性能表现产生致命的影响,在实际开发中我们需要特别注意这一点。

常用的数据关联:
一对一并联
一对多并联

一对一关联
一个典型的一对一关联的实例:中国公民只允许拥有一份护照,这里我们把用户[user5]和护照[passport]设定为一对一关系:
CREATE TABLE  `sample`.`user5` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(45) NOT NULL default '',
  `age` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id`)
)
CREATE TABLE  `sample`.`passport` (
  `id` int(11) NOT NULL default '0',
  `serial` varchar(30) NOT NULL default '',
  PRIMARY KEY  (`id`),
  CONSTRAINT `FK_passport` FOREIGN KEY (`id`) REFERENCES `user5` (`id`)
)
举例:
实体类
User5.java
package fire;

public class User5 implements java.io.Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private Passport passport;
    ……
    public Passport getPassport() {
        return passport;
    }
    public void setPassport(Passport passport) {
        this.passport = passport;
    }
}

Passport.java
package fire;

public class Passport implements java.io.Serializable {
    private Integer id;
    private String serial;
    private User5 user;
    ……
    public User5 getUser() {
        return user;
    }
    public void setUser(User5 user) {
        this.user = user;
    }
}

Hibernate映射文件
User5.hbm.xml
<hibernate-mapping>
  <class name="fire.User5" table="user5" catalog="sample">
    <id name="id" type="java.lang.Integer">
      <column name="id" />
      <!-- 自动采用数据库的主键生成方式 -->
      <generator class="native" />
    </id>
    <property name="name" type="java.lang.String">
      <column name="name" length="45" not-null="true" />
    </property>
    <property name="age" type="java.lang.Integer">
      <column name="age" not-null="true" />
    </property>
    <!-- 1.通过one-to-one节点,将User5类与Passport类相关联
           2.级联关系设置为all,它指的是关联对象是否同步执行同一操作 -->
    <one-to-one name="passport" class="fire.Passport" cascade="all" />
  </class>
</hibernate-mapping>
Passport.hbm.xml
<hibernate-mapping>
  <class name="fire.Passport" table="passport" catalog="sample">
    <id name="id" type="java.lang.Integer">
      <column name="id" />
      <!-- 通过foreign类型的主键生成器与外键共享主键值 -->
      <generator class="foreign">
        <param name="property">user</param>
      </generator>
    </id>
    <property name="serial" type="java.lang.String">
      <column name="serial" length="30" not-null="true" />
    </property>
    <!-- constrained属性必须设定为true,以告知Hibernate当前表主键上存在一个约束:passport表引用了user5表的主键 -->
    <one-to-one name="user" class="fire.User5" constrained="true" />
  </class>
</hibernate-mapping>

Hibernate配置文件
<hibernate-configuration>
     <session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
        jdbc:mysql://localhost:3306/sample
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="fire/User5.hbm.xml" />
    <mapping resource="fire/Passport.hbm.xml" />
     </session-factory>
</hibernate-configuration>

测试类
插入数据:
User5 user=new User5();
user.setName("zs");
user.setAge(new Integer(20));
Passport passport =new Passport();
passport.setSerial("654321");
passport.setUser(user);
user.setPassport(passport);
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
s.save(user);
t.commit();
HibernateSessionFactory.closeSession();
查询数据:
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
User5 user=(User5) s.get(User5.class,new Integer(1));
System.out.println(user.getName());
System.out.println(user.getPassport().getSerial());
t.commit();
HibernateSessionFactory.closeSession();

一对多关联
一对多关联在系统实现中非常常见,在我们现在的这个示例中,每个用户[user6]都关联到多个地址[address],如一个用户可能拥有办公室地址、家庭地址等多个地址属性。这样,在系统中,就反映为一个“一对多”关联:
CREATE TABLE  `sample`.`user6` (
  `id` varchar(32) NOT NULL default '',
  `name` varchar(45) NOT NULL default '',
  `age` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`)
)
CREATE TABLE  `sample`.`address` (
  `id` varchar(32) NOT NULL default '',
  `address` varchar(45) NOT NULL default '',
  `tel` varchar(45) NOT NULL default '',
  `user_id` varchar(32) NOT NULL default '',
  PRIMARY KEY  (`id`),
  CONSTRAINT `FK_addr` FOREIGN KEY (`user_id`) REFERENCES `user6` (`id`)
)
实体类
User6.java
package fire;

import java.util.HashSet;
import java.util.Set;
public class User6 implements java.io.Serializable {
    private String id;
    private String name;
    private Integer age;
    private Set addresses = new HashSet(0);
    ……
    public Set getAddresses() {
        return this.addresses;
    }
    public void setAddresses(Set addresses) {
        this.addresses = addresses;
    }
}

Address.java
package fire;

public class Address implements java.io.Serializable {
    private String id;
    private User6 user6;
    private String address;
    private String tel;
    ……
    public User6 getUser6() {
        return this.user6;
    }
    public void setUser6(User6 user6) {
        this.user6 = user6;
    }
}

Hibernate映射文件
User6.hbm.xml
<hibernate-mapping>
  <class name="fire.User6" table="user6" catalog="sample">
    <id name="id" type="java.lang.String">
      <column name="id" length="32" />
      <generator class="uuid.hex" />
    </id>
    <property name="name" type="java.lang.String">
      <column name="name" length="45" not-null="true" />
    </property>
    <property name="age" type="java.lang.Integer">
    <column name="age" not-null="true" />
    </property>
    <set name="addresses" inverse="true" cascade="all">
      <key><column name="user_id" length="32" not-null="true" /></key>
      <one-to-many class="fire.Address" />
    </set>
  </class>
注意:(</hibernate-mapping>
对于one-to-many关联关系,我们可以采用java.util.set类型的集合,表现在XML映射文件中也就是<set>…</set>节点。)
(这里inverse被设为“true”,意味着User6不再作为主控方,而是将关联关系的维护工作交给关联对象Address来完成。)
(这样Address对象在持久化过程中,就可以主动获取其关联的User6对象的id,并将其作为自己的user_id,之后执行一次insert操作即可完成全部工作。)

Address.hbm.xml
<hibernate-mapping>
  <class name="fire.Address" table="address" catalog="sample">
    <id name="id" type="java.lang.String">
      <column name="id" length="32" />
      <generator class="uuid.hex" />
    </id>
    <many-to-one name="user6" class="fire.User6" fetch="select">
      <column name="user_id" length="32" not-null="true" />
    </many-to-one>
    <property name="address" type="java.lang.String">
      <column name="address" length="45" not-null="true" />
    </property>
    <property name="tel" type="java.lang.String">
      <column name="tel" length="45" not-null="true" />
    </property>
  </class>
</hibernate-mapping>

Hibernate配置文件
<hibernate-configuration>
     <session-factory>
    <property name="connection.username">root</property>
    <property name="connection.url">
        jdbc:mysql://localhost:3306/sample
    </property>
    <property name="dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <property name="connection.password">root</property>
    <property name="connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <property name="show_sql">true</property>
    <mapping resource="fire/User6.hbm.xml" />
    <mapping resource="fire/Address.hbm.xml" />
     </session-factory>
</hibernate-configuration>

测试类
插入数据:
User6 user = new User6();
user.setName("zs");
user.setAge(new Integer(20));
Address addr1 = new Address();
addr1.setAddress("HongKong");
addr1.setTel("123456");
addr1.setUser6(user);
Address addr2 = new Address();
addr2.setAddress("ShangHai");
addr2.setTel("654987");
addr2.setUser6(user);
user.getAddresses().add(addr1);
user.getAddresses().add(addr2);
Session s = HibernateSessionFactory.getSession();
Transaction t = s.beginTransaction();
s.save(user);
t.commit();
HibernateSessionFactory.closeSession();
查询数据:
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
User6 user=(User6) s.load(User6.class,"297e421018c1a9bd0118c1a9bf7b0001");
System.out.println(user.getName());
Iterator it=user.getAddresses().iterator();
while(it.hasNext()){
    Address addr=(Address) it.next();
    System.out.println(addr.getAddress());
}
t.commit();
HibernateSessionFactory.closeSession();

Hibernate数据检索

Criteria Query

Criteria概述
Criteria Query通过面向对象的设计,将数据查询条件封装为一个对象。简单来讲,Criteria Query可以看作是传统SQL的对象化表示,如:
Criteria criteria=session.createCriteria(User.class);
criteria.add(Restrictions.eq("name","zs"));
criteria.add(Restrictions.eq("sex","男"));
Hibernate在运行期会根据Criteria中指定的查询条件(也就是上面代码中通过criteria.add方法添加的查询表达式)生成相应的SQL语句。
select * from user where name='zs' and sex='男'
这种方式的特点是比较符合Java程序员的编码习惯,并且具备清晰的可读性。正因为此,不少ORM实现中都提供了类似的实现机制。

Criteria查询表达式
Criteria本身只是一个查询容器,具体的查询条件需要通过Criteria.add方法添加到Criteria实例中。
如前例所示,Restrictions对象具体描述了查询条件。针对SQL语法,它提供了对应的查询限定机制。
        方法            描述
Restrictions.eq    对应SQL“field = value”表达式
如Expression.eq("name","zs")
Restrictions.allEq    参数为一个Map对象,其中包含了多个属性-值对应关系。相当于多个Expression.eq关系的叠加
Restrictions.gt        对应SQL中的 “field > value” 表达式
Restrictions.ge        对应SQL中的 “field >= value” 表达式
Restrictions.lt        对应SQL中的 “field < value” 表达式
Restrictions.le        对应SQL中的 “field <= value” 表达式
Restrictions.between    对应SQL中的 “between” 表达式
Restrictions.like        对应SQL中的 “field like value” 表达式
Restrictions.in        对应SQL中的 ”field in …” 表达式
Restrictions.eqProperty    对应SQL中的 “field = field” 表达式
Restrictions.gtProperty    对应SQL中的 “field > field” 表达式
Restrictions.geProperty    对应SQL中的 “field >= field” 表达式
Restrictions.ltProperty    对应SQL中的 “field < field” 表达式
Restrictions.leProperty    对应SQL中的 “field <= field” 表达式
Restrictions.and        and关系组合
Restrictions.or        or关系组合
Restrictions.not        not关系组合
注意:Restrictions条件方法中的属性名参数对应实体类的属性名,而非库表中的实际字段名称。
Criteria查询表达式示例
查询名为“zs”的用户记录:
Session s = HibernateSessionFactory.getSession();
Criteria criteria = s.createCriteria(User6.class);
criteria.add(Restrictions.eq("name", "zs"));
List list = criteria.list();
for (int i = 0; i < list.size(); i++) {
    User6 user = (User6) list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();
查询所有年龄大于18岁的用户记录:
Criteria criteria = s.createCriteria(User6.class);
criteria.add(Restrictions.gt("age",new Integer(18)));
查询所有年龄大于20岁且小于30的用户记录:
Criteria criteria = s.createCriteria(User6.class);
criteria.add(Restrictions.between("age", 20, 30));

Criteria示例查询
Example类实现了Criterion接口,同样,它也可以用作Criteria的查询条件。Example的作用是:根据已有对象,查找属性之相符的其它对象。
Session s = HibernateSessionFactory.getSession();
Criteria criteria = s.createCriteria(User6.class);
User6 exampleuser = new User6();
exampleuser.setName("zs");
criteria.add(Example.create(exampleuser));
List list = criteria.list();
for (int i = 0; i < list.size(); i++) {
    User6 user = (User6) list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();
这里我们新建了一个User6对象exampleUser,并作为范本,查询所有name属性与之相同的用户记录。

Criteria复合查询
我们需要查找出所有位于上海的用户,通过Criteria的复合查询我们可以轻松完成这个任务。
Session s=HibernateSessionFactory.getSession();   
Criteria criteria=s.createCriteria(User6.class);
Criteria addrcriteria=criteria.createCriteria("addresses");
addrcriteria.add(Restrictions.eq("address","shanghai"));
List list=criteria.list();
for(int i=0;i<list.size();i++){
    User6 user=(User6)list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();
可以看到,我们通过Criteria.createCriteria方法在原有的Criteria对象的基础上构建复合查询。


Criteria高级特性
限定返回的记录范围
Session s = HibernateSessionFactory.getSession();
Criteria criteria = s.createCriteria(User6.class);
//设置获取第一个记录的位置
criteria.setFirstResult(0);
//设置获取记录的最大数量
criteria.setMaxResults(10);
List list = criteria.list();
for (int i = 0; i < list.size(); i++) {
    User6 user = (User6) list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();

记录排序
Session s = HibernateSessionFactory.getSession();
Criteria criteria = s.createCriteria(User6.class);
//设置结果集的排序规则
criteria.addOrder(Order.desc("age"));
List list = criteria.list();
for (int i = 0; i < list.size(); i++) {
    User6 user = (User6) list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();

Order类
Session s=HibernateSessionFactory.getSession();
Criteria criteria=s.createCriteria(User6.class);
//设置参加分组的实体类属性
criteria.setProjection(Projections.groupProperty("age"));
Iterator it=criteria.list().iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
HibernateSessionFactory.closeSession();

Projections类
groupProperty()
avg()
sum()
min()
max()
count()

分组查询
Session s=HibernateSessionFactory.getSession();
Criteria criteria=s.createCriteria(User6.class);
//计算某个实体类属性的平均值
criteria.setProjection(Projections.avg("age"));
Iterator it=criteria.list().iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
HibernateSessionFactory.closeSession();

统计查询
Session s=HibernateSessionFactory.getSession();
Criteria criteria=s.createCriteria(User6.class);
//计算某个实体类属性的平均值
criteria.setProjection(Projections.avg("age"));
Iterator it=criteria.list().iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
HibernateSessionFactory.closeSession();

分类汇总
//实例化分组统计列表
ProjectionList projectionList=Projections.projectionList();
//向列表中添加分组条件
projectionList.add(Projections.groupProperty("age"));
//向列表中添加统计条件
projectionList.add(Projections.rowCount());
Session s=HibernateSessionFactory.getSession();
Criteria criteria=s.createCriteria(User6.class);
//设置分组统计
criteria.setProjection(projectionList);
Iterator it=criteria.list().iterator();
while(it.hasNext()){
    Object[] o=(Object[]) it.next();
    System.out.println(o[0]+"\t"+o[1]);
}
HibernateSessionFactory.closeSession();

Criteria综述
Criteria作为一种对象化的查询封装模式非常适合程序员的口味,简单易用,清晰明了。不过由于Hibernate在实现过程中更加集中在HQL查询语言上,因此Criteria的功能实现还没做到尽善尽美,因此,在实际开发中最常用的还是Hibernate官方推荐的查询封装模式:HQL。
这里需要说明的是,HQL和Criteria并不是非此即彼、相互孤立的技术,这两者相辅相成,在系统开发中,可以通过适当搭配使用这两种技术以获得最有效、最便捷的功能实现方式。


Hibernate Query Language[HQL]
Criteria提供了符合面对象编程风格的查询封装模式。不过,HQL提供了更加丰富灵活的特性,它在含盖了Criteria功能范围的前提下,提供了更为强大的查询能力,因此,在Hibernate官方开发手册中,也将HQL作为推荐的查询模式。
相对Criteria,HQL提供了更接近传统SQL语句的查询语法,也提供了更全面的特性。完整的HQL语法结构如下:
[select/update/delete…] [from…] [where…]
[group by… [having…]] [order by…]
可以看到,HQL的语法与SQL非常类似。HQL基于SQL,同时也提供了更加面向对象的封装。
鉴于HQL在Hibernate实体操作中的重要地位,下面我们将另起一节,专门围绕HQL的具体应用技术进行探讨。

HQL实用技术

实体查询
在之前的内容中,我们已经多次涉及HQL的相关内容。下面我们来看最简单的例子:
Session s=HibernateSessionFactory.getSession();
Query q=s.createQuery("from User6");
List list=q.list();
for(int i=0;i<list.size();i++){
    User6 user=(User6) list.get(i);
    System.out.println(user.getName());
}
HibernateSessionFactory.closeSession();
上面的hql:“from User6”,对应的SQL为“select * from user6”。
提示:HQL子句本身大小无关,但是其中出现的类名和属性名必须注意大小写区分。
where子句
如果我们需要取出名为“zs”的用户记录,类似SQL,我们可以通过HQL语句加以限定:
String hql="from User6 as user where user.name='zs'";
Query query=session.createQuery(hql);
List list=query.list();
这里我们新引入了两个子句“as”和“where”,as子句为类名创建了一个别名,而where子句指定了限定条件。其中as子句可忽略,如:
String hql="from User6 user where user.name='zs'";
Query query=session.createQuery(hql);
List list=query.list();
在where子句中,我们可以看出通过比较操作符指定筛选条件,如:
=,!=,<,>,>=,<=,between,in,is,like
where子句示例:
from User6 user where user.age>20
from User6 user where user.age between 20 and 30
from User6 user where user.age in(18,28)
from User6 user where user.name is null
from User6 user where user.name like '张%'
from User6 user where (user.age % 2=1)
from User6 user where (user.age>20) and (user.name like '张%')

属性查询
有时我们并不需要获取完整的实体对象,如在一个下拉框中显示用户名,此时我门需要的数据可能仅仅是实体对象的某个属性(库表记录中的某个字段信息)。同样,通过HQL我们也可以简单地做到这一点:
Query query=s.createQuery("select user.name from User6 user");
List list=query.list();
for(int i=0;i<list.size();i++){
    System.out.println(list.get(i));
}
HQL “select user.name from User6 user”指定了我们只需获取User6对象的name属性(也就是user6表的name字段)。此时返回的list数据结构中,每个条目都是一个String类型的name数据(而非User6对象)。

我们也可以通过一条HQL获取多个属性:
Query query=s.createQuery("select name,age from User6");
List list=query.list();
for(int i=0;i<list.size();i++){
    Object[] o=(Object[]) list.get(i);
    System.out.println(o[0]);
    System.out.println(o[1]);
}
“select user.name,user.age from User6 user”表明我们需要读取name和age属性的内容。而此时,返回的list数据结构中,每个条目都是一个对象数组(Object[]),其中依次包含了我们所获取的属性数据。

如果觉得返回数组的方式不够符合面向对象的风格,我们可以通过在HQL中动态构造对象实例的方法对这些平面化的数据进行封装。
Query query=s.createQuery("select new User6(name,age) from User6");
List list=query.list();
for(int i=0;i<list.size();i++){
    User6 user=(User6) list.get(i);
    System.out.println(user.getName());
    System.out.println(user.getAge());
}
上面,我们实现了通过HQL获取数据的部分属性值,与此同时,我们也可以在HQL的Select子句中使用统计函数:
Query query=s.createQuery("select count(*),min(age) from User6");
List list=query.list();
for(int i=0;i<list.size();i++){
    Object[] o=(Object[]) list.get(i);
    System.out.println(o[0]);
    System.out.println(o[1]);
}

实体更新与删除
通过delete、 update子句,数据的删除与更新操作可以以更加灵活的方式实现。
以下代码将所有用户的年龄属性更新为18:
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
Query query=s.createQuery("update User6 set age=18");
query.executeUpdate();
t.commit();
HibernateSessionFactory.closeSession();
以下代码删除了所有年龄大于18的用户记录:
Session s=HibernateSessionFactory.getSession();
Transaction t=s.beginTransaction();
Query query=s.createQuery("delete User6 where age>18");
query.executeUpdate();
t.commit();
HibernateSessionFactory.closeSession();


分组与排序
Order by子句
与SQL类似,HQL通过order by子句实现对查询结果的排序,如:
from User6 user order by user.name
默认情况下按顺序排序,我们可以通过指定排序策略进行调整:
from User6 user order by user.name desc
order by子句可指定多个排序条件:
from User6 user order by user.name,user.age desc

Group by子句
通过Group by子句可进行分组统计。如下例中, 我们通过Group by子句实现了同年龄用户的统计:
Query query=s.createQuery("select age,count(*) from User6 group by age");
List list=query.list();
for(int i=0;i<list.size();i++){
    Object[] o=(Object[]) list.get(i);
    System.out.println(o[0]);
    System.out.println(o[1]);
}


Having子句(和Group by连用在分组后的数据在满足条件)
在上面例子中,我们对同龄用户进行了统计,获得了每个年龄层次中的用户数量,假设我们只对超过10个人的年龄组感兴趣,该如何处理?
Having子句可以帮助我们简单地实现这一功能:
Query query=s.createQuery("select age,count(*) from User6 group by age having count(*)>10");
List list=query.list();
for(int i=0;i<list.size();i++){
    Object[] o=(Object[]) list.get(i);
    System.out.println(o[0]);
    System.out.println(o[1]);
}

参数绑定
在上面的HQL示例中,查询参数均直接在HQL中表达,如:
from User6 user where user.age>20
其中的“20”作为查询参数,直接写入了HQL。如果要求查询的年龄参数为变量,那么可以用以下方式进行HQL拼装:
String hql="from User6 user where user.age>"+age;
这种表达方式虽然同样能实现我们期望的功能,但却存在着以下缺陷:
编码更加凌乱,可读性降低、难以进行性能优化、引入额外的安全风险
通过Query接口进行参数填充:
Query query=s.createQuery("from User6 where name=?");
query.setString(0,"zs");
上面我们介绍了顺序占位符的使用,除了顺序占位符之外,Hibernate还支持引用占位符。如下:
Query query=s.createQuery("from User6 where name=:name");
query.setString("name","zs");
我们甚至还可以用一个JavaBena封装查询参数,如:
Query query=s.createQuery("from User6 where name=:name");
User6 user=new User6();
user.setName("zs");
query.setProperties(user);

引用查询
我们可能遇到过如下编码规范:“代码中不允许出现SQL语句”。
代码中是否出现SQL语句并不是我们这里所要探讨的主题。我们关心的是这条规则之后的制定意图:SQL语句混杂在代码之间将破坏代码的可读性,并使得系统的可维护性降低。为了避免这样的情况出现,我们通常采取将SQL配置化的方式,也就是说,将SQL保存在配置文件中,需要调用的时候再进行读取。
Hibernate提供了HQL可配置化的内置支持。
我们可以在实体影射文件中,通过query节点定义查询语句(与class节点同级):
<query name="queryByName">
    <![CDATA[from User6 where name=?]]>
</query>
之后,我们即可通过Session.getNamedQuery方法从配置文件中调用对应的HQL,如:
Query query=s.getNamedQuery("queryByName");
query.setString(0, "zs");
List list=query.list();


联合查询
我们知道,SQL总通过join子句实现多表之间的联合查询。HQL提供了以下几种联合查询机制:
内连接:
Query query=s.createQuery("from User6 user inner join user.addresses");
List list=query.list();
for(int i=0;i<list.size();i++){
    Object[] o=(Object[]) list.get(i);
    System.out.println(o[0]);
    System.out.println(o[1]);
}
条件连接:
from User6 u,Address a where u.id=a.user6.id

子查询
子查询是SQL中非常重要的功能。它可以在SQL中利用另外一条SQL的查询结果。
在之前的例子中,我们通过以下HQL获取了所有用户记录:
from User6
假设我们需要从此查询结果中提取居住在上海的User6对象,那么我们可以编写如下HQL:
from User6 where id in(select user6.id from Address where address='shanghai')

数据加载方式
在传统JDBC操作中,我们通常通过SQL语句加载所需的数据进行处理,当SQL提交之后,这些数据就被读取待用。
而在Hibernate世界里,我们拥有了更多的选择(针对关联数据):
1.即时加载 <set lazy="false">
2.延迟加载<set lazy="true">
3.预先加载<one-to-one fetch="join">

SQL查询
如果需要执行某此特殊的SQL语句,我们可以通过Session.connection()方法获取JDBC Connection实例进行调用。
Session s=HibernateSessionFactory.getSession();
Connection conn=s.connection();
Statement stmt=conn.createStatement();
ResultSet rs=stmt.executeQuery("select * from user6");
while(rs.next()){
    System.out.println(rs.getString("name"));
}
rs.close();
stmt.close();
conn.close();
HibernateSessionFactory.closeSession();










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值