QBC

来源:互联网 发布:论大数据的机遇和挑战 编辑:程序博客网 时间:2024/06/01 10:51
2009-07-17 14:52

Hibernate QBC 一

在第5章数据的简单操作中,已经介绍了如何通过session对持久化对象进行载入、更新、删除、保存。另外,还介绍了Hibernate的一种重要的数据检索方法HQL查询。通过第7章的学习,基本上可以满足我们对数据的检索要求了。但是,Hibernate还提供了一种功能强大,运用也非常广泛的QBC数据检索方法,它利用criteria接口可以满足开发人员不同特点的数据检索要求。另外,在数据库检索过程中,表之间的连接查询是很常见的,本章将介绍如何利用HQL和criteria的数据检索方式进行数据的连接查询。Hibernate还提供了在映射文件中设置检索策略,用于定义对象什么时候载入,如何载入,来提高数据的检索性能。
  本章的主要内容包括:
● QBC数据检索
● 连接查询
● Hibernate的数据检索策略
8.1 QBC数据检索
  前面,介绍了Hibernate常用的查询检索方式HQL。HQL是一种面向对象的,语法类似于SQL语句的检索语言。对于HQL检索,需要以字符串的形式来组织HQL语句,如果查询语句比较复杂,较长的HQL语句会影响程序的可读性。而且,除了参数外,HQL语句的条件和其他各部分组成都是静态地写入HQL语句字符串中的。在开发中,经常会遇到根据用户输入一些查询条件来设置查询,对于这种情况,组织HQL语句就比较麻烦。
  Hibernate还提供了另一种形式的面向对象的查询方式QBC(Query by Criteria)。这种查询是以函数API的方式动态地设置查询条件,组成查询语句。基本结构是session创建criteria接口,然后调用criteria接口的add函数,增加查询条件(即criterion类),然后调用criteria的list()函数,即可以以List的形式返回查询结果。
  除了有当前session创建的criteria接口外,Hibernate还提供了在session不可用时,组织查询语句的游离态的DetachCriteria类,它的查询条件设置与criteria类似,只是它不由session创建,而是在需要session可用时,再调用getExecutableCriteria()执行查询。
  另外,为了让criteria的查询方法与HQL一样功能丰富,Hibernate还提供了projections类,用于提供各种函数来产生可以使用聚集函数,进行投影查询和进行查询结果设置的各种projection接口,提供了Restrictions类用于提供各种函数来产生各种criterion作为查询条件,提供了用于结果排序的order类。
  图8-1是Criteria相关类的结构图。

图8-1 Criteria相关类图
  由图8-1可见,Criteria接口和DetachedCriteria类都继承了CriteriaSpecification接口,开发人员在使用QBC查询时,都是直接选择用Criteria或DetachedCriteria。而它们两个都是一样使用可以设置各种查询条件或者查询结果的Criterion,Projection等接口。
8.1.1 QBC查询主要类
  下面分别对QBC查询中使用的几个主要接口和类进行介绍。
  1. Criteria接口
  Criteria调用add函数添加Criterion对象来组成查询条件,然后根据查询条件来查询持久化类对象。与HQL查询的Query类一样,也是由Session调用createCriteria()函数创建。
  以下代码是检索价格大于25的Product对象。
      Criteria crit = session.createCriteria(Product.class);
          crit.add(Restrictions.gt("price",new Double(25.0)));
          List results = crit.list();
  相应地,HQL实现的代码如下:
          Query query = session.createQuery(
                  "from Product where price>:price");
          query.setDouble("price",25.0);
          List results = query.list();
  使用Criteria查询的步骤是:
  (1) 调用一个可用的session的createCriteria()方法创建Criteria对象,其中createCriteria()方法以被查询的实体类类型作为参数。
  (2) 根据需要使用add函数设置各种criterion查询条件,使用setProjection设置projection
  (3) 调用Criteria的list或者scroll方法即执行查询语句,检索数据库,把查询结果返回放在List或ScrollableResults中。
  (4) 读取List和ScrollableResults,即可取出查询对象。
  Criteria接口在调用List或者Scroll方法时执行数据库检索,返回结果可以是以List形式或者ScrollableResults形式,与Query相比,少了iterate()方法执行返回iterator形式的结果。另外,与Query类似,Criteria也可以使用setFlushMode、setCacheMode、setTimeOut等设置各种Criteria查询的环境,可以使用setFirstResult()、setMaxResult()等方法设置检索结果。
  下面介绍在Query中没有的,但在Criteria中使用的主要的函数:
  (1) Criteria add(Criterion criterion)
  用于添加查询条件,相当于设置HQL中的where从句。当有多个查询条件时,可以逐个增加。例如:
  Criteria crit = session.createCriteria(Product.class);
          crit.add(Restrictions.gt("price",new Double(25.0)));
          crit.add(Restrictions.gt("name","Clothes"));
  List results = crit.list();
