12月14日——培训第21天

本文介绍了 Hibernate 中 Criteria API 和 Query by Example 的使用方法,包括条件筛选、排序、抓取策略等内容。同时探讨了实体映射模式的选择以及主键生成策略,并简述了数据库建模技术和范式规范化的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

起晚了,太不顺利了,车又挤又堵的,差点就迟到了

--------------------------------------
QBC也是通过Session来完成的,例如
Criteria crit = sess.createCriteria(Cat.class);
List cats = crit.list();

Query query = sess.createQuery("from Cat");
List cats = query.list();

以上两者等价。

例:
session.beginTransaction();
Criteria c = session.createCriteria(User.class);
List l = c.list();
Iterator it = l.iterator();

while(it.hasNext())
{
 User u = (User)it.next();
 System.out.println(u.getUsername());
}
//这就取出了所有的用户名
session.getTransaction().commit();

 

Criteria c = session.createCriteria(User.class);
c = c.add(Restrictions.and(Restrictions.eq("username","aa"),Restrictions.eq("password","aa")));
List l = c.list();
Iterator it = l.iterator();

迭代后就查出了用户名为aa而且密码为aa的用户
add方法添加Criterion,Restrictions的所有方法都是静态的,里面的方法都返回Criterion的具体实现子类
也就是
 Criteria c = session.createCriteria(User.class);
 Criterion eq = Restrictions.eq("username","aa");
 Criterion eq2 = Restrictions.eq("password","aa")
 c = c.add(Restrictions.and(eq,eq2));
 
-----------------------------------------
排序在QBC中的使用:Order
addOrder(Order asc("name")) //按照name属性来升序排列
addOrder(Property.forName("name").asc())  //和上面一样,也是按照name属性来升序排列

