Hibernate查询

  

9.1  Hibernate数据查询

数据查询与检索是Hibernate的一个亮点。Hibernate的数据查询方式主要有3种,它们是:

l         Hibernate Query Language(HQL)

l         Criteria Query

l         Native SQL

下面对这3种查询方式分别进行讲解。

9.1.1  Hibernate Query Language(HQL)

Hibernate Query Language(HQL)提供了十分强大的功能,推荐大家使用这种查询方式。HQL具有与SQL语言类似的语法规范,只不过SQL针对表中字段进行查 询,而HQL针对持久化对象,它用来取得对象,而不进行update、delete和insert等操作。而且HQL是完全面向对象的,具备继承、多态和 关联等特性。

1.from子句

from字句是最简单的HQL语句,例如 from Student,也可以写成select s from Student s。它简单地返回Student类的所有实例。

除了Java类和属性的名称外,HQL语句对大小写并不敏感,所以在上一句HQL语句中,from与FROM是相同的,但是Student与student 就不同了,所以上述语句写成from student就会报错。下列程序演示如何通过执行from语句取得所有的Student对象。

Query query = session.createQuery(“from Student”);

List list = query.list();

for (int i=0;i<list.size(); i++)

{

    Student stu = (Student)list.get(i);

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

}

如果执行HQL语句“from Student, Course”,并不简单地返回两个对象,而是返回这两个对象的的笛卡儿积,这类似于SQL语句中字段的全外连接。在实际应用中,像“from Student, Course”这样的语句几乎不会出现。

2.select子句

有时并不需要得到对象的所有属性,这时可以使用select子句进行属性查询,例如,select s.name from Student s。下面程序演示如何执行这个语句:

Query query = session.createQuery(“select s.name from Student s”);

List list = query.list();

for (int i=0;i<list.size(); i++) {

    String name = (String)list.get(i);

    System.out.println(ame());

}

如果要查询两个以上的属性,查询结果会以数组的方式返回,如下所示:

Query query = session.createQuery(“select s.name, s.sex from Student as s”);

List list = query.list();

for (int i=0;i<list.size(); i++) {

    Object obj[] = (Object[])list.get(i);

    System.out.println(ame(obj[0] + “的性别是:” +obj[1]));

}

在使用属性查询时,由于使用对象数组,操作和理解都不太方便,如果将一个object[]中所有成员封装成一个对象就方便多了。下面的程序将查询结果进行了实例化:

Query query = session.createQuery(“select new Student(s.name, s.sex) from Student s”);

List list = query.list();

for (int i=0;i<list.size(); i++) {

    Student stu = (Student)list.get(i);

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

}

要正确运行以上程序,还需要在Student类中加入一个如下的构造函数:

public Student(String name, String sex)

{

    this.name = name;

    this.sex = sex;

}

3.统计函数查询

可以在HQL中使用函数,经常使用的函数有:

l         count():统计记录条数

l         min():求最小值

l         max():求最大值

l         sum():求和

l         age():求平均值

例如,要取得Student实例的数量,可以编写如下HQL语句:

select count(*) from Student

取得Student的平均年龄的HQL语句如下:

select avg(s.age) from Student as s

可以使用distinct去除重复数据:

select distinct s.age from Student as s

4.where子句

HQL也支持子查询,它通过where子句实现这一机制。where子句让用户缩小要返回的实例的列表范围,例如下面语句会返回所有名字为“Bill”的Student实例:

Query query = session.createQuery("from Student as s where s.name='Bill' ");

where子句允许出现的表达式包括了SQL中可以使用的大多数情况:

l         数学操作:+,-,*,/

l         真假比较操作:=,>=,<=,<>,!=,like

l         逻辑操作:and,or, not

l         字符串连接:||

l         SQL标量函数:例如upper()和lower()

如果子查询返回多条记录,可以用以下的关键字来量化:

l         all:表示所有的记录。

l         any:表示所有记录中的任意一条。

l         some:与any用法相同。

l         in:与any等价。

l         exists:表示子查询至少要返回一条记录。

例如,下面语句返回所有学生的年龄都大于22的班级对象:

from Group g where 22<all (select s.age from g.students s)

下述语句返回在所有学生中有一个学生的年龄等于22的班级:

from Group g where 22=any (select s.age from g.students s)

或者

from Group g where 22=some (select s.age from g.students s)

或者

from Group g where 22 in (select s.age from g.students s)

5.order by 子句

查询返回的列表可以按照任何返回的类或者组件的属性排序:

from Student s order by s.name asc

asc和desc是可选的,分别代表升序或者降序。

6.连接查询

与SQL查询一样, HQL也支持连接查询,如内连接、外连接和交叉连接。

l         inner join:内连接

l         left outer join:左外连接