或者使用方法链编程风格:
  List results=session.createCriteria(Product.class)
   .add(Restrictions.gt("price",new Double(25.0)))
   .add(Restrictions.gt("name","Clothes"))
   .list();
  多个add相当于对查询条件进行and操作,即要求同时满足这些条件。
  (2) Criteria addOrder(Order order)
  如果需要排序,可以使用Criteria的addOrder()方法增加排序用的Order类。
  (3) Criteria createAlias(String associationPath, String alias)
     Criteria createAlias(String associationPath, String alias, int joinType)
  用于设置连接查询,第一种重载形式使用的是默认的内连接;第二种重载形式可以选择连接方式。
  associationPath:属性的路径,使用点“.”作为分割符。例如,Basiccar持久化类中有集合属性persons,而persons中的元素是持久化类Person,而Person类中有集合属性clotheses,其中存有关联对象clothes,如果要从Basiccar对象关联到clothes对象,属性路径就写成:persons.clotheses,使用点隔开。
  alias:关联属性的别名,后面可以使用这个别名来设置关联条件。
  joinType:连接查询时选择的关联方式。joinType可以是内连接CriteriaSpecification.INNER_JOIN(默认值)、全连接CriteriaSpecification.FULL_JOIN, 或者左连接CriteriaSpecification.LEFT_JOIN。
  (4) Criteria createCriteria(String associationPath)
     Criteria createCriteria(String associationPath, int joinType)
  Criteria createCriteria(String associationPath, String alias)
  Criteria createCriteria(String associationPath, String alias, int joinType)
  创建一个指向主类关联的实体的criteria查询,在进行存在关联关系的对象检索时会用到。
  joinType可以指定关联是内连接、左联接还是全连接。alias则可以指定别名。
  返回值是被创建的子查询。
  (5) Criteria setProjection(Projection projection)
  如果需要使用投影查询、聚集函数等,可以按需要生成Projection类,然后使用Criteria的setProjection()方法,对返回结果进行某些列的投影或聚集运算。
  2. DetachedCriteria类
  使用Criteria查询需要当前有可用的session,但是,有时构造查询语句时,并不一定能得到可用的session。例如,在分层的项目开发过程中,通常会在表现层,根据用户选择的条件,动态生成SQL语句,进行查询。这个时候,表现层是得不到session的,但是它需要把构造好的查询传给业务层进行session查询。DetachedCriteria就可以解决这个问题,即在表现层,应用程序使用DetachedCriteria来构造查询条件,然后把这个detachedCriteria传递给业务层对象,让业务层在session范围内构造criteria查询。所以,detachedCriteria在构造查询时,session还不可用,detachedCriteria查询面对的是游离的POJO类,而不是Criteria查询时面对的持久化对象,所以称为detachedCriteria。以下是一个使用DetachedCriteria查询的简单例子。
  DetachedCriteria detachedCriteria= DetachedCriteria.forClass (BasicCar.class); detachedCriteria.add(Restrictions.eq("id", new Integer(1)));
  上面的代码片段先创建了一个DetachedCriteria的实例,然后只要在有可用的session时,使用它的getExecutableCriteria()方法,把这个可用的session关联进来即可,它会返回一个在这个session内进行查询的criteria类。代码如下所示:
  Criteria crit=detachedCriteria.getExecutableCriteria(session);
   List results = crit.list();
  DetachedCriteria 提供了4个静态方法forClass(Class)或forEntityName(Name)进行DetachedCriteria实例的创建。DetachedCriteria也提供了add(Criterion)、addOrder(Order)、setProjection(Projection)、createAlias()、createCriteria()等方法用于构造查询及其返回结果,与Criteria提供的函数不同的是,DetachedCriteria类提供的函数返回也是DetachedCriteria 类。
  3. Criterion和Retrictions类
  Criterion被Criteria接口的add()方法调用,作为查询条件。内置的Criterion类型由Restrictions这个工厂类提供。实现Criterion接口的可以用来作为查询条件的类包括Expression、Example、Jonction等。
  Criterion的类图关系如图8-2所示。