List cats = sess.createCriteria(Cat.class)
   .add( Restrictions.like("name", "F%")
   .addOrder( Order.asc("name") )
   .addOrder( Order.desc("age") )
   .setMaxResults(50)
   .list();

List cats = sess.createCriteria(Cat.class)
   .add( Property.forName("name").like("F%") )
   .addOrder( Property.forName("name").asc() )
   .addOrder( Property.forName("age").desc() )
   .setMaxResults(50)
   .list();

 

在QBC中是不可以将两个实体类进行关联的,只能将实体类和其中的属性关联(该属性是个集合),
这将在后面的课程中详细阐明。

比如:
List cats = sess.createCriteria(Cat.class)
  .add( Restrictions.like("name", "F%") )
  .createCriteria("kittens")
  .add( Restrictions.like("name", "F%") )
  .list();
注意这里kittens是Cat实体类中的一个属性,且kittens是个集合
也就是找出以F开头的名字的大猫中的以F开头的名字的小猫。

--------------------------------------------
抓取策略:
有20名老师,每个老师手底下有若干个学生
有老师和学生两张表,如果要加载老师和老师手下的所有学生的话,
那么需要这么作:
select * from teachers
然后select * from students where teacher_id = ?
后面这句根据teacher_id来查询的语句总共要出现20次,因为有
20个老师。如果有n个老师的话,那么就需要有n+1条sql语句,
这就是有名的n+1查询。

如果要将所有的老师和学生用一条语句查出来的话:
用左外联的方式,因为可能会出现有的老师手底下没有学生的情况:
select * from teachers t left outer join students s
on t.id = s.teacher_id

如果转换为oracle的形式的连接语句的话,
select * from teachers, students where teachers.id = students.teacher_id(+)

FetchMode.JOIN:就是上面的连接形式加载
FetchMode.SELECT :惰性加载,就是n+1查询加载

例子:
List cats = sess.createCriteria(Cat.class)
 .add( Restrictions.like("name", "Fritz%") )
 .setFetchMode("mate", FetchMode.EAGER)
 .setFetchMode("kittens", FetchMode.EAGER)
 .list();
加载猫的配偶和孩子,每只猫可能有配偶,可能有多个孩子

抓取策略的内容在后面的课程还会继续进行详细的讲解,所以这里先不用太在意这里。
-----------------------------------------
课间休息
-----------------------------------------

-------------------------------------------
QBE:

QBE是Query By Example的简写,其实也应该算是QBC的一部分。
QBE是基于示例的查询,QBE给出查询的范例,基于范例创建的查询将依据范例给出结果。
QBE的核心接口是org.hibernate.criterion.Example,方法create用于创建一个范例的实例。

如下代码:
Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();
将执行以下SQL:
select … from cat where (sex=‘F’ and color=‘B’)


例:
session.beginTransaction();

//第一步,创建一个样例
User u = new User();
u.setUsername("aa");
u.setPassword("aa");
Example e = Example.create(u).excludeZeroes().excludeProperty("username");
//注意这里的excludeZeroes方法排除了设置初值的情况,excludeProperty排除了setUsername,
//也就是尽管设置了username,也是无效的;当然还有其他的exclude方法


//第二步,创建criteria
Criteria c = session.createCriteria(User.class);

//第三步,将样例添加到criteria中去
c.add(e);

//按照样例查询时,是将那些不为空的属性及其值作为查询条件,
//可以使用excludeZeroes或者exludeProperty方法来排除一些特定的属性。
User uu = (User)c.uniqueResult();
session.getTransaction().commit();

---------------------------------------
老师的源代码:

   //第一步,创建一个样例
   User u = new User();
   u.setUsername("aa");
   //u.setPassword("aa");
           
   //按样例查询时,是将那些不为空的属性及其值作为查询条件
   //可以使用excludeZeroes/excludeProperty排除一些特定属性
   Example e = Example.create(u)
                      .excludeZeroes()
                      .excludeProperty("password");
   
   //第二步,创建criteria
   Criteria c = session.createCriteria(User.class);
   
   //第三步,将样例添加到criteria中
   c.add(e);
   
   
   User uu = (User)c.uniqueResult();
   System.out.println(uu.getUsername());
--------------------------------------------------

Hibernate中的持久化对象可以是任意满足JavaBean编写规则的类,如果满足以下四点要求,会更好地在Hibernate中工作:
1、必须有一个公共的无参的构造函数
2、最好包含一个标识属性
3、最好不是final类
4、声明持久化属性及他们的getter方法和setter方法
持久化对象可继承,子类应满足条件1、2。


动态模型:
属性hibernate.default_entity_mode必须配置为dynamic-map,除此之外还可以配置为dom4j
和pojo,默认为pojo

在属性配置文件中加上  hibernate.default_entity_mode dynamic-map(也就是用一个map对应一个表)
注意dynamic-map还可以改为dom4j(用xml文件对应一个表)和pojo(用实体类对应一个表,也是我们以前一直采用的方法)
(或者在配置文件中加一条property元素也可以,name属性设为default_entity_mode)

然后在映射文件中改为 <class table="users" entity-name="user"> 因为没有使用实体类,所以不需要在class中写name属性了
也就是不再为表指明一个实体类了,让所有的表都对应到一个map上去,让map的键对应表的字段名,
值对应表中对应记录的值.这里的entity-name是很必要的,因为可能有多个映射,但是现在没有实体类与之对应了,所以应该
自己手动指定一个entityName,可以查看文档中Session中的load方法说明。

这样映射文件中的下面一系列的property元素也要进行相关的修改。
<property name="username" column="username" type="string"/>
前者相当于map中的key值,后者相当于map中的key对应的具体值,由于没有了实体类的反射机制,
这里必须加上这个type属性,而且相对的,后面的所有的字段类型都必须显式的指明,不然是无法定位类型的。

session.beginTransaction();
Map user = (Map) session.load("user", new Integer(1)); //这里的“user”注意是entity-name
System.out.println(user.get("username"));  //如果运用到HQL中的话,from后面加的就是实体名entity-name
           //而不是实体类名了。

session.getTransaction().commit();


也就是说hibernate的映射并不一定非要把表和一个具体的实体类相关联,可以和一个map相关联(这样就省得创建实体类了),
也可以和一个xml文件相关联。

 

session.beginTransaction();

Map user = new HashMap();
user.put("username","java");
user.put("password","java");
session.save("user",user); //第一个"user"是实体名entity-name
要存进去或是更新等操作的话,记住一定指定entity-name!!!!

这里看一看Oracle的主键设置:
必须:<generator class="sequence">
  <param name="sequence">users_seq</param> //这里需要创建oracle数据表的序列,名为users_seq
  <generator>
注意这里使用了Oracle的驱动方言,所以对应的要使用Oracle的数据库

如果没有主键生成器的话也是可以的,这时插入操作就必须设置主键的值,有主键生成器的话在插入操作前设置主键
是没有意义的!

我们常用的是identity和sequence两种主键生成器!
还有increment,它也是递增,但是不解决并发问题(是程序来控制递增),也就是先到数据库中找主键的最大值,然后
再把主键加1后插进去。

identity是数据库控制的自增,可以解决并发问题
还有native自动选取。
hilo高低位算法生成一个主键
uuid通过网卡ip地址来定位一个字符串存成一个主键
--------------------------------------------------------

上午课程总结:
1 QBC :
Criteria来创建查询的范围(即从哪些类中来插,代表的是from语句),
用Restrictions的静态方法来创建Criterion查询条件
Criterion代表查询条件(也就是where语句)

Order.asc或者Order.desc代表排序的order by
Property代表一个具体的属性,可以通过它来执行组函数。

2 QBE
Example,创建时调用Example.create方法。
Hibernate根据样例中不为空的属性(即不等于null)来设定查询条件,所以
可以通过Example.excludeZeroes来排除为0的属性或者Example.excludeProperty
来排除指定的属性名。

3 实体的三种模式
(1)POJO,要求必须要有一个实体javaBean来对应一张具体的表
(2)dynamic-map,使用Map来映射所有的表,要求为每个实体都起一个名字,就是要为属性entity-name设置值。
(3)dom4j,目前还不支持,主要是用xml文件映射一个表

4 主键生成器
(1)主键生成器的作用
(2)各种类型的主键生成器有什么区别?会使用identity/sequence就可以了。
---------------------------------------------------------------------------------------------------------

PowerDesigner的使用:

1 创建概念模型,首先要找出实体,然后再确定实体之间的关系
2 生成报告、交给负责人认可
3 生成物理模型
4 建立数据库连接
5 导入数据库

-------------------------------
数据建模技术:

数据模型:数据库系统中关于数据和联系的逻辑组织的形式表示。
        层次模型、网状模型和关系模型三种,现在应用最广泛的是关系模型。
关系模型与面向对象思想不一致,因此有了ORM来解决表和对象之间的关系。

数据库模式就是数据库结构,确定数据库模式是数据库建模的最重要任务
模式分为逻辑模式、外模式和内模式,实际上可以分别对应到表、视图和索引
模式在英文中是schema

模式:也叫作逻辑模式,是数据库中全体数据的逻辑结构和特征的描述,是所有
用户的公共数据视图。

外模式:是数据库用户能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图
  是与某应用有关的数据的逻辑表示。

内模式:是数据物理结构和存储方式的描述,是数据在数据库内部的表示方式。

外模式也叫作子模式或者用户模式,内模式也叫作存储模式。

数据库建模就是确定数据库结构的过程,也就是定义数据库模式的过程。

将现实世界中的事物以及他们之间的联系抽象出来,并且以某种组织方式存储在数据库中。
这种组织方式就是数据库结构。

现实世界中的事物在数据库设计中称为实体,而事物与事物之间的联系叫做关系。

网上购物,业务流程是什么?有哪些事物呢?哪些事物需要持久化?哪些不需要?

数据库建模方法:1 ER图 2 对象定义语言(ODL:Object Defination Language)
矩形表示实体,椭圆表示属性,菱形表示关系

先有一个思路,然后画出ER图,确定好关系,然后导入关系型数据库中。

--------------------------------
数据库范式:
一范式:数据库表中不存在重复字段,并且字段不可分割
二范式:不存在非主键字段对任一主键字段的部分依赖关系(用来约束联合主键)
  一个爸爸和一个妈妈决定了一个孩子;一个妈妈决定了一个孩子就是不符合第二范式
  假如必须有两个主键但是你只设了一个主键的话,那么不符合第二范式
三范式:不存在非主键对任一主键的传递依赖关系。
  由a决定b,b决定c,但是没有a直接去决定c,就是传递依赖   
BC范式:
   任何字段都不存在传递函数依赖,即主键对主键没有传递依赖。

第一范式就不说了,只要是个合法的表都满足第一范式,但是假如有个地址的字段,里面包含
国、省份、市、地区的话,可以通过设置外键,或是分解字段的方法来解决。

二范式主要规范联合主键情况:要求非主键字段不能由符合主键的一个完全决定,而必须由符合主键共同决定!
      只要没有使用联合主键,肯定不会出现违反第二范式的情况!
例:学生选课表的设计应该如何实现?
 假设选课关系表为学号、姓名、年龄、课程名称、乘积、学分。
 那么这个表里面的主键应该为学号和课程名称联合作为主键,这是毫无疑问的,但是不符合第二范式:因为姓名
 可以由学号完全决定,和课程名称没有任何关系,也就是说姓名这个非主键字段对这个联合主键有部分依赖关系存在。
 同理,学分完全由课程名称来决定,也是部分依赖的。
 成绩是不存在部分依赖的,是由学号和课程名称共同决定的

所以这个表是不满足第二范式的,危害如下:
1 数据冗余
2 更新异常,若调整了课程的学分,那么每条记录都要调整
3 删除异常,假如要删一个学生,那么他选的课也跟着丢了,要是这门课没有别人选的话,该课程的信息就永远的丢失了
4 插入异常,假如新来了一门课程,但是没有人选,试问这怎么插入呢?没办法弄……
  
解决不满足第二范式情况的办法是分解主键
建立学生表(学号,姓名,年龄)课程表(课程名称、学分)和选课关系(学号,课程名称,成绩)

一般认为,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字

满足第二范式的设计肯定满足第一范式的要求!

下面说说第三范式:
要求不存在非关键字段对任一候选关键字段的传递函数依赖关系,其实,就是不能出现非主键字段决定其他非主键字段!

关键字段决定了非关键字段x,而x又决定了另外一个非关键字段y,这就不满足第三范式!

假设须生关系表(学号,姓名,年龄,所在班级,教室编号,班级人数)
主键是学号

学号确定了其余的字段,但是呢,所在班级直接决定了教室编号和班级人数,这就是典型的传递依赖,不满足第三范式

解决方法:
分解实体:学生(学号,姓名,年龄,所在班级)
  学院(班级,地点,人数)
  两者之间用主外键关系联系起来,二者是一对多的关系。

BC范式:主键也不可以有传递依赖关系。
例:
假如一个表(班号,课程号,老师号,课程时间)
那么课程号和班号唯一决定是课程时间
而且课程号和老师号也可以决定课程时间
但是呢,班号决定了老师号,这就是说主键存在传递依赖。

解决方法:分解关系
班级号,课程号,课程时间
班级号,老师号
建立成上面两个关系表就ok了

总的来说,各个范式都是在尽力去分解,不把数据都存在一个表中。
应该尽可能的去满足范式的要求!
在性能的要求下,也并非一定要严格遵从范式要求,以适当的数据冗余来提升性能也是一个很好的选择。


怎么在数据库中的表中表示关系:
关系种类:
一对一,多对多,一对多,多对一

怎么在数据库中的表里面表示上面这四种关系??
可以通过外键方式;还可以通过主键对应方式,比如学生和电脑的一对一的关系可以通过
让学生的主键和对应电脑的主键一致来做到,其中必然有一个主键是主动变化的,而另外一个
主键是跟随这这个主键来变化

还有可以通过再建立一个关系表的方式,这个关系表中就有两个主键,一个是电脑,一个是学生
将他们两个关联起来,在学生表和电脑表中都不考虑关系的问题,全部让关系表去考虑

一对多或是多对一的关系里面,用多的那一端来存放外键,还可以用关系表方式。

多对多的关系只能用关系表来解决了,没别的办法了。

因此一对一有三种方式:主键方式,外键方式和关系表方式
一对多有两种方式:外键和关系表方式
多对多只有关系表方式

hibernate中强烈建议使用关系表,这样把对象和关系分离开来,好处理。


-----------------------------------------
学生、课程、老师、班级这四个实体的关系把它们弄清楚!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值