l         right outer join:右外连接

l         full join:全连接,但不常用

下面重点讲解内连接查询,左外连接和右外连接查询和内连接大同小异,而全连接几乎不怎么使用。

inner join可以简写为join,例如在查询得到Group对象时,内连接取得对应的Student对象,实现的程序如下。

……//打开Session,开启事务

Student  stu = null;  //声明Student实例

Group  group = null; //声明Group实例

Query query = session.createQuery("from Group g join g.students");

List list = query.list();

Object obj[] = null;  //声明对象数组

for(int i=0;i<list.size();i++)  {

    obj = (Object[])list.get(i); //取得集合中的第i个数组

    group = (Group)obj[0];  //group是数组中第一个对象

    stu = (Student)obj[1];   //stu是数组中第二个对象

    System.out.println(stu.getName() + "属于:" +group.getName() );

}

    ……//提交事务,关闭Session

9.1.2  Criteria Query方式

当查询数据时,人们往往需要设置查询条件。在SQL或HQL语句中,查询条件常常放在where子句中。此外,Hibernate还支持Criteria 查询(Criteria Query),这种查询方式把查询条件封装为一个Criteria对象。在实际应用中,使用Session的createCriteria()方法构建一 个org.hibernate.Criteria实例,然后把具体的查询条件通过Criteria的add()方法加入到Criteria实例中。这样, 程序员可以不使用SQL甚至HQL的情况下进行数据查询,如例程9-1所示。

例程9-1  Criteria应用实例

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

Criteria cr = session.createCriteria(Student.class); //生成一个Criteria对象

cr.add(Restrictions.eq("name", "Bill"));//等价于where name=’Bill’

List list = cr.list();

Student stu = (Student)list.get(0);

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

1.常用的查询限制方法

在例程9-1中,Restrictions.eq()方法表示equal,即等于的情况。Restrictions类提供了查询限制机制。它提供了许多方法,以实现查询限制。这些方法及其他一些criteria常用查询限制方法列于表9-1中。

表9-1  Criteria Query常用的查询限制方法

方    法

说    明

Restrictions.eq()

equal,=

Restrictions.allEq()

参数为Map对象,使用key/value进行多个等于的对比,相当于多个Restrictions.eq()的效果

Restrictions.gt()

greater-than, >

Restrictions.lt()

less-than, <

Restrictions.le()

less-equal, <=

Restrictions.between()

对应SQL的between子句

Restrictions.like()

对应SQL的like子句

Restrictions.in()

对应SQL的in子句

Restrictions.and()

and关系

Restrictions.or()

or关系

Restrictions.isNull()

判断属性是否为空,为空返回true,否则返回false

Restrictions.isNotNull()

与Restrictions.isNull()相反

Order.asc()

根据传入的字段进行升序排序

Order.desc()

根据传入的字段进行降序排序

MatchMode.EXACT

字符串精确匹配,相当于“like 'value'”

MatchMode.ANYWHERE

字符串在中间位置,相当于“like '%value%'”

MatchMode.START

字符串在最前面的位置,相当于“like 'value%'”

MatchMode.END

字符串在最后面的位置,相当于“like '%value'”

例1:查询学生名字以t开头的所有Student对象。

Criteria cr = session.createCriteria(Student.class);

cr.add(Restrictions.like(“name”, “t%”))

List list = cr.list();

Student stu = (Student)list.get(0);

或者使用另一种方式:

Criteria cr = session.createCriteria(Student.class);

cr.add(Restrictions.like(“name”, “t”, MatchMode.START))

List list = cr.list();

Student stu = (Student)list.get(0);

例2:查询学生姓名在Bill, Jack和Tom之间的所有Student对象。

String[] names = {“Bill”, “Jack”, “Tom”}

Criteria cr = session.createCriteria(Student.class);

cr.add(Restrictions.in(“name”, names))

List list = cr.list();

Student stu = (Student)list.get(0);

例3:查询学生的年龄age等于22或age为空(null)的所有Student对象。

Criteria cr = session.createCriteria(Student.class);