图8-2 Criterion相关类图
  4. Projection和Projections
  Projection接口是Criteria查询结果集的投影表示。内置的Projection类由Projections工厂类提供。实现projection接口的类包括AliasedProjection、Distinct、ProjectionList、SimpleProjection、SQLProjection等5个。Projection的类图如图8-3所示。

图8-3 Projection相关类图
  Projection接口被作为Criteria的setProjection()函数的参数,对查询结果进行选择某些属性的投影,进行avg、max、min、count、sum等聚集运算。
  Projections提供多种静态方法来产生对查询的结果进行各种运算的projection实例。
  表8-1列出了Projections提供的各种结果运算的函数,以及相应的HQL运算符。
表8-1 Projections的函数
意    思
Projections函数
HQL例子
计算平均值
avg(String?propertyName)
Select avg(o.property) from Object o
计算数目
count(String?propertyName)
Select count(o.property) from Object o
过滤相同值的属性然后计算数目
countDistinct(String?propertyName)
Select count(distinct o.property) from Object o
过滤相同值的属性
distinct(Projection?proj)
Select distinct o.property from Object o
按某个属性分组
groupProperty(String?propertyName)
From Object o group by o.property
取对象的主键
id()
Select o.id from Object o
计算最大值
max(String?propertyName)
Select max(o.property) from Object o
计算最小值
min(String?propertyName)
Select min(o.property) from Object o
投影某个属性
property(String?propertyName)
Select o.property from Object o
计算行数目
rowCount()
Select count(*) from Object o
计算总和
sum(String?propertyName)
Select sum(o.property) from Object o
  5. Order
  Order类提供对Criteria查询结果集的排序,其静态函数生成升序或降序的Order类,被作为Criteria的参数传给addOrder()方法。
  其主要函数有:
  static Order asc(String propertyName) 按降序排
  static Order desc(String propertyName) 按升序排
8.1.2 使用Expression类和Example类设置查询条件
  下面分别介绍Expression类和Example类的查询条件。
  1.Expression类
  Expression类由Restrictions工厂类的各个设置条件的静态方法产生。例如,Restrictions类中的gt(String propertyName, Object value)静态方法,用于设置某个属性等于某个值的查询条件,返回的SimpleExpression相当于where propertyName='value'的条件。
  表8-2列出了Restrictions提供的各种条件设置,以及相应的HQL运算符。
