Hibernate课堂笔记
Hibernate课堂笔记
Hibernate第一天
一:hibernate的基础知识
Hibernate的整体思想就是“操作对象的过程,就是操作数据库表的过程”。如图:
1:Hibernate的入门配置:
第一步:建立itcastHibernate工程
第二步:建立数据库itcasthibernate,可将编码格式设置成UTF-8
第三步:引入hibernate相关的jar包
包结构的详解:
第四步:持久化对象Customer.java
在cn.itcast.a_primer中创建持久化对象的javabean,hibernate的实质就是操作对象的过程,就是操作数据库表。
创建表和对应的javabean文件:
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
//省略set和get方法
}
第五步:hibernate.cfg.xml
在cn.itcast.a_primer中创建hibernate.cfg.xml,hibernate.cfg.xml是hibernate的核心配置文件,用来查询数据库,配置如何操作数据库,加载映射文件等。
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 其他配置 -->
<!-- 方言:用来指定hibernate操作的数据库 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- hibernate操作对象用来执行操作数据库表,在控制台中显示sql语句 -->
<property name="hibernate.show_sql">true</property>
<!--
hibernate.hbm2ddl.auto:用来建立对象和表的关联
* create:每次都会先删除表,再创建表,问题不能保存数据
* none:默认值:有表的时候,可以直接操作,没有表就会报错,默认向表中追加数据
* update:没有表就创建表,有表就直接操作数据
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 加载映射文件
1:class="cn.itcast.a_primer.Customer"(要求类和映射文件放在同一个目录下,而且文件名要相同)
2:resource=cn/itcast/a_primer/Customer.hbm.xml(第二种方式)
<mapping resource="cn/itcast/a_primer/Customer.hbm.xml"/>-->
</session-factory>
</hibernate-configuration>
创建 Hibernate 配置文件, Hibernate 从其配置文件中读取和数据库连接的有关信息, 这个文件应该位于应用的 classpath 下.
注:该映射文件的规范在org.hibernate.hibernate-configuration-3.0.dtd文件中
第六步:customer.hbm.xml
在javabean的同级目录下创建hibernate的映射文件(映射文件用来将持久化对象和属性关联数据库表和字段
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
建立持久化对象和数据库表的关联关系(映射文件)
* 类映射表
* 类中的属性映射表中的字段
-->
<hibernate-mapping>
<!--
对象关联表
name:对象的全路径(底层使用反射)
table:映射表的名称
-->
<class name="cn.itcast.a_primer.Customer" table="a_customer">
<!--
对象的属性映射表的字段
type:表示hibernate的映射类型,用来建立java类型和数据库类型的桥梁
* 使用java类型,比如java.lang.Integer
* integer
name:对象中的属性名称
column:表中的字段名称
* generator:hibernate的生成策略(重点)
sql-type="varchar(20)",指定数据库表中列的类型,默认是varchar(255)
-->
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" type="string">
<column name="name"></column>
</property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
这里
创建对象-关系映射文件
l Hibernate 采用 XML 格式的文件来指定对象和关系数据之间的映射. 在运行时 Hibernate 将根据这个映射文件来生成各种 SQL 语句
l 映射文件的扩展名为 .hbm.xml 这里Customer.hbm.xml文件
注:该映射文件的规范在org.hibernate.hibernate-mapping-3.0.dtd文件中
property元素:设定持久化类的属性和表字段映射
name属性:指定持久化类的属性的名字
column属性:指定表中字段的名称
type属性指定Hibernate映射类型 Hibernate映射类型是java类型与SQL类型的桥梁
注:该映射文件的规范在org.hibernate. hibernate-mapping-3.0.dtd文件中
第七步:测试单表操作的CRUD
使用测试代码进行测试,创建App的测试类,使用junit进行测试
(1) hibernate的SessionFactory在初始化阶段只初始化一次即可,所有使用静
态代码块进行封装,其中Configuration是用来加载hibernate的配置文件和映射文件的,加载后可以获取SessionFactory。
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/a_primer/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
(2) 新增保存:save方法
/**保存*/
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("洪七公");
c.setAge(60);
c.setDes("帮助");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
(3) 更新:update方法
/**更新*/
@Test
public void update(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setId(3);
c.setName("黄老邪");
c.setAge(59);
c.setDes("药师");
s.update(c);
tr.commit();
s.close();
}
(4) 删除:delete方法
@Test
public void delete(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setId(3);
s.delete(c);
tr.commit();
s.close();
}
(5) 查询:get和load方法
/**使用ID查询信息*/
@Test
public void findCustomerById(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer) s.get(Customer.class, 2);
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge()+" "+c.getDes());
tr.commit();
s.close();
}
(6) 查询:query查询(支持hql语句,sql语句,qbc语句)
/**查询所有信息*/
@Test
public void findAllCustomerList(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
/**使用Hql语句:操作持久对象和属性
* 复习sql语句:操作数据库表和数据库表的列*/
Query query = s.createQuery("from Customer o");
List<Customer> list = query.list();
if(list!=null && list.size()>0){
for(Customer c:list){
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge()+" "+c.getDes());
}
}
tr.commit();
s.close();
}
附录:
2:Hibernate的API简介
(1)Configuration 类
l Configuration 类负责管理 Hibernate 的配置信息。包括如下内容:
• Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)。
• 持久化类与数据表的映射关系(*.hbm.xml 文件)
l 创建 Configuration 的两种方式
• 属性文件(hibernate.properties):
Configuration cfg = new Configuration();
• Xml文件(hibernate.cfg.xml)
Configuration cfg = new Configuration().configure();
(2)SessionFactory接口
l Configuration对象根据当前的配置信息生成 SessionFactory 对象。SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息(SessionFactory 对象中保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。同时,SessionFactory还负责维护Hibernate的二级缓存)。
Configuration cfg = new Configuration().configure();
SessionFactory sf = cfg.buildSessionFactory();
l 是线程安全的。
l SessionFactory是生成Session的工厂:
Session session = sf.openSession();
l 构造 SessionFactory 很消耗资源,一般情况下一个应用中只初始化一个 SessionFactory 对象
(3)Session接口
l Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,所有持久化对象必须在 session 的管理下才可以进行持久化操作。此对象的生命周期很短。Session 对象有一个一级缓存,显式执行 flush 之前,所有的持久层操作的数据都缓存在 session 对象处。相当于 JDBC 中的 Connection。
l 持久化类与 Session 关联起来后就具有了持久化的能力。
l 是线程不安全的
l Session 类的方法:
• 取得持久化对象的方法: get() load()
• 持久化对象都得保存,更新和删除:save(),update(),saveOrUpdate(),delete()
• 开启事务: beginTransaction().
• 管理 Session 的方法:isOpen(),flush(), clear(), evict(), close()等
(4)Transaction接口
l 代表一次原子操作,它具有数据库事务的概念。所有持久层都应该在事务管理下进行,即使是只读操作。
Transaction tx = session.beginTransaction();
l 常用方法:
• commit():提交相关联的session实例
• rollback():撤销事务操作
• wasCommitted():检查事务是否提交
3:Hibernate的运行过程
Hibernate的运行过程如下:
1、应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息,
2、并用这些信息生成一个SessionFactory对象,
3、然后从SessionFactory对象生成一个Session对象,
4、并用Session对象生成Transaction对象;
A、可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、等操作;
B、在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;如果没有异常,Transaction对象将提交这些操作到数据库中。
二:Hibernate的对象持久化概述
1:Hibernate是什么
面向java环境的对象/关系数据库映射工具。
1.开源的持久层框架.
2.ORM(Object/Relational Mapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.
3.连接java应用和数据库的中间件.
4.对JDBC进行封装,负责java对象的持久化.
5.在分层结构中处于持久化层,封装对数据库的访问细节,
使业务逻辑层更专注于实现业务逻辑
2:为什么要用Hibernate
1、Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
2、Hibernate是一个基于jdbc的主流持久化框架,是一个优秀的orm实现,它很大程度的简化了dao层编码工作。
3、Hibernate使用java的反射机制,而不是字节码增强程序类实现透明性
4、Hibernate的性能非常好,因为它是一个轻量级框架。映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
3:Java 应用的持久化分层
为了把数据访问细节和业务逻辑分开, 可以把数据访问作为单独的持久化层
l Hibernate中间件:Hibernate不和特定的业务领域相关,能够把任意一个Java应用与数据库系统连接,可以理解为是一种中间件。
l Hibernate的持久化对象:目的就是与数据库同步,使用对象操作数据库。
实体域对象在内存中创建后,不能永久存在。将实体域对象永久保存起来,就是持久化的过程。通常只有实体域对象需要持久化,过程域对象和事件域对象一般不需要持久化。广义持久化指增、删、改、查。
4:ORM:(Object/Relation Mapping): 对象/关系映射
• 主要解决对象-关系的映射
面向对象概念 |
面向关系概念 |
类 |
表 |
对象 |
表的行(记录) |
属性 |
表的列(字段) |
ORM的实现思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
ORM 采用元数据来描述对象-关系映射细节, 元数据通常采用 XML 格式, 并且存放在专门的对象-关系映射文件中
总结:Hibernate是ORM的设计实现
1:ORM
ORM:(Object/Relation Mapping): 对象/关系映射
ORM中间件的使用方法
采用元数据来描述对象-关系映射细节,元数据通常采用xml格式,并存放在专门的对象-关系映射文件中。只要配置了持久化类与表的映射关系,orm中间件在运行时就能够参照映射文件的信息,把域对象持久化到数据库中。
2:hibernate之所以操作javabean的过程即操作数据库的表,这hibernate底层会调用javabean持久化对象的set和get方法,对数据进行赋值和取值,并获取字段名称,组织sql语句,并执行sql语句,达到效果
3:hibernate要求javabean中要使用包装数据类型(Integer,Long,String等)
三:对象-关系映射基础
知识点1:Hibernate中持久化类的访问者有两个(从保存和查询两条路线看)
测试代码:
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
System.out.println("getName()方法!");
return name;
}
public void setName(String name) {
System.out.println("setName()方法!name:"+name);
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 持久化对象的映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.b_setGet.Customer" table="a_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" type="string">
<column name="name"></column>
</property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/b_setGet/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:Hibernate中持久化类的访问者有两个(从保存路线看)
* hibernate底层生成get方法,get方法用来拼装sql语句
* sql sql="insert into a_customer(name,age,des) values(c.getName,c.getAge,c.getDes)";
* 保存
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("洪七公");
c.setAge(60);
c.setDes("帮助");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
/**
* * 知识点1:Hibernate中持久化类的访问者有两个(从查询路线看)
* hibernate会调用javabean的set和get方法,用来封装返回的结果信息
* sql sql= "select id,name,age,des from a_customer where id = ?";
* 使用ID查询信息
* */
@Test
public void findCustomerById(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer) s.get(Customer.class, 2);
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge()+" "+c.getDes());
tr.commit();
s.close();
}
}
知识点2:基本数据类型和包装类型区别:
基本数据类型和包装类型对应的hibernate映射类型相同。
知识点3:Hibernate访问持久化类属性的策略
1.propertye 默认值:表明hibernate通过getXXX和
setXXX来访问类属性。推荐使用。提高域模型透明性。
2.field:hibernate通过java反射机制直接访问类属性。对于没有
javabean方法的属性可设置该访问策略。
3 noop(了解):它映射Java持久化类中不存在的属性,即主要用于HQL(用query接口测试,使用hql语句)中,当数据库中有某列,而实体中不存在的情况。
测试代码
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.c_access.Customer" table="a_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<!-- 该属性在Customer类中不存在get和set方法,使用field处理 -->
<!-- 该属性在Customer类中不存在,但在数据库存在该字段 使用noop处理 查询的时候忽略该字段-->
<property name="name" type="string" access="noop">
<column name="name"></column>
</property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试代码App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/c_access/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点3:Hibernate访问持久化类属性的策略
1.propertye 默认值:表明hibernate通过getXXX和
setXXX来访问类属性。推荐使用。提高域模型透明性。
2.field:hibernate通过java反射机制直接访问类属性。对于没有
javabean方法的属性可设置该访问策略。
3 noop(了解):它映射Java持久化类中不存在的属性,即主要用于HQL(用query接口测试,使用hql语句)中,当数据库中有某列,而实体中不存在的情况。
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setAge(50);
c.setDes("岛主");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
@Test
public void findObjectByID(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Query query = s.createQuery("from Customer where des = '岛主'");
List<Customer> list = query.list();
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
}
知识点4:在持久化类的方法中加入程序逻辑
在Customer.hbm.xml文件中无需映射firstname和lastname属性,而是映射name属性。
尽管类中并没有name属性,由于hibernate不是直接访问Name属性,而是调用
get、set方法,因此建立了Firstname、Lastname和表之间的联系。
测试代码:
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String firstname;
private String lastname;
private Integer age;
private String des;
public String getName() {
return firstname+","+lastname;
}
public void setName(String name) {//西毒,峰
String [] arrays = name.split(",");
if(arrays!=null && arrays.length>0){
this.firstname = arrays[0];
this.lastname = arrays[1];
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 持久化对象的映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.d_field.Customer" table="a_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string" access="field"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试代码App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/d_field/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点4:在持久化类的方法中加入程序逻辑
在Customer.hbm.xml文件中无需映射firstname和lastname属
性,而是映射name属性。
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("西毒,峰");
c.setAge(50);
c.setDes("蛤蟆");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
@Test
public void findObjectByID(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Query query = s.createQuery("from Customer where name='西毒,峰'");
List<Customer> list = query.list();
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
}
知识点5:设置派生属性
利用<property>元素的formula属性,用来设置一个sql表达式,hibernate将根据它来计算出派生属性的值。
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
private Double price;
private Double totalPrice;
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(Double totalPrice) {
this.totalPrice = totalPrice;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 持久化对象的映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.e_formular.Customer" table="b_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
<property name="price" column="price" type="double"></property>
<!--
用来计算价格price的和
formula:
1:执行的是sql语句
2:表要存在别名
3:在sql语句要添加小括号
-->
<property name="totalPrice" formula="(select sum(o.price) from b_customer o)"></property>
</class>
</hibernate-mapping>
l 测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/e_formular/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点5:设置派生属性
利用<property>元素的formula属性,用来设置一个sql表达式,hibernate将根据它来计算出派生属性的值。
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("黄老邪");
c.setAge(60);
c.setDes("岛主");
c.setPrice(20000d);
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
/**使用ID查询信息*/
@Test
public void findCustomerById(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer) s.get(Customer.class, 2);
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge()+" "+c.getDes()+" "+c.getPrice()+" "+c.getTotalPrice());
tr.commit();
s.close();
}
}
知识点6:控制insert、update语句
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 持久化对象的映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.f_insertUpdate.Customer" table="a_customer" dynamic-update="true">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/f_insertUpdate/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点6:控制insert、update语句
映射属性作用
<property>
insert属性若为false,在insert语句中不包含该字段,该字段永远不能被插入。默认值true。
<property>
update属性若为false,update语句不包含该字段,该字段永远不能被更新。默认值为true。
<class>
mutable属性若为false,等价于所有的<property>元素的update属性为false,整个实例不能被更新。默认为true。
<class>
dynamic-insert属性若为true,等价于所有的<property>元素的insert为true,保存一个对象时,动态生成insert语句,语句中仅包含取值不为null的字段。默认false。
<class>
dynamic-update属性若为true,等价于所有的<property>元素的update为true,更新一个对象时,动态生成update语句,语句中仅包含属性值发生变化的字段。默认false。
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("杨过");
c.setAge(10);
c.setDes(null);
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
@Test
public void update(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setId(7);
c.setName("黄老邪1");
c.setAge(60);
c.setDes("岛主1");
s.update(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
/**使用ID查询信息*/
@Test
public void findCustomerById(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer) s.get(Customer.class, 2);
c.setName("郭啸天");//更新
c.setDes("大侠");//语句中没有des的字段,不更新
s.update(c);
tr.commit();
s.close();
}
}
知识点7:处理sql引用表示符
在SQL语法中,表示符是指用于为数据库表、视图、字段或索引等名字的字符串,常规表示符不包括空格,也不包含特殊字符,因此无需使用引用符号。如果数据库表名或列名包含特殊字符,可以使用引用表示符(键盘~下面的字符)。
持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
持久化对象的映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.g_space">
<class name="Customer" table="c_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="`xxxxx des`" type="string"></property>
</class>
</hibernate-mapping>
测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/g_space/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点7:处理sql引用表示符
在SQL语法中,表示符是指用于为数据库表、视图、字段或索引等名字的字符串,
常规表示符不包括空格,也不包含特殊字符,因此无需使用引用符号。
如果数据库表名或列名包含特殊字符,可以使用引用表示符(键盘~下面的字符)。
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("杨过");
c.setAge(10);
c.setDes("有大雕");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
}
知识点8:设置类的包名
如果在一个映射文件中包含多个类,并且这些类位于同一个包中,可以设置<hibernate-mapping>元素的package属性,避免为每个类提供完整的类名。
四:映射对象标识符
1:Hibernate的唯一性标识
(1)Java按地址区分同一个类的不同对象.
(2)关系数据库用主键区分同一条记录.
(3)Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系。对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate来为OID付值。
2:hibernate支持自然主键和代理主键
(1) 自然主键:具有业务含义的字段,如name
(2) 代理主键:不具有业务含义的字段,如id
3:hibernate生成OID的策略代码
Hibernate中用对象表示符(OID)来区分对象
OID是关系数据库中的主键在java对象模型中的等价物。在运行时,hibernate根据OID来维持java对象和数据库记录的对应关系。(session的一级缓存和快照)
4:Hibernate的一级缓存和快照
(1) 一级缓存:
l hibernate在内存地址中存在一个一级缓存,存在在一级缓存中的对象,就说明该对象具有了持久化的能力,如果对象具有持久化能力就能操作数据库。一级缓存中的数据使用oid用来区分对象,当查询数据库的时候,如果一级缓存中存在oid的数据,则不会产生select语句,Hibernate对查询数据库实现了性能优化,当Session关闭,一级缓存的数据就没有了。如何让缓存中的数据一直存在呢(即使Session关闭也存在),大家可以期待Hibernate的二级缓存。
(2) 快照
l Hibernate除了在内存中定义一级缓存之外,还存在一个快照区域(即对数据库数据的一份复制),在提交事务(清理缓存)的时候,会用缓存的数据和快照的数据进行比对,如果没有发生变化不执行update语句,如果发生了变化,则执行update语句
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.h_oid.Customer" table="a_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
测试代码App.java:
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/h_oid/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点5: Hibernate中用对象表示符(OID)来区分对象
* OID是关系数据库中的主键在java对象模型中的等价物。
* 在运行时,hibernate根据OID来维持java对象和数据库记录的对应关系。
*
测试一级缓存
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("小龙女");
c.setAge(10);
c.setDes("美女");
s.save(c);
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
/**
* * 测试一级缓存的作用
* 当使用id查询数据库表的数据的时候,hibernate会先到session的一级缓存中查找,
* * 如果没有找到就会查询数据库
* * 如果找到了,就直接返回对象,返回的对象和Session一级缓存的对象是一样的
*
* 当Session关闭的时候,一级缓存的数据就没有了
* */
@Test
public void testFirstCache(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer) s.get(Customer.class, 2);//产生select语句
Customer c2 = (Customer) s.get(Customer.class, 2);//不产生select语句
Customer c3 = (Customer) s.get(Customer.class, 2);//不产生select语句
tr.commit();
s.close();
s = sf.openSession();
tr = s.beginTransaction();
Customer c4 = (Customer) s.get(Customer.class, 2);//产生select语句
tr.commit();
s.close();
}
/**
* 测试快照
* hibernate将查询获取到结果,放置到Session的一级缓存中一份,同时也放置到快照中一份(相当于对数据库数据的一个份复制)
* * 当改变Session的一级缓存中的数据,然后提交事务,清理缓存的时候,hibernate会根据一级缓存中的数据和快照中的数据进行比对
* * 如果发生了变化,就会执行update语句
* * 如果没有发生编号,不会执行update语句,更新的方向是从缓存到数据库
* */
@Test
public void testPhoto(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer) s.get(Customer.class, 2);//产生select语句
c.setName("郭啸天");
//s.update(c);
tr.commit();
s.close();
}
}
5:掌握increment、identity、assigned (自然主键)、uuid、联合主键等主键策略
映射文件中:主键策略id和generator说明:
l 映射文件在Customer.hbm.xml中:
非联合主键
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.h_oid.a_increment.Customer" table="d_customer">
<id name="id" type="integer">
<column name="id"></column>
<-- identity、assigned、native、uuid -->
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
联合主键方式一
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.h_oid.d_composite.Customer" table="g_customer">
<composite-id>
<key-property name="firstname" column="firstname" type="string"></key-property>
<key-property name="lastname" column="lastname" type="string"></key-property>
</composite-id>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
联合主键方式二
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.h_oid.e_composite.Customer" table="h_customer">
<composite-id name="customerID" class="cn.itcast.h_oid.e_composite.CustomerID">
<key-property name="firstname" column="firstname" type="string"></key-property>
<key-property name="lastname" column="lastname" type="string"></key-property>
</composite-id>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapp
此时需要设置CustomerID的类,设置联合主键
public class CustomerID implements java.io.Serializable {
private String firstname;
private String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/h_oid/a_increment/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**测试向数据库中保存数据*/
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("杨过");
c.setAge(10);
c.setDes("过儿");
System.out.println("没有持久化之前c.getId:"+c.getId());
s.save(c);
System.out.println("持久化之后c.getId:"+c.getId());//缓存中的数据存在oid
tr.commit();//实质上执行了2步操作,1:s.flush();//清理缓存,让session缓存中的数据与数据库同步,2:事务提交
s.close();//Session的缓存就没有了
}
}
五:操纵持久化对象
1:理解Hibernate的Session对象
Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载Java 对象的方法.
在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
Session中的方法总结:
flush: 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一系列sql语句,但不提交事务,;
commit:先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。
refresh:刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.
clear:清空缓存,等价于list.removeAll();
close:执行先执行commit方法提交事务,再关闭Session的缓存。
l 持久化对象Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 映射文件Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.i_po.Customer" table="a_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/i_po/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点4:清理session的缓存
session.flush:清理缓存,让缓存中的数据与数据库同步,方向是从缓存到数据库
* 清理Session缓存的时候,缓存中的数据不丢失
* */
@Test
public void testFlush(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("成吉思汗1");
c.setAge(60);
c.setDes("蒙古王1");
s.save(c);
s.flush();//清理缓存,让缓存中的数据与数据库同步
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点4_2:清理session的缓存,测试flush和clear方法的使用
*
* session.flush:清理缓存,让缓存中的数据与数据库同步,方向是从缓存到数据库
* 清理Session缓存的时候,缓存中的数据不丢失
session.clear:清空缓存,此时缓存中的数据就没有了
*/
@Test
public void testFlushAndClear(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 11);//产生select语句
s.flush();//清理缓存
Customer c2 = (Customer)s.get(Customer.class, 11);//不产生select语句
s.clear();//清空缓存
Customer c3 = (Customer)s.get(Customer.class, 11);//产生select语句
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点4_4:清理session缓存,测试refresh方法的使用
* session.refresh():重新清理缓存,让缓存中的数据与数据库同步,方向是从数据库到缓存
*/
@Test
public void testRefresh(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 11);//产生select语句
c.setName("傻姑");
s.refresh(c);
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点4_5:清理session的缓存(设置缓存的清理模式)
session.setFlushMode(FlushMode.AUTO);
*/
@Test
public void testFlushMode(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
s.setFlushMode(FlushMode.NEVER);//从不清理缓存
Customer c = new Customer();
c.setName("成吉思汗2");
c.setAge(60);
c.setDes("蒙古王2");
s.save(c);
s.flush();//只有调用flush方法的时候,才会清理缓存
tr.commit();//因为执行了s.setFlushMode(FlushMode.NEVER);此时事务提交的方法将无效
s.close();
}
/**
* 使用s.setFlushMode(FlushMode.NEVER);//从不清理缓存
* 完成优化
session.setFlushMode(FlushMode.NEVER);方法优化系统,保存时可解决内存溢出
*/
@Test
public void testFlushMode_400000(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
s.setFlushMode(FlushMode.NEVER);//从不清理缓存
for(int i=1;i<=4000000;i++){
Customer c = new Customer();
c.setName("老顽童");
c.setAge(60);
c.setDes("就是玩");
s.save(c);
/**解决添加大批量数据*/
if(i%1000 == 0){
s.flush();//清理缓存,让缓存中的数据与数据库同步,方向是缓存到数据库
s.clear();//清空缓存,缓存中数据就没有了,用来解决内存溢出
}
}
s.flush();//只有调用flush方法的时候,才会清理缓存
tr.commit();//因为执行了s.setFlushMode(FlushMode.NEVER);此时事务提交的方法将无效
s.close();
}
}
清理session的缓存(设置缓存的清理模式)
session.setFlushMode(FlushMode.AUTO);
2:在hibernate中java对象的状态
l Hibernate 把对象分为 4 种状态:
持久化状态,
临时状态,
游离状态,
删除状态.
Session 的特定方法能使对象从一个状态转换到另一个状态
(1)临时状态
(2)持久化状态
(3)删除状态
(4)游离状态
对象之间状态的转换图:
测试4种状态之间转换的代码,类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/i_po/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点5:在hibernate中java对象的状态
Hibernate 把对象分为 4 种状态:
持久化状态,
临时状态,
游离状态,
删除状态.
Session 的特定方法能使对象从一个状态转换到另一个状态
* */
@Test
public void testState(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
/**
* 1:临时状态
* * 在使用代理主键的情况下, OID 通常为 null
* 不处于 Session 的缓存中
* 在数据库中没有对应的记录
*/
Customer c = new Customer();
c.setName("成吉思汗1");
c.setAge(60);
c.setDes("蒙古王1");
//c与Session操作,c变成持久状态
s.save(c);
tr.commit();//先执行s.flush,然后再提交事务
s.close();
/***********************************************************/
s = sf.openSession();
tr = s.beginTransaction();
/**
* 2:持久化对象(也叫”托管”)(Persist):
* OID 不为 null
* 位于 Session 缓存中
* 当执行查询的时候,持久化对象和数据库中的相关记录对应;
* 如果从临时对象转变而来,此时在数据库中不存在对应记录。
* Session 在清理缓存时, 会根据持久化对象的属性变化, 来同步更新数据库
* 在同一个 Session 实例的缓存中, 数据库表中的每条记录只对应唯一的持久化对象
*/
Customer c1 = (Customer)s.get(Customer.class, 1);
System.out.println(c1.getId()+" "+c1.getName());
tr.commit();//先执行s.flush,然后再提交事务
s.close();//session的一级缓存就没有了
/**
* 3:游离状态
* 游离对象(也叫”脱管”)(Detached)
* OID 不为 null
* 不再处于 Session 的缓存中
* 一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录
*/
System.out.println(c1.getId()+" "+c1.getName());//可以输出
c1 = null;
System.out.println(c1.getId()+" "+c1.getName());//不能输出
/**************************************************/
s = sf.openSession();
tr = s.beginTransaction();
Customer c2 = (Customer)s.get(Customer.class, 1);
/**
* 4:删除状态:
* * OID 不为 null
* 从一个 Session实例的缓存中删除
* Session 已经计划将其从数据库删除, Session 在清理缓存时, 会执行 SQL delete 语句, 删除数据库中的对应记录
* 一般情况下, 应用程序不该再使用被删除的对象
*/
s.delete(c2);
tr.commit();//先执行s.flush,然后再提交事务
s.close();//session的一级缓存就没有了
}
/**
* 知识点8:游离对象(也叫”脱管”)(Detached)
* 测试evict()方法:将持久对象转换成游离对象
测试update()方法,将游离对象转换成持久对象
*/
@Test
public void testEviceAndUpdate(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
s.evict(c1);//将持久对象转换成游离对象
s.update(c1);//将游离对象转换成持久对象
Customer c2 = (Customer)s.get(Customer.class, 1);
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点13_2: 操纵持久化对象-update()
* 若希望 Session 仅当修改了 News 对象的属性时, 才执行 update() 语句,
* 可以把映射文件中 <class> 元素的 select-before-update(更新之前先查询) 设为 true. 该属性的默认值为 false
*/
@Test
public void testSelectBeforeUpdate(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
s.evict(c1);//将持久对象转换成游离对象
s.update(c1);//将游离对象转换成持久对象
Customer c2 = (Customer)s.get(Customer.class, 1);
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点13_3: 操纵持久化对象-update()
* 当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
* 因为一个Session的缓存中不能存放2个相同的OID
*/
@Test
public void testUpdate(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
s.evict(c1);//将持久对象转换成游离对象
Customer c2 = (Customer)s.get(Customer.class, 1);//会产生select语句
s.update(c1);//将游离对象转换成持久对象,这里会抛出异常,因为一个Session的缓存中不能出现2个相同的oid
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点13_4: 操纵持久化对象-update()
* 当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常.
*/
@Test
public void testUpdate1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
s.evict(c1);//将持久对象转换成游离对象
//手工删除数据库id=1的数据
s.update(c1);//将游离对象转换成持久对象
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
/**
* 知识点15: 操纵持久化对象-get() load()
* 都可以根据给定的 OID 从数据库中加载一个持久化对象
区别:
1:当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null
2:两者采用不同的延迟检索策略(后面讲)
*/
@Test
public void testGetAndLoad(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer)s.load(Customer.class, 20000000);
//System.out.println(c.getId()+" "+c.getName());
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
}
3:Session操作数据库的方法
l save()方法:将临时对象转换持久对象
l update()方法:将游离对象转换持久对象
(1) 注意:当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
(2) 当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常.
l evict()方法:将持久对象转换成游离对象
l saveOrUpdate()方法:存在save方法和update方法的特征
关于saveOrUpdate()方法:大家可以了解
判定对象为临时对象的标准
Java 对象的 OID 为 null
映射文件中为 <id> 设置了 unsaved-value 属性, 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配,执行插入操作
根据以上判断临时对象的标准id=null是临时对象。但可以定义属性为int id
* 此时id默认值是0而不是null。如果将id设置为1,而此时应该在<id>中设置unsaved-value的值除了1之外的值。2个值不相等,此时应该执行更新id为1操作
* 但实际我们要执行的插入操作。这时,可以在id中设置unsaved-value=1 ,与在javabean中定义的int id=1对应,2个值相等则执行插入。其中:unsaved-value=0(默认值)
测试代码:
l 持久化化类Customer.java
public class Customer implements Serializable{
private Integer id = 3;
private String name;
private Integer age;
private String des;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
l 持久化类的映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.i_po2.Customer" table="a_customer">
<!--
总结:1:Customer.java文件中设置private Integer id = 2;
同时在Customer.hbm.xml中设置unsaved-value="2"
如果2个值相等:此时saveOrUpdate就相当于save,执行保存的操作
2:Customer.java文件中设置private Integer id = 3;
同时在Customer.hbm.xml中设置unsaved-value="2"
如果2个值不相等:此时saveOrUpdate就相当于update,更新Integer id = 3;
-->
<id name="id" type="integer" unsaved-value="2">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<property name="des" column="des" type="string"></property>
</class>
</hibernate-mapping>
l 测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/i_po2/hibernate.cfg.xml");
configuration.addClass(Customer.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点14_5: 操纵持久化对象-saveOrupdate()
判定对象为临时对象的标准
* Java 对象的 OID 为 null
* 映射文件中为 <id> 设置了 unsaved-value 属性, 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配,执行插入操作
* */
@Test
public void testState(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("华争");
c.setAge(60);
c.setDes("公主");
s.saveOrUpdate(c);
tr.commit();//先执行s.flush,然后再提交事务
s.close();
}
}
我们需要掌握的知识:
hibernate第二天
一:映射一对多的关联关系
分析设计:一对多的关联设计思想,如图:
l 单项关联:仅仅建立从Order到Customer的多对一关联,即仅仅在Order类中定义customer属性。或者仅仅建立从Customer到Order的一对多关联,即仅仅在Customer类中定义orders集合。
l 双项关联:既建立从Order到Customer的多对一关联,又建立从Customer到Order的一对多关联。
1:一对多关联关系(单项关联)的基本配置
(1)配置2个持久化对象,Customer.java和Order.java
l Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
l Order.java
public class Order implements Serializable{
private Integer id;
private String orderNumber;
private Double price;
//映射从多的一端关联一的一段,此时是一的一端的对象
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
(2)建立2个持久化类对应的映射文件Customer.hbm.xml和Order.hbm.xml:
l Customer.hbm.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.oneToMany.Customer" table="j_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
</class>
</hibernate-mapping>
l Order.hbm.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.oneToMany.Order" table="j_order">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="orderNumber" column="orderNumber" type="string"></property>
<property name="price" column="price" type="double"></property>
<!--
many-to-one:映射多对一
* customer:持久对象中的属性
* class:持久对象中的属性对应的类型
* column name="customer_id":对应多的一端的外键列的名称
* cascade:级联:
save-update:级联保存更新
-->
<many-to-one name="customer" class="cn.itcast.oneToMany.Customer" cascade="save-update">
<column name="customer_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
这里:
Hibernate 使用 <many-to-one> 元素来映射多对一关联关系
测试代码App.java:
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/oneToMany/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:先保存订单,再保存客户
* Hibernate: insert into j_order (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
* Hibernate: insert into j_customer (name, id) values (?, ?)
* Hibernate: update j_order set orderNumber=?, price=?, customer_id=? where id=?
* */
@Test
public void saveOrderAndCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("郭靖");
Order o = new Order();
o.setOrderNumber("NO1");
o.setPrice(1000d);
//建立关联关系
o.setCustomer(c);
//先保存订单,再保存客户
s.save(o);
s.save(c);
tr.commit();
s.close();
}
/**
* 知识点2:先保存客户,再保存订单
* Hibernate: insert into j_customer (name, id) values (?, ?)
* Hibernate: insert into j_order (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
*/
@Test
public void saveCustomerAndOrder(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("黄蓉");
Order o = new Order();
o.setOrderNumber("NO2");
o.setPrice(2000d);
//建立关联关系
o.setCustomer(c);
//先保存客户,再保存订单
s.save(c);
s.save(o);
tr.commit();
s.close();
}
/**
* 知识点3:查询订单
* sql语句:
* select * from j_order order0_ where order0_.id=?
select * from j_customer customer0_ where customer0_.id=?(懒加载(延迟检索))
*/
@Test
public void findOrderByID(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Order o = (Order) s.get(Order.class, 1);
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
Customer c = o.getCustomer();
System.out.println(c.getId()+" "+c.getName());
tr.commit();
s.close();
}
/**
* 知识点4:先保存客户,再保存订单
在下面的代码中注释掉session.save(c),会有什么后果
org.hibernate.TransientObjectException: 临时对象异常,与其关联的对象没有放置到session缓存中
object references an unsaved transient instance -
save the transient instance before flushing: cn.itcast.j_oneToMany.Customer
* 解决方案:
* 当hibernate持久化一个临时对象时,在默认情况下,它不会自动持久化所关联的其他临时对象,
* 会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,
* 可实现自动持久化所关联的对象。(在Order的映射文件中添加)
*/
@Test
public void saveCustomerAndOrder_1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("洪七公");
Order o = new Order();
o.setOrderNumber("NO3");
o.setPrice(3000d);
//建立关联关系
o.setCustomer(c);
//先保存客户,再保存订单
//s.save(c);
s.save(o);
tr.commit();
s.close();
}
}
2:一对多关联关系(双向关联)的基本配置
第一步:配置2个持久化对象的javabean,Customer和Order
l Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
//建立一的一端到多的一端的映射,此时一的一端是一个Set集合
private Set<Order> orders = new HashSet<Order>();
//省略set和get方法
}
l Order.java
public class Order implements Serializable{
private Integer id;
private String orderNumber;
private Double price;
//映射从多的一端关联一的一段,此时是一的一端的对象
private Customer customer;
//省略set和get方法
}
第二步:建立2个持久化类对应的映射文件Customer.hbm.xml和Order.hbm.xml:
l Customer.hbm.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.oneToManyDouble.Customer" table="j_customer">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<!--
set:用来定义从一的一端到多的一端的映射
* name:持久对象中集合的属性名称
* table:映射多的一端的表名称
* <key>:customer表的主键,
* column name="customer_id":映射外键列的名称
* one-to-many class="":Customer中集合属性的类型
* cascade:级联
save-update:级联保存更新与其所关联的对象
delete:删除客户的同时,级联删除该客户对应的订单
delete-orphan:删除孤儿,删除多的一端的外键列出现null的情况下,级联删除孤儿的对象
* inverse:设置在set元素中
* inverse="true":表示此时是由多的一端维护关联关系,那么一的一端关联就没有效果(因为说了不算)
* order by:对集合的属性进行排序,要求:属性名称+空格+排序的方式
-->
<set name="orders" table="j_order" cascade="delete-orphan" inverse="true" order-by="id desc">
<key>
<column name="customer_id"></column>
</key>
<one-to-many class="cn.itcast.oneToManyDouble.Order"/>
</set>
</class>
</hibernate-mapping>
这里:Hibernate使用set元素来映射一对多关联关系
其中:set中的inverse表示反转,inverse=true表示由多的一端维护关联关系,此时多的一端关联一的一端可以使用,一的一端即使关联多的一端,也不能没有任何效果。
cascade表示级联:delete表示级联删除,save-update表示级联更新保存(将与其Customer关联的对象也放置到Session的缓存中)
cascade附录:
l Order.hbm.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.k_oneToManyDouble.Order" table="j_order">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="orderNumber" column="orderNumber" type="string"></property>
<property name="price" column="price" type="double"></property>
<!--
many-to-one:映射多对一
* customer:持久对象中的属性
* class:持久对象中的属性对应的类型
* column name="customer_id":对应多的一端的外键列的名称
* cascade:级联:
save-update:级联保存更新
-->
<many-to-one name="customer" class="cn.itcast.k_oneToManyDouble.Customer" cascade="save-update">
<column name="customer_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
第三步:创建hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
第四步:一对多操作的增删改查,测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/oneToManyDouble/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点5:定义为接口类型
* */
@Test
public void testInterfaceSet(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 1);
System.out.println(c.getId()+" "+c.getName());
Set<Order> orders = c.getOrders();
if(orders!=null && orders.size()>0){
for(Order o:orders){
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
}
}
tr.commit();
s.close();
}
/**
* 知识点6:保存客户和订单(客户和订单建立双向关联)
* insert into j_customer (name, id) values (?, ?)
* Hibernate: insert into j_order (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
* Hibernate: update j_order set customer_id=? where id=?
*/
@Test
public void saveCustomerAndOrder(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("黄老邪");
Order o = new Order();
o.setOrderNumber("NO1");
o.setPrice(1500d);
//建立双向关联
c.getOrders().add(o);
o.setCustomer(c);
s.save(c);
s.save(o);
tr.commit();
s.close();
}
/**
* 知识点7:保存客户和不保存订单
在下面的代码中注释掉session.save(order1),会有什么后果
抛出异常:object references an unsaved transient instance:临时对象异常
解决方案:在set元素中添加<set name="orders" table="j_order" cascade="save-update">
*/
@Test
public void saveCustomerAndNoOrder(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("黄老邪");
Order o = new Order();
o.setOrderNumber("NO1");
o.setPrice(1500d);
//建立双向关联
c.getOrders().add(o);
o.setCustomer(c);
s.save(c);
//s.save(o);
tr.commit();
s.close();
}
/**
* 知识点8:查询客户和订单
* select * from j_customer customer0_ where customer0_.id=?
* select * from j_order orders0_ where orders0_.customer_id=?
*/
@Test
public void findCustomerAndOrder(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 1);
System.out.println(c.getId()+" "+c.getName());
Set<Order> orders = c.getOrders();
if(orders!=null && orders.size()>0){
for(Order o:orders){
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
}
}
tr.commit();
s.close();
}
/**
* 知识点9:对象导航
*/
@Test
public void objectRelation(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("华争");
Order o1 = new Order();
o1.setOrderNumber("NO5");
o1.setPrice(1500d);
Order o2 = new Order();
o2.setOrderNumber("NO6");
o2.setPrice(1600d);
Order o3 = new Order();
o3.setOrderNumber("NO7");
o3.setPrice(1700d);
//建立对象的关联
o1.setCustomer(c);
c.getOrders().add(o2);
c.getOrders().add(o3);
//s.save(o1);//4条insert语句
//s.save(c);//3条insert语句
s.save(o2);//1条insert语句
tr.commit();
s.close();
}
/**
* 知识点10:保持程序的健壮性
* */
@Test
public void testHealth(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("丘处机");
Order o = new Order();
o.setOrderNumber("NO7");
o.setPrice(2500d);
//建立对象的关联
o.setCustomer(c);
//该代码添加与不添加不会影响程序的运行
//c.getOrders().add(o);
s.save(c);
s.save(o);
tr.commit();
s.close();
}
/**
* 知识点11:订单变更客户
更改订单表id=6的customer_id=6更改为4
* 为什么会产生2条update语句
* Hibernate: update j_order set orderNumber=?, price=?, customer_id=? where id=?
* Hibernate: update j_order set customer_id=? where id=?
*/
@Test
public void updateOrderWithCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询4号客户
Customer c4 = (Customer)s.get(Customer.class, 4);
//重新建立关联关系
o6.setCustomer(c4);
c4.getOrders().add(o6);
tr.commit();
s.close();
}
/**
* 知识点11:set中inverse属性
* 注释:c4.getOrders().add(o6);
*/
@Test
public void testInverse(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询4号客户
Customer c4 = (Customer)s.get(Customer.class, 4);
//重新建立关联关系
o6.setCustomer(c4);
//c4.getOrders().add(o6);//该代码加与不加没有影响,因为多的的一端说了算
tr.commit();
s.close();
}
/**
* 知识点11:set中inverse属性
* 注释:o6.setCustomer(c4);
*/
@Test
public void testInverse_1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询4号客户
Customer c4 = (Customer)s.get(Customer.class, 4);
//重新建立关联关系
//o6.setCustomer(c4);
c4.getOrders().add(o6);//此时一的一端不能执行更新数据库,因为此时是多的一端说了算,此时不会执行update语句
tr.commit();
s.close();
}
/**
* 知识点12:解除关联关系
* 解除6号订单和6号客户的关联
*
* Hibernate: update j_order set orderNumber=?, price=?, customer_id=? where id=?
* */
@Test
public void removeOrderAndCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询6号客户
Customer c6 = (Customer)s.get(Customer.class, 6);
//解除关联关系
o6.setCustomer(null);
c6.getOrders().remove(o6);
tr.commit();
s.close();
}
/**
* 知识点13_1:级联删除1号客户都同时,删除1号客户所关联的所有订单
* 如果外键表中存在数据,此时不能删除,会抛出外键关联异常
* 解决:在Customer.hbm.xml中,<set name="orders" table="j_order" cascade="delete" inverse="true">
* delete from j_order where id=?
* delete from j_customer where id=?
*/
@Test
public void deleteCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号客户
Customer c = (Customer)s.get(Customer.class, 1);
s.delete(c);
tr.commit();
s.close();
}
/**
* 知识点13_2:保存多的一端订单对象时级别保存一的一方的客户对象
* cascade属性可以放置在many-to-one的元素中,设置cascade=“save-update”
*/
@Test
public void saveOrderAndCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("托雷");
Order o = new Order();
o.setOrderNumber("NO1");
o.setPrice(1500d);
o.setCustomer(c);
s.save(o);
tr.commit();
s.close();
}
/**
* 解除关联关系 ---父子关系
解除6号订单和6号客户的关联,同时删除6号订单
如果按照该代码执行,会设置6号订单的customer_id的值null(孤儿),这样是不合理的
解决方案:
<set name="orders" table="j_order" cascade="delete-orphan" inverse="true">
*/
@Test
public void removeOrderAndCustomer_1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询6号客户
Customer c6 = (Customer)s.get(Customer.class, 6);
//解除关联关系
o6.setCustomer(null);
c6.getOrders().remove(o6);
tr.commit();
s.close();
}
/**
* 知识点15:在数据库中对集合排序
* <set name="orders" table="j_order" cascade="delete-orphan" inverse="true" order-by="id asc">
*/
@Test
public void testOrderBy(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号客户
Customer c6 = (Customer)s.get(Customer.class, 6);
System.out.println(c6.getId()+" "+c6.getName());
Set<Order> orders = c6.getOrders();
for(Order o:orders){
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
}
tr.commit();
s.close();
}
}
3:一对多中操作的重要方法
(1)定义接口类型
Hibernate要求在持久化类中定义集合属性时,必须把属性声明为接口类型,如Set、Map、List.声明为接口类型可提高持久化类的透明性,当hibernate调用setOrders()方法时,传递的参数是Hibernate自定义的实现该接口类的实例。如果定义成类(如HashSet)型,强迫hibernate把该类型的实例传给他。
底层代码:
Set<E> orders= PersistentSet
class PersistentSet implements java.util.Set
通常在定义集合属性时,直接初始化为一个实现类的实例。
private Set orders = new HashSet(0);
可避免空指针异常。
(2)保存客户和订单,建立双向关联关系,为了保证程序的健壮性,需要都添加:
测试代码如下:
/**
* 知识点6:保存客户和订单(客户和订单建立双向关联)
* insert into j_customer (name, id) values (?, ?)
* Hibernate: insert into j_order (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
* Hibernate: update j_order set customer_id=? where id=?
*/
@Test
public void saveCustomerAndOrder(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Customer c = new Customer();
c.setName("黄老邪");
Order o = new Order();
o.setOrderNumber("NO1");
o.setPrice(1500d);
//建立双向关联
c.getOrders().add(o);
o.setCustomer(c);
s.save(c);
s.save(o);
tr.commit();
s.close();
}
(3)在订单中对客户进行变更
测试代码如下:
/**
* 知识点11:订单变更客户
更改订单表id=6的customer_id=6更改为4
* 为什么会产生2条update语句
* Hibernate: update j_order set orderNumber=?, price=?, customer_id=? where id=?
* Hibernate: update j_order set customer_id=? where id=?
*/
@Test
public void updateOrderWithCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询4号客户
Customer c4 = (Customer)s.get(Customer.class, 4);
//重新建立关联关系
o6.setCustomer(c4);
c4.getOrders().add(o6);
tr.commit();
s.close();
}
(4)解除关联关系
测试代码如下
/**
* 知识点12:解除关联关系
* 解除6号订单和6号客户的关联
*
* Hibernate: update j_order set orderNumber=?, price=?, customer_id=? where id=?
* */
@Test
public void removeOrderAndCustomer(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询6号订单
Order o6 = (Order)s.get(Order.class, 6);
//查询6号客户
Customer c6 = (Customer)s.get(Customer.class, 6);
//解除关联关系
o6.setCustomer(null);
c6.getOrders().remove(o6);
tr.commit();
s.close();
}
(5)掌握set中inverse属性
l 在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
l 在没有设置 inverse=true 的情况下,父子两边都维护父子关系
l 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
l 在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句
结论:
1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,这可以提高性能。
2.在建立两个对象的关联时,应该同时修改关联两端的相应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);
二:映射多对多关联关系
1:多对多关联关系的基本配置
分析设计:多对多的关联设计思想,如图:
多对多采用中间表存储数据:特点:
l 将多对多的关系映射成2个一对多关系
l 中间表的2个键是一个联合主键
l sid对应student表的外键,cid对应course表的外键
第一步:创建2个javabean文件,Student和Course类
l Student类
public class Student implements Serializable{
private Integer id;
private String name;
//建立多对多的关联关系,此时在其中一端是一个Set集合
private Set<Course> courses = new HashSet<Course>();
//忽略set和get方法
}
l Course类
public class Course implements Serializable{
private Integer id;
private String name;
//建立多对多的关联关系,此时在其中一端是一个Set集合
private Set<Student> students = new HashSet<Student>();
//忽略set和get方法
}
第二步:创建Student.hbm.xml和Course.hbm.xml的映射文件
l Sudent.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.manyToMany.Student" table="k_student">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<!--
set:映射多对多的关联集合
* name:Student类中的集合属性名称
* table:多对多的中间表的名称k_student_course
* key:表示Student表中的主键
* column name="sid":映射中间表的外键列的名称
* many-to-many:映射多对多
* class="cn.itcast.l_manyToMany.Course":Student类中集合属性的类型
column="cid":中间表中联合主键对应另一个表的外键列的名称
inverse="true":将主控方设置为课程一端
-->
<set name="courses" table="student_course" inverse="true">
<key>
<column name="sid"></column>
</key>
<many-to-many class="cn.itcast.manyToMany.Course" column="cid"></many-to-many>
</set>
</class>
</hibernate-mapping>
l Course.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.manyToMany.Course" table="k_course">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<!--
set:映射多对多的关联集合
* name:Course类中的集合属性名称
* table:多对多的中间表的名称k_student_course
* key:表示Course表中的主键
* column name="cid":映射中间表的外键列的名称
* many-to-many:映射多对多
* class="cn.itcast.l_manyToMany.Student":Course类中集合属性的类型
column="sid":中间表中联合主键对应另一个表的外键列的名称
* cascade="delete":删除课程表的同时,级联删除对应的学生
-->
<set name="students" table="k_student_course" cascade="delete">
<key>
<column name="cid"></column>
</key>
<many-to-many class="cn.itcast.manyToMany.Student" column="sid"></many-to-many>
</set>
</class>
</hibernate-mapping>
第三步:hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
第四步:多对多测试的增删改查,App.java测试类中,加载Student.hbm.xml和Course.hbm.xml
configuration.addClass(Student.class);
configuration.addClass(Course.class);
测试代码
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/manyToMany/hibernate.cfg.xml");
configuration.addClass(Student.class);
configuration.addClass(Course.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点4:测试保存
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Student s1 = new Student();
s1.setName("郭靖");
Student s2 = new Student();
s2.setName("洪七公");
Course c1 = new Course();
c1.setName("java");
Course c2 = new Course();
c2.setName("c++");
//建立关联关系
s1.getCourses().add(c1);
s1.getCourses().add(c2);
s2.getCourses().add(c1);
s2.getCourses().add(c2);
c1.getStudents().add(s1);
c1.getStudents().add(s2);
c2.getStudents().add(s1);
c2.getStudents().add(s2);
s.save(s1);
s.save(s2);
s.save(c1);
s.save(c2);
tr.commit();
s.close();
}
/**
* 知识点5
* 查询一号学生具有的课程
*/
@Test
public void queryStudentAndCourse(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号学生
Student s1 = (Student)s.get(Student.class, 1);
Set<Course> courses = s1.getCourses();
if(courses!=null && courses.size()>0){
for(Course c:courses){
System.out.println(c.getId()+" "+c.getName());
}
}
tr.commit();
s.close();
}
/**
* 知识点6:解除1号学生和1号课程的关联关系
* Hibernate: delete from k_student_course where cid=? and sid=?
*/
@Test
public void removeStudentAndCourse(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号学生
Student s1 = (Student)s.get(Student.class, 1);
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
c1.getStudents().remove(s1);
s1.getCourses().remove(c1);
tr.commit();
s.close();
}
/**
* 知识点7:改变1号学生和2号课程的关联关系,改为1号学生和1号课程
* delete from k_student_course where cid=? and sid=?
* insert into k_student_course (cid, sid) values (?, ?)
*/
@Test
public void removeStudentAndCourse_1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号学生
Student s1 = (Student)s.get(Student.class, 1);
//查询2号课程
Course c2 = (Course)s.get(Course.class, 2);
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
//改变1号学生和2号课程的关联关系
s1.getCourses().remove(c2);
c2.getStudents().remove(s1);
//建立1号学生和1号课程
s1.getCourses().add(c1);
c1.getStudents().add(s1);
tr.commit();
s.close();
}
/**
* 知识点8:删除2号学生
* 此时抛出异常,不能删除,因为在中间表中存在主外键的关系,并中间表中存在数据
*/
@Test
public void deleteStudent(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询2号学生
Student s2 = (Student)s.get(Student.class, 2);
s.delete(s2);
tr.commit();
s.close();
}
/**
* 知识点9:删除1号课程
* Hibernate: delete from k_student_course where cid=?
* Hibernate: delete from k_course where id=?
*/
@Test
public void deleteCourse(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
s.delete(c1);
tr.commit();
s.close();
}
/**
* 知识点10:删除1号课程的同时,要把1号和2号学生删掉?
* Hibernate: delete from k_student_course where cid=?
* Hibernate: delete from k_student where id=?
* Hibernate: delete from k_student where id=?
* Hibernate: delete from k_course where id=?
*/
@Test
public void deleteCourseCache(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
s.delete(c1);
tr.commit();
s.close();
}
}
2:多对多关联关系的重要方法
(1) 测试保存代码:
/**
* 知识点4:测试保存
* */
@Test
public void save(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Student s1 = new Student();
s1.setName("郭靖");
Student s2 = new Student();
s2.setName("洪七公");
Course c1 = new Course();
c1.setName("java");
Course c2 = new Course();
c2.setName("c++");
//建立关联关系
s1.getCourses().add(c1);
s1.getCourses().add(c2);
s2.getCourses().add(c1);
s2.getCourses().add(c2);
c1.getStudents().add(s1);
c1.getStudents().add(s2);
c2.getStudents().add(s1);
c2.getStudents().add(s2);
s.save(s1);
s.save(s2);
s.save(c1);
s.save(c2);
tr.commit();
s.close();
}
(2) 解除关联关系
/**
* 知识点5:解除1号学生和1号课程的关联关系
* Hibernate: delete from k_student_course where cid=? and sid=?
*/
@Test
public void removeStudentAndCourse(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号学生
Student s1 = (Student)s.get(Student.class, 1);
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
c1.getStudents().remove(s1);
s1.getCourses().remove(c1);
tr.commit();
s.close();
}
(3) 改变关联关系
/**
* 知识点6:改变1号学生和2号课程的关联关系,改为1号学生和1号课程
* delete from k_student_course where cid=? and sid=?
* insert into k_student_course (cid, sid) values (?, ?)
*/
@Test
public void removeStudentAndCourse_1(){
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
//查询1号学生
Student s1 = (Student)s.get(Student.class, 1);
//查询2号课程
Course c2 = (Course)s.get(Course.class, 2);
//查询1号课程
Course c1 = (Course)s.get(Course.class, 1);
//改变1号学生和2号课程的关联关系
s1.getCourses().remove(c2);
c2.getStudents().remove(s1);
//建立1号学生和1号课程
s1.getCourses().add(c1);
c1.getStudents().add(s1);
tr.commit();
s.close();
}
多对多关系的配置说明:
l set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,
l 其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,
l 因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a.
l 注意:对于双向 n-n 关联, 须把其中一端的 inverse 设置为 true, 否则可能会造成主键冲突.
三:hibernate的映射类型
基本数据类型
四:hibernate的检索策略
1:Customer和Order两端的配置:
主要体现hibernate的检索性能
客户和订单2端的配置
(1) 创建2个javabean文件,Customer和Order类
l Customer类
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
//建立一的一端到多的一端的映射,此时一的一端是一个Set集合
private Set<Order> orders = new HashSet<Order>(); //忽略set和get方法
}
l Order类
public class Order implements Serializable{
private Integer id;
private String orderNumber;
private Double price;
//映射从多的一端关联一的一段,此时是一的一端的对象
private Customer customer;
//忽略set和get方法
}
(2) 创建Customer.hbm.xml和Order.hbm.xml的映射文件
l Customer.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--
lazy:表示查询类的时候,是立即检索还是延迟检索
lazy=true:(默认值)延迟检索
lazy="false":立即检索
-->
<class name="cn.itcast.query.Customer" table="l_customer" lazy="true" batch-size="3">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false" batch-size="3">
<key>
<column name="customer_id"></column>
</key>
<one-to-many class="cn.itcast.query.Order"/>
</set>
</class>
</hibernate-mapping>
l Order.hbm.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.query.Order" table="l_order">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="orderNumber" column="orderNumber" type="string"></property>
<property name="price" column="price" type="double"></property>
<many-to-one name="customer" class="cn.itcast.query.Customer" lazy="false" fetch="select">
<column name="customer_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
2:hibernate区分立即检索和延迟检索
立即检索: 立即加载检索方法指定的对象,对应配置文件中lazy=false
延迟检索: 延迟加载检索方法指定的对象,对应配置文件中lazy=true
测试立即检索和延迟检索的代码,注意只有load()方法进行查询的时候,会产生延迟检索。
/**
* 知识点2:区分立即检索和延迟检索
查询编号为1的客户
立即检索: 立即加载检索方法指定的对象
延迟检索: 延迟加载检索方法指定的对象
* get()方法:立即检索,只要调用get方法查询数据库,马上执行sql语句,查询对应的结果
* load()方法:会产生延迟检索,调用load()方法的时候,不会马上查询数据库,而是产生一个代理对象,
* 代理对象中只初始化对象的OID,不会初始化其他的属性值
* 而是调用其他属性值的时候,例如c.getName(),此时才会组织sql语句,查询数据库,返回对应的结果
* * load()方法如何立即检索呢?
* 只需要更改
* <class name="cn.itcast.m_query.Customer" table="l_customer" lazy="false">
* * 注意:只有load方法才会出现立即检索和延迟检索。因为get()方法永远理解检索
* 使用load方法可以对性能进行优化,如果只想获取oid的值(比如删除),此时会采用load()方法比get()更适合,因为不需要查询数据库,就可以获取oid
*
*
*/
@Test
public void testLazy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户(get()和load())
Customer c = (Customer)s.load(Customer.class, 1);
System.out.println(c.getClass());
System.out.println(c.getId());
System.out.println(c.getName());
tx.commit();
s.close();
}
3:理解延迟检索中的代理
(1) 基本知识
代理实例有以下特征:
l 代理类实例有如下特征:
• 由 Hibernate 在运行时采用 javassist 工具动态生成
• Hibernate 创建代理类实例时, 仅初始化其 OID 属性
• 在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例
(2) 如何初始化代理的数据
方法一:加载除oid以外的其它属性
方法二:使用Hibernate.initialize(c);的方法
(3) 类级别和关联级别中都存在代理对象(如配置文件中的<class>、<set>、<manyToOne>、<manyToMany >的配置中)
具体配置可看代码(但是不是重点,可以作为掌握知识)
(4) 延迟检索中存在的问题:
应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。
测试代码如下:
/**
* 延迟检索的缺点:
* 应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。
* 此时抛出:
* org.hibernate.LazyInitializationException: could not initialize proxy - no Session
没有session的环境下调用代理对象除oid之外的其他属性
解决:只有在Session关闭之前查询数据库即可
* */
@Test
public void testLazy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c =(Customer)s.load(Customer.class, 1);
System.out.println(c.getId());
//System.out.println(c.getName());
tx.commit();
s.close();
//将c变成游离状态
System.out.println(c.getId());
System.out.println(c.getName());
}
4:测试代码:测试立即检索和延迟检索,即类级别检索策略(App.java)
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/query/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:初始化测试数据
* */
@Test
public void initData(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c = new Customer();
c.setName("Rose");//Jack、Rose
c.setAge(20+new Random().nextInt(12));
s.save(c);
for(int i=1;i<=10;i++){
Order o = new Order();
o.setOrderNumber("NO"+i);
o.setPrice(5000+i*10d);
//建立关联关系
c.getOrders().add(o);
o.setCustomer(c);
s.save(o);
}
tx.commit();
s.close();
}
/**
* 知识点2:区分立即检索和延迟检索
查询编号为1的客户
立即检索: 立即加载检索方法指定的对象
延迟检索: 延迟加载检索方法指定的对象
* get()方法:立即检索,只要调用get方法查询数据库,马上执行sql语句,查询对应的结果
* load()方法:会产生延迟检索,调用load()方法的时候,不会马上查询数据库,而是产生一个代理对象,
* 代理对象中只初始化对象的OID,不会初始化其他的属性值
* 而是调用其他属性值的时候,例如c.getName(),此时才会组织sql语句,查询数据库,返回对应的结果
* * load()方法如何立即检索呢?
* 只需要更改
* <class name="cn.itcast.m_query.Customer" table="l_customer" lazy="false">
* * 注意:只有load方法才会出现立即检索和延迟检索。因为get()方法永远理解检索
* 使用load方法可以对性能进行优化,如果只想获取oid的值(比如删除),此时会采用load()方法比get()更适合
* 因为不需要查询数据库,就可以获取oid
*
*
*/
@Test
public void testLazy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户(get()和load())
Customer c = (Customer)s.load(Customer.class, 1);
System.out.println(c.getClass());
System.out.println(c.getId());
System.out.println(c.getName());
tx.commit();
s.close();
}
/**
* 知识点3:初始化延迟检索中的代理
*/
@Test
public void initProxy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c = (Customer)s.load(Customer.class, 1);
if(!Hibernate.isInitialized(c)){
System.out.println(c.getClass());
System.out.println(c.getId());
//解决产生延迟检索中的代理对象,方案一:只要获取除了OID之外的其他属性值,此时就会查询数据库,返回对应的属性值
//System.out.println(c.getName());
//解决产生延迟检索中的代理对象,方案二:调用Hibernate.initialize(),将代理对象放置到方法中,此时就会查询数据库,返回对应的真实对象
Hibernate.initialize(c);
System.out.println(c.getName());
}
tx.commit();
s.close();
}
/**
* 知识点4:区分类级别和关联级别的检索
*/
@Test
public void classAndSetPorxy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
//查询id为1的客户
Customer c = (Customer)s.load(Customer.class, 1);
System.out.println(c.getId());
System.out.println(c.getName());
Set<Order> orders = c.getOrders();
System.out.println("集合的长度:"+orders.size());
tx.commit();
s.close();
}
/**
* Query 的 list() 方法在类级别总是使用立即检索策略
*/
@Test
public void testQuery(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer where id = 1");
List<Customer> list = query.list();
Customer c = list.get(0);
System.out.println(c.getId());
System.out.println(c.getName());
tx.commit();
s.close();
}
}
类级别检索策略的规范
l 类级别可选的检索策略包括立即检索和延迟检索, 默认为延迟检索
l 类级别的检索策略可以通过 <class> 元素的 lazy 属性进行设置
l 如果程序加载一个对象的目的是为了访问它的属性, 可以采取立即检索. 如果程序加载一个持久化对象的目的是仅仅为了获得它的引用, 可以采用延迟检索
l 无论 <class> 元素的 lazy 属性是 true 还是 false, Session 的 get() 方法及 Query 的 list() 方法在类级别总是使用立即检索策略
l 若 <class> 元素的 lazy 属性为 true 或取默认值, Session 的 load() 方法不会执行查询数据表的 SELECT 语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:
1. 由 Hibernate 在运行时采用 javassist 工具动态生成
2. Hibernate 创建代理类实例时, 仅初始化其 OID 属性
3. 在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例
5:测试代码:测试集合级别的检索策略(AppSet.java)
public class AppSet {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/query/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点6:关联级别的检索策略
* 一对多和多对多关联的检索策略(set): <set> 元素的 lazy 和 fetch 属性
* fetch (默认值select) Lazy(默认值是true) 策略
Joinfalse采用迫切左外联接检索。
Jointrue采用迫切左外联接检索。
joinextra采用迫切左外联接检索。
select * from l_customer customer0_
left outer join l_order orders1_ on customer0_.id=orders1_.customer_id
where customer0_.id=?
* */
@Test
public void testSet(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 1);
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getOrders().size());
tx.commit();
s.close();
}
/**
* 知识点6:关联级别的检索策略
* 一对多和多对多关联的检索策略(set): <set> 元素的 lazy 和 fetch 属性
* fetch (默认值select) Lazy(默认值是true) 策略
selectfalse采用立即检索
selectTrue采用延迟检索
selectextra采用延迟检索(及其懒惰)
c.getOrders().size() 执行 select count(id) from orders where customer_id =?
for(Order o:set){ o.getOrderNumber();} 将执行:
select customer_id , id,order_number ,price from orders where customer_id=?
Hibernate: select * from l_customer customer0_ where customer0_.id=?
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
* */
@Test
public void testSet_1(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 1);
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getOrders().size());
tx.commit();
s.close();
}
/**
* 知识点6:关联级别的检索策略
* 一对多和多对多关联的检索策略(set): <set> 元素的 lazy 和 fetch 属性
* fetch (默认值select) Lazy(默认值是true) 策略
subselectfalse/true/extra也分为3中情况嵌套子查询(检索多个customer对象时) Lazy属性决定检索策略)
select customer_id,order_number,price from orders where customer_id
s (select id from customers)
Query query = s.createQuery("from Customer");查询所有客户时出现:
Hibernate: select * from l_customer customer0_
Hibernate: select * from l_order orders0_
where orders0_.customer_id in (select customer0_.id from l_customer customer0_)
Query query = s.createQuery("from Customer where id=1");查询单个客户时出现:
Hibernate: select * from l_customer customer0_ where customer0_.id=1
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
* */
@Test
public void testSet_2(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer");
List<Customer> list = query.list();
for(Customer c:list){
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getOrders().size());
}
tx.commit();
s.close();
}
/**
* 知识点6
*
* <set name="orders" table="l_order" inverse="true/false/extra" lazy="true" fetch="join">
*
* Query 的list() 方法会忽略映射文件中配置的迫切左外连接检索策略,
* 而依旧采用立即检索还是延迟加载策略由set集合的lazy属性决定
*
* select * from l_customer customer0_
select * from l_order orders0_ where orders0_.customer_id=?(3条)
*/
@Test
public void testQuery(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer");
List<Customer> list = query.list();
for(Customer c:list){
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getOrders().size());
}
tx.commit();
s.close();
}
}
集合级别检索策略的规范:
l 在映射文件中, 用 <set> 元素来配置一对多关联及多对多关联关系. <set> 元素有 lazy 和 fetch 属性
• lazy: 主要决定 orders 集合被初始化的时机. 即到底是在加载 Customer 对象时就被初始化, 还是在程序访问 orders 集合时被初始化
• fetch: 取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式; 若取值为”join”, 则决定 orders 集合被初始化的时机
• 若把 fetch 设置为 “join”, lazy 属性将被忽略
6:测试代码:测试多对一和一对一的检索策略(AppManyToOne.java)
public class AppManyToOne {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/query/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点7: 多对一和一对一关联的检索策略
<many-to-one> 元素也有一个 lazy 属性和 fetch 属性.
fetch(默认值select)Lazy(默认值是proxy)策略
Joinfalse采用迫切左外联接检索。
Joinproxy采用迫切左外联接检索。
joinno-proxy采用迫切左外联接检索。
select * from l_order order0_
left outer join l_customer customer1_ on order0_.customer_id=customer1_.id
where order0_.id=?
* */
@Test
public void testManyToOne(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Order o = (Order)s.get(Order.class, 1);
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
tx.commit();
s.close();
}
/**
* 知识点7: 多对一和一对一关联的检索策略
<many-to-one> 元素也有一个 lazy 属性和 fetch 属性.
fetch(默认值select)Lazy(默认值是proxy)策略
selectfalse采用立即检索
selectproxy1. 如果对端Customer.hbm.xml中类级别的检索是立即检索,则为立即检索
2. 如果对端Customer.hbm.xml中类级别的检索是延迟检索,则为延迟检索
selectno-proxy----不研究
Hibernate: select * from l_order order0_ where order0_.id=?
Hibernate: select * from l_customer customer0_ where customer0_.id=?
* */
@Test
public void testManyToOne_1(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Order o = (Order)s.get(Order.class, 1);
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
tx.commit();
s.close();
}
/**
* Query 的 list 方法会忽略映射文件配置的迫切左外连接检索策略,
* 而采用延迟检索或立即检索策略。
* 如果fatch=join,lazy=false,则采用立即检索;
* 如果采用fatch=join,lazy=proxy则根据customer类级别的laze属性,laze=true为延迟检索,laze=false为立即检索
*
* Hibernate: select * from l_order order0_
Hibernate: select * from l_customer customer0_ where customer0_.id=?(3条)
*/
@Test
public void testQuery(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Order");
List<Order> list = query.list();
if(list!=null && list.size()>0){
for(Order o:list){
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
}
}
tx.commit();
s.close();
}
}
多对一和一对一关联的检索策略 详细说明
l 和 <set> 一样, <many-to-one> 元素也有一个 lazy 属性和 fetch 属性.
• 若 fetch 属性设为 join, 那么 lazy 属性被忽略
• 迫切左外连接检索策略的优点在于比立即检索策略使用的 SELECT 语句更少.
• 无代理延迟检索需要增强持久化类的字节码才能实现
l Query 的 list 方法会忽略映射文件配置的迫切左外连接检索策略, 而采用延迟检索或立即检索策略。如果fatch=join,lazy=false,则采用立即检索;如果采用fatch=join,lazy=proxy则根据customer类级别的laze属性,laze=true为延迟检索,laze=false为立即检索
l 如果在关联级别使用了延迟加载或立即加载检索策略,
可以设定批量检索的大小, 以帮助提高延迟检索或立即检索的运行性能.
7:测试代码:测试组合检索(set+manyToOne)
public class AppManyToOneAndSet {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/m_query/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点7: 组合1 many2one立即检索+set立即检索
<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="select">
+
<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false">
Hibernate: select * from l_order order0_ where order0_.id=?
Hibernate: select * from l_customer customer0_ where customer0_.id=?
-----<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="select">
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
-----<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false">
* */
@Test
public void testManyToOneAndSet(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Order o = (Order)s.get(Order.class, 1);
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
tx.commit();
s.close();
}
/**
* 知识点7: 知识点7: 组合2 many2one迫切左外+set立即检索
<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="join">
+
<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false">
Hibernate: select * from l_order order0_
left outer join l_customer customer1_ on order0_.customer_id=customer1_.id where order0_.id=?
-----<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="join">
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
-----<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false">
* */
@Test
public void testManyToOneAndSet_1(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Order o = (Order)s.get(Order.class, 1);
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
tx.commit();
s.close();
}
/**
* 知识点7: 组合3 many2one立即检索+set迫切左外
<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="select">
+
<set name="orders" table="l_order" inverse="true" fetch="join" lazy="false">
Hibernate: select * from l_order order0_ where order0_.id=?
----<many-to-one name="customer" class="cn.itcast.m_query.Customer" lazy="false" fetch="select">
Hibernate: select * from l_customer customer0_
left outer join l_order orders1_ on customer0_.id=orders1_.customer_id where customer0_.id=?
-----<set name="orders" table="l_order" inverse="true" fetch="join" lazy="false">
*/
@Test
public void testManyToOneAndSet_2(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Order o = (Order)s.get(Order.class, 1);
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
tx.commit();
s.close();
}
}
这里的测试说明,如果在Hibernate中配置关联关系(即一对多,多对一,多对多,一对一),且在Hibernate的配置关系中都是立即检索,可能查询的sql语句会出现重复,这也是Hibernate存在的弊端,即如果一个项目中数据库表的关联比较多,使用Hibernate会产生更多的sql语句,影响性能。
8:测试代码:批量检索(batch-size)
public class AppBatchSize {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/query/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点8:批量检索 从一的一端查询 查询所有的客户
*
* 没有优化前:
* Hibernate: select * from l_customer customer0_
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
Hibernate: select * from l_order orders0_ where orders0_.customer_id=?
优化后:在set元素中添加<set name="orders" table="l_order" inverse="true" fetch="select" lazy="false" batch-size="3">
Hibernate: select * from l_customer customer0_
Hibernate: select * from l_order orders0_ where orders0_.customer_id in (?, ?, ?)
* */
@Test
public void testBatchSize_1(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer");
List<Customer> list = query.list();
if(list!=null && list.size()>0){
for(Customer c:list){
System.out.println(c.getId());
System.out.println(c.getName());
System.out.println(c.getOrders().size());
}
}
tx.commit();
s.close();
}
/**
* 知识点9:批量检索 从多的一端查询 查询所有的订单
*
* 优化前:
* Hibernate: select * from l_order order0_
Hibernate: select * from l_customer customer0_ where customer0_.id=?
Hibernate: select * from l_customer customer0_ where customer0_.id=?
Hibernate: select * from l_customer customer0_ where customer0_.id=?
优化后:<class name="cn.itcast.m_query.Customer" table="l_customer" lazy="true" batch-size="3">
Hibernate: select * from l_order order0_
Hibernate: select * from l_customer customer0_ where customer0_.id in (?, ?, ?)
*/
@Test
public void testBatchSize_2(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Order");
List<Order> list = query.list();
if(list!=null && list.size()>0){
for(Order o:list){
System.out.println(o.getId());
System.out.println(o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getName());
}
}
tx.commit();
s.close();
}
/**
* 延迟检索的缺点:
* 应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。
* 此时抛出:
* org.hibernate.LazyInitializationException: could not initialize proxy - no Session
没有session的环境下调用代理对象除oid之外的其他属性
解决:只有在Session关闭之前查询数据库即可
* */
@Test
public void testLazy(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c =(Customer)s.load(Customer.class, 1);
System.out.println(c.getId());
//System.out.println(c.getName());
tx.commit();
s.close();
//将c变成游离状态
System.out.println(c.getId());
System.out.println(c.getName());
}
}
批量检索:从一的一端查询,查询所有客户
<set> 元素有一个 batch-size 属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能. 默认值是1
注:query.list()属于hql检索,hql检索忽略关联级别的迫切左外连接检索,只与lazy属性有关
.批量检索 从多的一端查询 查询所有的订单
在Customer.hbm.xml文件中增加batch-size属性
附录:
需要大家掌握的知识点:
hibernate第三天
一:Hibernate的检索方式
1:Hibernate 提供了以下几种检索对象的方式
(1)HQL 检索方式: 使用面向对象的 HQL 查询语言
l HQL(Hibernate Query Language) 是面向对象的查询语言
l HQL语句的检索方式与SQL语句的区别
* HQL是面向对象和对象中的属性
* SQL是面向过程(面向数据库表和表中的列)
(2)QBC 检索方式: 使用 QBC(Query By Criteria) API 来检索对象.
(3)SQL 检索方式: 使用本地数据库的 SQL 查询语句
注意:
l HQL vs SQL:
• HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后根据对象-关系映射文件中的映射信息, 把 HQL 查询语句翻译成相应的 SQL 语句. HQL 查询语句中的主体是域模型中的类及类的属性
• SQL 查询语句是与关系数据库绑定在一起的. SQL 查询语句中的主体是数据库表及表的字段.
2:测试方法:
Hql查询
(1)条件查询
Query query = session.createQuery("from Customer o where o.name='Tom'");
List<Customer> list = query.list();
(2)排序(使用与sql)
Query query = session.createQuery("from Customer c order by c.id desc");
List<Customer> list = query.list();
(3)别名查询:
通过HQL检索一个类的实例时,如果查询语句的其他地方需要引用它,
应该为这个类指定一个别名
from Customer as c where c.name=:custname
as 可省略
(4)多态查询:
//查询出所有的实体(当前类和所有子类的实例)
Query query = session.createQuery(“from Customer”);
query.list();
//检索出所有实现serializable接口的实例
Query query = session.createQuery(“ from java.io.Serializable”)
query.list();
//检索出所有的持久化对象
Query query = session.createQuery(“ from java.lang.Object”)
query.list();
(5)分页:
l 分页查询:
• setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下, Query 从查询结果中的第一个对象开始检索
• setMaxResult(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和 Criteria 接口检索出查询结果中所有的对象
Query query = session.createQuery("from Order o order by o.id desc");
//第一页
//query.setFirstResult(0);//表示从第一条开始检索,0表示1
//query.setMaxResults(10);//表示当前页最多显示多少条记录
//第二页
//query.setFirstResult(10);//表示从第一条开始检索,0表示1
//query.setMaxResults(10);//表示当前页最多显示多少条记录
//第三页
query.setFirstResult(20);//表示从第一条开始检索,0表示1
query.setMaxResults(10);//表示当前页最多显示多少条记录
List<Order> list = query.list();
tx.commit();
s.close();
(6)惟一结果集
Query query = session.createQuery("from Customer c order by c.id");
query.setMaxResults(1);//做多返回几条记录
Customer c = (Customer)query.uniqueResult();
(7)参数绑定
hql 查询:使用setString/setInteger
方式一:指定名称参数绑定
Query query = session.createQuery("from Customer c where " +
" c.name=:custname and c.age=:custage");
//第一个参数代表名字,第二个参数代表值
query.setString("custname", "Tom");
query.setInteger("custage", 21);
List list = query.list();
方式二:指定参数的位置绑定
Query query = session.createQuery("from Customer c
where c.name=? and c.age=?");
query.setString(0,"Tom");
query.setInteger(1, 21);
query.list();
Hql查询:使用setParameter
方式一:指定名称参数绑定
Query query = session.createQuery("from Customer c where c.name=:customerName and c.age=:customerAge");
query.setParameter("custermName", "Tom");
query.setParameter("customerAge", 21);
List<Customer> list = query.list();
方式二:指定参数的位置绑定
Query query = s.createQuery("from Customer c where c.name=? and c.age=?");
query.setParameter(0, "Tom");
query.setParameter(1, 21);
List<Customer> list = query.list();
(8)在映射文件中定义命名查询语句
<class>
.......
</class>
<query name="findCustomersByName">
<![CDATA[from Customer c where c.name like ?]]>
</query>
------------------------------------------------------------------
Query query = session.getNamedQuery(“findCustomersByName”);
query.setString(0,”%T%”);
query.list();
(9)迫切左外连接:
使用的sql语句仍然是左外连接,它将返回一个对象,要想获取和它关联对象,需要遍历循环。
缺点:循环太多,返回的结果集过于复杂
/**知识点10: 迫切左外连接,返回值是一个对象,要想查询和对象关联的其他对象,需要遍历循环之后获取*/
Query query =session.createQuery("from Customer c left outer join fetch c.orders");
List<Customer> list = query.list();
if(list!=null && list.size()>0){
for(Customer c:list){
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
Set<Order> orders = c.getOrders();
if(orders!=null && orders.size()>0){
for(Order o:orders){
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
}
}
}
}
(10)左外连接:
/**知识点11:左外连接*/
/**如果查询一个对象,则返回Object对象
* 如果查询的是多个对象,则返回Object数组对象*/
Query query =session.createQuery("from Customer c left outer join c.orders o");//hql语句等价于:select c,o from Customer c left outer join c.orders o
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] object:list){
Customer c = (Customer)object[0];
Order o = (Order)object[1];
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
System.out.println("---------------------");
}
}
(11)内连接
/**知识点12: 内连接*/
Query query =session.createQuery("from Customer c inner join c.orders o where c.name like 'T%'");//
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] object:list){
Customer c = (Customer)object[0];
Order o = (Order)object[1];
System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
System.out.println("---------------------");
}
}
(12)迫切内连接(了解)
//Inner join fetch表示迫切内连接检索策略 ,也就是覆盖映射文件中指定的检索策略
Query query = session.createQuery("from Customer c inner join fetch
c.orders o where c.name like 'T%'");
List list = query.list();
(13)隐式内连接(了解)
/**
知识点14: 隐式内连接(x) (了解知识)
HQL:
createQuery(“from Order o where o.customer.name like ‘T%’”);
等价于:
from Order o join o.customer c where c.name like ‘T%’
*/
//Query query = session.createQuery("from Order o where o.customer.name like 'T%'");//返回值只有个Order对象
Query query = session.createQuery("from Order o join o.customer c where c.name like 'T%'");//返回值是一个Object对象,其中一个值是Customer,其中一个值Order
query.list();
(14)右连接(了解)
HQL: right outer join 右外连接 返回是对象数组
Query query = session.createQuery("from Customer c
right outer join c.orders o where c.name like ‘t%'");
List list = query.list();
(15)使用SQL风格的交叉连接和隐式内连接(了解)
HQL:
交叉连接查询:from Customer,Order
标准SQL风格的内连接:from Customer c inner join c.orders
SQL风格的隐式内连接查询::from Customer c,Order o where c.id = o.customer.id
---------------------------------------------------------------------------------
Query query = session.createQuery(“from Customer c,Order o where c.name like ?”);
query.setString(0,”tom”);
List list = query.list();
(16)投影查询
l 投影查询: 查询结果仅包含实体的部分属性. 通过 SELECT 关键字实现.
l Query 的 list() 方法返回的集合中包含的是数组类型的元素, 每个对象数组代表查询结果的一条记录
l 可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录, 使程序代码能完全运用面向对象的语义来访问查询结果集.
l 可以通过 DISTINCT 关键字来保证查询结果不会返回重复元素
/**知识点17:投影查询(实例)*/
/**如果投影查询,查询一个值,此时返回Object对象
* 如果投影查询,查询多个值,此时返回Object数组*/
Query query = s.createQuery("select c,o.price from Customer c left outer join c.orders o where c.name='Tom'");
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] object:list){
Customer c = (Customer)object[0];
System.out.println(c.getId()+" "+c.getName()+" "+object[1]);
}
}
//知识点17:投影查询(list集合中存放的对象数组,数组中存放的查询的部分属性)
Query query = s.createQuery("select c.name,o.orderNumber,o.price from Customer c left outer join c.orders o where c.name='Tom'");
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] object:list){
System.out.println(object[0]+" "+object[1]+" "+object[2]);
}
}
实例1:
select c,o.price from Customer c join c.orders o where c.name like ‘T%’
如果希望查询结果中只包含Customer对象,可使用以下形式:
select c from Customer c join c.orders o where c.name like T%’
---------------------------------------------------------------------------------
实例2:
select关键字还能用于选择对象的部分属性
session.createQuery(“select c.id,c.name,o.orderNumber from Customer c
join c.orders o where c.name like ‘T%’”)
对应的sql语句为:
select c.ID,c.NAME,o.ORDER_NUMBER from CUSTOMERS c inner join
ORDERS o on c.ID-=o.CUSTOMER_ID where o.ORDER_NUMBER like’T%’
------------------------------------------------------------------------------------
实例3:
过滤重复元素
createQuery(“select distinct c.name from Customer c”);
总结:投影查询的返回的是Object数组或者是一个Object对象
(17)构造函数查询
//第一步:创建一个javabean对象,用来封装构造器函数
public class CustomerRow {
private String name;
private String orderNumber;
private Double price;
//构造器
public CustomerRow(){
}
public CustomerRow(String name,String orderNumber,Double price){
this.name = name;
this.orderNumber = orderNumber;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
//第二步:构造器函数
Query query = s.createQuery("select new cn.itcast.n_queryMethod.CustomerRow(c.name,o.orderNumber,o.price) from Customer c left outer join c.orders o where c.name='Tom'");
List<CustomerRow> list = query.list();
if(list!=null && list.size()>0){
for(CustomerRow cr:list){
System.out.println(cr.getName()+" "+cr.getOrderNumber()+" "+cr.getPrice());
}
}
(19)聚合函数
l 报表查询用于对数据分组和统计, 与 SQL 一样, HQL 利用 GROUP BY 关键字对数据分组, 用 HAVING 关键字对分组数据设定约束条件.
l 在 HQL 查询语句中可以调用以下聚集函数
• count()
• min()
• max()
• sum()
• avg()
//使用聚集函数
Query query = s.createQuery("select o.name,count(*) from Customer o group by o.name");
List<Object[]> list = query.list();
System.out.println(list.size());
for(int i=0;i<list.size();i++){
Object [] o = list.get(i);
System.out.print(o[0]+” “+o[1]);
System.out.println();
}
使用聚集函数
方式一: count
Query query = session.createQuery("select count(*) from Customer c");
Integer count=(Integer)query.uniqueResult();
System.out.println("count "+count);
/***********************************************************/
方式二:avg
Query query = session.createQuery("select avg(c.age) from Customer c");
Float avg=(Float)query.uniqueResult();
System.out.println("avg "+avg);
/***********************************************************/
方式三:max,min
Query query = session.createQuery("select max(c.age),min(c.age) from Customer c");
Object[] maxmin=(Object[])query.uniqueResult();
System.out.println("max "+(Long)maxmin[0]);
System.out.println("min "+(Long)maxmin[1]);
/***********************************************************/
方式四:sum
Query query = session.createQuery("select sum(c.age) from Customer c");
Long sum=(Long)query.uniqueResult();
System.out.println("sum "+sum);
Sql查询
检索方式:
方式一:
Query query = session.createSQLQuery(“select o.name from CUSTOMERS c where c.name=‘tom’”);
query.list();
方式二:参数查询
Query query = session.createSQLQuery(“select * from CUSTOMERS c where c.name=?”);
query.setString(“0”,’tom’)
query.list();
此时返回Object数组或者是返回一个Object对象(这里如果查询结果是1个值,就是Object对象,如果查询结果是多个值就是Object数组
方式三:Sql语句封装对象
SQLQuery sqlquery = session.createSQLQuery("select {c.*} from CUSTOMERS c where c.name =:customerName");
// 动态绑定参数
sqlquery.setString("customerName", “tom");
//“c”用来引用数据表的别名,例如以上代码中{c.*}表示使用c来作为customers表别名。 把sql查询返回的关系数据映射为对象
sqlquery.addEntity("c", Customer.class);
// 执行sql select语句,返回查询结果。
List list = sqlquery.list();
(1)条件查询
Query query = s.createSQLQuery("select * from m_customer o where o.name='Tom'");
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] o:list){
System.out.println(o[0].toString()+" "+o[1].toString()+" "+o[2].toString());
}
}
此时返回Object数组或者是返回一个Object对象
* 如果条件查询只有1个字段的时候,此时是一个Object对象
* 如果条件查询有多个字段的时候,此时是一个Object数组
(2)参数查询
方式一:
Query query = s.createSQLQuery("select * from m_customer o where o.name=:customer");
query.setParameter("customer", "Tom");
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] o:list){
System.out.println(o[0].toString()+" "+o[1].toString()+" "+o[2].toString());
}
}
方式二:
Query query = s.createSQLQuery("select * from m_customer o where o.name=?");
query.setParameter(0, "Tom");//0表示?号第1个位置
List<Object[]> list = query.list();
if(list!=null && list.size()>0){
for(Object[] o:list){
System.out.println(o[0].toString()+" "+o[1].toString()+" "+o[2].toString());
}
}
(3)封装实体
//封装对象实体
SQLQuery query = s.createSQLQuery("select {o.*} from m_customer o where o.name=?");
query.setParameter(0, "Tom");//0表示?号第1个位置
query.addEntity("o", Customer.class);
List<Customer> list = query.list();
if(list!=null && list.size()>0){
for(Customer c:list){
System.out.println(c.getName()+" "+c.getAge());
}
}
QBC查询
(1)条件查询
//主要由Criteria、Criterion接口和Expression类组成,他支持在运行时动态生成查询语句。
Criteria criteria = s.createCriteria(Customer.class);
criteria.add(Restrictions.eq("name", "Tom"));
List<Customer> list = criteria.list();
//方法链编程:
session.createCriteria(Customer.class)
.add(Restrictions.eq("name", "tom1"))
.list();
(2)排序
//QBC排序
Criteria criteria = s.createCriteria(Customer.class);
criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
List<Customer> list = criteria.list();
(3)分页查询
Criteria criteria = s.createCriteria(Order.class);
criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
//第3页
criteria.setFirstResult(20);
criteria.setMaxResults(10);
List<Order> list = criteria.list();
(4)qbc返回惟一结果集(单个对象)
Criteria criteria = s.createCriteria(Order.class);
criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
criteria.setMaxResults(1);
Order o= (Order)criteria.uniqueResult();
(5)QBC可以指定2个对象用来连接查询条件
Restrictions.like(propertyName, value);
Expression.like(propertyName, value);
附录:qbc的语法(大家有时间可以看看)
Hql运算符和QBC运算符比较
附录:sql语句和hql语句的联合查询:
Sql语句的连接条件,HQL语句与SQL语句相似,但是它是面向对象和属性的,而Sql语句是面向表和字段的。
/**
* 知识点,复习sql的连接语法
* 左外连接left outer join .. on(left join.. on):
* * 左侧表的数据全部要显示出来,如果不符合关联查询条件,此时用null的值在字段上显示
* 右外连接right outer join.. on(right join.. on)
* * 右侧表的数据全部要显示出来,如果不符合关联查询条件,此时用null的值在字段上显示
* 内连接inner join.. on(join.. on)
* * 只有完全符合查询条件的数据才能显示处理出来
* */
Query query = s.createSQLQuery("select * from customer c left outer join m_order o on c.id = o.customer_id");
List<Object[]> list = query.list();
3:测试代码
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/queryMethod/hibernate.cfg.xml");
configuration.addClass(Customer.class);
configuration.addClass(Order.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:初始化测试数据
* */
//@Test
//public void initData(){
//Session s = sf.openSession();
//Transaction tx = s.beginTransaction();
//Customer c = new Customer();
//c.setName("Rose");//Jack、Rose
//c.setAge(20+new Random().nextInt(12));
//s.save(c);
//for(int i=1;i<=10;i++){
//Order o = new Order();
//o.setOrderNumber("NO"+i);
//o.setPrice(5000+i*10d);
////建立关联关系
//c.getOrders().add(o);
//o.setCustomer(c);
//s.save(o);
//}
//tx.commit();
//s.close();
//}
/**
* 知识点:测试query方法
* */
@Test
public void testMethod(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
/**知识点1: 简单的查询*/
//Query query = s.createQuery("from Customer o where o.name='Tom'");
//List<Customer> list = query.list();
//Criteria criteria = s.createCriteria(Customer.class);
//criteria.add(Restrictions.eq("name", "Tom"));
//List<Customer> list = criteria.list();
/**知识点2: sql检索方式*/
//参数查询1
//Query query = s.createSQLQuery("select * from m_customer o where o.name=:customer");
//query.setParameter("customer", "Tom");
//参数查询2
//Query query = s.createSQLQuery("select * from m_customer o where o.name=?");
//query.setParameter(0, "Tom");//0表示?号第1个位置
//List<Object[]> list = query.list();
//if(list!=null && list.size()>0){
//for(Object[] o:list){
//System.out.println(o[0].toString()+" "+o[1].toString()+" "+o[2].toString());
//}
//}
//封装对象实体
//SQLQuery query = s.createSQLQuery("select {o.*} from m_customer o where o.name=?");
//query.setParameter(0, "Tom");//0表示?号第1个位置
//query.addEntity("o", Customer.class);
//List<Customer> list = query.list();
//if(list!=null && list.size()>0){
//for(Customer c:list){
//System.out.println(c.getName()+" "+c.getAge());
//}
//}
//知识点4: 多态查询(是指查询出当前类及所有子类的实例)
//查询出所有的实体(当前类和所有子类的实例)
//Query query = s.createQuery("from Customer");
//query.list();
//检索出所有实现serializable接口的实例
//Query query = s.createQuery("from java.io.Serializable");
//query.list();
//检索出所有的持久化对象
//Query query = s.createQuery("from java.lang.Object");
//query.list();
/**知识点5: 对查询结果排序*/
//Query query = s.createQuery("from Customer c order by c.id desc");
//List<Customer> list = query.list();
//QBC排序
//Criteria criteria = s.createCriteria(Customer.class);
//criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
//List<Customer> list = criteria.list();
/**知识点6: 分页查询*/
/**
* setFirstResult(int firstResult): 设定从哪一个对象开始检索,
* 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下, Query 从查询结果中的第一个对象开始检索
setMaxResult(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和 Criteria 接口检索出查询结果中所有的对象
*/
//Query query = s.createQuery("from Order o order by o.id desc");
////第一页
query.setFirstResult(0);//表示从第一条开始检索,0表示1
query.setMaxResults(10);//表示当前页最多显示多少条记录
////第二页
query.setFirstResult(10);//表示从第一条开始检索,0表示1
query.setMaxResults(10);//表示当前页最多显示多少条记录
////第三页
//query.setFirstResult(20);//表示从第一条开始检索,0表示1
//query.setMaxResults(10);//表示当前页最多显示多少条记录
//List<Order> list = query.list();
//Criteria criteria = s.createCriteria(Order.class);
//criteria.addOrder(org.hibernate.criterion.Order.desc("id"));
////第3页
//criteria.setFirstResult(20);
//criteria.setMaxResults(10);
//List<Order> list = criteria.list();
/**知识点7: 检索单个对象*/
//Query query = s.createQuery("from Customer c order by c.id");
//query.setMaxResults(1);//做多返回几条记录
//Customer c = (Customer)query.uniqueResult();
//QBC检索
//Criteria criteria = s.createCriteria(Customer.class);
//criteria.addOrder(org.hibernate.criterion.Order.asc("id"));
//criteria.setMaxResults(1);
//Customer c = (Customer)criteria.uniqueResult();
/**知识点8_1: 绑定参数的形式,按参数名称绑定*/
//方案一:
//Query query = s.createQuery("from Customer c where c.name=:custermName");
//query.setParameter("custermName", "Tom");
//List<Customer> list = query.list();
//方案二:
//Query query = s.createQuery("from Customer c where c.name=?");
//query.setParameter(0, "Tom");
//List<Customer> list = query.list();
/**知识点9: 在映射文件中定义命名查询语句*/
//Query query = s.getNamedQuery("findCustomersByName");
//query.setParameter(0, "%T%");
//List<Customer> list = query.list();
/**
* 知识点,复习sql的连接语法
* 左外连接left outer join .. on(left join.. on):
* * 左侧表的数据全部要显示出来,如果不符合关联查询条件,此时用null的值在字段上显示
* 右外连接right outer join.. on(right join.. on)
* * 右侧表的数据全部要显示出来,如果不符合关联查询条件,此时用null的值在字段上显示
* 内连接inner join.. on(join.. on)
* * 只有完全符合查询条件的数据才能显示处理出来
* */
//Query query = s.createSQLQuery("select * from m_customer c left outer join m_order o on c.id = o.customer_id");
//List<Object[]> list = query.list();
/**知识点10: 迫切左外连接,返回值是一个对象,要想查询和对象关联的其他对象,需要遍历循环之后获取*/
//Query query =s.createQuery("from Customer c left outer join fetch c.orders");
//List<Customer> list = query.list();
//if(list!=null && list.size()>0){
//for(Customer c:list){
//System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
//Set<Order> orders = c.getOrders();
//if(orders!=null && orders.size()>0){
//for(Order o:orders){
//System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
//}
//}
//
//}
//}
/**知识点11:左外连接*/
/**如果查询一个对象,则返回Object对象
* 如果查询的是多个对象,则返回Object数组对象*/
//Query query =s.createQuery("from Customer c left outer join c.orders o");//hql语句等价于:select c,o from Customer c left outer join c.orders o
//List<Object[]> list = query.list();
//if(list!=null && list.size()>0){
//for(Object[] object:list){
//Customer c = (Customer)object[0];
//Order o = (Order)object[1];
//System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
//System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
//System.out.println("---------------------");
//}
//}
///**知识点12: 内连接*/
//Query query =s.createQuery("from Customer c inner join c.orders o");//
//List<Object[]> list = query.list();
//if(list!=null && list.size()>0){
//for(Object[] object:list){
//Customer c = (Customer)object[0];
//Order o = (Order)object[1];
//System.out.println(c.getId()+" "+c.getName()+" "+c.getAge());
//System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
//System.out.println("---------------------");
//}
//}
/**知识点14: 隐式内连接(x) (了解知识)*/
//Query query = s.createQuery("from Order o where o.customer.name like 'T%'");//返回值只有个Order对象
//Query query = s.createQuery("from Order o join o.customer c where c.name like 'T%'");//返回值是一个Object对象,其中一个值是Customer,其中一个值Order
//query.list();
/**知识点15: 右连接*/
//Query query =s.createQuery("from Customer c right outer join c.orders o");//
//List<Object[]> list = query.list();
/**知识点16:使用SQL风格的交叉连接和隐式内连接(了解知识)*/
//Query query = s.createQuery("from Customer c,Order o where c.name like ?");
//query.setString(0,"Tom");
//query.list();
/**知识点17:投影查询(实例)*/
/**如果投影查询,查询一个值,此时返回Object对象
* 如果投影查询,查询多个值,此时返回Object数组*/
//Query query = s.createQuery("select c,o.price from Customer c left outer join c.orders o where c.name='Tom'");
//List<Object[]> list = query.list();
//if(list!=null && list.size()>0){
//for(Object[] object:list){
//Customer c = (Customer)object[0];
//System.out.println(c.getId()+" "+c.getName()+" "+object[1]);
//}
//}
//过滤查询(去掉重复值)
//Query query = s.createQuery("select distinct c.name from Customer c left outer join c.orders o where c.name='Tom'");
//List<Object> list = query.list();
//知识点17:投影查询(list集合中存放的对象数组,数组中存放的查询的部分属性)
//Query query = s.createQuery("select c.name,o.orderNumber,o.price from Customer c left outer join c.orders o where c.name='Tom'");
//List<Object[]> list = query.list();
//if(list!=null && list.size()>0){
//for(Object[] object:list){
//System.out.println(object[0]+" "+object[1]+" "+object[2]);
//}
//}
//构造器函数
//Query query = s.createQuery("select new cn.itcast.n_queryMethod.CustomerRow(c.name,o.orderNumber,o.price) from Customer c left outer join c.orders o where c.name='Tom'");
//List<CustomerRow> list = query.list();
//if(list!=null && list.size()>0){
//for(CustomerRow cr:list){
//System.out.println(cr.getName()+" "+cr.getOrderNumber()+" "+cr.getPrice());
//}
//}
//使用聚集函数
Query query = s.createQuery("select o.name,count(*) from Customer o group by o.name");
query.list();
tx.commit();
s.close();
}
}
二:hibernate的二级缓存
1:C3p0连接池(了解)
* 引入c3p0-0.9.1.jar
* 在hibernate.cfg.xml文件中增加如下配置
<!-- C3P0连接池设定-->
<!-- 使用c3po连接池 配置连接池提供的供应商-->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider </property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>
<!-- 设置自动提交 -->
<property name="connection.autocommit">true</property>
2:事务并发问题
(1)并发概念
(2)使用事务隔离级别
(3)设置hibernate的隔离级别
设置隔离级别
每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通
过数据库配置设置,也可在应用程序中设置.例如Hibernate:
hibernate.connection.isolation = 4
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
注意:
* Hibernate不可能改变在受管环境下由应用服务器提供的数据库连接
的隔离级别,只能通过改变应用服务器配置的方式来改变.
* 设置隔离级别是全局选项,会影响所有的连接和事务.有时需要为某个特定
事务指定更多的限制.
在hibernate.cfg.xml中设置事务的隔离级别
<property name="hibernate.connection.isolation">4</property>
(4)管理Session(Session与本地线程绑定):
l 尽管让程序自主管理 Session 对象的生命周期也是可行的, 但是在实际 Java 应用中, 把管理 Session 对象的生命周期交给 Hibernate 管理, 可以简化 Java 应用程序代码和软件架构
l Hibernate 3 自身提供了三种管理 Session 对象的方法
• Session 对象的生命周期与本地线程绑定
• Session 对象的生命周期与 JTA 事务绑定
• Hibernate 委托程序管理 Session 对象的生命周期
l 在 Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括
• thread: Session 对象的生命周期与本地线程绑定
• jta*: Session 对象的生命周期与 JTA 事务绑定
• managed: Hibernate 委托程序来管理 Session 对象的生命周期
分析:
在hibernate的配置文件中,设置session与本地线程绑定的代码
<property name="hibernate.current_session_context_class">thread</property>
测试Session与本地线程绑定
/**知识点4:在hibernate中如何配置,测试Session与本地线程绑定*/
/**
* 线程开启的时候,自动创建一个Sesion
* 当线程结束的时候,自动关闭一个Session
*/
@Test
public void testSessionThread(){
//Session s1 = sf.openSession();
//Session s2 = sf.openSession();
Session s1 = sf.getCurrentSession();
Session s2 = sf.getCurrentSession();
System.out.println(s1==s2);//false:此时说明2个Session不是一个对象;true:2个Session是一个对象(与本地线程绑定)
//s1.close();
//s2.close();
}
这里:不再调用sessionFactory.openSession().而是调用sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session,
* 当前线程如果存在session对象,取出直接使用
* 当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定
(5)缓存的定义:
(6)Hibernate的二级缓存的定义
l Hibernate中提供了两个级别的缓存
• 第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预
• 第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存
l SessionFactory 的缓存可以分为两类:
• 内置缓存: Hibernate 自带的, 不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate 会把映射元数据和预定义的 SQL 语句放到 SessionFactory 的缓存中, 映射元数据是映射文件中数据的复制, 而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的. 该内置缓存是只读的.
• 外置缓存(二级缓存): 一个可配置的缓存插件. 在默认情况下, SessionFactory 不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘
(7)Hibernate的二级缓存的机构
(8)二级缓存并发策略
l 两个并发的事务同时访问持久层的缓存的相同数据时, 也有可能出现各类并发问题.
l 二级缓存可以设定以下 4 种类型的并发访问策略, 每一种访问策略对应一种事务隔离级别
• 非严格读写(Nonstrict-read-write): 不保证缓存与数据库中数据的一致性. 提供 Read Uncommited 事务隔离级别, 对于极少被修改, 而且允许脏读的数据, 可以采用这种策略
• 读写型(Read-write): 提供 Read Commited 数据隔离级别.对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读
• 事务型(Transactional): 仅在受管理环境下适用. 它提供了 Repeatable Read 事务隔离级别. 对于经常读但是很少被修改的数据, 可以采用这种隔离类型, 因为它可以防止脏读和不可重复读
• 只读型(Read-Only):提供 Serializable 数据隔离级别, 对于从来不会被修改的数据, 可以采用这种访问策略(很强,但是性能低
(9)适合二级缓存中存放的数据(即二级缓存的应用场景)
l 适合放入二级缓存中的数据:
• 很少被修改
• 不是很重要的数据, 允许出现偶尔的并发问题
l 不适合放入二级缓存中的数据:
• 经常被修改
• 财务数据, 绝对不允许出现并发问题
• 与其他应用数据共享的数据
(10)提供缓存的供应商
l Hibernate 的二级缓存是进程或集群范围内的缓存, 缓存中存放的是对象的散装数据
l 二级缓存是可配置的的插件, Hibernate 允许选用以下类型的缓存插件:
• EHCache: 可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持
• OpenSymphony OSCache:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
• SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
• JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存
l 4 种缓存插件支持的并发访问策略(x 代表支持, 空白代表不支持)
(11)配置hibernate的二级缓存
1 拷贝ehcache-1.5.0.jar到当前工程的lib目录下
2:在hibernate.cfg.xml中:定义
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 指定缓存的供应商,在hibernate的核心包下查找供应商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
</property>
3 指定使用二级缓存的类
在hibernate.cfg.xml中配置:
<mapping resource="cn/itcast/o_secondCache/Customer.hbm.xml"/>
<mapping resource="cn/itcast/o_secondCache/Order.hbm.xml"/>
<!-- 指定使用二级缓存的类 放在maping下面 -->
<!-- 配置类级别的二级缓存 -->
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Customer"/>
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Order"/>
<!-- 配置集合级别的二级缓存 -->
<collection-cache usage="read-write" collection="cn.itcast.o_secondCache.Customer.orders"/>
这里也可以在映射文件中指定:例如:
类级别的缓存和集合级别的缓存
4:使用二级缓存时,添加2个jar包
在srping下能找到
..\lib\concurrent\backport-util-concurrent.jar
..\lib\jakarta-commons\commons-logging.jar
5:二级缓存的总结:
(1)类级别的缓存
类级别的缓存区域
* 存放的是对象的散装数据,散装数据使用OID从新组织一个新的对象,散装数据中存放的是类中属性的值
(2)集合级别的缓存
集合级别的缓存区域
* 存放的是对象的OID,如果要想获取真正的实体对象,还要到类级别的二级缓存中获取
总结:集合级别的缓存和查询级别的缓存都依赖于类级别的缓存
(3)查询级别的缓存
查询级别的缓存区域(重点),查询缓存指Query接口,Query接口支持HQL语句
* 存放的是对象的OID,如果要想获取真正的实体对象,还要到类级别的二级缓存中获取
总结:集合级别的缓存和查询级别的缓存都依赖于类级别的缓存
(4)更新时间戳级别的缓存
更新时间戳级别的缓存区域
* (1)将查询的对象放置到类、集合、查询级别的二级缓存中一份,而且设置放置对象的时间T1
* (2)当执行增、删、改操作的时候,更新时间戳会记录一个时间T2
如果当T1>T2的话,说明查询在后,更新在前,说明二级缓存中存放的数据是最新数据,那么此时从二级缓存中获取数据,不会查询数据库
如果当T1<T2的话,说明查询在前,更新在后,说明二级缓存中存放的数据不是最新数据,那么此时从二级缓存中获取数据,会查询数据库,查询到最新的结果
作用:保证二级缓存中的数据是最新的数据
(12)配置ehcache.xml
1)配置ehcache默认的配置文件ehcache.xml(名字固定)(放在类路径下)
ehcache.xml文件
<diskStore path="D:\cache" />
<cache name=" “
maxElementsInMemory="10“
eternal=“true"
overflowToDisk="true“
maxElementsOnDisk=“10000000”
diskPersistent="false“
diskExpriyThreadIntervalSeconds=“120” />
</ehcache>
2)配置进程范围内的二级缓存(配置ehcache缓存)
l <diskStore>:指定一个目录, 当 EHCache 把数据写到硬盘上时, 将把数据写到这个文件目录下. 默认是C:\WINDOWS\Temp
l <defaultCache>: 设置缓存的默认数据过期策略
l <cache> 设定具体的命名缓存的数据过期策略
使用name属性,cn.itcast.second.Order
l 每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。
3)cache元素的属性
• name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字
• maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
• eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
• timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
• timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
• overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
• diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
• diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
• memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
(13)配置使用二级缓存的测试代码
第一步:hibernate.cfg.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- C3P0连接池设定-->
<!-- 使用c3po连接池 配置连接池提供的供应商-->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>
<!-- 设置自动提交 -->
<property name="connection.autocommit">true</property>
<!--
设置hibernate的隔离级别
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
<!--
管理Session与本地线程绑定
thread: Session 对象的生命周期与本地线程绑定
jta*: Session 对象的生命周期与 JTA 事务绑定
managed: Hibernate 委托程序来管理 Session 对象的生命周期
-->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 指定缓存的供应商,在hibernate的核心包下查找供应商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="cn/itcast/o_secondCache/Customer.hbm.xml"/>
<mapping resource="cn/itcast/o_secondCache/Order.hbm.xml"/>
<!-- 指定使用二级缓存的类 放在maping下面 -->
<!-- 配置类级别的二级缓存 -->
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Customer"/>
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Order"/>
<!-- 配置集合级别的二级缓存 -->
<collection-cache usage="read-write" collection="cn.itcast.o_secondCache.Customer.orders"/>
</session-factory>
</hibernate-configuration>
第二步:创建Customer.java和Order.java
l Customer.java
public class Customer implements Serializable{
private Integer id;
private String name;
private Integer age;
//建立一的一端到多的一端的映射,此时一的一端是一个Set集合
private Set<Order> orders = new HashSet<Order>();
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
l Order.java
public class Order implements Serializable{
private Integer id;
private String orderNumber;
private Double price;
//映射从多的一端关联一的一段,此时是一的一端的对象
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
第三步:创建持久化对象的映射文件Customer.hbm.xml和Order.hbm.xml
l Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--
lazy:表示查询类的时候,是立即检索还是延迟检索
lazy=true:(默认值)延迟检索
lazy="false":立即检索
-->
<class name="cn.itcast.o_secondCache.Customer" table="m_customer">
<!-- 指定二级缓存的类,将Customer类放置到二级缓存中
<cache usage="read-write"/>-->
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="integer"></property>
<set name="orders" table="m_order" inverse="true">
<!-- 配置二级缓存的集合缓存,将Customer类对应的Set中Order对象的集合也放置到二级缓存中
<cache usage="read-write"/>-->
<key>
<column name="customer_id"></column>
</key>
<one-to-many class="cn.itcast.o_secondCache.Order"/>
</set>
</class>
</hibernate-mapping>
l Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.o_secondCache.Order" table="m_order">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="orderNumber" column="orderNumber" type="string"></property>
<property name="price" column="price" type="double"></property>
<many-to-one name="customer" class="cn.itcast.o_secondCache.Customer">
<column name="customer_id"></column>
</many-to-one>
</class>
</hibernate-mapping>
第四步:测试二级缓存类级别,测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/o_secondCache/hibernate.cfg.xml");
sf = configuration.buildSessionFactory();
}
/**
* 知识点:测试c3p0数据源
* */
@Test
public void testC3p0(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c = (Customer)s.get(Customer.class, 1);
System.out.println(c.getId()+" "+c.getName());
tx.commit();
s.close();
}
/**知识点4:在hibernate中如何配置,测试Session与本地线程绑定*/
/**
* 线程开启的时候,自动创建一个Sesion
* 当线程结束的时候,自动关闭一个Session
*/
@Test
public void testSessionThread(){
//Session s1 = sf.openSession();
//Session s2 = sf.openSession();
Session s1 = sf.getCurrentSession();
Session s2 = sf.getCurrentSession();
System.out.println(s1==s2);//false:此时说明2个Session不是一个对象;true:2个Session是一个对象(与本地线程绑定)
//s1.close();
//s2.close();
}
/**
* 知识点12:测试二级缓存和散装数据(类级别的二级缓存)
* 如果去掉<class-cache usage="read-write" class="cn.itcast.o_secondCache.Customer"/>
* 此时二级缓存中不能存放数据了,此时c3应该查询数据库
*/
@Test
public void testSecondCache(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
Customer c2 = (Customer)s.get(Customer.class, 1);//不产生select语句,从Session的一级缓存中读取
tx.commit();
s.close(); //一级缓存没有了
/*******************************************************/
s = sf.openSession();
tx = s.beginTransaction();
Customer c3 = (Customer)s.get(Customer.class, 1);//没有,从SessionFactory的二级缓存中获取数据
Customer c4 = (Customer)s.get(Customer.class, 1);//不产生select语句,从Session的一级缓存中读取
System.out.println(c1==c2);
System.out.println(c2==c3);
System.out.println(c3==c4);
tx.commit();
s.close(); //一级缓存没有了
}
/**
* 知识点12:测试类级别的二级缓存只适用于get和load获取数据,
* 对query接口可以将数据放置到类级别的二级缓存中,但是不能使用query接口从缓存中获取数据;
* 因为:query接口将查询的对象放置到二级缓存的查询缓存
*/
@Test
public void testQuery(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Query query = s.createQuery("from Customer c where c.id=1");
query.list();//将oid=1的Customer对象放置到一级缓存中一份,同时也放置到二级缓存中一份
Customer c2 = (Customer)s.get(Customer.class, 1);//不产生select语句,从Session的一级缓存中读取
tx.commit();
s.close(); //一级缓存没有了
/*******************************************************/
s = sf.openSession();
tx = s.beginTransaction();
//Query query1 = s.createQuery("from Customer c where c.id=1");
//query1.list();//将oid=1的Customer对象放置到一级缓存中一份,同时也放置到二级缓存中一份
Customer c3 = (Customer)s.get(Customer.class, 1);//不产生select语句,从Session的二级缓存中读取
Customer c4 = (Customer)s.get(Customer.class, 1);//不产生select语句,从Session的一级缓存中读取
tx.commit();
s.close(); //一级缓存没有了
}
}
第五步:测试二级缓存集合级别,测试类AppSet.java
public class AppSet {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/o_secondCache/hibernate.cfg.xml");
sf = configuration.buildSessionFactory();
}
/**
* 知识点12:测试集合级别的二级缓存(存放查询条件,即OID)
* (1)当同时配置
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Order"/>
<collection-cache usage="read-write" collection="cn.itcast.o_secondCache.Customer.orders"/>
此时当Session关闭的时候,查询订单集合的操作,不会产生select语句,从集合级别的二级缓存中获取对应的OID,再到类级别的二级缓存中获取真正的实体对象(除OID之外的其他属性)
因为类级别的二级缓存中存放了Order对象(10个OID对应的Order),如果找的了此时不会查询数据库,如果没有找到就会查询数据库
(2)当去掉配置
<collection-cache usage="read-write" collection="cn.itcast.o_secondCache.Customer.orders"/>
此时当Session关闭的时候,查询订单集合的操作,会产生select语句,因为二级缓存的集合缓存不存在数据(1条),1条语句是使用客户ID查询所有订单
(3)当去掉配置
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Order"/>
此时当Session关闭的时候,查询订单集合的操作,会产生select语句,因为二级缓存的集合缓存存在数据,而类级别的缓存没有存放数据(10条),10条语句都是使用订单ID查询对应的订单
*/
@Test
public void testSetSecondCache(){
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
System.out.println(c1.getName());
System.out.println(c1.getOrders().size());
tx.commit();
s.close(); //一级缓存没有了
/*******************************************************/
s = sf.openSession();
tx = s.beginTransaction();
Customer c2 = (Customer)s.get(Customer.class, 1);//没有,从SessionFactory的二级缓存中获取数据
System.out.println(c2.getName());
System.out.println(c2.getOrders().size());
tx.commit();
s.close(); //一级缓存没有了
}
/**
* 知识点13:测试一级缓存更新数据会同步到二级缓存
*/
@Test
public void testUpdate() {
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, 1);
System.out.println(customer.getAge());
customer.setAge(45);
tx.commit();
session.close();
/***********************************/
session=sf.openSession();
tx=session.beginTransaction();
customer=(Customer)session.load(Customer.class, 1);//不产生select语句,从二级缓存中获取
System.out.println(customer.getAge()); //45
tx.commit();
session.close();
}
/**
* 知识点14:测试二级缓存的数据存放到临时目录
*/
@Test
public void testowerFlow() {
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Query query=session.createQuery(" from Order o");//30个对象
query.list();
tx.commit();
session.close();
}
/**知识点15:时间戳缓存区域*/
/**
* 更新时间戳级别的缓存区域
* (1)将查询的对象放置到类、集合、查询级别的二级缓存中一份,而且设置放置对象的时间T1
* (2)当执行增、删、改操作的时候,更新时间戳会记录一个时间T2
如果当T1>T2的话,说明查询在后,更新在前,说明二级缓存中存放的数据是最新数据,那么此时从二级缓存中获取数据,不会查询数据库
*/
@Test
public void testUpdateTime(){
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("update Customer c set c.age=22 where c.id = 1");
int row = query.executeUpdate();//T2
System.out.println(row);
tx.commit();
Customer customer=(Customer)session.load(Customer.class, 1);//产生一个时间T1
System.out.println(customer.getAge());
session.close();
/***********************************/
session=sf.openSession();
tx=session.beginTransaction();
customer=(Customer)session.load(Customer.class, 1);//不产生select语句
System.out.println(customer.getAge());
tx.commit();
session.close();
}
/**知识点15:时间戳缓存区域*/
/**
* 更新时间戳级别的缓存区域
* (1)将查询的对象放置到类、集合、查询级别的二级缓存中一份,而且设置放置对象的时间T1
* (2)当执行增、删、改操作的时候,更新时间戳会记录一个时间T2
如果当T1<T2的话,说明查询在前,更新在后,说明二级缓存中存放的数据不是最新数据,那么此时从二级缓存中获取数据,会查询数据库,查询到最新的结果
*/
@Test
public void testUpdateTime_2(){
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class, 1);//产生一个时间T1
System.out.println(customer.getAge());
Query query = session.createQuery("update Customer c set c.age=22 where c.id = 1");
int row = query.executeUpdate();//T2
System.out.println(row);
tx.commit();
session.close();
/***********************************/
session=sf.openSession();
tx=session.beginTransaction();
//重新查询数据库
customer=(Customer)session.load(Customer.class, 1);//产生select语句
System.out.println(customer.getAge());
tx.commit();
session.close();
}
/**
* 知识点16: Query 接口的 iterator() 方法
* Query 接口的 iterator() 方法
同 list() 一样也能执行查询操作
list() 方法执行的 SQL 语句包含实体类对应的数据表的所有字段
Iterator() 方法执行的SQL 语句中仅包含实体类对应的数据表的 ID 字段
当遍历访问结果集时, 该方法先到 Session 缓存及二级缓存中查看是否存在特定 OID 的对象, 如果存在, 就直接返回该对象, 如果不存在该对象就通过相应的 SQL Select 语句到数据库中加载特定的实体对象
大多数情况下, 应考虑使用 list() 方法执行查询操作. iterator() 方法仅在满足以下条件的场合, 可以稍微提高查询性能:
* 要查询的数据表中包含大量字段
* 启用了二级缓存, 且二级缓存中可能已经包含了待查询的对象
*/
@Test
public void testIterator(){
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Order o where o.id<=11");//1-11条记录,放置到一级缓存一份,同时放置到二级缓存一份
query.list();//产生select语句,查询订单id是1-11的值
tx.commit();
session.close();
/***********************************/
session=sf.openSession();
tx=session.beginTransaction();
query = session.createQuery("from Order o");//所有的记录,其中也包括OID是1-11条记录
Iterator<Order> ite = query.iterate();//只获取对象的OID
while(ite.hasNext()){
Order o = ite.next();
System.out.println(o.getId());
System.out.println(o.getPrice());
}
tx.commit();
session.close();
}
/**
* 知识点17:查询缓存
* * 对于经常使用的查询语句, 如果启用了查询缓存, 当第一次执行查询语句时, Hibernate 会把查询结果存放在查询缓存中. 以后再次执行该查询语句时, 只需从缓存中获得查询结果, 从而提高查询性能
* 查询缓存使用于如下场合:
应用程序运行时经常使用查询语句
很少对与查询语句检索到的数据进行插入, 删除和更新操作
* 使用查询缓存的步骤
1:配置二级缓存, 因为查询缓存依赖于二级缓存
2:在 hibernate 配置文件中启用查询缓存
<property name=“hibernate.cache.use_query_cache">true</property>
3:对于希望启用查询缓存的查询语句, 调用 Query 的 setCacheable(true) 方法
4:必须将查询的Customer对象放置到类级别的二级缓存中
如果在hibernate.cfg.xml中去掉:
<class-cache usage="read-write" class="cn.itcast.o_secondCache.Customer"/>
当查询对象的时候,先到查询级别的缓存区域查找对应OID,然后使用查询获取到的OID到类级别的缓存区域查找真正的实体对象
* 如果找到了,不会产生select语句
* 如果没有找到,就会产生select语句(4条),使用主键OID查询数据库,产生4条
* */
@Test
public void testQuery(){
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Customer");//1-4的记录
query.setCacheable(true);
query.list();//产生select语句,查询客户id是1-4的值
tx.commit();
session.close();
/***********************************/
session=sf.openSession();
tx=session.beginTransaction();
query = session.createQuery("from Customer");//1-4的记录
query.setCacheable(true);
query.list();//不产生select语句
tx.commit();
session.close();
}
}
三:补充映射一对一的关联关系
l 一对一关联指两个表之间的记录是一一对应的关系。分为两种:外键关联和主键关联。
l 比如一家公司(Company)和它所在的地址(Address)。在业务逻辑中要求一家公司只有唯一的地址,一个地址也只有一家公司。 下图表现为外键关联关系。
1:外键关联
l 对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加 many-to-one 元素。为 many-to-one元素增加 unique=“true” 属性来表示为1-1关联,并用name属性来指定关联属性的属性名
l 另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
(1)测试代码:
l Company.java(公司)
public class Company implements Serializable{
private Integer id;
private String name;
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
l Company.hbm.xml(公司映射文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.p_oneToOne.Company" table="n_company">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<many-to-one name="address"
class="cn.itcast.p_oneToOne.Address"
column="aid"
unique="true">
</many-to-one>
</class>
</hibernate-mapping>
l Address.java(地址)
public class Address implements Serializable{
private Integer id;
private String city;
private String country;
private Company company;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
l Address.hbm.xml(地址映射文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.p_oneToOne.Address" table="n_address">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="city" column="city" type="string"></property>
<property name="country" column="country" type="string"></property>
<one-to-one name="company" class="cn.itcast.p_oneToOne.Company" property-ref="address"></one-to-one>
</class>
</hibernate-mapping>
l hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
l 一对一的数据库表的测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/p_oneToOne/hibernate.cfg.xml");
configuration.addClass(Company.class);
configuration.addClass(Address.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:测试保存
* */
@Test
public void testSave(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Company company=new Company();
company.setName("传智播客");
Address address=new Address();
address.setCity("北京");
address.setCountry("中国");
//建立关联关系
company.setAddress(address);
address.setCompany(company);
session.save(address);
session.save(company);
tx.commit();
session.close();
}
/**
* 知识点2:测试唯一性(报错)
*/
@Test
public void insertUnique(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
//创建公司
Company company=new Company();
company.setName("宝钢");
//查询地址
Address address=(Address)session.load(Address.class, 1);//传智播客对应的地址
//建立双向关联
company.setAddress(address);
address.setCompany(company);
//保存公司
session.save(company);
tx.commit();
session.close();
}
}
2:主键关联
l 一对一的另一种解决方式就是主键关联,在这种关联关系中,要求两个对象的主键必须保持一致,通过两个表的主键建立关联关系,无须外键参与。
l 基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. <param> 子元素指定使用当前持久化类的那个属性作为 “对方”
l 采用foreign主键生成器策略的一端增加 one-to-one 元素映射关联属性,其 one-to-one 属性还应增加 constrained=“true” 属性;另一端(company)增加one-to-one元素映射关联属性。
l constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键
(1)测试代码:
l Company.java(公司)
public class Company implements Serializable{
private Integer id;
private String name;
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
l Company.hbm.xml(公司映射文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.q_oneToOne.Company" table="o_company">
<id name="id" type="integer">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name" column="name" type="string"></property>
<one-to-one name="address"
class="cn.itcast.q_oneToOne.Address">
</one-to-one>
</class>
</hibernate-mapping>
l Address.java(地址)
public class Address implements Serializable{
private Integer id;
private String city;
private String country;
private Company company;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
l Address.hbm.xml(地址映射文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.q_oneToOne.Address" table="o_address">
<id name="id" type="integer">
<column name="id"></column>
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="city" column="city" type="string"></property>
<property name="country" column="country" type="string"></property>
<one-to-one name="company" class="cn.itcast.q_oneToOne.Company" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
l hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/itcasthibernate?useUnicode=true&characterEncoding=utf8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
l 一对一的数据库表的测试类App.java
public class App {
private static SessionFactory sf = null;
static{
Configuration configuration = new Configuration();
configuration.configure("cn/itcast/q_oneToOne/hibernate.cfg.xml");
configuration.addClass(Company.class);
configuration.addClass(Address.class);
sf = configuration.buildSessionFactory();
}
/**
* 知识点1:测试保存
* */
@Test
public void testSave(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Company company=new Company();
company.setName("宝钢");
Address address=new Address();
address.setCity("上海");
address.setCountry("中国");
//建立关联关系
company.setAddress(address);
address.setCompany(company);
session.save(address);
session.save(company);
tx.commit();
session.close();
}
/**
* 知识点2:测试唯一性(报错)
*/
@Test
public void insertUnique(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
//创建公司
Company company=new Company();
company.setName("宝钢");
//查询地址
Address address=(Address)session.load(Address.class, 1);//传智播客对应的地址
//建立双向关联
company.setAddress(address);
address.setCompany(company);
//保存公司
session.save(company);
tx.commit();
session.close();
}
}
需要大家掌握的知识点:
Hibernate第四天
一:创建数据库及数据库的表
1:创建数据库
tests2hproject
2:创建数据库的表
#公司表
CREATE TABLE S_Company(
companyID INT PRIMARY KEY, #主键ID
companyName VARCHAR(50) NULL, #公司名称
companyAddress VARCHAR(100) NULL, #公司地址
companyTel VARCHAR(100) NULL #公司电话
)
#用户表
CREATE TABLE S_User(
userID INT PRIMARY KEY, #主键ID
userName VARCHAR(50) NULL, #用户姓名
logonName VARCHAR(50) NULL, #登录名
logonPwd VARCHAR(50) NULL, #密码#
sex VARCHAR(10) NULL, #性别(例如:男,女)
birthday VARCHAR(50) NULL, #出生日期
education VARCHAR(20) NULL, #学历(例如:研究生、本科、专科、高中)
telephone VARCHAR(50) NULL, #电话
interest VARCHAR(20) NULL, #兴趣爱好(例如:体育、旅游、逛街)
remark VARCHAR(500) NULL, #备注
companyID INT, #公司ID
CONSTRAINT FOREIGN KEY(companyID) REFERENCES S_Company(companyID)
)
二:使用hibernate操作数据库,并映射数据库的表
1:首先创建web工程,并导入struts+hibernate需要的jar包
2:javabean持久化对象(2个)
l 在cn.itcast.project.domain中创建Company.java
public class Company implements Serializable {
private Integer companyID; //主键ID
private String companyName; //公司名称
private String companyAddress;//公司地址
private String companyTel; //公司电话
//一个公司对应多个用户,该集合中存放的是User对象
private Set<User> users = new HashSet<User>(0);
//set和get方法省略
}
l 在cn.itcast.project.domain中创建User.java
public class User implements Serializable {
private Integer userID; //主键ID
private String userName; //用户姓名
private String logonName; //登录名
private String logonPwd; //密码
private String sex; //性别(例如:男,女)
private String birthday; //出生日期
private String education; //学历(例如:研究生、本科、专科、高中)
private String telephone; //电话
private String interest; //兴趣爱好(例如:体育、旅游、逛街)
private String remark; //备注
// 多个用户对应一个公司
private Company company;
//省略set和get方法
}
3:javabean对象对应的映射文件
l 在domain下创建Company.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="cn.itcast.project.domain.Company" table="S_Company">
<id name="companyID" column="companyID" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="companyName" column="companyName" type="java.lang.String"/>
<property name="companyAddress" column="companyAddress" type="java.lang.String"/>
<property name="companyTel" column="companyTel" type="java.lang.String"/>
<!-- 一对多的映射关系 -->
<set name="users" table="s_user" inverse="true">
<key>
<column name="companyID"/>
</key>
<one-to-many class="cn.itcast.project.domain.User"/>
</set>
</class>
</hibernate-mapping>
l 在domain下创建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="cn.itcast.project.domain.User" table="S_User">
<id name="userID" column="userID" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="userName" column="userName" type="java.lang.String"/>
<property name="logonName" column="logonName" type="java.lang.String"/>
<property name="logonPwd" column="logonPwd" type="java.lang.String"/>
<property name="sex" column="sex" type="java.lang.String"/>
<property name="birthday" column="birthday" type="java.lang.String"/>
<property name="education" column="education" type="java.lang.String"/>
<property name="telephone" column="telephone" type="java.lang.String"/>
<property name="interest" column="interest" type="java.lang.String"/>
<property name="remark" column="remark" type="java.lang.String"/>
<!-- 多对一的映射 -->
<many-to-one name="company" class="cn.itcast.project.domain.Company">
<column name="companyID"></column>
</many-to-one>
</class>
</hibernate-mapping>
4:hibernate操作数据库配置文件(放置到src的类路径下)
l 名称叫做hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!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.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tests2hproject?useUnicode=true&characterEncoding=utf-8</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.show_sql">true</property>
<!--
设置事务的隔离级别
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
<!--
session对象的生命周期与本地线程绑定
hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括
* thread: Session 对象的生命周期与本地线程绑定
* jta*: Session 对象的生命周期与 JTA 事务绑定
* managed: Hibernate 委托程序来管理 Session 对象的生命周期
-->
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="cn/itcast/project/domain/User.hbm.xml"/>
<mapping resource="cn/itcast/project/domain/Company.hbm.xml"/>
</session-factory>
</hibernate-configuration>
5:测试是否能使用hibernate操作数据库
使用junit完成测试
@Test
public void save(){
Configuration configuration = new Configuration();
//加载hibernate.cfg.xml和Xxxxx.hbm.xml文件
configuration.configure();//默认路径(类路径)下的hibernate.cfg.xml
SessionFactory sf = configuration.buildSessionFactory();
Session s = sf.openSession();
Transaction tr = s.beginTransaction();
Company c = new Company();
c.setCompanyName("华为");
c.setCompanyAddress("深圳华强北");
c.setCompanyTel("12312312");
User u = new User();
u.setLogonName("小强");
u.setLogonPwd("123");
u.setSex("男");
//建立关联关系
u.setCompany(c);
s.save(c);
s.save(u);
tr.commit();
s.close();
}
三:使用struts2负责MVC的控制和转发
1:在web容器中添加一个过滤器(这是struts2运行的核心)
l web.xml
<!-- 这是struts2运行的核心 -->
<filter>
<filter-name>StrutsPrepareAndExecuteFilter</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>StrutsPrepareAndExecuteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2:必须在src下创建struts2的核心配置文件,struts.xml
l struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!--配置struts2的请求后缀,如果有多个,用,隔开"-->
<constant name="struts.action.extension" value="do"/>
<!-- struts2的开发模式,默认是生产模式,这样优点:自动加载struts.xml,在控制台输出更多的错误信息 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 修改UI主题,这样一来去掉struts提供的样式,一般项目中由美工添加样式 -->
<constant name="struts.ui.theme" value="simple"></constant>
<package name="company" namespace="/company" extends="struts-default">
<action name="companyAction_*" class="cn.itcast.project.web.action.CompanyAction" method="{1}">
</action>
</package>
<package name="user" namespace="/user" extends="struts-default">
<action name="loginAction_*" class="cn.itcast.project.web.action.LoginAction" method="{1}">
</action>
<action name="userAction_*" class="cn.itcast.project.web.action.UserAction" method="{1}">
</action>
</package>
</struts>
四:搭建各层的结构
1:Action层,在cn.itcast.project.web.action中创建3个Action类
l LoginAction.java
public class LoginAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}
}
l CompanyAction.java
public class CompanyAction extends ActionSupport implements ModelDriven<Company> {
private Company company = new Company();
public Company getModel() {
return company;
}
}
l UserAction.java
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}
}
2:Service层,在cn.itcast.project.service中
l 将接口放置到service包下
l 将接口的实现类放置到service包下impl的包下
l 在业务层中要控制事务(包括提交和回滚)
//Service层的方法
public void save(){
//操作新增A表(ADao)
//操作新增B表(BDao)
//操作新增C表(CDao)
}
总结,事务处理应该放在到业务层
3:Dao层,在cn.itcast.project.dao中
l 将接口放置到dao包下
l 将接口的实现类放置到dao包下impl的包下
4:DBC层(创建SessionFactory用来获取Session,从而操作数据库)
在cn.itcast.procject.dbc中创建一个类:
目的:创建SessionFactory用来获取Session,从而操作数据库,因为SessionFactory在项目中的使用只需要创建一次就可以了
public class HibernateUtil {
private static SessionFactory sf=null;
static {
Configuration config=new Configuration();
config.configure();
sf=config.buildSessionFactory();
}
/**
* 获取session
*/
public static Session getSession(){
if(sf!=null){
//配置session与本地线程绑定
return sf.getCurrentSession();
}
return null;
}
/**
* 提交
*/
public static void commit(Transaction tx){
if(tx!=null&&!tx.wasCommitted()){
tx.commit();
}
}
/**
* 回滚
*/
public static void rollbakck(Transaction tx){
if(tx!=null&&!tx.wasRolledBack()){
tx.rollback();
}
}
}
5:导入原型,将原型中的jsp、image、css、js
6:struts2的转发逻辑
人员管理系统提出的疑问
1:在每个Action、Service、Dao中需要用接口来创建实现类
private IUserService userService = new UserServiceImpl();
缺点:如果类UserServiceImpl名称发生变化,此时Action、Service、Dao对应的类就会报错
能不能通过一个容器统一来管理对象呢?
2:使用Hibernate操作之后,DAO层代码已经是优化(操作简单)
不足:service层代码,需要控制事务,所以必然会添加事务的控制代码:
Session s = HibernateUtil.getSession();
Transaction tr = s.beginTransaction();
try {
HibernateUtil.commit(tr);
} catch (Exception e) {
e.printStackTrace();
HibernateUtil.rollbakck(tr);
}
不足:显得过于麻烦
3:通过静态的方法(即dbc中HibernateUtils类)用来加载SessionFactory,这样第一次访问的时候一定会很慢
能不能想个办法,当web容器启动的时候,就加载SessionFactory呢?
如果学习spring之后,3个问题全部解决
单表查询的CRUD
一对多的CRUD
多对多的CRUD
二级缓存:
二级缓存的配置:
l 类级别的二级缓存测试
l 集合级别的二级缓存测试
l 查询级别的二级缓存的测试
Hibernate课堂笔记相关推荐
- Hibernate学习笔记(一)----针对不同的数据库不同的配置
Hibernate初学笔记 l Hibernate初步配置: 1 新建项目 2 学习建立user-library-hibernate,并加入相应的jar包(hibernate核心jar包,lib下的所 ...
- 管理系统中计算机应用第四章重点,管理系统中计算机应用课堂笔记第四章(4)...
管理系统中计算机应用课堂笔记第四章(4) 分类:自考 | 更新时间:2016-07-08| 来源:转载 这个分析和抽象工作可分以下三步进行: 5.2.1数据流程图的绘制 数据流程图既是对原系统进行分析 ...
- hibernate学习笔记二
上一篇关于hibernate学习笔记一,主要是作为hibernate的入门知识.没有和spring发生任何关系,这一篇我将把spring集成进去,看spring如何管理hibernate,还有和未使用 ...
- AI公开课:19.04.10颜水成—360副总裁《人工智能:观察与实践》课堂笔记以及个人感悟—191017再次更新
AI公开课:19.04.10颜水成-360副总裁<人工智能:观察与实践>课堂笔记以及个人感悟 导读 颜水成,新加坡国立大学副教授.360集团副总裁.人工智能研究院院长. 颜水成 ...
- AI公开课:19.05.16漆远-蚂蚁金服集团CF《金融智能的深度与温度》课堂笔记以及个人感悟—191017再次更新
AI公开课:19.05.16漆远-蚂蚁金服集团CF<金融智能的深度与温度>课堂笔记以及个人感悟-191017再次更新 导读 漆远,麻省理工学院博士后,39岁被评为美国普渡大 ...
- AI英特尔杯公开课:2019.06.27在线直播《研究生人工智能创新大赛—AI赋能,创新引领》课堂笔记和感悟(二)
AI英特尔杯公开课:2019.06.27在线直播<研究生人工智能创新大赛-AI赋能,创新引领>课堂笔记和感悟(二) 导读 讲解总体不错,知识点比较基础,适合入门,各种主流框架都有 ...
- AI英特尔杯公开课:2019.06.27在线直播《研究生人工智能创新大赛—AI赋能,创新引领》课堂笔记和感悟(一)
AI英特尔杯公开课:2019.06.27在线直播<研究生人工智能创新大赛-AI赋能,创新引领>课堂笔记和感悟(一) 导读 讲解总体不错,知识点比较基础,适合入门,各种主流框架都有 ...
- AI公开课:19.05.29 浣军-百度大数据实验室主任《AutoDL 自动化深度学习建模的算法和应用》课堂笔记以及个人感悟
AI公开课:19.05.29 浣军 百度大数据实验室主任<AutoDL 自动化深度学习建模的算法和应用>课堂笔记以及个人感悟 导读 浣军博士,汉族,1975年出生于江苏苏州, ...
- AI公开课:19.05.15施尧耘-达摩院量子实验室主任《量子计算:前景与挑战》课堂笔记以及个人感悟
AI公开课:19.05.15施尧耘-达摩院量子实验室主任<量子计算:前景与挑战>课堂笔记以及个人感悟 导读 施尧耘1997年本科毕业于北京大学,后在普林斯顿大学取得计算机科 ...
最新文章
- oracle下使用sql命令,ORACLE笔记(2)ORACLE 学习中用到的SQL命令
- Matplotlib 2016-04-15
- 阻塞队列之七:DelayQueue延时队列
- Spring Boot错误–创建在类路径资源DataSourceAutoConfiguration中定义的名称为“ dataSource”的bean时出错...
- 默认HotSpot最大直接内存大小
- 转码与重定向的区别之于SpringMVC
- iphone闪退修复工具_支持iOS13~13.3越狱工具发布(附下载地址)
- oracle判断是否包含字符串的方法
- 小强的HTML5移动开发之路(32)—— JavaScript回顾7
- like效率 regexp_Oracle中REGEXP_LIKE与LIKE的区别
- java cp classpath_java -cp、java -jar、java -classpath
- MYSQL8.0 OCP考试题库(如需完整版请留言)
- 又是一年“剁手”时,AI一下更优惠?
- 激光视觉惯导融合的slam系统
- 如何删除数据库中的冗余数据…
- **懒得给孩子讲故事怎么办**
- 新手入门必懂:关于西瓜视频广告,你不可不知的基础知识
- 现货黄金有什么需要注意的?
- 雷电模拟器连接android studio教程
- 2.muduo之Channel