cr.add(Restrictions.eq(“age”, new Integer(22));

cr.add(Restrictions.isNull(“age”));

List list = cr.list();

Student stu = (Student)list.get(0);

例4:查询学生姓名以字母F开头的所有Student对象,并按姓名升序排序。

Criteria cr = session.createCriteria(Student.class);

cr.add(Restrictions.like(“name”, “F%”);

cr.addOrder(Order.asc(“name”));

List list = cr.list();

Student stu = (Student)list.get(0);

Tips

 

调用Order.asc的方法应是Criteria的addOrder()方法。

2.连接限制

在Criteria 查询中使用FetchMode来实现连接限制。在HQL语句中,可以通过fetch关键字来表示预先抓取(Eager fetching),如下所示:

from Group g

left join fetch g.students s

where g.name like ' 05'

可以使用Criteria的API完成同样的功能,如下所示:

Criteria cr = session.createCriteria(Group.class);

cr.setFetchMode(“students”, FetchMode.EAGER);

cr.add(Restrictions.like(“name”, “2005”, MatchMode.END))

List list = cr.list();

以上两种方式编写的代码,都使用相同的SQL语句完成它们的功能,如下所示:

select g.*, s.* from Group g

left outer join Student s

on g.id = s.group_id

where g.name like ' 05'

9.1.3  Native SQL查询

本地SQL查询(Native SQL Query)指的是直接使用本地数据库(如Oracle)的SQL语言进行查询。它能够扫清你把原来直接使用SQL/JDBC 的程序迁移到基于 Hibernate应用的道路上的障碍。

Hibernate3允许你使用手写的SQL来完成所有的create、update、delete和load操作(包括存储过程)。

1.创建一个基于SQL的Query

Native SQL查询是通过SQLQuery接口来控制的,它是通过调用Session.createSQLQuery()方法来获得的,例如:

String sql = "select {s.*} from t_student s where s.age>22";

SQLQuery slqQuery = session.createSQLQuery(sql);

sqlQuery.addEntity("s", Student.class);

List list = sqlQuery.list();

for (int i=0;list.size();i++) {

Student stu = (Student)list.get(i);

System.out.println(stu.getAge() +" "+ stu.getName());

}

createSQLQuery(String sql)利用传入的SQL参数构造一个SQLQuery实例(SQLQuery是Query的子接口)。使用这个方法时,还需要传入查询的实体类,因此要配合SQLQuery的addEntity()方法一起使用。

addEntity()方法是将实体类别与别名联系在一起的方法,此方法的定义如下:

public SQLQuery addEntity(String alias, Class entityClass)

{}号用来引用数据表的别名,例如以上代码中{s.*}表示使用s来作为t_student表的别名。

2.命名SQL查询

与HQL的命名查询相似,也可以将本地的SQL查询语句定义在映射文件中,然后像调用一个命名HQL查询一样直接调用命名SQL查询。

例如在Student.hbm.xml中定义一个命名SQL查询,如下所示:

<hibernate-mapping>

<class name="Student" table="student" lazy="false">

……

</class>

<sql-query name="QueryStudents">

<![CDATA[

             select {s.*} from t_student s where s.age>22

]]>

<return alias="s" class="Student"/>

</sql-query>

</hibernate-mapping>

<sql- query>元素是<hibernate-mapping>元素的一个子元素。利用<sql-query>元素的子元素<return>指定别名与实体类相关联。配合映射文件中的定义,编写如下代码来调用这个命名SQL查询:

Query query = session.getNamedQuery(“QueryStudents”);

List list = query.list();

for (int i=0;list.size();i++) {

Student stu = (Student)list.get(i);

System.out.println(stu.getAge() + “ ”+ stu.getName());

}

也可以在命名查询中设定查询参数,如下所示:

……

<sql-query name=”QueryStudents”>

    <![CDATA[

        select {s.*} from t_student s where s.age>:age

    ]]>

    <return alias=”s” class=”Student”/>

</sql-query>

…..

编写如下代码来调用这个命名SQL查询,并且把查询中定义的参数传入:

Query query = session.getNamedQuery(“QueryStudents”);

query.setInteger(“age”,22);

List list = query.list();

for (int i=0;list.size();i++) {

Student stu = (Student)list.get(i);

System.out.println(stu.getAge() + “ ”+ stu.getName());

}

3.自定义insert、update和delete语句

Hibernate3.x 的映射文件中新添了<sql-insert>、<sql-update> 和<sql-delete>3个标记。可以使用这3个标记自定义自己的insert、update和delete语句,例如在 Student.hbm.xml中定义这些语句如下:

<hibernate-mapping>

<class name="Student" table="student" lazy="false">

<id name="id" unsaved-value="null" type="string" column="id">

    <generator class="uuid.hex"/>

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

<property name="age" type="int" />

<sql-insert> <!--insert语句-->

    insert into t_student(name, age, id) values(?,?,?)

    </sql-insert>

    <sql-update> <!--update语句-->

    update t_student set name=?, age=? where id=?

    </sql-update>

    <sql-delete> <!--delete语句-->

        delete from t_student where id=?

</sql-delete>

</class>

</hibernate-mapping>

对于上述文件中自定义的SQL语句,要注意以下几点。

l         insert和update语句中定义的字段必须和映射文件声明的属性相对应,一个都不能少。

l         在insert和update语句中,属性出现的顺序必须和映射文件中声明的顺序一致。

l         在insert语句中,主键id总是放在最后。

在程序中实现以上自定义的insert语句如下:

……

Student stu = new Student();

stu.setName(“Bill”);

stu.setAge(22);

session.save(stu);

运行上述程序,控制台显示的信息如下:

Hibernate: insert into t_student(name,age,id) values(?,?,?)

如果不想在insert或update语句中包括所有属性,则可以在属性定义时,加上insert="false"或update="false",如下所示:

<property name=”name” type=”string” insert=”false” update=”false” />

    <sql-insert>  insert into t_student(age, id) values(?,?)  </sql-insert>

<sql-update> update t_student set age=? where id=?  </sql-update>

实例:

9.2  利用关联关系操纵对象

数据对象之间关联关系有一对一、一对多及多对多关联关系。在数据库操作中,数据对象之间的关联关系使用JDBC处理很困难。本节讲解如何在 Hibernate中处理这些对象之间的关联关系。本节使用到4个类,它们分别是Student(学生)、Card(学生证)、Group(班级)和 Course(课程),它们之间的关联关系如图9-1所示。这些实体存在级联(cascade)问题。例如,当删除一个班级的信息时,还要删除该班的所有 学生的基本信息。如果直接使用JDBC执行这种级联操作,会非常烦琐。Hibernate通过把实体对象之间关联关系及级联关系在映射文件中声明,比较简 便地解决了这类级联操作问题。

图9-1  对象关联图

9.2.1  一对一关联关系的使用

一对一关系在实际生活中是比较常见的,例如学生与学生证的关系,通过学生证可以找到学生。一对一关系在Hibernate中的实现有两种方式,分别是主键关联和外键关联。

1.以主键关联

主键关联的重点是,关联的两个实体共享一个主键值。例如,Student与Card是一对一关系,它们在数据库中对应的表分别是t_student和 t_card。它们共用一个主键值id,这个主键可由t_student表或t_card表生成。问题是如何让另一张表引用已经生成的主键值呢?例 如,t-student表填入了主键id的值,t_card表如何引用它?这需要在Hibernate的映射文件中使用主键的foreign生成机制。

为了表示Student与Card之间的一对一关联关系,在Student和Card的映射文件Student.hbm.xml和Card.hbm.xml中都要使用<one-to-one>标记,如例程9-2所示。

例程9-2  Student.hbm.xml

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

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

    "-//Hibernate/Hibernate Mapping DTD//EN"

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

<hibernate-mapping>

<class name="test.Student" table="T_STUDENT" lazy="true"><!-- 把类与表关联起来-->

<id name="id" column="id" type="int">

<generator class="increment" />

</id>

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

<!--property name="card_id" column="CARD_ID" type="int" /--> <!--映射学生证号-->

<property name="sex" column="SEX" type="string" />

<property name="age" column="AGE" type="int" />

<one-to-one  name="card"  class="test.Card"

    fetch="join" cascade="all"  />

</class>

</hibernate-mapping>

<class>元素的lazy属性设定为true,表示延迟加载,如果lazy的值设置为false,则表示立即加载。下面对立即加载和延迟加载这两个概念进行说明。

l         立即加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1)。

l         延迟加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,不会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1),而是等到需要时,才会从数据库取得数据,组装此关联对象。