表8-2 Restrictions的函数
意    思
Restrictions函数
HQL例子
查询条件逻辑and运算
and(Criterion?lhs, Criterion?rhs)
From Object o where o.property1 = ? and o.property2=?
检索在两个数值之间的
between(String?propertyName, Object?lo,
Object?hi)
From Object o where o.property between ? and ?
等于
eq(String?propertyName, Object?value)
eqProperty(String?propertyName, String?otherPropertyName)
From Object o where
o.property=?
大于等于
ge(String?propertyName, Object?value)
geProperty(String?propertyName, String?otherPropertyName)
From Object o where
o.property>=?
大于
gt(String?propertyName, Object?value)
gtProperty(String?propertyName, String?otherPropertyName)
From Object o where
o.property>?
主键等于
idEq(Object?value)
From Object o where o.id=?
字符串匹配,不区分大小写
ilike(String?propertyName, Object?value)
ilike(String?propertyName, String?value, MatchMode?matchMode)
From Object o where lower
(o.property) like ??
(续表)  
意    思
Restrictions函数
HQL例子
范围运算
in(String?propertyName, Collection?values)
in(String?propertyName, Object[]?values)
From Object o where o.property in(‘a’,’b’…)
检查对象的一个集合属性是否为空,也就是在多对多或多对一双向关联中,“一”方对应的“多”方为空,没有关联的
isEmpty(String?propertyName)
From Object o where
o.properties is empty
检查对象的一个集合属性是否不为空
isNotEmpty(String?propertyName)
From Object o where
o.properties is not empty
一个属性是否不为空
isNotNull(String?propertyName)
From Object o where o.property is not null
一个属性是否为空
isNull(String?propertyName)
From Object o where o.property
is null
小于等于
le(String?propertyName, Object?value)
leProperty(String?propertyName, String?otherPropertyName)
From Object o where
o.property<=?
字符串匹配
like(String?propertyName, Object?value)
like(String?propertyName, String?value, MatchMode?matchMode)
From Object o where o.property
like ‘??’
小于
lt(String?propertyName, Object?value)
ltProperty(String?propertyName, String?otherPropertyName)
From Object o where
o.property<?
Not equal
ne(String?propertyName, Object?value)
neProperty(String?propertyName, String?otherPropertyName)
From Object o where o.property not
查询条件逻辑非
not(Criterion?expression)
From Object o where not(一个查询条件)
查询条件逻辑and
运算
or(Criterion?lhs, Criterion?rhs)
From Object o where o.property1 =? or o.property2=?
  
  具体各种方法的使用例子,请看8.3节。
  2.Example类
  使用Example类进行查询,首先需要创建一个对象样板,然后检索出所有与这个样板对象相同的对象。例如,
  BasicCar bc=new BasicCar();
  bc.setFactory("aaaa");
  
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add(Example.create(bc));
  
  List results = crit.list();
  Example使用静态方法create(Object entity)来创建一个Criterion对象,它按照对象的属性来比较查询条件,以上的程序段检索factory属性为"aaaa"的BasicCar对象。
  另外,Example也提供了给这个样板对象设置一些查询过滤。例如,
  Example exampleUser =Example.create(bc)
  .ignoreCase() //忽略大小写
  .enableLike(MatchMode.ANYWHERE); //任意位置的匹配
  
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add(exampleUser);
  List results = crit.list();
  这个查询相当于查询factory属性为"%aaaa%",即只要对factory属性值进行aaaa的模糊字符串匹配。
  Example类提供的各种进行查询过滤的方法如下。
● enableLike(MatchMode):用于过滤string匹配,有4个选择。分别为MatchMode.ANYWHERE(默认匹配模式,表示任意位置的匹配,形如%value%)、MatchMode.END(表示匹配结尾,形如%value)、MatchMode.EXACT(表示完全匹配)、MatchMode.START (表示匹配开头,形如value%)。
● excludeNone():排除为null或0的属性值匹配。
● excludeProperty(String name):排除名字为name的属性的匹配。
● excludeZeroes():排除为0的属性值匹配。
● ignoreCase():匹配String时忽略大小写。
● setEscapeCharacter(Character escapeCharacter):对like从句的字符串匹配,排除某些字符。
● setPropertySelector(Example.PropertySelector selector):选择属性的匹配策略。
  这些过滤方法都是返回Example类,所以可以连接着设置各种过滤。