<one-to-one>元素的cascade属性表明操作是否从父对象级联到被关联的对象,它的取值如下。

l         none:在保存、删除或修改对象时,不对其附属对象(关联对象)进行级联操作。这是默认设置。

l         save-update:在保存、更新当前对象时,级联保存、更新附属对象(临时对象、游离对象)。

l         delete:在删除当前对象时,级联删除附属对象。

l         all:在所有情况下均进行级联操作,即包含save-update和delete操作。

l         delete-orphan:删除和当前对象解除关系的附属对象。

<one-to-one>元素的fetch属性的可选值是join和select,默认值是select。当fetch属性设定为join时,表示连接抓取(Join fetching : Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。 当fetch属性设定为select时,表示查询抓取(Select fetching:需要另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。

例程9-3中<one-to-one>元素的cascade属性设置为“all”,表示增加、删除及修改Student对象时,都会级联增加、删除和修改Card对象。

例程9-3  Card.hbm.xml

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

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

    "-//Hibernate/Hibernate Mapping DTD//EN"

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

<hibernate-mapping>

<class name="test.Card" table="t_card" lazy="true"><!-- 把类与表关联起来-->

<id name="id" column="id">

<generator class="foreign" >

    <param name="property">student</param>

    </generator>

</id>

<one-to-one name="student"  class="test.Student" constrained="true"/>

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

<!-- one-to-one name="student"  class="test.Student" constrained="true"/-->

</class>

</hibernate-mapping>

在例程9-3中,Card.hbm.xml的主键id使用外键(foreign)生成机制,引用代号为“student”对象的主键作为Card表的主键 和外键。student在该映射文件的<one-to-one>元素中进行了定义,它是Student对象的代号。<one-to- one>元素的属性Constrained="true"表示Card引用了student的主键作为外键。

需要特别注意的是,Student类中要相应地加入一对get/set方法:

public Card getCard() {

    return this.card;   

    }

    public void setCard(Card card) {

this.card = card;

}

在Card类中也要相应地加入一对get/set方法:

public Student getStudent() {

    return this.stu;

    }

public void setStudent(Student stu) {

    this.stu = stu;

}

在客户端测试程序中操纵Student和Card对象的方法如例程9-4所示。

例程9-4  客户端测试程序

package test;

import org.hibernate.*;

import org.hibernate.cfg.*;

import java.io.File;

import java.util.List;

public class Test {

    public static void main(String[] args) {

                 

        File file = new File("D:\\eclipse3.2\\workspace\\HibernateTest\\hibernate.cfg.xml");

                 

        Configuration  conf = new Configuration().configure(file);

                 

        SessionFactory  sf = conf.buildSessionFactory();

                 

        Session session = sf.openSession();

                 

        Transaction tx = session.beginTransaction();

                 

        //新建Student对象

        Student stu = new Student();

            stu.setName("Walker");

            stu.setSex("male");

            stu.setAge(22);

            //新建Card对象

            Card card = new Card();

            card.setName("Walker");

                           

        //设置Student对象与Card对象之间的关联

        stu.setCard(card);

        card.setStudent(stu); //此句不能省略,否则card将不知从何处取得主键值

        

        try {

            session.save(stu);

        tx.commit();

        session.close();

        System.out.println("Data have been inserted into DB.");

        } catch (HibernateException e) {

            e.printStackTrace();

            tx.rollback();

        session.close();

        }   

    }

}

运行以上代码后,将会在t_student表和t_card表中插入相应的数据。

2.以外键关联

以外键关联的要点是:两个实体各自有不同的主键,但其中一个实体有一个外键引用另一个实体的主键。例如,假如Student和Card是外键关联的一对一 关系,它们在数据库中相应的表分别是t_student表和t_card表,t_student表有一个主键id,t_card表有一个主键id和一个外 键 stu_id,此外键对应student表的主键id。

Student的映射文件Student.hmb.xml见例程9-2。但Card的映射文件Card.hbm.xml要做相应变动,如例程9-5所示。

例程9-5  Card.hbm.xml

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

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

<class name="test.Card"  table="T_CARD" lazy= "true"><!--把类与表关联起来-->

<id name="id" >

<generator class="increment" ><!--不再是foreign了-->

    </generator>

</id>

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

<many-to-one  name="student"  class="Student" column="stu_id"

    unique="true"/> <!--唯一的多对一,实际上变成一对一关系了-->

</class>

</hibernate-mapping>

在例程9-5中,<many-to-one>元素的name属性声明外键关联对象的代号,class属性声明该外键关联对象的类,column属性声明该外键在数据表中对应的字段名,unique属性表示使用DDL为外键字段生成一个唯一约束。

以外键关联对象的一对一关系,其实本质上变成了一对多的双向关联了,应直接按照一对多和多对一的要求编写它们的映射文件。当<many-to-one>元素的unique属性设定为true,多对一的关系实际上变成了一对一的关系。

在客户端程序中操纵外键关联一对一关系的对象的方法见例程9-4。

9.2.2  一对多关联关系的使用

一对多关系很常见,例如父亲和孩子、班级与学生的关系就是很好的一对多的关系。在实际编写程序时,一对多关系有两种实现方式:单向关联和双向关联。单向的 一对多关系只需在一方进行映射配置,而双向的一对多需要在关联的双方进行映射配置。下面以Group(班级)和Student(学生)为例讲解如何配置一 对多的关系。

1.单向关联

单向的一对多关系只需在一方进行映射配置,所以我们只配置Group(班级)的映射文件Group.hbm.xml,如例程9-6所示。

例程9-6  Group.hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

<class name="test.Group" table="T_GROUP" lazy="true"><!--把类与表关联起来-->

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

<generator class="increment" >

    </generator>

</id>

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

    update="true" insert="true" />

<set  name="students"

    table="T_STUDENT"

    lazy="false"

    inverse="false"

    cascade="all"

    sort="unsorted"

<key column="ID"/>

<one-to-many class="test.Student"/>

</set>

</class>

</hibernate-mapping>

在以上映射文件中,<property>元素的insert属性表示被映射的字段是否出现在SQL的 INSERT语句中;update属性表示被映射的字段是否出现在SQL的 UPDATE语句中。

<set>元素描述的字段(本例中为students)对应的类型为java.util.Set,它的各个属性的含义如下。

l         name:字段名,本例的字段名为students,它属于java.util.Set类型。

l         table:关联表名,本例中,students的关联数据表名是t_student。

l         lazy:是否延迟加载,lazy=false表示立即加载。

l         inverse:用于表示双向关联中的被动方的一端,inverse的值为false的一方负责维护关联关系。默认值为false。本例中Group将负责维护它与Student之间的关联关系。

l         cascade:级联关系;cascade=all表示所有情况下均进行级联操作,即包含save-update和delete操作。

l         sort:排序关系,其可选取值为unsorted(不排序)、natural(自然排序)、comparatorClass(由某个实现了java.util.comparator接口的类型指定排序算法)。

<key>子元素的column属性指定关联表(本例中t_student表)的外键,<one-to-many>子元素的class属性指定了关联类的名字。

此外,在Group类中增加如下get/set方法:

private Set students;

    public Set getStudents() {

        return this.students;

    }

    public void setStudents(Set stu) {

        this.students = stu;

}

假如我们想为一个班级添加一个学生对象,实现的代码如下:

Transaction tx = session.beginTransaction();

Student stu = new Student();

stu.setName("Walker");

stu.setSex("male");

stu.setAge(22);

group.getStudents().add(stu);

session.save(group);

tx.commit();

2.双向关联

如果要设置一对多双向关联,那么还需要在“多”方的映射文件中使用<many-to-one>标记。例如,在Group与Student一对 多的双向关联中,除了Group的映射文件Group.hbm.xml和Group类进行设置和修改外,还需要在Student的映射文件 Student.hbm.xm中加入:

<many-to-one

        name="group"

        class="test.Group"

        cascade="none"

        outer-join="auto"

        update="true"

        insert="true"

        column="ID"

        />

name、class等属性前面已经解释过了,这里只说明insert和update属性。insert和update设定是否对column属性指定的关联字段进行insert和update操作。在Student类还要相应添加一对get/set方法:

public Group getGroup() {

    return this.group;

    }

    public void setGroup(Group g) {

        this.group = g;

    }

此外,把Group.hbm.xml(如例程9-6所示)中的<set>元素的inverse属性的值设定为true,如下所示。

<set  name="students" table="T_STUDENT" lazy="false"

    inverse="true" cascade="all" sort="unsorted">

<key column="ID"/>

<one-to-many class="Student"/>

</set>

当 Group.hmb.xml中<set>元素的inverse属性的值设定为false时,Group和Student之间的关联关系由 Group维护,Group负责将自己的id告诉Student,然后Hibernate发送update语句去更新记录。但现在inverse的值设定 为true后,Group和Student之间的关联关系转由Student来维护,由Student自动去取得Group的id,而这个Student 取得Group的id的动作,其实就是完成一个“学生添加到班级”的动作。

9.2.3  多对多关联关系的使用

Student(学生)和Course(课程)的关系就是多对多的关系。在映射多对多关系时,需要另外使用一个连接表(例如,Student_Course)。 Student_Course表包含2个字段:CourseId和StuId。此外,在它们的映射文件中使用<many-to-many>标记。

Student的映射文件Student.hbm.xml中加入以下描述信息:

<set  name="courses"  table=" Student_Course" lazy="false"

    inverse="false" cascade="save-update" >

<key column="StuId"/>

<many-to-many class="test.Course" column="CourseId" />

</set>

相应地,Course的映射文件Course.hbm.xml加入以下描述信息:

<set  name="students"  table=" Student_Course" lazy="false"

inverse="true" cascade="save-update" >

<key column="CourseId"/>

<many-to-many class="test.Student" column="StuId"  />

</set>

1.添加关联关系

首先让我们编一个程序来看看一个名为Bill的学生选择了什么课程:

……

//获得包含Bill的Student对象

Student stu = (Student) session.createQuery(“from Student s where s.name =

‘Bill’ ”) .uniqueResult();

List ls = new ArrayList(stu.getCourses());

for(int i=0; i<ls.size(); i++) {

    Course course = (Course)ls.get(i);  //获得Course对象

    System.out.println(course.getName()); //打印Bill所选课程的清单

}

…..

现在Bill还想选修business课程,这对于程序员来说只是为Bill添加了一个到business的关联,也就是说在student_course表中新添一条记录,而T_Student 和T_Course表都不用变更。

……

Student stu = (Student) session.createQuery(“from Student s where s.name = ‘Bill’ ”) .uniqueResult();

Course course = (Course) session.createQuery(“from Course c where c.name =

‘business’ ”) .uniqueResult();

//设置stu与course的相互关系

stu.getCourses().add(course);

course.getStudents().add(stu);

…..

2.删除关联关系

删除关联关系比较简单,直接调用对象集合的remove()方法删除不要的对象即可。例如,要从学生Bill的选修课清单中删除politics和chemistry两门课,程序如下:

…….

Student stu = (Student) session.createQuery("from Student s where s.name = 'Bill' ") .uniqueResult();

Course course1 = (Course) session.createQuery("from Course c where c.name =

'politics' ") .uniqueResult();

Course course2 = (Course) session.createQuery("from Course c where c.name =

'chemistry' ") .uniqueResult();

stu.getCourse().remove(course1); //删除politics课程

stu.getCourse().remove(course2); //删除chemisty课程

…….

运行以上语句将从student_course表中删除这两条记录,但T_Student和T_Course表没有任何变化。

0

0

阅读(1407) 评论 (1) 收藏(0) 转载(2) 喜欢 打印举报
已投稿到:
排行榜
前一篇:云空间
后一篇:Oracle 存储过程
< 前一篇云空间
后一篇 >Oracle 存储过程

新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

新浪公司 版权所有

幻灯播放

转载于:https://www.cnblogs.com/CooderIsCool/p/4745774.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/273039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux x86 io端口映射,linux中的 IO端口映射和IO内存映射

下面是今天看到两篇关于linux中的 IO端口映射和IO内存映射的文章,时间关系,没来得及深入理解,有空好好看看CPU地址空间CPU地址空间(一)地址的概念1)物理地址&#xff1a;CPU地址总线传来的地址&#xff0c;由硬件电路控制其具体含义。物理地址中很大一部分是留给内存条中的内存…

单例模式 创建对象

两种选择 1 使用pthread_once&#xff0c; once是类的成员变量 只执行一次Create create的作用是创建一个对象 2 使用 static lock 如下所示&#xff0c;注意lock必须是static的&#xff0c;否则是局部变量&#xff0c;每个线程都有自己的lock&#xff0c;无法保证只执行一次。…

Linux c编译库路径,【一点一点学Linux C】交叉编译时候如何配置连接库的搜索路径...

交叉编译的时候不能使用本地(i686机器&#xff0c;即PC机器&#xff0c;研发机器)机器上的库&#xff0c;但是在做编译链接的时候默认的是使用本地库&#xff0c;即/usr/lib,/lib两个目录。因此&#xff0c;在交叉编译的时候&#xff0c;要采取一些方法使得在编译链接的时候找到…

[NBUT 1458 Teemo]区间第k大问题,划分树

裸的区间第k大问题&#xff0c;划分树搞起。 #pragma comment(linker, "/STACK:10240000") #include <map> #include <set> #include <cmath> #include <ctime> #include <deque> #include <queue> #include <stack> #inc…

Linux的软件包封装格式有,linux软件安装包详解---全

详细介绍了常见的四种Linux应用软件安装包及其安装方法。一、解析Linux应用软件安装包&#xff0c;通常Linux应用软件的安装包有四种&#xff1a;1) tar包&#xff0c;如software-1.2.3-1.tar.gz。他是使用UNIX系统的打包工具tar打包的。2) rpm包&#xff0c;如software-1.2.3-…

人生的第一个博客(●'◡'●)ノ♥--开博典礼

嘛&#xff0c;说实话&#xff0c;现在才开始&#xff0c;实在是有点晚了&#xff0c;一不小心大学都过去1年了_(:3 」∠)_ 我在专业方面的起步也是相当晚的&#xff0c;身为计算机专业&#xff0c;编程却从大学才开始正式接触&#xff0c;进入大学时其他方面的能力也都约等于0…

linux查看运行钟的tomcat,linux查看tomcat启动运行日志

Linux0&period;11内核--进程调度分析之2&period;调度[版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596830.html ] 上一篇说到进程调度归根结底是调用timer_interrupt函数, ...iReport 下载地址iReport 下载地址: https://osdn.jp/projects/sfnet…

8月面试题目收录

面试题收录 常见兼容性问题&#xff1f; * png24位的图片在iE6浏览器上出现背景&#xff0c;解决方案是做成PNG8.* 浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。* IE6双边距bug:块属性标签float后&#xff0c;又有横行的margin情况…

linux如何升级php版本升级,Linux 升级php版本

近来因工作需要,又没有服务器维护人员,只能自己上阵啦。从php5.3.28->5.5.30,先自己下载php包到/usr/local/下​&#xff0c;# 解压缩安装包tar zxvf php-5.5.30.tar.gz# 进入目录cd php-5.5.30// 编译的时候一定要加入参数--enable-fpm#./configure --prefix/usr/local/php…

opencv配置

OpenCV的简单安装和一次性配置在这里就不赘述了&#xff0c;网上教程很多&#xff0c;可以参考一下这个链接里面的教程http://wenku.baidu.com/view/3b40de25453610661ed9f46b.html。 但是很多情况下面&#xff0c;我们新建一个项目就要重新配置一次OpenCV&#xff0c;那就相当…

linux ftp 工作过程,linux中ftp的安装过程记录[运维篇]

安装FTP的全过程记录&#xff0c;对于相同情况希望有所帮助。【centOS】1、查询本机是否安装vsftpd: rpm -qa |grep vsftpd &#xff1b;2、安装ftp服务 yum install vsftpd;3、开启ftp服务 chkconfig vsftpd on&#xff0c;开机启动&#xff1b;4、手动操作ftp服务&#xff0c…

代码命名,代码里的命名规则:错误的和正确的对比 命名方法总结 “自我描述的源代码”用代码表达出你的思想,让其他人通过代码能明白你的意图。...

http://www.aqee.net/express-names-in-code-bad-vs-clean/ 编程初学者总是把大量的时间用在学习编程语言&#xff0c;语法&#xff0c;技巧和编程工具的使用上。他们认为&#xff0c;如果掌握了这些技术技巧&#xff0c;他们就能成为不错的程序员。然而&#xff0c;计算机编程…

linux 动态执行cp,Linux常用命令之cp、mv、rm、cat、more、head、tail、ln命令讲解

上一章节中&#xff0c;我们了解到了Linux系统的最基础的几个文件处理命令&#xff0c;核心的是ls命令&#xff0c;在今天这章中&#xff0c;我们来继续学习Linux对于文件操作相关的一些命令&#xff0c;比如复制、移动、删除、查看等命令。1、cp 命令解释命令名称&#xff1a;…

使用DBI(perl)实现文本文件的导入导出mysql

DBI 是perl脚本连接数据库的一个模块。perl脚本相对shell更灵活&#xff0c;功能更强大&#xff0c;跨平台能力强。相对可执行jar包要简单很多。 ​1、下载安装包DBI-1.631.tar.gzperl脚本下载的网站http://www.cpan.org/ 很多perl的组件都可以在这个网站上下载 2、解压tar -xz…

linux 车载视频监控,基于Linux平台车载视频监控系统研发-计算机科学与技术专业论文.docx...

基于Linux平台车载视频监控系统研发-计算机科学与技术专业论文目录HYPERLINK \l "_bookmark0" 第一章 绪论1 HYPERLINK \l "_bookmark1" 1.1 研究背景1 HYPERLINK \l "_bookmark2" 1.2 研究动态1 HYPERLINK \l "_bookmark3" 1.3 本文工…

Linux鼠标回报率修改,鼠标回报率怎么调? 设置鼠标回报率的三种方法

鼠标回报率如何设置呢&#xff1f;鼠标回报率又称刷新率&#xff0c;是指鼠标MCU与电脑传输数据频率。鼠标回报率对于游戏玩家而言至关重要&#xff0c;但同时鼠标回报率与电脑性能息息相关。只有电脑硬件性能良好&#xff0c;才能适当提升鼠标回报率&#xff0c;以实现更高的鼠…

linux下vi修改文件用法

进入vi的命令 vi filename :打开或新建文件&#xff0c;并将光标置于第一行首 vi n filename &#xff1a;打开文件&#xff0c;并将光标置于第n行首 vi filename &#xff1a;打开文件&#xff0c;并将光标置于最后一行首 vi /pattern filename&#xff1a;打开文件&#xff…

linux在芯片设计与实现,基于Linux的Atheros无线芯片网卡驱动的设计与实现

Design and Implementation of Linux based Atheros wireless network cards driverDU Qingbo1杜清波(1985-)&#xff0c;男&#xff0c;硕士研究生&#xff0c;主要研究方向&#xff1a;嵌入式系统与网络通信1、School of Computer Science,Beijing University of Posts and T…

[转载]孙婧妍:高考语文148分是这样炼成的(附:孙婧妍

原文地址&#xff1a;孙婧妍&#xff1a;高考语文148分是这样炼成的(附&#xff1a;孙婧妍2013高考作文《手机论》)作者&#xff1a; 语文新高考高考语文148分是这样炼成的 (附&#xff1a;孙婧妍2013高考作文《手机论》) 来源&#xff1a;网络 作者&#xff1a;孙婧妍…

linux ps 命令安装,Linux上安装pstree命令(-bash: pstree: command not found)

一、pstree命令的安装1、在Mac OS上brew install pstree2、在Fedora/Red Hat/CentOSyum -y install psmisc3、在 Ubuntu/Debianapt-get install psmisc二、pstree命令详解pstree指令用ASCII字符显示树状结构&#xff0c;清楚地表达程序间的相互关系。如果不指定程序识别码或用户…