8.1.3 使用QBC各种检索例子
  下面将通过具体的例子来介绍,如何使用Criteria查询,结合各种Criterion查询条件和Projection投影运算,满足各种数据检索要求。
  1.返回所有的对象
  下面是一个最简单的Criteria查询,只是在创建Criteria时制定检索的对象,不设置任何查询条件,即返回的List为所有的BasicCar对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  List results = crit.list();
  返回的List中存放的是BasicCar对象。
  以上的代码相当于执行了select * from basiccar。
  2. 聚集函数
  Criteria使用Projections类可以使用聚集函数max、min、avg、count、countDistinct、rowCount等。
  如果需要使用多个Projections设置多个条件,则需要使用Projections的projectionList()函数创建一个ProjectList。
  下面的例子Projections的max、min、countDistinct返回BasicCar的最大id、最小日期和计算不同的名字有多少个。
  Criteria crit = session.createCriteria(BasicCar.class)
  ProjectionList projList = Projections.projectionList();
  projList.add(Projections.max("id"));
  projList.add(Projections.min("date"));
  projList.add(Projections.countDistinct("name"));
  crit.setProjection(projList);
  List results = crit.list();
  返回的List中存放有最大id,最小日期,不同name的个数,这三者组成的对象数组。
  其对应的SQL语句如下:
  select max(this_.id) as y0_, min(this_.date) as y1_, count(distinct this_.name) as y2_ from basiccar this_
  3. many-to-one双向关联检索
  检索sid=1的Salesman对象关联的carorder对象cid大于1的数据
  Criteria crit = session.createCriteria(Salesman.class);
  crit.add(Restrictions.eq("sid",new Long(1)));
  Criteria prdCrit = crit.createCriteria("carorders");
  prdCrit.add(Restrictions.gt("cid",new Long(1)));
  List results = crit.list();
  返回的List中存放的是Salesman对象。
  如果使用了连接检索策略,以上查询相当于执行以下的SQL语句:
  select * from salesman this_ inner join carorder1 carorder1_ on this_.sid=carorder1_.salesId left outer join salesman salesman4_ on carorder1_.salesId=salesman4_.sid where this_.sid=? and carorder1_.cid>?
  4.使用数学运算符>=、>、!=、=
  下面的例子检索日期大于等于"1994-03-04"的BasicCar对象,并且使用setMaxResults(1)函数设置返回的列表只取一个结果。
     Criteria crit = session.createCriteria(BasicCar.class);
   crit.add( Expression.ge("date", Date.valueOf("1994-03-04")) );
   crit.setMaxResults(1);
   List results = crit.list();
  其对应的SQL语句如下:
  select * from basiccar b where b.date>='1994-03-04'
  下面的例子检索id大于1的BasicCar对象。
     Criteria crit = session.createCriteria(BasicCar.class);
   crit.add( Expression.gt("id", new Long(1)) );
   List results = crit.list();
  其对应的SQL语句如下:
  select * from basiccar b where b.id>1
  下面的例子检索id不等于1的BasicCar对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add( Expression.ne("id", new Long(1)) );
  List results = crit.list();
  其对应的SQL语句如下:
  select * from basiccar b where b.id<>1
  5. 查询id
  下面的例子检索主键为1的对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add( Expression idEq(new Long(1)) );
  crit.setMaxResults(1);
  List results = crit.list();
  其对应的SQL语句如下:
  select * from basiccar b where b.id = 1
  6. group by
  下面的例子按name来进行分组。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.setProjection(Projections.groupProperty("name"));
  List results = crit.list();
  返回对象List用来存放不同名字的数组,存放的是string行的name。
  其对应的SQL语句如下:
  select b.name from basiccar b group by b.name
  7. 字符串匹配,like和islike
  下面的例子使用like()函数检索name开头字母为A的BasicCar对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add(Restrictions.like("name","A%"));
  List results = crit.list();
  其对应的SQL语句如下:
  select * from basiccar b where b.name like ‘A%’
  使用islike()函数,代表不区分大小写的字符串匹配。
  MatchMode可以选择匹配模式,MatchMode.END从末端开始匹配,MatchMode.ANYWHERE表示任意地方的字符串匹配, MatchMode.START从开始的地方开始匹配, MatchMode.EXACT表示整个字符串精确匹配。
  下面的例子检索数据库name的后面字符为不区分大小写的a的BasicCar对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add(Restrictions.islike("name","a", MatchMode.END));
  List results = crit.list();
  8. is null
  下面的例子检索数据库name为空的BasicCar对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.add(Restrictions.isNull("name"));
  List results = crit.list();
  9. 投影某些属性
  使用Projections.property()函数,可以对结果进行某些属性的投影,相当于使用select语句。
  下面的例子返回结果进行name和date列的投影。
  Criteria crit = session.createCriteria(BasicCar.class);
  ProjectionList projList = Projections.projectionList();
  projList.add(Projections.property("name"));
  projList.add(Projections.property("date"));
  crit.setProjection(projList);
  List results = crit.list();
  返回的List中存放的是由name和date组成的对象数组。
  其对应的SQL语句如下:
  select b.name as n, b.date as d from basiccar b
  10. 计算列返回的数目
  下面的例子计算返回的BasicCar对象的数量。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.setProjection(Projections.rowCount());
  List results = crit.list();
  其对应的SQL语句如下:
  select count(*) as y0_ from basiccar this_
  11. 设置返回结果
  下面的例子使用setFirstResult(2)设置从第二个开始返回,使用setMaxResults(2)设置每次最多返回2个对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.setFirstResult(2);
  crit.setMaxResults(2);
  List results = crit.list();
  下面的例子使用uniqueResult得到单个对象。
  Criteria crit = session.createCriteria(BasicCar.class);
  Criterion price = Restrictions.gt("name",new Lone(1));
  crit.setMaxResults(1);
  Product product = (Product) crit.uniqueResult();
  12. 结果排序
  使用Order类调用desc()函数、asc()函数可以进行排序。
  下面的例子根据BasicCar对象的name进行升序排序。
  Criteria crit = session.createCriteria(BasicCar.class);
  crit.addOrder(Order.desc("name"));
  对应的SQL语句是:
  Select * from basiccar b by b.name desc
  13. 逻辑or、and条件
  下面的例子中设置了3个条件,分别是id大于等于1,name开头字母为P,factory的开头字母为f。然后把前两个条件进行逻辑或操作,即只要满足其中之一,再把最后一个条件进行逻辑与操作。
  Criteria crit = session.createCriteria(BasicCar.class);
  Criterion id = Restrictions.gt("id",new Long(1));
  Criterion name = Restrictions.like("name","P%");
  LogicalExpression orExp = Restrictions.or(id,name);
  crit.add(orExp);
  crit.add(Restrictions.ilike("factory","f%"));
  List results = crit.list();
  在上面的例子中Restrictions.or()把id和name两个检索条件进行or结合;在原来的查询条件上不断地add(),那么就相当于把检索条件进行and逻辑与操作。
  其对应的SQL语句如下:
  select * from basiccar this_ where (this_.id>? or this_.name like ?) and lower(this_.factory) like ?
8.2 连 接 查 询
  在数据库检索过程中,表之间的连接查询是很常见,先来看一下各种连接的定义。
8.2.1 连接定义
  本节将介绍三种连接方式,分别是内连接、右外连接和左外连接。
● 左连接:左连接的结果集包括LEFT OUTER 子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表的列均为空值。
● 右连接:右连接是左连接的反向连接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
● 内连接:内连接使用比较运算符根据每个表共有的列的值匹配两个表中的行,即返回相关联的列所匹配的行。
  下面用具体实例来说明这三种连接。
  数据库中有两个表,person和basiccar,其中person的列carId是关联basiccar的外键,现在表中的数据如下:
  basiccar表
  +----+------+----------+------------+
  | id | name | factory | date         |
  +----+------+----------+------------+
  | 1 | car1 | shenzhen | 1992-02-02 |
  | 2 | car2 | nanjing | 1992-04-02 |
  +----+------+----------+------------+
  person表
  +----+-----------+-------+
  | id | salesname | carId |
  +----+-----------+-------+
  | 1 | man1        |     1 |
  | 2 | man2        | NULL |
  | 3 | man3        |     1 |
  +----+-----------+-------+
  
  
  由上面的数据可以看出,basiccar表有2组数据;person表有3组数据。其中存在两组关联:id为1的person和id为1的basiccar关联;id为3的person和id为1的basiccar关联。下面,分别来看看各种连接下,数据的检索结果。
  1.内连接
  在数据库中输入以下查询语句:
  select person.id as pid,
         basiccar.id as bid,
         person.carid as carid,
         person.salesname as sName,
         basiccar.name as carName,
         basiccar.factory factory,
         basiccar.date as date
  from person person inner join basiccar basiccar
  on person.carId=basiccar.id;
  数据查询结果如下:
  +-----+-----+------+-------+--------+----------+------------+
  | pid | bid | carid | sName | carName | factory | date       |
  +-----+-----+------+-------+---------+----------+-----------+
  |   1 |   1 |     1 | man1 | car1    | shenzhen | 1992-02-02 |
  |   3 |   1 |     1 | man3 | car1    | shenzhen | 1992-02-02 |
  +-----+-----+------+-------+---------+----------+-----------+
  由上面的查询结果可以看出,内连接检索出有关联关系的两组对象组成的数据,内连接是把关联关系返回。
  2.左连接
  先看使用person表左连接basiccar表的查询结果。在数据库中输入以下查询语句:
  select person.id as pid,
         basiccar.id as bid,
         person.carid as carid,
         person.salesname as sName,
         basiccar.name as carName,
         basiccar.factory factory,
         basiccar.date as date
  from person person left join basiccar basiccar
  on person.carId=basiccar.id;
  数据查询结果如下:
  
  +-----+-----+-------+-------+---------+----------+----------+
  | pid | bid | carid | sName | carName | factory | date       |
  +-----+------+-------+-------+---------+----------+---------+
  |   1 |    1 |     1 | man1 | car1    | shenzhen | 1992-02-02 |
  |   2 | NULL | NULL | man2 | NULL    | NULL       | NULL        |
  |   3 |    1 |     1 | man3 | car1    | shenzhen | 1992-02-02 |
  +-----+------+-------+-------+---------+----------+---------+
  由上面的查询结果可以看出,person表的3组数据都被检索出来了,与其相关联的basiccar数据,如果有值,则把关联的basiccar记录检索出来;如果为空,则赋值null。
  下面再看一下使用basiccar表左连接person表的查询结果。在数据库中输入以下查询语句:
  select person.id as pid,
         basiccar.id as bid,
         person.carid as carid,
         person.salesname as sName,
         basiccar.name as carName,
         basiccar.factory factory,
         basiccar.date as date
  from basiccar basiccar left join person person
  on person.carId=basiccar.id;
  数据查询结果如下:
  +------+-----+-------+-------+---------+----------+---------+
  | pid | bid | carid | sName | carName | factory | date       |
  +------+-----+-------+-------+---------+----------+---------+
  |    1 |   1 |     1 | man1 | car1    | shenzhen | 1992-02-02 |
  |    3 |   1 |     1 | man3 | car1    | shenzhen | 1992-02-02 |
  | NULL |   2 | NULL | NULL | car2    | nanjing   | 1992-04-02 |
  +------+-----+-------+-------+---------+----------+---------+
  由上面的查询结果可以看出,basiccar表的两组数据都被检索出来了,与其相关联的person数据,如果为空,则赋值null。在上表结果中,由于basiccar.id=1的basiccar记录关联两组person记录(id=1,id=3),所以会把这两组组合作为两个记录返回;basiccar.id=2的basiccar记录,虽然没有关联的person记录,则把person记录记为null,也把其作为一个记录返回。
  由此可见,左连接是内连接的检索结果加上左连接左边表中没有关联关系的数据。
  3.右连接
  先看一下使用person表右连接basiccar表的查询结果。在数据库中输入以下查询语句:
  select person.id as pid,
         basiccar.id as bid,
         person.carid as carid,
         person.salesname as sName,
         basiccar.name as carName,
         basiccar.factory factory,
         basiccar.date as date
  from person person right join basiccar basiccar
  on person.carId=basiccar.id;
  数据查询结果如下:
  +------+-----+-------+-------+---------+---------+----------+
  | pid | bid | carid | sName | carName | factory | date       |
  +------+-----+-------+-------+---------+---------+----------+
  |    1 |   1 |     1 | man1 | car1    | shenzhen | 1992-02-02 |
  |    3 |   1 |     1 | man3 | car1    | shenzhen | 1992-02-02 |
  | NULL |   2 | NULL | NULL | car2    | nanjing   | 1992-04-02 |
  +------+-----+-------+-------+---------+---------+----------+
  由上面的查询结果可以看出,person表右连接basiccar表的查询结果与basiccar表左连接person表的查询结果一样。basiccar表的两组数据都被检索出来了,与其相关联的person数据,如果为空,则赋值null。在上表结果中,由于basiccar.id=1的basiccar记录关联两组person记录(id=1,id=3),所以会把这两组组合作为两个记录返回;basiccar.id=2的basiccar记录,虽然没有关联的person记录,则把person记录记为null,也把其作为一个记录返回。
  再看一下使用basiccar表右连接person表的查询结果。在数据库中输入以下查询语句:
  select person.id as pid,
         basiccar.id as bid,
         person.carid as carid,
         person.salesname as sName,
         basiccar.name as carName,
         basiccar.factory factory,
         basiccar.date as date
  from basiccar basiccar right join person person
  on person.carId=basiccar.id;
  数据查询结果如下:
  +-----+------+-------+-------+---------+---------+----------+
  | pid | bid | carid | sName | carName | factory | date       |
  +-----+------+-------+-------+---------+---------+----------+
  |   1 |    1 |     1 | man1 | car1    | shenzhen | 1992-02-02 |
  |   2 | NULL | NULL | man2 | NULL    | NULL      | NULL         |
  |   3 |    1 |     1 | man3 | car1    | shenzhen | 1992-02-02 |
  +-----+------+-------+-------+---------+---------+----------+
  由上面的查询结果可以看出,basiccar表右连接person表的查询结果与person表左连接basiccar表的查询结果一样。person表的3组数据都被检索出来了,与其相关联的basiccar数据,如果有值,则把关联的basiccar记录检索出来;如果为空,则赋值null。
  由此可见,右连接是内连接的检索结果加上右连接右边表中没有关联关系的数据。
8.2.2 Hql,Criteria对连接的支持
  分别在数据库中建立两张关联的salesman表和carorder1表,其中carorder1表使用salesId列关联到salesman。
  其中,salesman表及其数据如下:
  +-----+-----------+
  | sid | salesName |
  +-----+-----------+
  |   1 | man32      |
  +-----+-----------+
  carorder1表及其数据如下:
  +-----+-------------+---------+
  | cid | carname      | salesId |
  +-----+-------------+---------+
  |   1 | order212122 |       1 |
  |   2 | order2221    |       1 |
  +-----+-------------+---------+
  使用多对一双向关联来建立它们的对象关系模型。如果两个对象模型之间没有设立关联关系,虽然数据库表中有关联关系,也无法通过Hibernate 的Hql和Criteria进行连接查询,因为这两个查询是面向对象的,必须是对象之间建立了关联关系。如果是两个对象间没有关系的表进行连接查询,那么可以使用SQL查询,这样可以使用on来指定进行任意列的关联。下面是两个表对应的Salesman对象和CarOrder对象的主要部分。
  Salesman对象的代码:
  public class Salesman implements java.io.Serializable{
   private long sid;
  
   private String salesName;
  
   private Set carOrders = new HashSet();
  //…省略了getter/setter和构造函数
  
  Salesman对象的映射文件Salesman.hbm.xml的主要部分,定义了salesman的属性和数据库表映射,以及关联到carorder的集合映射。代码如下:
  <class name="basicCar.bean.Salesman" table="salesman" lazy="false">
   <id column="sid" name="sid" type="long">
   <generator class="increment"></generator>
   </id>
   <property name="salesname" column="salesName" type="string"></property>
   <set name="carorders" inverse="false" cascade="delete">
   <key column="salesId"/>
   <one-to-many class="basicCar.bean.CarOrder"/>
   </set>
   </class>
  CarOrder对象的代码:
  public class CarOrder implements java.io.Serializable {
  
   private long cid;
  
   private String carName;
  
   private Salesman salesman;
  //…省略了getter/setter和构造函数
  CarOrder对象的映射文件CarOrder.hbm.xml的主要部分,定义了carorder的属性和数据库表映射关系,以及和salesman对象的关联。
  <class name="basicCar.bean.CarOrder" table="carorder1" lazy="false">
   <id column="cid" name="cid" type="long">
   <generator class="increment"></generator>
   </id>
   <property name="carname" column="carname" type="string"></property>
     <many-to-one name="salesman"
     column="salesId"
     class="basicCar.bean.Salesman"
     cascade="all"
     not-null="false">
     </many-to-one>
   </class>
0 0