hibernate
一、 hibernate介绍
hibernate事实上就是ormapping框架,此框架的作用就是简单话数据库的操作。
hibernate就是将用户提交的代码。參照持久化类配置文件,转换成sql语句。
1、 JDBC的优/缺点:
1. 缺点:
1、 查询代码特别繁琐。
2、 反复性代码多。频繁的try,catch。
3、 没有对数据的缓存(就是将先将数据放入内存中,当dao层再向数据库要数据时。直接到内存中去取。速度会快非常多,也避免了频繁的和数据库交互)。
4、 对sql代码的移植性非常差。(不能解决sql语句跨数据库的问题)。
2. 长处:
1、 最原始的操作数据库,所以速度比較快。
2、 hibernate的优/缺点:
1. 缺点:
1、 由于hibernate对数据库是面向对象操作,所以不须要写sql(自己主动生成sql)语句,也就导致sql语句是不可控的(如:在查询数据时有非常多种方式来查询到我们想要的数据,能够凭自己的项目须要来选择那种方式。但在hibernate中无法编写sql代码(自己主动生成),所以就导致假设这个项目对sql语句优化查询效率要求特别高的话,就不适合使用hibernate框架了).
2、 假设数据量特别大。也不适合使用hibernate。
3. 长处:
1、 代码比較简单
2、 有对数据的缓存(hibernate对数据的缓存总共同拥有三种:一级缓存、二级缓存、查询缓存。hibernate的一级缓存是世界级的。效率比較高。)
3、 hibernate操作数据库是以面向对象的方式操作数据库,所以sql代码的移植性比較好。
二、 ormapping介绍
1、 对象关系映射
让关系型数据中的表和java中的对象产生关联,也就是以后操作数据库能够用面向对象的方式操作。
但须要一个命名为*.hbm.xml的配置文件(映射文件)进行配置,让数据库中的表和对象产生关联关系.
映射文件里须要映射的关系:
数据库中的表 java中类
表中的字段名 类中的属性名
表中字段的类型 类中属性的类型
表中关系(一对一,一对多,多对多等) java中面向对象的关系
那么通过此*.hbm.xml关系映射文件。就能够将关系型数据的表和java中的对象互相产生关联。
3、 POJO
在hibernate中与数据库表相应的类叫持久化类此类实例化的对象叫持久化对象,统称为POJO
POJO必须是一个javaBean对象
三、 hibernate须要导入的基本jar包
1、 版本号:
hibernate-3.5.6-Final-dist
4、 jar包
c3p0-0.9.1.jar 数据库连接池须要的jar包
commons-collections-3.1.jar 集合的操作工具jar包
commons-logging-1.1.1.jar 日志工具jar包
dom4j-1.6.1.jar xml的DOM解析工具
ehcache-1.5.0.jar 缓存框架
javassist-3.9.0.GA.jar hibernate的核心jar包(解决代理模式的懒载入问题)
jta-1.1.jar
log4j.jar 日志工具jar包
mysql-connector-java-5.1.10-bin.jar MySQL的驱动jar包
slf4j-api-1.5.8.jar
slf4j-log4j12.jar
antlr-2.7.6.jar
backport-util-concurrent.jar
四、 hibernate的配置文件
1、 配置文件命名规范及存放位置:
hibernate的配置文件必须在WEB-INF/class(相应的开发文件夹为src)下,名称为固定写法:hibernate.cfg.xml
5、 此配置文件的作用
1、 用于配置数据库的连接信息
2、 用于描写叙述持久化类的映射配置文件(及载入)
6、 打开此配置文件的工具
此配置文件打开用MyEclipse Hibernate Config Editor工具打开
选中此xml配置文件-->右键-->Open With-->Other 选择此工具双击就可以
7、 此配置文件模板
<?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标签表示配置一个数据库 -->
<session-factory>
<!-- property标签表示一个数据项(数据库的连接信息)-->
<!-- 假设此标签的name属性为:hibernate.connection.username,就表示要设置连接数据库的用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 假设此标签的name属性为:hibernate.connection.password,就表示要设置连接数据库的密码 -->
<property name="hibernate.connection.password">123</property>
<!-- 假设此标签的name属性为:hibernate.connection.url,就表示要设置数据库的url地址(包含数据名) -->
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/数据库名</property>
<!-- hibernate.dialect:表示设置方言(在web开发中必须配置方言及设置url)
也就是告诉hibernate连接的是哪个数据库(这里是MySQL数据库)
此选项能够不用填写,由于hibernate会自己主动通过数据库url识别
但假设不设置,在有些环境下会出错
注意:假设要设置此值,MySQL数据库的取值范围有两个:
org.hibernate.dialect.MySQLInnoDBDialect(此值在web开发中測试时才会有实用,在java測试时会连接不到数据库)
org.hibernate.dialect.MySQLDialect(推荐使用)(在web开发和java測试是都实用)
假设发现无法自己主动创建表。因更换參数后再试-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--
hibernate.hbm2ddl.auto:表示依据持久化类和映射文件生成表(当启动hibernate容器的时候,hibernate对表的处理情况)
取值:
validate 表示仅仅验证表的结构是否正确,但不会创建表(默认值)
create-drop 表示当启动hibernate时创建表,当hibernate销毁时就删除表(一般不用)
create 表示每当启动hibernate时就创建此表,假设有原表。有可能会覆盖掉原表中的数据(一般不用)
update 表示当启动hibernate时就检查数据库中是否有此表,假设没有就创建表,假设有就仅仅是验证表的结构(推荐使用)
注意:这里仅仅是自己主动创建表,数据库不会自己主动创建。
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- hibernate.connection.driver_class:表示配置数据库的驱动地址 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 在控制台打印hibernate自己主动生成的sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- mapping标签表示:描写叙述持久化类的映射配置文件(并载入) -->
<mapping resource="cn/domain/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
五、 持久化类的配置文件(POJO)
在hibernate中与数据库表相相应的java类叫持久化类(持久化类必须是一个javaBean),相应的java对象叫持久化对象。
1、 配置文件的命名规范及存放位置
持久化类的配置文件必须和持久化类放在同一个包下,而且此配置文件的开头必须以持久化类的类名开头:持久化类名.hbm.xml(.hbm.xml为固定写法)
1. 演示样例:
如有一个User的持久化类,那么它的持久化配置文件的名称应为:User.hbm.xml
8、 持久化类的注意事项
必须实现序列化接口(Serializable),应防止此对象无法在网络上传输或存储在本地。
持久化类必须是一个javaBean
持久化类的属性不能使用keyword
9、 对象关系映射(此文件的作用)
文件的作用是让关系型数据库中的表和java中的对象产生映射关系(关联起来 )
产生此映射关系后那么通过hibernate就能够以面向对象的方式操作数据库了。
1. java类和数据库中的表须要映射的关系:
数据库中的表 java中类
表中的字段名 类中的属性名
表中字段的类型 类中属性的类型
表中关系(一对一,一对多,多对多等) java中面向对象的关系
那么通过此*.hbm.xml关系映射文件,就能够将关系型数据的表和java中的对象互相产生关联。
10、 此配置文件的工具
此配置文件用MyEclipse Hibernate Mapping Editor工具打开
选中此持久化类配置文件-->点击右键-->Open With-->Other 选择此工具双击就可以
11、 此配置文件的模板
<?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: 持久化类的全名
table: 表的名称(假设表名和类名一致。就能够不写,由于其默认值和持久化类名一样)
catalog: 数据库的名称 一般不写(由于能够从数据库配置文件(hibernate.cfg.xml)的数据库url中获取到此数据库名称) -->
<class name="cn.domain.User">
<!--id 标签表示描写叙述数据库表的主键
属性:
name: java持久化类中的主键属性的名称
column: 数据库表中主键字段的名称(能够不用写此属性。默认值和name的值一致)
type:java类中的主键的类型(完整名称) 此属性的取值另一种:(不推荐此种写法,效率低)
假设是java.lang.Integer类型 直接写:integer(首字母小写)
java.lang.String类型 直接写:string
java.lang.Long类型 直接写:long (依次类推) 这样的写法hibernate也能识别
length:表中此主键字段类型的长度(假设不写将是最大值)
-->
<id name="uid" length="10" type="java.lang.Long">
<!-- generator标签表示此主键生产器
告诉hibernate容器用什么样的方式产生此主键。这里是increment:自增长的方式
-->
<generator class="increment"></generator>
</id>
<!-- property标签用于描写叙述一个表中普通的字段和类中的属性的关系
属性:
name:java持久化类中属性的名称,
column:数据库表中字段的名称(能够不用写此属性。默认值和name的值一致)
type:java持久化类中属性的类型(完整名称)
length:表中此字段类型的长度(假设不写将是最大值)
-->
<property name="username" length="20" type="java.lang.String"></property>
<!-- property标签用于描写叙述一个表中普通的字段和类中的属性的关系
属性:
name:java持久化类中属性的名称,
column:数据库表中字段的名称(能够不用写此属性,默认值和name的值一致)
type:java持久化类中属性的类型(完整名称)
length:表中此字段类型的长度(假设不写将是最大值)
-->
<property name="password" length="10" type="java.lang.String" ></property>
</class>
</hibernate-mapping>
六、 载入hibernate配置文件的两种方式及SessionFactory对象和Session对象
1、 hibernate配置文件的载入
1. 规范的hibernate配置文件载入方式(推荐使用)
此方式,hibernate配置文件必须遵循:名称固定为:hibernate.cfg.xml。而且必须放在WEB-INF/classes(相应的开发文件夹为src)文件夹下 的规则。
用下面方式进行载入:
//创建一个hibernate配置文件载入对象
Configuration configuration = new Configuration();
//将此配置文件载入到内存中,并会依据此配置文件的配置方式,检索是否须要自己主动创建表(依据hibernate.cfg.xml配置文件对hibernate.hbm2ddl.auto属性的设置),并检查表的结构和java的类是否相相应
configuration.configure();
4. 不规范的hibernate配置文件载入方式
此方式,hiberate配置文件的名称及位置能够任意,但在载入配置文件时须要将此文件的路径传入。
如:hibernate的配置文件存放在cn.domain包下,并取名为abc.xml,那么应该用下面方式载入此配置文件
//创建一个hibernate配置文件载入对象
Configuration configuration = new Configuration();
//将此配置文件载入到内存中(传入配置文件的路径及名称),并会依据此配置文件的配置方式,检索是否须要自己主动创建表(依据hibernate.cfg.xml配置文件对hibernate.hbm2ddl.auto属性的设置),并检查表的结构和java的类是否相相应.
configuration.configure("/cn/domain/abc.xml");
12、 SessionFactory对象
1. SessionFactory对象的作用
SessionFactory对象中封装了一个数据库的全部信息,包含:数据库的连接信息、持久化类的映射文件信息、持久化类的信息.
SessionFactory是一个重量级的对象(比較重要的对象和特别牛的对象);
5. SessionFactory的产生
1- SessionFactory对象的获取
当Configuration对象载入完毕hibernate配置文件和持久化类关系映射配置文件后便会通过此这些配置文件生成此SessionFactory对象。所以能够通过Configuration对象的buildSessionFactory()方法获取到此SessionFactory对象。
2- SessionFactory对象的生命周期及线程安全
当hibernate配置文件载入完毕时便会产生此SessionFactory对象,并会永久驻留在内存中。直到此应用在server上卸载。
SessionFactory对象是通过单例模式产生的。所以此对象在应用中仅仅会有一个。
SessionFactory对象也是一个线程安全的对象,内部将共享数据的操作放在了线程同步代码块中。
13、 Session对象
1. Session对象的介绍
Session对象就相当于JDBC中的Connection(连接)对象,在JDBC中通过Connection就能够直接用sql语句的方式对数据库进行操作。而在hibernate中能够直接通过Session对象以面向对象的方式对数据库进行操作。
6. Session对象的获取
Session对象能够通过SessionFactory对象的openSession()方法(表示打开一个连接) 获取到此Session对象。
和Connection(连接)对象一样 此Session对象使用完后必须调用此对象的close()方法将这个连接又一次放回到数据库连接池中。
7. Session对象对事物的控制
假设要对表进行增、删、改操作,必须通过调用此Session对象的beginTransaction()方法开启事务,并会返回一个Transaction事务对象,当对表操作完毕后需调用此Transaction事务对象的commit()方法提交事务,假设没有开启事务,将会导致操作不成功(由于hibernate不会自己主动提交事务)。
一个Session对象能够开启两次以上事务,但必须提交事务后才干又一次开启事务。
8. Session的save(Object)方法
此方法是将一个持久化对象(參照此持久化类的映射配置文件)保存到数据库中。
注意:此方法的參数必须是一个持久化对象(也就是持久化类的演示样例对象)。
14、 hibernate载入 hibernate配置文件和持久化类的映射配置文件及得到SessionFactory对象 的工具类编写
hibernate的配置文件及持久化类的映射配置文件应仅仅被载入一次,并生成SessionFactory对象,所以载入这些配置文件应放在一个静态域中载入。
此工具类因以HibernateUtils命名
1. 此工具类的编写演示样例:
package cn.utils;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
//此变量的訪问权限为protected,表示继承了此类也能够获取到sessionFactory对象
//此对象是一个静态的表示会永久驻留在内存中,而且此对象是不可改动的
protected final static SessionFactory sessionFactory;
//hibernate的配置文件和持久化类的映射配置文件。仅仅须要被载入一次,并生成SessionFactory对象
//把载入配置文件的方法写在一个静态域中,表示仅仅被载入一次
static{
//获取到载入对象
Configuration configuration = new Configuration();
//载入配置文件
configuration.configure();
//获取到sessionFactory数据库对象
sessionFactory = configuration.buildSessionFactory();
}
//获取此sessionFactory对象的方法(此方法能够依据自己的须要 写与不写)
public static SessionFactory getSessionFactory(){
return HibernateUtils.sessionFactory;
}
}
七、 以面向对象的方式对数据库中的数据进行简单的CRUD(增、删、改、查)操作及一个 主键唯一 的 代码演示样例
1、 持久化类
1. 包名:
cn.domain;
9. 类名:
User
10. 代码:
package cn.domain;
import java.io.Serializable;
//持久化类(持久化类必须是一个javaBean)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer uid;
private String username;
private String password;
public User(){}
public User(String username, String password){
this.username = username;
this.password = password;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
15、 持久化类和表的映射关系配置文件
1. 存放的包:
cn.domain;
11. 文件名称:
User.hbm.xml
12. 关系映射配置文件代码:
<?
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: 持久化类的全名
table: 表的名称(假设表名和类名一致。就能够不写,由于其默认值和持久化类名一样)
catalog: 数据库的名称 一般不写(由于能够从数据库配置文件(hibernate.cfg.xml)的数据库url中获取到此数据库名称) -->
<class name="cn.domain.User">
<!--id 标签表示描写叙述数据库表的主键
属性:
name: java持久化类中的主键属性的名称
column: 数据库表中主键字段的名称(能够不用写此属性,默认值和name的值一致)
type:java类中的主键的类型(完整名称) 此属性的取值另一种:(不推荐此种写法。效率低)
假设是java.lang.Integer类型 直接写:integer(首字母小写)
java.lang.String类型 直接写:string
java.lang.Long类型 直接写:long (依次类推) 这样的写法hibernate也能识别
length:表中此主键字段类型的长度(假设不写将是最大值)
-->
<id name="uid" column="uid" length="10" type="java.lang.Integer">
<!-- generator标签表示此主键生产器
告诉hibernate容器用什么样的方式产生此主键,这里是increment:自增长的方式
-->
<generator class="increment"></generator>
</id>
<!-- property标签用于描写叙述一个表中普通的字段和类中的属性的关系
属性:
name:java持久化类中属性的名称,
column:数据库表中字段的名称(能够不用写此属性,默认值和name的值一致)
type:java持久化类中属性的类型(完整名称)
length:表中此字段类型的长度(假设不写将是最大值)
-->
<property name="username" column="username" length="20" type="java.lang.String"></property>
<!-- property标签用于描写叙述一个表中普通的字段和类中的属性的关系
属性:
name:java持久化类中属性的名称,
column:数据库表中字段的名称(能够不用写此属性。默认值和name的值一致)
type:java持久化类中属性的类型(完整名称)
length:表中此字段类型的长度(假设不写将是最大值)
-->
<property name="password" column="password" length="10" type="java.lang.String" ></property>
</class>
</hibernate-mapping>
16、 hibernate数据库配置文件
1. 存放的包:
在根文件夹, WEB-INF/classes(相应的开发环境文件夹为src/)
13. 文件名称:
hibernate.cfg.xml
14. 数据库配置文件里的代码:
<?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标签表示配置一个数据库 -->
<session-factory>
<!-- property标签表示一个数据项-->
<!-- 假设此标签的name属性为:hibernate.connection.username。就表示要设置连接数据库的用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 假设此标签的name属性为:hibernate.connection.password,就表示要设置连接数据库的密码 -->
<property name="hibernate.connection.password">123</property>
<!-- 假设此标签的name属性为:hibernate.connection.url,就表示要设置数据库的url地址(包含数据名) -->
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/db_myhibernate_20140808</property>
<!-- hibernate.dialect:表示设置方言
也就是告诉hibernate连接的是哪个数据库(这里是MySQL数据库)
此选项能够不用填写,由于hibernate会自己主动通过数据库url识别
但假设不设置,在有些环境下会出错
注意:假设要设置此值,MySQL数据库的取值范围有两个:
org.hibernate.dialect.MySQLInnoDBDialect
org.hibernate.dialect.MySQLDialect(推荐使用)
假设发现无法自己主动创建表,因更换參数后再试-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--
hibernate.hbm2ddl.auto:表示依据持久化类和映射文件生成表(当启动hibernate容器的时候,hibernate对表的处理情况)
取值:
validate 表示仅仅验证表的结构是否正确,但不会创建表(默认值)
create-drop 表示当启动hibernate时创建表。当hibernate销毁时就删除表(一般不用)
create 表示每当启动hibernate时就创建此表,假设有原表。有可能会覆盖掉原表中的数据(一般不用)
update 表示当启动hibernate时就检查数据库中是否有此表。假设没有就创建表,假设有就仅仅是验证表的结构(推荐使用)
注意:这里仅仅是自己主动创建表,数据库不会自己主动创建。
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- hibernate.connection.driver_class:表示配置数据库的驱动地址 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 在控制台打印hibernate自己主动生成的sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- mapping标签表示:描写叙述持久化类的配置文件(及载入) -->
<mapping resource="cn/domain/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
17、 hibernate配置文件及持久化类映射关系配置文件的载入工具类
1. 包名:
cn.utils;
15. 类名:
HibernateUtils
16. 说明:
hibernate配置文件及持久化类映射关系配置文件 仅仅须要载入一次 并会生成SessionFactory对象。此对象会伴随此应用一直驻留在内存中(所以在工具类中的修饰符是:final static)
假设要用sessionFactory对象,仅仅需调用此工具类的getSessionFactory()方法或继承此工具类
17. 代码:
package cn.utils;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
//Hibernate配置文件载入及sessionFactory对象获取 的工具类
public class HibernateUtils {
//此变量的訪问权限为protected,表示继承了此类也能够获取到sessionFactory对象
//此对象是一个静态的表示会永久驻留在内存中,而且此对象是不可改动的
protected final static SessionFactory sessionFactory;
//hibernate的配置文件和持久化类的映射配置文件,仅仅须要被载入一次,并生成SessionFactory对象
//所以把载入配置文件的方法写在一个静态域中,表示仅仅被载入一次
static{
//获取到载入对象
Configuration configuration = new Configuration();
//载入配置文件
configuration.configure();
//获取到sessionFactory数据库对象
sessionFactory = configuration.buildSessionFactory();
}
//获取此sessionFactory对象的方法(此方法能够依据自己的须要 写与不写)
public static SessionFactory getSessionFactory(){
return HibernateUtils.sessionFactory;
}
}
18、 单元測试类(对表中数据简单的CRUD:增删改查)
1. 包名:
cn.test;
18. 类名:
TestUserCRUD
19. CRUD代码:
package cn.test;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.domain.User;
import cn.utils.HibernateUtils;
//測试类因继承了HibernateUtils工具类所以能够直接使用sessionFactory对象
public class TestUserCRUD extends HibernateUtils{
//通过Session对象 以面向对象的方式对数据库中数据的增删改查操作
//注意:增删改查 时,假设传入一个对象操作,那么这个对象必须是一个持久化类的演示样例对象
@Test//添加用户
public void addUser(){
//得到连接
Session session = sessionFactory.openSession();
//事务开启事务,并得到事务对象
Transaction transaction = session.beginTransaction();
//创建一个用户
User newUser = new User("王五","789");
//将此用户保存到表中
session.save(newUser);
//提交事务
transaction.commit();
//关闭连接
session.close();
}
@Test//改动用户
public void updateUser(){
//得到连接
Session session = sessionFactory.openSession();
//事务开启事务,并得到事务对象
Transaction transaction = session.beginTransaction();
//方式一:(推荐使用)
//通过主键,先查询到要改动的用户对象
User updateUser = (User)session.get(User.class, 1);
//改动此对象要改动的值(注意 主键不能改动)
updateUser.setPassword("abc");
//将此用户传入update方法,改动此用户(注意。是通过此用户的主键进行改动的)
session.update(updateUser);
/*
//方式二:(不推荐使用)
//创建一个用户(注意这里必须将此用户的属性值填满,不然没填满的字段,改动时将给表中的此字符赋值为null)
User updateUser1 = new User("aaa","abc");
//并比此用户的主键设置为数据库中要改动的用户的主键
updateUser1.setUid(1);
session.update(updateUser1);
*/
//提交事务
transaction.commit();
//关闭连接
session.close();
}
@Test//删除用户
public void deleteUser(){
//得到连接
Session session = sessionFactory.openSession();
//事务开启事务,并得到事务对象
Transaction transaction = session.beginTransaction();
//方式一:
//通过主键来得到要删除的用户
User deleteUser = (User)session.get(User.class, 3);
//将此对象传入delete方法中(这里的删除是通过此对象的id进行删除的)进行删除
session.delete(deleteUser);
//方式二:
//创建一个用户对象(此对象中除了主键以外的其它属性能够不填)
User deleteUser1 = new User();
//设置此用户的主键(必须设置主键,由于hibernate就是通过主键来删除用户的)
deleteUser.setUid(2);
//将此用户传入delete方法进行删除(通过此对象的主键进行删除的)
session.delete(deleteUser1);
//提交事务
transaction.commit();
//关闭连接
session.close();
}
@Test//查询用户
public void findUser(){
//得到连接(由于是查询。所以不须要开启事务)
Session session = sessionFactory.openSession();
//方式一:查询一个用户(通过主键)
User user = (User)session.get(User.class, 1);
System.out.println("uid:"+user.getUid()+",username:"+user.getUsername()+",password"+user.getPassword());
//方式二:批量查询(通过hql语句),这里表示查询全部的User,并得到一个Query对象
Query q = session.createQuery("from User");
//通过Query对象的list方法将查询的全部User对象 以一个List集合返回
List<User> userList = q.list();
//遍历这个list集合
Iterator<User> it = userList.iterator();
while(it.hasNext()){
User u = it.next();
System.out.println("uid:"+u.getUid()+",username:"+u.getUsername()+",password"+u.getPassword());
}
//关闭连接
session.close();
}
@Test//唯一的主键
public void uniquePrimaryKey(){
//得到连接
Session session = sessionFactory.openSession();
//事务开启事务,并得到事务对象
Transaction transaction = session.beginTransaction();
//得到主键为4的用户
User user = (User)session.get(User.class, 4);
//创一个新的用户
User user1 = new User();
//并设置其主键也为4
user1.setUid(4);
//当再进行改动是会报错,由于经过hibernate处理的同一个持久化类的实例对象出现了两个对象主键同一时候为4
session.update(user1);
/*
*那么hibernate是怎么检測到的有两个对象的主键都为4呢 (下面结果为猜的,也能够參考:持久化对象的状态)。
* 当用session的get()方法查询这个用户时,hibernate就记录了这个对象的引用地址(堆中的地址),及主键 为4。
* 并以此主键为key 对象引用地址为value的形式存储在一个Map集合中(此Map专用于存储User持久化类的实例对象的 主键及对象的引用地址)
*
* 当用session.update()方法进行改动时。hibernate发现此对象的主键为4,便到此持久化对象相应的Map集合中去取出主键为4的对象的引用地址,
* 但发现和此对象的引用地址不一致(也就证明不是同一个对象),也间接的说明了有一个以上的对象引用了一个主键(主键不唯一了),那么就会抛出此异常
* org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [cn.domain.User#4]
* 所以经过hibernate处理的同一个持久化类的全部实例对象,的主键必须唯一
*/
//提交事务
transaction.commit();
//关闭连接
session.close();
}
}
八、 用MyEclipse工具通过数据中的表,自己主动生成:持久化类、类与表的映射配置文件、hibernate配置文件
以上都是通过载入hibernate配置文件时会在数据库中自己主动创建表(假设在hibernate.cfg.xml配置文件里配置了的话),但在实际开发中都是先创建数据库中的表,所以MyEclipse工具提供了一种依据表来自己主动生成:持久化类和映射配置文件及hibernate的配置文件。前提是此project必须是一个hibernateproject,而且要创建好相应的包。
1、 在MyEclipse Hibernate perspective页面导入数据库
1. 第一步:
2. 第二步:
进入MyEclipse Hibernate perspective界面后 点击树形菜单条-->右键 New
3. 第三步:
那么数据库就导入好了
19、 将当前的project加入为hibernateproject。自己主动生成hibernate.cfg.xml配置文件
选中当前project-->点击MyEclipse--> Project Facets--> Install Hibernate Facet然后会弹出提示框
1. 第一步:
2. 第二步:
3. 第三步:
4. 第四步:
那么此project就是一个hibernateproject了,而且已经建好了hibernate.cfg.xml配置文件
20、 通过表自己主动生成持久化类,和持久化类的映射配置文件
1. 第一步:
2. 第二步:
3. 第三步:
4. 第四步:
那么持久化类和持久化类映射配置文件就自己主动生成好了
九、 主键生成器
主键生成器在持久化类映射配置文件的 id标签中用<generator class="主键生成器"></generator>标签进行设置
主键生成器仅仅有在加入记录时才会被启动
1、 increment主键生成器
这样的方式。hibernate会自己主动从当前数据库中查询出最大的主键。然后再在此主键上加1。设置成下一条记录的主键
此种方式是在数据库中查询出最大的主键然后加1。然后hiberante会将此主键保存在一个静态的全局变量中,下一次还须要向此表中添加数据时,仅仅须要从此静态变量中获取,并将此静态变量的值加1就可以,从而就不须要每次都到数据库中进行获取最大的主键了。
所以此中方式的主键必须是数字,而且效率较低,由于多了一条查询语句。
21、 identity主键生成器
这样的方式是:数据库中自己主动对此表的主键进行生成
但此表必须支持主键自己主动生成的功能(MySQL支持。但Oracle中的表不支持自己主动生成主键),而且还设置了此功能(就是MySQL中的主键自增长)
使用此种主键生成器。当调用save(保存操作)方法时。一定会被运行成功。无论是否使用evict或clear方法。详情请參照下面 save方法
此种方式效率高
22、 assigned主键生成器(非常少用)
此方式是程序猿自己编写方法生成主键,然后手动将此主键赋值给持久化对象,进行操作
23、 uuid
此种方式,hibernate会自己主动生成UUID字符串的方式作为此持久化对象的主键
此种方式主键必须为字符串类型
24、 sequence
此方式是依据Oracle数据库中序列来生成主键
十、 持久化对象的状态
在hibernate中将一个持久化对象分行三个状态,暂时状态、持久化状态、脱管状态,来对对象进行处理。
事实上另一个保存状态,通过save方法保存的对象的状态就是保存状态。假设此对象没有主键,hibernate会依据主键生成机制。生成此对象的主键,并将此保存到session的一级缓存中,并将此对象的状态改为保存状态,当在运行事务提交时hibernate会检查session对象的缓存中的全部对象,假设为保存状态就运行insert语句。
在hibernate中一个持久化对象的状态是针对某一个Session对象而言的。比方说,如今有两个Session对象。当一个持久化对象通过第一个Session对象进行了save()操作,那么这个对象在第一个Session对象中的状态就是持久化状态。而此对象在第二个Session对象中的状态为暂时状态。
也能够这么说:仅仅要一个持久化对象被存储在了Session对象中而且还没有运行。那么此对象的状态就是: 持久化状态或删除状态或脱管状态。仅仅要某一个持久化对象没有被保存到Session对象中。那么此对象的状态就是:暂时状态
持久化对象的状态转换图
1、 暂时状态
当一个持久化对象手动被创建出来时,那么它的状态就为暂时状态(事实上就是一个新建的持久化对象还没有经过hibernate内存时)。
Session对象没有保存此持久化对象。那么此对象的状态就是暂时状态。
25、 持久化状态
就是当一个持久化对象经过了hibernate(增删改查时),那么此对象的状态就是持久化状态,假设是通过hibernate查询出来的对象,那么此对象的状态也是持久化状态。
当一个对象变为持久化状态时,hibernate的快照机制,会创建出此对象的快照(相当于保存了此对象的全部信息(包含此对象的引用地址)),
当在提交事务时,hibernate会检查全部的持久化状态的对象,将此持久化状态的对象和其開始创建的快照进行对照。假设此对象发生了改动。那么hibernate会自己主动生成update语句。自己主动更新表中的相应的此条记录,而不用手动进行update操作;
也间接性的说明了对象的持久化状态,仅仅有在开启手动提交事务的情况下才会有此状态(也就是说仅仅有开起了手动提交事务,那么更新了持久化状态的对象,才会在提交事务时自己主动更新表中相应的此条记录);
一个持久化类的随意对象,不同意有两个或以上ID一致的持久化对象,的状态变成持久化状态(也就是说,同一个持久化类的持久化对象,不同意有两个或以上主键一致的持久化对象,的状态变为持久化状态);
持久化状态的对象都会被保存到Session对象中(Session对象引用了持久化状态对象的存储地址)。
26、 删除状态
当一个持久化对象运行了Session对象的delete()方法,那么这个持久化对象在Session对象中就变成了删除状态了。
此持久化对象在Session对象中变为删除状态前,Session对象会检索此对象是否有主键。假设没有主键,将会抛出异常。
删除状态的对象也都会被保存到Session对象中(Session对象引用了持久化状态对象的存储地址)。
假设一个对象在session对象中的状态是删除状态,那么再用session对象的get方法或load方法获取此对象,无论是否提交事务,那么将获取不到此session对象(通过主键来对照的)。
代码演示样例:
@Test
public void testDelete(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//获取到这个学生。
Student stu = (Student)session.get(Student.class, 1);
//将session对象中的一级缓存中的全部对象清除掉
session.clear();
//将此学生增加到session的缓存中,并将其状态该为删除状态。
session.delete(stu);
//再从数据库中查找此对象(这里将查找不到此stu对象,返回值为null)
Student stu1 = (Student)session.get(Student.class, 1);
/*这也说明了当去查找一个对象时,先会到此session对象中的一级缓存中去查找(通过此主键),
假设这个对象在session对象的一级缓存中存在,而且是删除状态。就直接返回null,而不会去数据库中查询此对象了
*/
transaction.commit();
}
27、 脱管状态
当事务提交后,那么此对象就属于脱管状态了,那么再改动此对象的值,就不会被更新到相应表中的相应记录上面去了,
当调用Session对象的evict(Object)方法,将这个对象在Session中的状态改为脱管状态。当调用Session对象的clear()方法时。是将此Session中的全部持久化对象的状态都改为脱管状态。
而且Session对象的close方法也会将这个Session对象中的全部持久化对象的状态改为脱管状态,
28、 快照机制
当一个持久化对象。的状态变为了持久化状态,那么同一时候,hibernate也会生成此对象的快照(将相当于克隆了一个此对象,并保存了此被克隆对象的引用地址),当运行提交事务时,hibernate就检查全部的持久化状态的对象。并将将此对象和此对象的快照进行对照。假设是主键对照不上。将抛异常(由于主键不能被改动),假设其它字段对照不上,就说明了此对象被改动过,那么hibernate就是自己主动生成update语句,将更新此对象相应表中相应的记录(依据主键查找的此记录)。假设此对象和此对象相应的快照一致,就不会发出update语句;
20. 快照机制演示样例:
package cn.test;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.domain.User;
import cn.utils.HibernateUtils;
public class UserTest extends HibernateUtils{
@Test//持久化对象的状态測试
public void statusTest(){
//打开连接
Session session = sessionFactory.openSession();
//開始手动提交事务
Transaction transaction = session.beginTransaction();
//依据主键查询出此User对象,由于是查询出来的对象(经过了hibernate),那么此对象的状态就是持久化状态
//并在hibernate内部生成了此对象的快照
User user = (User)session.get(User.class, 1);
//改动此对象的password属性的值为“111111”
user.setPassword("111111");
//当提交事务时。hibernate会检查状态为持久化状态的全部对象。并将这个对象和其相应的快照进行对照
//这里把user对象和其快照进行对照,那么肯定不一致。hibernate就会自己主动生成update语句,将此对象传入进行更新
//提交事务
transaction.commit();
//关闭连接
session.close();
}
}
/*
* 结果:User表中主键为1的记录,的password字段的值被改动为了“111111”
*/
十一、 Session对象
Session对象在hibernate中代表一个连接。比方JDBC中的Commection对象,但Session对象中封装了Commection连接对象,而且还添加了一些hibernate特有的功能。
Session对象是通过SessionFactory对象的openSession方法得到的。
1、 Session对象的方法介绍
1. save方法
此方法接收的是一个持久化对象,调用此方法时但会检查此对象的主键,假设映射配置文件里 主键的生成器是identity(数据库自己生成主键)。那么此方法会直接生成insert sql语句,并将此sql语句保存到事务对象的sql语句缓存区中。然后在数据库中查询出下一个主键的值,设置到此持久化对象中(以保证此对象在事务提交时不会自己主动生成inser语句了),再将此对象保存到Session对象中,并生成快照。
1- 代码演示样例:
@Test//
public void test1(){
//生成ID的方式为identity
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
User user = new User("aaaaa","bbbbb");
//保存此用户时就已经生成了insert sql语句保存在了transaction事务对象的sql缓存区
session.save(user);//
//此处尽管在session对象中移除了全部的持久化对象,包含user,但在transaction事务对象中的insert sql语句没有删除
session.clear();
//所以在运行事务提交时还是会保存user对象
transaction.commit();
session.close();
}
假设是其它ID生成器。那么save方法会将删除此对象的主键(假设有主键的话),然后再将此对象保存到Session对象中(对象的状态变为持久化状态)。
21. update方法
此方法接收一个持久化对象,当调用此方法时,此方法会检查传入的持久化对象。假设没有主键,就抛出异常。
假设此持久化对象有主键。那么就将此持久化对象保存到session对象中(对象的状态变为持久化状态),并生成一个快照对象,注意这里的快照并非此持久化对象的快照,这样就保证了快照一定和此对象不一致。运行commit方法时也就一定会通过此对象来生成update sql语句(详情请參考下面 事务对象的事务提交操作)
22. clear方法
运行clear方法就是将此session对象中的全部持久化对象移除(也就是将session对象中的全部持久化对象的状态变为暂时状态),提交commit方法时。因为commit方法无法从session对象中获取到持久化对象。所以也就无法对持久化对象进行操作。
1- 演示样例代码:
@Test//測试clear方法
public void testClear(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//创建一个新的User对象
User user = new User("1111","2222");
//将此对象加入到Session对象中。并会清除此对象的主键(假设有主键的话)
session.save(user);
//移除Session对象中的全部持久化对象
session.clear();
//由于clear将session对象中的持久化对象移除了,commit方法就从session对象中获取不到持久化对象了。
也就不会user对象进行操作了
transaction.commit();
/*
* 结果是user对象无法被加入到表中(不会抛异常)
*/
session.close();
}
23. evict方法
此方法接收的是一个session对象中存在的持久化对象,当调用evict方法时将会从session对象中移除此对象,让移除此对象后commit就无法从session对象中获取到此持久化对象。也就无法对此对象进行操作了
1- 代码演示样例:
@Test//測试evict方法
public void testEvict(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//创建两个对象
User user1 = new User("111111","111111111");
User user2 = new User("2222222","22222222");
//将这两个对象加入到session对象中
session.save(user1);
session.save(user2);
//运行evict方法。仅仅将user1从session对象中移除
session.evict(user1);
//当运行commit方法时。commit方法从session中获取的全部持久化对象中没有user1对象。所以user1不会被加入到表中
//详情參考:事务对象的事务提交操作
transaction.commit();
session.close();
/*
* 结果:
* user1不会被保存
* user2对象会被保存成功
*/
}
24. get方法
此方法接收两个參数。第一个是持久化类的字节码对象,第二个參数是主键
此方法相当于用主键查询出表中的某一条记录。
25. createQuery方法
此方法接收一个hql表达式,能够查询出表中的多条记录,并会转换成持久化对象。并返回一个Query结果集对象。能够调用此Query对象的list方法获取到查询出来的多个对象;
26. load方法
此方法接收两个參数,第一个參数是持久化类的字节码对象。第二个參数是主键。
此方法相当于用主键查询出表中的某条记录.
但此方法查询出来的对象是懒载入模式。
27. refresh方法
此方法接收一个持久化对象, 此方法将会到数据库中又一次查询出传入的持久化对象,并覆盖掉原有的持久化对象。
以达到从数据库更新内存中的持久化对象的目的。
28. flush方法
此方法是将一级缓存中的持久化对象以对应的sql语句更新到数据库中(此方法不会提交事务,也不会清除一级缓存中的持久化对象,仅仅是将对应的sql语句提交给了数据库)。
当调用session.flush()方法时所做的事情。
1. 获取session对象中的全部持久化对象。
2. 检查全部持久化对象的状态。假设为保存状态(通过save方法增加的)或删除状态(通过delete方法增加的),就直接依据其快照 产生insert或delete sql语句,提交给数据库。并将这些对象的状态改为持久化状态。
3. 检查全部的持久化对象的状态,假设为持久化状态。那么就和其快照进行对照,假设和其快照不一致,就直接依据这个对象生成update sql语句。提交给数据库。(并不会提交事务)
4. 在检查以上2、3步的同一时候会检查此持久化对象的关联对象(就是有关系的对象(一对多,多对多等)),是否有级联操作,假设有就级联操作此关联的对象。还会检查是否对此关联对象运行关系操作。假设有就维护此对象与此关联对象的关系。
29、 Session对象操作持久化对象的原理(仅仅包含:增、删、改)
当一个持久化对象被加入到Session对象中时,那么这个持久化对象的状态就变成了持久化状态。所以Session对象决定了持久化对象的状态。
1. Transaction事务对象
在hibernate中运行增删改操作必须开启事务,不然无法提交成功。
在hibernate中的事务对象仅仅归属于某一个Session对象(仅仅能和一个Session对象绑定)。
1- 事务对象的sql语句缓存区
每个Transaction事务对象中都有一个sql缓存区。这个缓冲区仅仅缓存增、删、改操作的sql语句。当提交事务时Transaction事务提交对象会将此缓存中的全部sql语句遍历出来运行。
错误纠正:事实上有可能没有这个sql缓存区。而是直接将此sql语句提交给了数据库了,仅仅是没有提交事务而已。
也有可能有sql缓存区,用的是JDBC的批处理的sql缓存区。(眼下还不确定)
3- 事务对象的事务提交操作
当调用了Transaction事务对象的commit()方法(事务提交)时,hibernate不会立即提交事务,而是运行下面操作后再提交事务。
(1) 对此Session对象中全部持久化状态对象的处理
此事务对象会检索此与之相应的Session对象中的全部持久化状态的对象,检索此对象主要检索两个方面。
第一个方面:
检索此对象的主键是否有值,假设没有值,就自己主动生成此对象的insert sql语句(添加操作),并把此sql语句保存到Transaction事务对象的sql语句缓存区,这里就不会检索此对的第二个方面了.
假设此对象的主键有值,就无论。直接检索此对象的第二个方面。
第二个方面:(仅仅有当此对象的主键有值时才会检索此对象的此方面)
检索此对象和此对象的快照是否一致,假设假设不一致。那么就依据此对象的主键自己主动生成update sql语句(改动操作),并将此语句保存到Transaction事务对象中的sql语句缓存区。
假设一致就不做不论什么操作
(2) 对此Session对象中全部删除状态的处理
将Session对象中的全部删除状态的对象,对比此类的映射配置文件,将此持久化对象相应的表和此对象的主键遍历出来。
并生成delete sql语句(删除操作),并将此语句保存到Transaction事务对象的sql语句缓存区。
运行了以上操作后,hibernate才会真正的提交事务。
hibernate会将此事务对象中的sql语句缓存区中的全部sql语句遍历出来并运行。
事务提交并不会移除session对象中的持久化对象,但会更新此对象的快照对象。
(一) 代码演示样例:
@Test//commit方法对session对象中的持久化对象的快照进行更新的測试
public void test2(){
//得到一个session连接对象
Session session = sessionFactory.openSession();
//开启手动提交事务(第一次的事务)。事务对象为:transaction
Transaction transaction = session.beginTransaction();
//获取一个持久化对象
User user = (User)session.get(User.class, 1);
user.setUsername("abc");
//运行commit事务提交,会在表中更新此user对象的数据,由于user和其快照不一致了
transaction.commit();
//开启还有一个事务
Transaction transaction1 = session.beginTransaction();
//此处不会生成sql语句,也就是不会有什么操作,也就是说明了,user对象在上次提交事务时的快照进行了更新,
//所以此处的commit将user对象和其快照对照时是一致的。所以就不会发生update操作
transaction1.commit();
session.close();
}
4- 一个session对象多次提交事务
一个session对象能够多次开启事务。但必须提交事务后才干再次开启。每提交一次事务。那么都会检查session对象中的持久化对象是否和其快照一致,假设不一致也会自己主动运行update方法
(1) 代码演示样例:
@Test//session对象的多次提交事务測试
public void testCommit(){
//得到一个session连接对象
Session session = sessionFactory.openSession();
//开启手动提交事务(第一次的事务),事务对象为:transaction
Transaction transaction = session.beginTransaction();
//获取一个持久化对象
User user = (User)session.get(User.class, 1);
//运行commit事务提交,这里什么也不会发生。由于user对象和其快照是一致的
transaction.commit();
//开启还有一个事务
Transaction transaction1 = session.beginTransaction();
//更新user的username属性
user.setUsername("abc");
//此处会对user进行更新。也间接性的说明了commit方法不会移除session中的持久化对象
//这里也说明了仅仅要提交一次事务,那么都会对session中的全部持久化对象进行和其快照进行对照。假设不一致也会运行update方法
transaction1.commit();
session.close();
}
5- 回滚事务
直接调用transaction. rollback()方法就可以。
注意仅仅能回滚还没有提交的事务,提交了的事务部能被回滚。
30、 Session对象查询持久化对象的原理(仅仅包含:查询)
当用Session对象进行查询时,查询出来的对象的状态就是持久化状态。并且查询是没有事务的,当运行一个查询操作时hibernate会立即生成sql语句并立即从数据库中查询出此数据,而不会等到事务提交以后再查询。
31、 持久化对象的状态范围
持久化对象的状态是相对于某一个Session对象而言的。也就是说 此对象在一个Session中是某一个状态,而在还有一个Session对象中可能又是还有一种状态。
1. 代码演示样例:
@Test//持久化对象的状态范围演示样例
public void testSession(){
Session session1 = sessionFactory.openSession();
Transaction transaction1 = session1.beginTransaction();
//获取一个user对象,那么这个持久化对象保存在了在session1这个对象中。并生成了此对象的快照
//也就是说。user对象在session对象中的状态是持久化状态
User user = (User)session1.get(User.class, 1);
//改动此对象的username
user.setUsername("aa");
//transaction1事务对象提交,会改动user对象在表中相应的记录信息
transaction1.commit();
//关闭此连接
session1.close();
Session session2 = sessionFactory.openSession();
Transaction transaction2 = session2.beginTransaction();
//再次改动user中的username
user.setUsername("bbbbbbbbbbb");
//应为transaction2事务提交对象是session2对象中获取的。那么此事务对象仅仅会检查session2中的持久化对象并操作
//但这里transaction2事务对象没有获取到user这个持久化对象。所以这里不会在表中更新此user对象中的数据
transaction2.commit();
session2.close();
}
十二、 session对象的加强
1、 session对象的产生方式(或參考hibernate10_thread_sessionproject)
session的产生方式有两种,第一种是通过sessionFactory.openSession()此方式来获得session对象,还有一种方式是通过sessionFactory.getCurrentSession()方法获取到此session对象。
1. 第一种方式:(通过openSession()方法获取session对象)
此方式是通过sessionFactory对象的openSession();方法获取到此session对象的。
此中方式获取到的session对象的生命周期是:当调用sessionFactory.openSession对象时产生,当调用session.close()方法时销毁。
通过此方式获取到的session对象都是一个新的session连接对象,也相当于是一个独立的session连接对象。但有时候无法满足业务需求,比方下面业务需求图:
所以在以上图的业务需求中不适合用单独的session来做此业务,所以hibernate就提供了另外一种方式获取session对象。
29. 另外一种方式:(通过getCurrentSession()方法获取session对象)
此方式是通过sessionFactory对象的getCurrentSession();方法获取到此session对象的。
此中方式获取到的session的生命周期,当创建线程时创建此session对象,当此线程结束时session对象销毁。
1- 获取此对象的原理
此种方式获取到的session对象是当前线程范围内的session对象。当一个用户訪问时(开启一个独立的线程)。hibernate会创建一个用于存储session对象的ThreadLocal对象(线程范围内的共享变量)。
当用getCurrentSession();方法获取此session对象时。此方法会去当前线程中的ThreadLocal对象中去取此session对象,假设返回为null,就新创建一个session对象。存入ThreadLocal对象中,并返回此session对象。假设不为null就直接返回此session对象。
6- 使用此对象的注意事项
也就是说getCurrentSession()方法获取到的session对象是当前线程中唯一的session对象。
在使用此对象时必须开启事务(包含增删改查),不然会抛异常,而且在使用完毕后仅仅须要用transaction事务对象提交事务就可以,而不须要关闭session对象,session对象在提交事务时会自己主动关闭(hibernate自己主动完毕的)。
所以能够这样理解,此session对象和事务绑定在一起了。
假设要用此对象。必须在hibernate.cfg.xml配置文件里配置上<property name="current_session_context_class">thread</property>,表示创建线程范围内的session对象
7- 用线程范围内的session对象来做“独立的session引发数据异常图”图中的样例:(或參考:hibernate09_thread_sessionproject)
//线程范围内的seesion对象測试
//必须在hibernate.cfg.xml配置文件里配置<property name="current_session_context_class">thread</property>
//表示创建线程范围内的session对象
public class SessionTest extends HibernateUtils{
//调用此方法,创建一个班级和一个学生。必须在同一个事务下完毕。
@Test
public void SessionTest(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
try{
//创建一个班级
createClasses();
//创建一个学生
createStudent();
//int i = 1/0;假设在此处发生异常。以上班级和学生将会创建不成功。
//在此处打断点(仅仅有在运行完下面事务提交时,createClasses();createStudent(); 方法中的事务才会被提交)
transaction.commit();
/*
* 这样就保证了在提交事务前出了异常,就绝对不可能创建出班级和学生;
*/
}catch(Exception e){
//回滚事务
transaction.rollback();
System.out.println("创建班级和创建学生不成功。!");
}
}
//创建一个班级
public void createClasses(){
//获取线程范围内的session对象
Session session = sessionFactory.getCurrentSession();
//由于在SessionTest方法中已经开启了事务,此处开不开事务都一样
Classes cla1 = new Classes("13java1班");
session.save(cla1);
//但此处不能提交事务
}
//创建一个学生
public void createStudent(){
//获取线程范围内的session对象
Session session = sessionFactory.getCurrentSession();
Student stu1 = new Student("一一",20);
session.save(stu1);
//但此处不能提交事务
}
}
十三、 hibernate的关系操作
hiberante中的关系就是怎么将java中的面向对象转换成数据库中的一对一、一对多、多对多 关系 。
1、 注意事项重点:
比方一个班级有多个学生,当我们查询这个班级时,没用获取此班级的学生,或没有使用此班级的学生。那么Hibernate不会去查询此班级的全部学生。仅仅有当使用此班级中的学生时,Hibernate才会到数据库中查询出此班级的全部学生。
所以在优化时也因注意下面的操作;(此方式是用代理方式实现的)
注意事项:
比方查询一个班级。假设在session对象关闭前没有调用此班级中的全部学生,那么当session对象关闭后(session.close()),假设在调用此班级的全部学生会抛异常。
如:
@Test
public void fun(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//查询出一个课程对象
Course cou2 = (Course)session.get(Course.class, 2);
//cou2.getStudents().size();//假设没有此操作下面会抛异常,
//这里就相当于在数据库中查询出了此课程对象的全部学生
transaction.commit();
session.close();
//获取此课程对象的全部学生的个数。(假设没有以上:cou2.getStudents().size();语句 操作此处会抛异常。由于hibernate去查询此课程对象的全部学生时发现连接已被关闭)
Integer len = cou2.getStudents().size();
System.out.println("len=:"+len);
}
关系优化重点
一般多对多关系在操作时。应用少的一方操作多的一方法。而在一对多的双向关联中就没有此问题。但有还有一个问题,在少的一方操作。hibernate每次都会产生一个更新语句来更新多的一方的外键。所以在一对多操作时应以多的一方操作一的一方。
多对多的样例:
比方一个课程对象要加入一些学生(这些学生在数据库中以存在,而且已有其它的课程)。应该用这个课程去加入这些学生,而不是用这些学生去加入这个课程。
用这个课程去加入这些学生的特点:(少的一方去加入多的一方。推荐使用)
用这个课程时去加入全部的学生。
第一步:查询出此课程对象;
第二步:获取到这个课程的全部学生的集合;
第三步:将这些学生查询出来。
第四步:将这些学生加入到此课程的全部学生的集合中;
以上并没实用到这些学生的全部课程的集合。所以数据库就不会查询出全部被加入学生的全部课程对象;
仅仅是查询出了这个课程的全部学生。
(sql仅仅会产生一句)(而且代码要少)
用这些学生去加入这个课程的特点:(多的一方去加入少的一方:不推荐使用)
用这些学生去加入此课程
第一步:查询出此课程对象;
第二步:查询出这些学生
第三步:获取到这些学生的全部课程的集合。
第四步:将此课程加入到这些学生的这些集合中。
以上第三步会大大减少效率,由于会查询出这些学生每个学生的全部课程对象。(会添加非常多sql语句,效率会减少)(而且代码要多)
一对多的双向关联的样例:
比方一个班级要加入多个被新创建的学生
用这些学生去加入这个班级(多的一方去 加入 一的一方。推荐使用)
第一步:创建出这些学生
第二步:查询出这个班级
第三步:将这个班级加入到这些学生的班级引用属性中。
在生成sql语句时,由于多的一方的映射配置文件没有inverse属性(强制维护关系)所以,hibernate就不会去检查学生的映射配置文件了,直接保存学生时维护关系(设置此学生的外键) 。所以此中方式比下面方式少了一步,所以效率要高(尽管代码会多一些)
用这个班级去加入这些学生(一的一方 去加入 多的一方,不推荐使用)
第一步:创建出这些学生
第二步:查询出这个班级
第三步:将这些学生加入到这个班级的set集合中
而此种方式,hibernate在生成sql语句时检查班级对象。发现多了几个学生,就先在数据库中保存这些学生(没有设置外键),再查看班级映射配置文件里集合标签的inverse属性的值是否为:"false",假设为false,才更新这些学生的外键。
所以此处多了最后一步更新学生的外键操作。所以不推荐使用此种方式。
总结:从以上样例中能够看出。仅仅要是一对多的双向关系操作(单向操作任意)。就以多的一方操作效率更高。
仅仅要是多对多的关系操作,那么就以少(少就是数量少)的一方操作效率更高.
32、 一对多的单向关联
在一个持久化对象中用什么集合来存储还有一个持久化对象,那么就在持久化映射配置文件里加入一个什么样的标签来描写叙述此集合。
如:
在一个持久化类中用Set集合来引用了还有一个持久化类。那么就在持久化映射配置文件里加入一个set标签来描写叙述此集合,hiberante会将此集合转换为表中的关系。
如:
public class Classes implements Serializable {
private Integer cid;//主键
private String name;//班级名称
private Set<Student> students;//代表这个班级的学生
...下面是get和set方法;
}
操作还分为非常多中。比方将一个学生从此Set集合中移除,那么仅仅会解除此学生和此班级之间的关系(就是学生的外键被清空了),
将一个数据库中已有的学生加入到此Set集合中。那么这个学生的外键会被改动为当前的这个班级的主键。
假设要删除班级或学生仅仅能通过session对象的delete方法进行删除
单项关联就是在java对象中 一个对象能够关联到还有一个对象。但另个对象却关联不到如今的这个对象。
如在班级类中加入一个set集合,用于存储此班级全部的学生。
那么此班级这个持久化类的映射配置文件里就须要加入一个set标签来描写叙述此关系了
如:(假设是其它的集合就用对应的标签来描写叙述就可以)
<set name="students" cascade="all" inverse="false"><!-- 描写叙述此set集合标签 -->
<key><!-- 外键 -->
<!-- 描写叙述描写叙述引用了外键的字段,此字段会被加入到学生student表中 -->
<column name="cid"></column>
</key>
<!-- 描写叙述哪个表须要引用外键 -->
<one-to-many class="cn.domain.Student"/>
</set>
<!-- 以上能够看着:Student表中的cid外键字段引用了Classes表中的主键(cid)字段 -->
set标签
此set标签就是描写叙述此班级中的set集合。
属性
name: 就是描写叙述此班级中用于存储学生的set集合的属性名。
cascade: 此属性是级联的意思。当对班级表进行某一种操作时,假设涉及到了学生表中的数据。是否运行级联操作。
此属性的取值范围:
save-update: 当对班级进行保存和更新时,就就对此学生也运行保存和更新操作(假设须要保存的话。就是此学生和快照不一致的话)
delete: 当对班级进行删除时,同一时候删除此班级中的全部学生
all: 就是以上两种取值都包括。(推荐选此值)
inverse: 此属性的意思是 是否不维护关系。
(缺省值为false)(这里的关系就是学生表的外键的引用,这里是是否维护此外键)
取值范围:
true: 就是不维护班级和学生之间的关系(比方在加入一个班级时,仅仅会加入此班级和此班级中的全部学生,而不会给此学生的外键加入此班级主键的引用)
false: 意思是维护班级和学生之间的关系(默认值,推荐选此值);
key标签
此key标签是描写叙述学生表的外键的。
column标签
描写叙述学生表外键的列
属性
name:就是给学生表中的外键取一个名字
one-to-many标签(一对多)
描写叙述哪一个表须要此外键
属性
class: 就是须要外键的表相应的持久化类的完整类名
当配置好此配置文件后,假设hibernate的配置文件里设置了自己主动创建表,那么hibernate会依据此配置文件来创建出一个学生表的cid外键字段引用了班级表的主键字段 这种两张表
1. 练习(或參考hibernate04_relationproject)
//一对多的单项关联的练习
public class RelationTest2 extends HibernateUtils{
//3、新建一个班级的时候同一时候新建一个学生,但不建立关系
@Test
public void addStudentClass(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//第一种方式:
//Classes net1 = new Classes("13.net1班");
//Student stu1 = new Student("一一",21);
//
//session.save(net1);
//session.save(stu1);
//另外一种方式:将Classes.hbm.xml关系映射配置文件里的set标签中的inverse属性的值改为true
//表示不更新关系。
Classes net2 = new Classes("13.net2班");
Student stu2 = new Student("二二",22);
Set<Student> set = new HashSet<Student>();
set.add(stu2);
net2.setStudents(set);//Classes.hbm.xml配置文件里set表的cascade属性的值必须是save-update或all
session.save(net2);
transaction.commit();
session.close();
}
//4、已经存在一个班级,新建一个学生,建立学生与班级之间的关系
//须要将Classes.hbm.xml关系映射配置文件里的set标签中的inverse属性的值改为false
//表示更新关系
@Test
public void addStudentRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes net1 = (Classes)session.get(Classes.class, 3);
Student stu3 = new Student("三三",23);
//这里会在stu3的外键上引用net1的主键
net1.getStudents().add(stu3);//Classes.hbm.xml配置文件里set表的cascade属性的值必须是save-update或all
//这里会自己主动更新net1的数据。由于net1的状态是持久化状态
transaction.commit();
session.close();
}
// 5、已经存在一个学生,新建一个班级,把学生增加到该班级
@Test
public void addClassesStudentRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student stu5 = (Student)session.get(Student.class, 5);
//新建立一个班级
Classes java3 = new Classes("13java3班");
Set<Student> set = new HashSet<Student>();
//将这个查询出来的学生加入到此班级中
set.add(stu5);
java3.setStudents(set);
//将此班级加入到Session对象中
session.save(java3);
transaction.commit();
session.close();
}
// 6、把一个学生从一个班级转移到还有一个班级
@Test
public void updateStudentClass(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到要加入的班级
Classes java1 = (Classes)session.get(Classes.class, 1);
//获取到要转移的学生
Student stu2 = (Student)session.get(Student.class, 2);
//将此学生加入至此班级就可以
java1.getStudents().add(stu2);
transaction.commit();
session.close();
}
// 7、解除一个班级和一个学生之间的关系。
//这里仅仅是移除关系(就是将学生的外键删除)。不会删除学生
@Test
public void removeClassesStudentRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到这个班级
Classes net1 = (Classes)session.get(Classes.class, 3);
//获取到这个学生
Student stu6 = (Student)session.get(Student.class, 6);
//将这个学生从这个班级中删除(注意这里仅仅会将stu6学生中的外键删除,不会删除stu6这个学生)
net1.getStudents().remove(stu6);
//由于net1是这个对象的状态是持久化状态。所以这里不须要运行update语句
transaction.commit();
session.close();
}
//8、解除一个班级和一些学生之间的关系
//解除主键为4的班级与 主键为4、5、6的学生的关系
@Test
public void removeClassesStudentRelation2(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到主键为4的班级
Classes cla4 = (Classes)session.get(Classes.class, 4);
//得到此班级中的全部学生
Set<Student> students = cla4.getStudents();
//遍历这些学生
Iterator<Student> it = students.iterator();
while(it.hasNext()){
Student stu = it.next();
if(stu.getSid() >= 4 && stu.getSid() <= 6){
//假设符合条件就删除
it.remove();
}
}
//应为cla4对象的状态是持久化状态,所以这里不须要写update语句
transaction.commit();
session.close();
}
// 9、解除该班级和全部的学生之间的关系
//注意这里仅仅是解除关系(将学生的外键清空),不是删除学生
@Test
public void removeClassesAllStudentRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到这个班级
Classes cla4 = (Classes)session.get(Classes.class, 4);
//将这个班级中的学生清空
cla4.setStudents(null);
transaction.commit();
session.close();
}
//11、已经存在一个班级,已经存在多个学生,建立多个学生与班级之间的关系
@Test
public void classesAddStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//查询出此班级
Classes cla4 = (Classes)session.get(Classes.class, 4);
//查询出这些学生
Student stu3 = (Student)session.get(Student.class, 3);
Student stu4 = (Student)session.get(Student.class, 4);
Student stu5 = (Student)session.get(Student.class, 5);
Student stu6 = (Student)session.get(Student.class, 6);
//将这些学生加入到此班级中
Set<Student> students = cla4.getStudents();
students.add(stu3);
students.add(stu4);
students.add(stu5);
students.add(stu6);
transaction.commit();
session.close();
}
//12、删除学生(这里是将此学生从此数据库中删除)
@Test
public void removeStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//第一中方式
////查询出此学生
//Student stu6 = (Student)session.get(Student.class, 6);
////将此学生从此数据库中删除
//session.delete(stu6);
//另外一种方式
//新建一个学生
Student stu6 = new Student();
//设置其主键为要删除学生的主键
stu6.setSid(6);
//直接删除此学生(将此学生加入到session对象中,并将其状态改为删除状态)
session.delete(stu6);
transaction.commit();
session.close();
}
/*13、删除班级(从数据库中删除,不是移除关系)
* 1删除班级的时候同一时候删除学生(须要将Classes.hbm.xml配置文件里set标签的cascade属性的值改为all或delete)
*/
@Test
public void removeClassesStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到此班级
Classes cla4 = (Classes)session.get(Classes.class, 4);
//删除此班级(同一时候会将此班级中的全部学生删除)
session.delete(cla4);
transaction.commit();
session.close();
}
/*13、删除班级(从数据库中删除。不是移除关系)
* 2在删除班级之前。解除班级和学生之间的关系(仅仅删除班级,不删除学生)
*/
@Test
public void removeClasses(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取此班级
Classes cla3 = (Classes)session.get(Classes.class, 3);
//先解除此班级和全部学生之间的关系
cla3.setStudents(null);
//然后在删除此班级
session.delete(cla3);
transaction.commit();
session.close();
}
}
33、 一对多的双向关联
一对多的双向关联是在一对多的单向关联的基础上建立起来的。所以必需要先操作完毕单向关联;
须要在“多”的一方的持久化类中加入对“一”的一方的引用属性。比方这里的学生类中须要建立一个对班级类的属性引用属性。
一对多的关系,在多的一方维护关系,效率比較高(由于不许再次改动多的一方的外键了);
如:
public class Student implements Serializable {
private Integer sid;//主键
private String name;//学生姓名
private Integer age;//学生年龄
private Classes classes;//引用班级
...下面是get和set方法;
}
在配置文件里描写叙述此班级的属性;
<!-- 描写叙述 此班级中的班级属性 -->
<!-- may-to-one表示描写叙述 多对一
属性
name:为学生表中引用班级的属性名
class:表示被引用的类(班级)的完整名称
column:描写叙述学生表中的外键字段
cascade:级联。当对学生表进行某一种操作时,假设涉及到了班级表,是否运行级联操作。
取值范围:
save-update:表示当对学生表进行操作保存或更新时,也对班级进行保存或更新(推荐使用)
delete:表示仅仅当删除学生时假设涉及到了班级,那么将班级也一起删除
(不推荐使用,当删除学生时会将此班级也删除。而且还会删除此班级中的全部学生)
all:以上两种都包含。
-->
<many-to-one name="classes" class="cn.domain.Classes" column="cid" cascade="save-update"></many-to-one>
1. 练习:(或參考hibernate05_relationproject)
//一对多的双向关联
public class RelationTest extends HibernateUtils{
//反向操作
//反向操作就是在多的一方操作,这样就降低了sql代码(不须要每次都更新学生表的外键。来维护关系)
//通过添加学生来添加班级
@Test
public void addStudentClasses(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//创建一个班级
Classes net3 = new Classes("13.net3班");
//创建一个学生
Student stu7 = new Student("七七",22);
//将此班级加入到此学生中
stu7.setClasses(net3);
//保存该学生,这里保存学生会将班级一并保存。而且建立关系
session.save(stu7);
transaction.commit();
session.close();
}
//新建一个学生,将此学生加入至一个已有的班级中。(反向操作)
@Test//从此測试方法。生成的sql语句来看,反向操作的sql语句要少。所以效率要高
public void addStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//创建出一个学生
Student stu8 = new Student("八八",22);
//查询出这个班级
Classes cla1 = (Classes)session.get(Classes.class, 1);
//将此班级加入到此学生中
stu8.setClasses(cla1);
//保存此学生
session.save(stu8);
transaction.commit();
session.close();
}
//将某一个学生从某一个班级中移除(反向操作)
@Test
public void removeStudent2(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到此学生
Student stu8 = (Student)session.get(Student.class, 8);
//删除此学生中的班级
stu8.setClasses(null);
transaction.commit();
session.close();
}
//删除学生,假设学生表的映射配置文件里的many-to-one标签的 cascade属性的值是delete或all就会将此学生的班级也删除。
@Test
public void removeStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//查询出一个学生
Student stu7 = (Student)session.get(Student.class, 6);
//将其删除
session.delete(stu7);
transaction.commit();
session.close();
}
}
34、 多对多关系(或參考:hibernate06_relationproject)
多对多的关系在数据库中是靠第三张表来维护的。所以操作多对多的关系都是在操作第三张表;
如:
解除关系
把第三张表的一行数据删除掉
建立关系
在第三张表中添加一条记录就可以
变更关系
在第三张表中,先解除如今的对象的关系。再创建与还有一个对象关系
多对多关系用MyEclipse通过表来创建java类(表的反向project),自己主动生成的java类不是多对多的关系,而是一个 一对多和一个 多对一 java类组合起来的。
多对多关系。以少的一方操作效率比較高(比方如今 一个课程对象要加入一些学生,应从这个课程对象中去加入这些学生,而不是从这些学生对象中去加入这个课程)在以上的《注意事项重点》 中已讲过;
1. 持久化类的映射配置文件演示样例:(以学生和课程为案例)
学生持久化类:
//学生持久化类
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Integer sid;
private String name;
private Integer age;
private Set<Course> courses;//存储全部的课程
public Student(){}
public Student(String name, Integer age){
this.name = name;
this.age = age;
}
下面是get和set方法...
}
课程持久化类:
//课程持久化类
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
private Integer cid;
private String name;
private Set<Student> students;//存储全部的学生
public Course(){}
public Course(String name){
this.name = name;
}
下面是get和set方法...
}
学生持久化类的映射配置文件:
<?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.domain.Student">
<id name="sid" length="6" type="java.lang.Integer">
<generator class="increment"></generator>
</id>
<property name="name" length="15" type="java.lang.String"></property>
<property name="age" length="2" type="java.lang.Integer"></property>
<!--
下面set是描写叙述的第三张表:student_course(关系表)
name:描写叙述的是当前Student(学生)持久化类中引用Course(课程)的set集合的属性名
cascade:级联,当对Student学生操作时。假设涉及到了Course(课程)时是否运行级联
取值:save-update:表示仅仅当进行保存或更新时级联操作(这里推荐使用save-update,假设用下面两种,当删除学生时会将课程一起删除)
delete:表示仅仅当进行删除时级联操作
all:表示以上两种都包括
table:第三张关系表的名称
inverse:Student类是否不维护关系
false为须要维护(缺省的。推荐的)
true为不须要维护
key:是描写叙述的当前持久化类(Student)在student_course表中的外键
column:当前持久化类(Student)在student_course表中的外键的名称
name:名称
many-to-many:描写叙述的是多对多的关系
class:描写叙述的是student_course表中要关联的还有一个类(课程持久化类)
column:描写叙述的是student_course表中要关联的还有一个类的外键名称
-->
<set name="courses" cascade="save-update" table="student_course" inverse="false">
<key>
<column name="sid"></column>
</key>
<many-to-many class="cn.domain.Course" column="cid"></many-to-many>
</set>
</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.domain.Course">
<id name="cid" length="6" type="java.lang.Integer">
<generator class="increment"></generator>
</id>
<property name="name" length="15" type="java.lang.String" />
<!--
set标签描写叙述的是第三张表的关系
name:描写叙述的是此Course课程类中引用的Student学生类的set集合的属性名
cascade:级联,当对Course课程表操作时,假设涉及到了Student(学生)时是否运行级联
取值:save-update:表示仅仅当进行保存或更新时级联操作(这里推荐使用save-update,假设用下面两种,当删除课程时会将此课程的学生一起删除,假设用此中方式,必须先解除关系后再删除)
delete:表示仅仅当进行删除时级联操作
all:表示以上两种都包括
table:第三张表(关系表)的名称
key标签描写叙述的是当前Course课程表在第三张表(关系表student_course)中的外键
column标签:当前Course课程表在第三张表(关系表student_course)中的字段名称
属性:name:名称
many-to-many:描写叙述的是多对多的关系
属性:class:描写叙述的是student_course表中要关联的还有一个类(学生持久化类)
column:描写叙述的是student_course表中要关联的还有一个类的外键名称
-->
<set name="students" cascade="save-update" table="student_course">
<key>
<column name="cid"></column>
</key>
<many-to-many class="cn.domain.Student" column="sid"></many-to-many>
</set>
</class>
</hibernate-mapping>
30. 多对多关系的练习
package cn.test;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.domain.Course;
import cn.domain.Student;
import cn.utils.HibernateUtils;
//多对多的练习《学生对课程》
public class RelationTest extends HibernateUtils{
//多对多操作时,以少的一方操作效率会高一些(sql语句少些)
//新建课程的同一时候新建学生 并创建关系
@Test
public void createStudentCourseRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Course course1 = new Course("javaWeb");
Student stu1 = new Student("一一",20);
Student stu2 = new Student("二二",21);
//第一种方式:(建议使用)
//创建一个存储学生的set集合
Set<Student> students = new HashSet<Student>();
//将学生一和学生二都存储到此集合中
students.add(stu1);
students.add(stu2);
//将此集合设置到此课程中(建立关系)
course1.setStudents(students);
//将此课程保存到数据库中(会级联保存学生,
//由于:课程类(Course)的映射配置文件,中的set标签的cascade属性的值是"save-update",所以在保存课程时会级联保存学生)
session.save(course1);
//另外一种方式:(不推荐)
//创建一个存储课程的set集合
//Set<Course> courses = new HashSet<Course>();
//将此课程加入到此集合中
//courses.add(course1);
//
//将此集合设置到 学生1和学生2的课程集合中(建立关系)
//stu1.setCourses(courses);
//stu2.setCourses(courses);
////保存学生1和学生2(会级联保存课程
//由于:学生类(Student)的映射配置文件。中的set标签的cascade属性的值是“save-update”,所以在保存学生时会级联保存课程 )
//session.save(stu1);
//session.save(stu2);
transaction.commit();
session.close();
}
//已经存在一个课程,新建一个学生。建立课程和学生之间的关系
@Test
public void addStudentRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到这个课程
Course cou1 = (Course)session.get(Course.class, 1);
//创建一个学生
Student stu3 = new Student("三三", 22);
//将这个学生保存到课程中的students集合中
cou1.getStudents().add(stu3);
//此处的事务提交会自己主动保存cou1课程(由于cou1是一个持久化状态的对象)
transaction.commit();
session.close();
}
//5、已经存在一个学生,新建一个课程。建立课程和学生之间的关系
@Test
public void addCourseRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到这个学生
Student stu3 = (Student)session.get(Student.class, 3);
//创建一个课程
Course cou2 = new Course("Oracle");
//将这个课程加入到学生的课程集合中
stu3.getCourses().add(cou2);
//此处的事务提交会自己主动保存cou1课程(由于cou1是一个持久化状态的对象)
transaction.commit();
session.close();
}
//把已经存在的一些学生增加到已经存在的一个课程中
@Test//将sid为4567的学生加入到cid为2的课程中
public void addStudentCourseRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到这个课程
Course cou2 = (Course)session.get(Course.class, 2);
//获取到这些学生
List<Student> students = session.createQuery("from Student where sid in(4,5,6,7)").list();
//第一种方式(推荐使用)
//将这些学生加入到此课程中的学生集合中
//这里是从少的一方进行操作,这里仅仅会查询出此班级的全部学生,而不会查询出这些学生的全部班级(由于没实用到),所以会大大提升效率(sql语句会大大降低)
cou2.getStudents().addAll(students);
//另外一种方式:不推荐使用
//由于在调用getCoures()方法时。hibernate会将数据库中此 学生 的全部课程查询出来,
//下面有4个学生。所以就须要查询这4个学生每个学生的全部课程,会大大减少效率,所以对多对关系不适合从多的一方操作少的一方
//students.get(0).getCourses().add(cou2);
//students.get(1).getCourses().add(cou2);
//students.get(2).getCourses().add(cou2);
//students.get(3).getCourses().add(cou2);
//
//
//session.save(students.get(0));
//session.save(students.get(1));
//session.save(students.get(2));
//session.save(students.get(3));
//此处会自己主动进行更新。由于cou2是一个持久化状态的对象
transaction.commit();
session.close();
}
//把一个学生增加到一些课程中
@Test//将id为7的学生加入到id为3、4的课程中
public void addStudentCourse(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到此学生
Student stu7 = (Student)session.get(Student.class, 7);
//获取到id为3、4的课程
List<Course> Courses34 = session.createQuery("from Course where cid in(3,4)").list();
//第一种方式:会产生5条sql语句(在少的一方操作。
推荐使用)
//将3、4的课程加入到学生的课程集合中
stu7.getCourses().addAll(Courses34);
//另外一种方式:会产生6条sql语句(在多的一方操作。不推荐)
//将此学生加入到3和4课程对象的学生集合中。
//for(Course course : Courses34){
//course.getStudents().add(stu7);
//}
transaction.commit();
session.close();
}
//把多个学生增加到多个课程中
@Test
public void addStudentCourseMany(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到多个学生
List<Student> students = session.createQuery("from Student where sid in(1,2,3)").list();
//获取到多个课程
List<Course> courses = session.createQuery("from Course where cid in(1,2)").list();
//方式一:把这些学生加入到这些课程中(推荐)。在少的一方操作。课程的数量要少,所以推荐使用此种方式
for(Course cou : courses){
cou.getStudents().addAll(students);
}
//方式二:把这些课程加入到每一个学生中(不推荐)
for(Student stu : students){
stu.getCourses().addAll(courses);
}
transaction.commit();
session.close();
}
//把一个已经存在的学生从一个已经的课程中移除
@Test//事实上就是在第三张表中移除此关系
public void removeStudentCourseRelation(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//移除id为3和课程为3的关系
//获得此课程
Course cou3 = (Course)session.get(Course.class, 3);
//获取的学生
Student stu3 = (Student)session.get(Student.class, 3);
//将此课程中此学生的课程集合中删除
stu3.getCourses().remove(cou3);
transaction.commit();
session.close();
}
//把一些学生从一个已经存在的课程中移除
@Test
public void removeStudentCourseRelation2(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到此课程
Course cou1 = (Course)session.get(Course.class, 1);
//遍历此课程中的学生
Set<Student> students = cou1.getStudents();
Iterator<Student> it = students.iterator();
while(it.hasNext()){
Student stu = it.next();
//假设此学生的id等于1或2就将其从此集合中删除(删除关系)
if(stu.getSid() == 1 || stu.getSid() == 2){
it.remove();
}
}
transaction.commit();
session.close();
}
// 把一个学生从一个课程转向到另外一个课程
@Test//将id为1的学生 从id为2的课中移到id为1的课程中去
public void test(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取此学生原有的课程
Course cou2 = (Course)session.get(Course.class, 2);
//获取到此学生要移转的课程
Course cou1 = (Course)session.get(Course.class, 1);
//获取到此学生
Student stu1 = (Student)session.get(Student.class, 1);
//方式一:自己主动生成了6条sql语句(从少的一方操作。推荐使用)
//得到此学生的全部课程
Set<Course> courses = stu1.getCourses();
//将此id为2的课程从此集合中删除
courses.remove(cou2);
//将id为1的课程加入到此集合中
courses.add(cou1);
//方式二:自己主动生成了7条sql语句(从多的一方操作。
不推荐使用)
//cou2.getStudents().remove(stu1);
//cou1.getStudents().add(stu1);
transaction.commit();
session.close();
}
//仅仅删除课程
@Test
public void removeCourse(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到要删除的课程
Course cou4 = (Course)session.get(Course.class, 4);
//移除此课程中的全部学生(移除此课程和此课程中全部学生的关系)
cou4.setStudents(null);
//再删除课程
session.delete(cou4);
transaction.commit();
session.close();
}
//仅仅删除学生
@Test
public void removeStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到要删除的学生
Student stu3 = (Student)session.get(Student.class, 3);
//移除此学生的全部课程(移除此学生的课程与此学生的关系)
stu3.setCourses(null);
//再删除此学生
session.delete(stu3);
transaction.commit();
session.close();
}
//删除课程并将此课程中的学生一起删除(不建议这样做)
@Test
public void removeCourseStudent(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//得到此课程
Course cou3 = (Course)session.get(Course.class, 3);
//得到此课程的全部学生
Set<Student> students = cou3.getStudents();
//遍历这些学生
Iterator<Student> it = students.iterator();
while(it.hasNext()){
Student stu = it.next();
//将这些学生中的全部课程的关系移除
stu.setCourses(null);
//再删除此学生
session.delete(stu);
}
//再删除此课程
session.delete(cou3);
transaction.commit();
session.close();
}
}
35、 一对一的关系(或參考:hibernate07_relationproject)
在数据库中操作一对一的关系:比方如今又两张表,Person(人),Address(地址)。 将Person的主键字段建立一个外键引用Address表的主键,就相当于一个人相应一个地址。
1. 持久化类的映射配置文件演示样例:(以人和地址为例)
人的持久化类
public class Person implements Serializable {
private Integer pid;//主键
private String name;//姓名
private Integer age;//年龄
private Address address;//地址
public Person(){}
public Person(String name, Integer age){
this.name = name;
this.age = age;
}
下面是get和set方法...
}
地址的持久化类
public class Address implements Serializable {
private Integer aid;
private String site;//地址
public Address(){}
public Address(String site){
this.site = site;
}
下面是get和set方法...
}
人的映射配置文件
<?
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.domain.Person">
<id name="pid" length="10" type="java.lang.Integer">
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<property name="name" length="10" type="java.lang.String"></property>
<property name="age" length="2" type="java.lang.Integer"></property>
<!--
one-to-one标签:表示一对一关系
name属性:描写叙述引用还有一个类的属性名
class属性:描写叙述引用还有一个类的完整类名
constrained:描写叙述此表的主键是否创建外键约束引用Address的主键(建立外键约束)
cascade:当对此Person操作时,假设涉及到了Address类是否级联操作。
-->
<one-to-one name="address" class="cn.domain.Address" constrained="true" cascade="all"></one-to-one>
</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.domain.Address">
<id name="aid" length="11" type="java.lang.Integer">
<generator class="increment"></generator>
</id>
<property name="site" length="40" type="java.lang.String"></property>
</class>
</hibernate-mapping>
31. 一对一关系的练习
package cn.test;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.junit.Test;
import cn.domain.Address;
import cn.domain.Person;
import cn.utils.HibernateUtils;
//一对一的单向关联
public class RelationTest extends HibernateUtils{
@Test//创建有关系的表
public void createRelationTable(){
}
@Test//加入一个人及一个地址
public void addPersonAddress(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Person person = new Person("二二",23);
Address address = new Address("四川达州广安");
person.setAddress(address);
session.save(person);
transaction.commit();
session.close();
}
@Test//删除一个人和这个地址,cascade属性的值必须为:"all"或“save-update”
public void test1(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//查询出此人
Person person1 = (Person)session.get(Person.class, 1);
//删除此人
session.delete(person1);
transaction.commit();
session.close();
}
}
十四、 hibernate中的懒载入(或參考hibernate08_lazyproject)
在hibernate优化中发出的sql语句越少效率越高(还需參照数据的大小,比方一种方式:尽管发出的sql语句少但查询出的数据非常多非常多,而还有一种方式,尽管多发出了一些sql语句但查询出来的数据相对而言要少的多。此时就应又一次选择使用的方式了)。
懒载入,hibernate通过代理模式从写了此这些对象中的方法,仅仅有当使用这个属性时才会到数据库中去查询出此属性的值,这样就大大的提升了程序的效率了;
所以假设要在session对象关闭后使用被懒载入的数据,必需要在session对象还没关闭前调用获取一次此数据,以便从数据库中查询出此数据。
1、 类的懒载入
类的懒载入就是对此对象属性的懒载入(不包含关系的属性(比方引用了还有一个类的集合或引用了还有一个类的属性))。
就是获取一个对象时当仅仅有获取此对象的属性时,hibernate才会从数据库中查询出此条记录。(除主键外)。
类的懒载入须要用session对象的load方法,查询出此对象,那么此对象才会被懒载入。
代码演示样例:
//类的懒载入
@Test
public void classLazy(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取某一个班级
//调用load方法用主键获取到Classes表的字段
//load方法是类的懒载入,也就是当你用到了cla1对象中的属性时才会到数据库中去查询
Classes cla1 = (Classes)session.load(Classes.class, 1);
//运行到此处不会产生sql语句(也就没有与数据库交互)
//此处也不会产生sql语句,由于仅仅获取此对象的主键,hibernate会去数据库中查询,而是直接返回load方法传入的主键;
System.out.println(cla1.getCid());
//此处才会到数据库中查询出此班级的全部信息,但不包含全部的学生
System.out.println(cla1.getName());
transaction.commit();
session.close();
}
36、 集合的懒载入(对多的一方的懒载入)
集合的懒载入,在关系中就是”一”的一方对”多”的一方进行懒载入。所以要在”一”的一方的set(或其它集合的标签)标签中设置一个lazy属性,此属性有三个取值:
false:表示此集合不须要被懒载入。马上载入。
说明:当查询出”一”的一方的对象时。就立即载入此对象的集合(“多”的一方)。
true:表示此集合须要被懒载入(缺省值。推荐使用)
说明:当使用到此”一”的一方的对象中集合中的元素时(“多”的一方),就立即载入此集合中的全部元素(这里的元素能够代表学生)。
extra:更强度的懒载入(一般不推荐使用)
说明:此值表示更强度的懒载入。当获取此集合的长度时不会在数据库中查询此集合中的全部元素,而是用聚合函数在数据库查询出的此集合的个数
代码示1:(”一”的一方的映射配置文件里的set标签的lazy属性的值为true)
//集合的懒载入(也就是对多的一方的懒载入)
//需要在一的一方的持久化类映射配置文件里的set(此标签是相应集合的)标签的lazy属性的值必需要为true(缺省值也是true)
@Test
public void setLazy(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取此班级对象,此处仅仅会查询出此班级的信息(由于这里没有使用load方法(类的懒载入),所以这里一開始就是查询出此班级对象的全部属性的值,但不包含学生的集合)
Classes cla1 = (Classes)session.get(Classes.class, 1);
//获取此班级的全部学生,这里不会产生不论什么sql语句,
Set<Student> students = cla1.getStudents();
//运行到此处hibernate才会到数据库中去查询此班级的全部学生
int len = students.size();
System.out.println(len);
transaction.commit();
session.close();
}
代码演示样例2:(”一”的一方的映射配置文件里的set标签的lazy属性的值为extra)
//集合的懒载入(也就是对多的一方的懒载入)
//需要在一的一方的持久化类映射配置文件里的set(此标签是相应集合的)标签的lazy属性的值必需要为extra(更懒形式的载入)
@Test//更强度的懒载入
public void setLazyExtra(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取此班级对象,此处仅仅会查询出此班级的信息(由于这里没有使用load方法(类的懒载入),所以这里一開始就是查询出此班级对象的全部属性的值,但不包含学生的集合)
Classes cla1 = (Classes)session.get(Classes.class, 1);
//获取此班级的全部学生,这里不会产生不论什么sql语句,
Set<Student> students = cla1.getStudents();
//获取此集合的长度
int len = students.size();//相应的sql语句为:select count(sid) from Student where cid =?//这个sql语句能够看出并没有查询出此集合中的元素
System.out.println(len);
transaction.commit();
session.close();
}
37、 单端关联的懒载入(对一的一方 的懒载入)
此中方式其有用不着懒载入,由于对”一”的一方进行懒载入。但”一”的一方的对象,仅仅有一个。所以懒不懒载入对效率的影响不大,(使用器缺省值就可以,缺省值是须要被懒载入);
在”多”的一方的隐式配置文件里的many-to-one标签中设置lazy属性的值为:no-proxy(缺省值。推荐使用此值),表示对”一”的一方使用懒载入(值也能够是false。表示不正确”一”的一方使用懒载入)。
配置文件演示样例:(为Student类(”多”的一方)的映射配置文件)
<many-to-one name="classes" class="cn.domain.Classes" column="cid" cascade="save-update" lazy="no-proxy"></many-to-one>
代码演示样例:
//集合的懒载入(对"一"的一方 的懒载入)
//需要在"多"的一方的持久化类映射配置文件里的many-to-one(多对一)标签的lazy属性的值必需要为no-proxy(懒载入,缺省值,推荐使用)
@Test//更强度的懒载入
public void setLazy1(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到某一个学生对象(由于此处不是用load方法,所以或查询出此学生的全部属性,但不包含老师(关系属性))
Student stu1 = (Student)session.get(Student.class, 1);
//获取到该学生的老师(此处不会产生不论什么的sql语句)
Classes cla = stu1.getClasses();
//此处才会到数据库中去查询出此学生的老师
System.out.println(cla.getName());
transaction.commit();
session.close();
}
十五、 抓取策略(或參考:hibernate09_fetch project)
抓取策略就是依据某一种方式生成查询的sql语句。主要针对查询“多“的一方。
抓取策略的值须要在集合(这里是set)标签中设置fetch属性的值:
比标签的取值范围:
join: 左外连接
表示以左外连接的方式生成查询sql语句(注意,此种方式,比方在查询一个班级时,没有使用这个班级的学生,也会以左外连接查询出此班级的全部学生,所以有时候效率会减少)。
(还应当注意,假设需求的数据能够形成子查询sql语句,那么join(左外连接)将失效)
select: 缺省值
表示生成一条一条的查询sql语句进行查询(缺省值)。
subselect: 子查询
表示生成子查询的查询sql语句进行查询(推荐使用)。
(还应当注意,假设需求的数据能够形成子查询sql语句,那么join(左外连接)将失效)
1、 代码演示样例:(班级与学生的案例(一对多))
1. 班级持久化类的映射配置文件:
<?
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.domain.Classes">
<id name="cid" length="11" type="java.lang.Integer">
<generator class="increment"></generator>
</id>
<property name="name" length="10" type="java.lang.String"></property>
<!-- set标签就相当于描写叙述set集合, -->
<!--
fetch属性 表示生成什么样的查询sql语句
join:表示用左外连接的方式生成sql语句
select:表示直接一条一条的select查询语句生成sql语句。(缺省值)
subselect:表示以子查询的方式生成sql语句(推荐)
-->
<set name="students" cascade="all" inverse="false" lazy="true" fetch="subselect">
<key><!-- 外键 -->
<!-- 描写叙述描写叙述引用了外键的字段,此字段会被加入到学生student表中 -->
<column name="cid"></column>
</key>
<!-- 描写叙述哪个表须要引用外键 -->
<one-to-many class="cn.domain.Student"/>
</set>
<!-- 以上能够看着:Student表中的cid外键字段引用了Classes表中的主键(cid)字段 -->
</class>
</hibernate-mapping>
32. 測试代码演示样例:
//左外连接測试,set标签的fetch的值为join(获取一个班级的全部学生)
@Test//
public void test(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到此班级
Classes cla4 = (Classes)session.get(Classes.class, 1);
//获取到此班级的全部学生
Set<Student> students = cla4.getStudents();
/*当fetch属性的值为join时。以上查询此班级语句生成的sql语句
select
classes0_.cid as cid0_1_,
classes0_.name as name0_1_,
students1_.cid as cid0_3_,
students1_.sid as sid3_,
students1_.sid as sid1_0_,
students1_.name as name1_0_,
students1_.age as age1_0_,
students1_.cid as cid1_0_
from
Classes classes0_
left outer join
Student students1_
on classes0_.cid=students1_.cid
where
classes0_.cid=?
这里还没实用到此班级的学生,而此处已经将此班级的全部学生查询出来了。也就是说用左外连接有时会减少效率。
*/
//遍历这些学生
for(Student stu : students){
System.out.println(stu.getName());
}
System.out.println(cla4.getName());
/*
* 以上操作 Classes持久化类的映射配置文件里的set标签中的fetch属性的值为:select(缺省值)的话,以上会产生两条sql语句。
*
* 假设以上操作Classes持久化类的映射配置文件里的set标签中的fetch属性的值为:join的话,那么仅仅会产生一条sql语句
*
*/
transaction.commit();
session.close();
}
//子查询的測试,set标签的fetch的值为subselect(查询全部班级的全部学生)(推荐使用)
//需将 Classes持久化类的映射配置文件里的set标签中的fetch属性的值设置为subselect
@Test
public void test2(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//获取到全部的班级
List<Classes> classess = session.createQuery("from Classes").list();
for(Classes cla:classess){
Set<Student> students = cla.getStudents();
for(Student stu : students){
System.out.println(stu.getName());
}
}
/*
假设以上fetch的值为:select(缺省值),将会产生n+1条sql语句(这里的n就是少的一方的数量(这里代表班级的数量))。
假设以上fetch的值为:subselect(子查询)。仅仅会产生2条sql语句。(而且会以子查询的方式生成sql语句)
*/
transaction.commit();
session.close();
}
十六、 hibernate中的缓存
1、 一级缓存(或參考hibernate11_session_cache1project)
hiberante中的一级缓存是在session对象中,一级缓存的生命周期也就是session对象的生命周期。
1. 数据的安全性
hibernate中的一级缓存缓存的是私有的数据(仅仅能当前线程可以訪问)。
而session时保存在当前线程中的session对象,其它线程不能訪问。所以也就保证了数据的安全性。
33. 将对象保存到一级缓存中
此session对象的缓存生命周期,是依据此session对象的生命周期而定的(也就是说。每一个session对象中都有一个一级缓存并且和其它session对象的一级缓存是独立开的,并且必须通过此session对象才干向此session对象中的一级缓存中存入值)。
一个对象被保存在了session对象中(一个对象的状态是持久化状态),那么这个对象就被保存在了这个session对象的一级缓存中。
就是通过session对象的save、update、get、load、createQuery方法将此对象放入到session对象中的一级缓存中。尽管delete方法也就是将一个对象放入到了一级缓存中,但此对象的状态就为删除状态了,也就无法获取到此对象了。
34. 获取此session对象的一级缓存中的持久化对象
当调用session对象的get或load方法时。此方法会先到此session对象的一级缓存中去查找此对象。假设在缓存中找到了此持久化对象。而且此对象的持久化状态不是删除状态。那么直接返回此持久化对象,假设此对象是删除状态。就返回null。
假设在此session对象的一级缓存中没有找到此持久化对象(通过主键),那么才会到数据库中进行查询出持久化对象。
注意createQuery方法不会到缓存中去查找对象,而是直接到数据库中查找对象。
35. 删除此session对象的一级缓存中的持久对象
调用session对象的evict(Object obj)方法时,会移除掉session对象中一级缓存中的此对象(调用evict方法传入的持久化对象)。
当调用session对象的clear方法时,会删除此session对象的缓存中的全部持久化对象。
36. 从数据库刷新缓存中的某一个对象
当调用session对象的refresh(Object obj1)方法时,refresh方法会从数据库中从新查询出此对象,并将一级缓存中的此对象覆盖掉。从而实现了刷新一级缓存中的持久化对象。
代码演示样例:
//refresh 是又一次将一个缓存中的对象从数据库中查询出来。并覆盖掉缓存中的这个对象
@Test
public void testRefresh(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//从学生表中查询出主键为1 的学生(此学生的姓名叫"一一")
Student stu1 = (Student)session.get(Student.class, 1);
//设置此学生名称叫“张三”
stu1.setName("张三");
//此方法是刷新一级缓存中的此对象
//此方法会通过此对象的主键。从数据库中又一次查询出此对象,并将一级缓存中的此对象覆盖掉。
session.refresh(stu1);
//此处会打印"一一"
System.out.println(stu1.getName());
transaction.commit();
session.close();
}
37. 将缓存中的对象刷新到数据库中
当调用session. flush()方法时,会将session对象一级缓存中的全部持久化对象更新到数据库中.
当调用session.flush()方法时所做的事情。
1. 获取session对象中的全部持久化对象。
2. 检查全部持久化对象的状态,假设为保存状态(通过save方法增加的)或删除状态(通过delete方法增加的),就直接依据其快照 产生insert或delete sql语句,提交给数据库。并将这些对象的状态改为持久化状态。
3. 检查全部的持久化对象的状态,假设为持久化状态,那么就和其快照进行对照。假设和其快照不一致,就直接依据这个对象生成update sql语句,提交给数据库。
(并不会提交事务)
4. 在检查以上2、3步的同一时候会检查此持久化对象的关联对象(就是有关系的对象(一对多,多对多等)),是否有级联操作,假设有就级联操作此关联的对象。
还会检查是否对此关联对象运行关系操作,假设有就维护此对象与此关联对象的关系。
flush()并不会提交事务,也不会清空session对象一级缓存中的全部持久化对象。
事实上在commit()事务提交方法中就先调用了flush方法。
代码演示样例1:()
//測试flush方法。将缓存中的全部对象,以对应的方式更新到数据库中
@Test
public void testFlush(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student stu5 = new Student("五五",26);
session.save(stu5);
//此处仅仅会生成以上语句的insert sql语句,并将此语句提交给数据库
session.flush();
//提交事务
transaction.commit();
session.close();
}
代码演示样例2:
//批量操作
@Test
public void testFlush2(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//向数据库中 插入1百万条数据
for(int i=0; i<1000000; i++){
Student stu = new Student("abc",10);
session.save(stu);
//隔50条就向数据库提交此sql语句并清空session一级缓存中的数据(不然数据量太大有可能内存溢出)
if(i%50 == 0){
//将内存中的数据按一定的sql语句提交给数据库(通过内存中的对象更新数据库中的数据)
session.flush();
//清除session一级缓存中的全部持久化对象
session.clear();
}
}
//提交事务
transaction.commit();
session.close();
}
38、 二级缓存(或參考hibernate12_cache2project)
1. 二级缓存存储数据的类型
二级缓存中存储的都是共同拥有的数据(就是全部的线程都可以訪问)。
38. 二级缓存中的数据的适用场合
存储在二级缓存中的数据不能频繁更新。
并且存储在二级缓存中的数据不能是某一个线程私有的数据。
39. 开启二级缓存
二级缓存在hibernate中默认是关闭的,需手动开启
手动开启hibernate的二级缓存。在hibernate.cfg.xml配置文件里(必须先导入ehcache-1.5.0.jar 工具包(hibernate的基本jar包有此包))
<!-- 开启二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置二级缓存的提供商 -->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
40. 配置二级缓存
假设某一个持久化对象要存储到二级缓存中,就必须对此持久化类的映射配置文件 进行配置。
方式一:
在hibernate.cfg.xml配置文件里配置要存储在二级缓存中的持久化类。
<class-cache usage=”read-only” class=””> class的值为要存储在二级缓存中的对象的完整类名。
方式二:(推荐使用)
在要存储到二级缓存对象的持久化类的映射配置文件里的ID标签前面配置下面标签:
<cache usage=”read-only”/>
以上标签的usage属性是缓存策略。直接设置为read-only就可以
41. 二级缓存的生命周期
hibernate中的二级缓存是在sessionFactory对象中,所以二级缓存的生命周期和sessionFactory对象的生命周期一致,当载入hibernate.cfg.xml配置文件时创建,当从server上卸载此应用时被摧毁.
42. 将持久化对象存储在二级缓存中
假设一个持久化对象在配置文件配置了能够存储在二级缓存,那么在使用save、update、load、get方法时会自己主动存储在二级缓存中。
43. 获取二级缓存中的数据
当使用get或load方法通过主键获取某个表中的某个对象时。hibernate先会到一级缓存中去查找,假设没有再会到二级缓存中去查找,假设还没有,才到数据库去查找。(前提是这个对象必须能够存储在二级缓存中)
44. 代码演示样例
//Student类已经被配置为能够存储在二级缓存中
@Test//二级缓存的測试
public void test(){
Session session = sessionFactory.openSession();
//查询出主键为1的学生
Student stu1 = (Student)session.get(Student.class, 1);
session.clear();
//换班session对象//说明这个session的一级缓存被关闭了
session.close();
//再打开一个session对象
session = sessionFactory.openSession();
//再次查询主键为1的学生(此处不会生成sql语句,由于是从二级缓存中查询出来的)
stu1 = (Student)session.get(Student.class, 1);
System.out.println(stu1.getName());
session.close();
//以上仅仅会产生一条sql语句,说明了stu1这个对象被加入到了二级缓存中,第二次查询此对象时,直接到二级缓存中去查找的此对象
}
39、 查询缓存(或參考hibernate13_cache3project)
查询缓存是建立在二级缓存之上的,也就是说必需要开启二级缓存。而且将要存储在二级缓存的对象的映射配置文件里的最前端 配置<cache usage=”read-only”/>标签表示此对象能够被存储在二级缓存中。
1. 开启查询缓存
除了要做开启二级缓存的事项外,而且必须还要手动开启查询缓存。在hibernate.cfg.xml配置文件里用下面标签进行开启查询缓存:
<!-- 开启查询缓存 -->
<property name="cache.use_query_cache">true</property>
45. 将对象存储在查询缓存中
Query query = session.createQuery(“from Student”);//设置hql语句。并得到一个Query对象
query.setCacheable(true);//此语句表示先通过此hql语句为key到二级缓存的Map集合中去查询这个list集合,假设没有就从数据库中查询出全部的学生将其转换为list集合,并将此学生的list集合以此hql语句为key(Map<Student,List>)存储到二级缓存的一个Map集合中。
query.list();//返回此list集合。
那么这个list集合就以此hql语句为key被存储在了二级缓存的Map集合中。
假设下次再用此hql语句查询,那么就会直接到此二级缓存中的Map集合中去取。前提是必须设置Query对象的setCacheable()方法为true;
1- 代码演示样例:
//Student类已经被配置为能够存储在二级缓存中
//必须在二级缓存的基础上再在hibernate.cfg.xml配置文件设置
//<property name="cache.use_query_cache">true</property>表示手动开启查询缓存
//查询缓存測试
@Test
public void testCreateQuery(){
Session session = sessionFactory.openSession();
//设置查询全部学生的hql语句
Query query = session.createQuery("from Student");
//先到二级缓存中去查找此值,假设没有就到数据库中去查找此hql语句的值。并将此结果存储到二级缓存中。(这里会从数据库中查找此hql语句的值。并存储在二级缓存中)
query.setCacheable(true);
//将这个查询到的结果以List集合的形式返回
List<Student> students1 = query.list();
//关闭session对象
session.close();
//再开启一个新的session对象
session = sessionFactory.openSession();
//再次设置查询全部学生的hql语句
query = session.createQuery("from Student");
//表示先到二级缓存中去查找此值,假设没有就从数据库中去查找此值,并将查询到的值存储在二级缓存中
query.setCacheable(true);
//应为“from Student”的hql语句的值已经被存储在了查询缓存中,所以此处不会到数据库中查找此hql语句的值。
List<Student> students2 = query.list();
session.close();
}
40、 超大数据缓存的解决方式(或參考hibernate14_cache4project)
假设我们要存储在二级缓存的数据相当大,那么我们就能够将此数据存储到磁盘上.
比方一个类的对象须要存储在二级缓存的数量相当大。那么我们就能够将一定的对象从二级缓存中取出存储在磁盘上。
(比方一个对象在通过ehcache.xml配置文件,设置此对象在二级缓存中最大存储数量为5个,那么二级缓存中的此对象超出5个后,将把溢出的对象存储在磁盘上)
假设要将一个持久化类的对象存储在磁盘上,那么这个类必须能存储在二级缓存上(所以须要配置此持久化类能在二级缓存中存储,能够不开启查询缓存);
存储在磁盘上的对象,还是以从二级缓存中取出数据的方式取出此磁盘上的对象数据.
1. 配置文件:
要将一定的二级缓存中的对象存储在磁盘上。必须在WEB-INF/classes文件夹下(相应的开发文件夹为src)。下配置一个配置文件;
此配置文件名称必须为:ehcache.xml
1- 此配置文件的模板:(注意此配置文件里不能存在中文字符,凝视了也不行,在复制时需将下面中文删除)
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--复制此模板时,需将此模板中的全部中文字符删除(包含凝视的了)-->
<!-- 设置将数据存储到磁盘的文件夹 -->
<diskStore path="C:\TEMP1"/>
<!-- 给下面属性配置默认的值 -->
<defaultCache
maxElementsInMemory="12"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!-- 针对Student这个类来配置-->
<Cache
name="cn.domain.Student"<!--设置要被缓存的持久化类的全名-->
maxElementsInMemory="5" <!--设置二级缓存中此Student对象的最大数目,也就是说,当此持久化对象在二级缓存中的数量超过此数目时,将把移除的对象存储在磁盘上-->
eternal="false"<!--设置存储在磁盘上的Student对象是否为永久的,true表示永只是期,false表示超过timeToLiveSeconds属性的时间过期。 -->
timeToIdleSeconds="0"<!--设置对象空暇最长时间,以秒为单位,超过这个时间,对象过期,EHCache会把此对象中缓存中清除.假设此值为0表示此对象能够无限期处于空暇状态.-->
timeToLiveSeconds="0"<!--设置对象的生成最长时间,超过这个时间,对象过期,假设此值为0,表示对象能够无限期地存储在缓存中,该属性的最大值必须大于等于timeToIdleSeconds属性的值。
-->
overflowToDisk="true"<!--当内存中的Student对象达到maxElementsInMemory属性的上限后,是否把溢出的对象写到基于硬盘的缓存中,true为写入到磁盘中,false为不写入到磁盘中-->
maxElementsOnDisk="10000000"<!--在磁盘上存储此Student对象的最大值。-->
diskPersistent="false"<!--当JVM结束时是否在磁盘上持久化Student对象true表示持久化,false不持久化,默认是false -->
diskExpiryThreadIntervalSeconds="120"<!--指定专门用于清除过期对象的监听线程的轮询时间。
-->
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
46. 測试代码
//将二级缓存中的对象存储到磁盘上的代码測试
@Test
public void test(){
Session session = sessionFactory.openSession();
//从数据库中查询出全部的学生,并存放到一级缓存中、二级缓存中、
Query query = session.createQuery("from Student");
List<Student> students = query.list();
session.close();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 此Student对象在二级缓存中的对象超出了5个,
* 那么将会把溢出的Student对象存储在ehcache.xml配置文件配置的文件夹下
* <diskStore path="E:\TEMP1"/>//这里配置的是E:\TEMP1文件夹
*/
}
十七、 hiberante中的HQL语句(或參考hibernate15_hqlproject)
hibernate的hql语句和sql语句都差点儿相同,包含where(条件)、order by(排序)、group by(分组)、聚合函数、in(匹配)、like(模糊查询)、子查询、having(分组条件)等都能够使用,仅仅是有少数地方不同.
1、 单表查询
from后面跟的不是表名,是持久化类的名称,返回的是持久化类的属性。也不是表中的字段。
运行hql语句是通过session.createQuery(“hql语句”);方式来运行的。
from Student 表示查询的是全部学生,假设以list集合返回。那么返回的list集合结构是:List<Student>
select sid, name from Student 返回自己定义的字段,假设以list集合返回,那么返回的list集合结构是:List<Object[]>,数组中存储的是每一条记录的每个字段
select new cn.domain.Student(sid, name) from Stduent; 返回自己定义的字段,假设以list集合返回,那么返回的list集合结构是:List<Student>。返回的Student对象除了sid和name字段外,其它字段都为null。使用此此种方式还必须保证Student类中必须有此构造方法。
from Student where name=:studentName 此种方式是名称占位符的方式,必须在返回Query对象中设置此注入的值,如:query.setString(“studentName”,”张三”);
from Student where name=? 此中方式是”?
”号占位符的方式,在JDBC中以用过,这里必须调用返回的Query对象的setString(Stirng是依据注入值的类型来改变的)方法进行注入”?
”号的值,如:query.setString(0,””);注意”?”号的下标是从0開始的。
from Student where cid =(select cid from Classes where name=?
); 子查询的方式(这里的”?”号需注入值)
hql语句还支持sql语句的非常多功能,写法大概都一样。
query对象中有一个uniqueResult()方法,当返回的结果仅仅有一条记录时,能够用此方法.但假设返回的结果有多条记录,用此方法会抛异常。
41、 一对多
1. 等值连接
from Student s, Classes c where c.cid = s.classes.cid 返回的List结构是:List<Object[]> 每个Object[]数组中都存放了Student对象和Classes 对象,假设要控制Object[]数组中存放的持久化对象的位置,仅仅须要改变from后面的持久化类的顺序就可以。
47. 内连接
from Classes c inner join c.students s 返回的List结构是:List<Object[]>每个Object[]数组中都存放了Student对象和Classes对象
, 假设要控制Object[]数组中存放的持久化对象的位置,仅仅须要改变from后面的持久化类的顺序就可以
48. 左外连接
from Student s left join s.classes c 返回的List结构是:List<Object[]>每个Object[]数组中都存放了Student对象和Classes对象
, 假设要控制Object[]数组中存放的持久化对象的位置,仅仅须要改变from后面的持久化类的顺序就可以
49. 迫切连接
迫切连接是将以上连接的值以持久化对象的方式返回,仅仅须要在joinkeyword后面加上”fetch”keyword就可以,如:
from Student s left join fetch s.classes c 那么返回的List集合的结构是:List<Student>,每一个Student持久化对象中都有Classes持久化对象.
还能够转换持久化类的位置,如:
from Classes c left join fetch c.students s 那么返回的List集合的结构是:List<Classes>,每一个Classes班级对象中都有Student学生对象。
50. 将自己定义的返回值放入一个新的对象中(通过构造方法的方式)
select new cn.domain.Temp(s.name, c.name) from Classes c left join c.students s
那么返回的List集合的结构是:List<Temp>,每一个Temp对象中都有学生名称和班级名称(Temp能够不是持久化类)
42、 多表查询,多对多(也可參考hibernate16_hql2project)
多表查询时,一般先找出中间表(核心表),然后通过中间表去查询其它的表(将中间表放在fromkeyword进跟着的后面。)。
多对多查询时,返回的List集合中的元素可能反复,所以要将这个List转换为HashSet,从而达到去重的目的。
一般仅仅有fromkeyword紧跟着的类名的对象会出现反复,其它类的元素不会出现反复。
1. 代码演示样例:
//hibernate的hql语句測试
public class CacheTest1 extends HibernateUtils {
//多对多的hql语句查询
//此种语句查询出来以后会有反复的现象。所以没查询出结果来必须去掉反复的(这里用的是将List集合转化为Set集合从而达到去重的目的)
@Test//查询出全部学生的全部课程
public void testHql1(){
Session session = sessionFactory.openSession();
List<Student> studentsList = session.createQuery("from Student s inner join fetch s.courses c").list();
//去反复,假设没有此语句查询出来的结果可能有反复的学生(这里反复的仅仅有学生,而课程对象元素不会反复)。
Set<Student> students = new HashSet<Student>(studentsList);
//遍历这些学生的全部课程
for(Student stu : students){
System.out.print(stu+" ");
for(Course cou : stu.getCourses()){
System.out.print(cou);
}
System.out.println();
}
session.close();
}
@Test//查询出全部课程的学生
public void testHql2(){
Session session = sessionFactory.openSession();
List<Course> coursesList = session.createQuery("from Course c inner join fetch c.students").list();
//取掉反复的课程(这里反复的仅仅是课程对象,课程中的学生不会反复)
Set<Course> courses = new HashSet(coursesList);
//遍历这些课程的全部学生
for(Course cou : courses){
System.out.print(cou+" ");
for(Student stu : cou.getStudents()){
System.out.print(stu);
}
System.out.println();
}
session.close();
}
//查询全部学生的班级及课程(一般向这样的多表查询。首先找中间表,将中间表放在fromkeyword的第一个)
@Test
public void testHql3(){
Session session = sessionFactory.openSession();
List<Student> studentsList = session.createQuery("from Student s inner join fetch s.classes inner join fetch s.courses").list();
//去掉反复的学生
Set<Student> students = new HashSet<Student>(studentsList);
//遍历这个学生的班级及全部课程
for(Student stu : students){
System.out.println("学生:"+stu);
System.out.println("班级"+stu.getClasses());
for(Course cou : stu.getCourses()){
System.out.println("课程"+cou);
}
System.out.println(" ");
}
session.close();
}
}
十八、 hibernate的分页(或參考:hibernate17_pagingproject)
hibernate中的分页仅仅须要给返回的Query对象中设置对应的參数就可以。再调用Query对象的list方法返回要查看页的记录。
1、 代码演示样例:
@Test
public void pagingTest(){
Integer pageSize = 3;//每页显示的条数
Integer pageNumber = 3;//要看的页数
Session session = sessionFactory.openSession();
String sql = "from User";
Query query = session.createQuery(sql);
query.setFirstResult((pageNumber-1)*pageSize); //设置从第几列開始检索
query.setMaxResults(pageSize); //每页显示的最大条数
List<User> list = query.list();
for(User user : list){
System.out.println(user.getUid()+":"+user.getName()+":"+user.getPassword()+":"+user.getAge()+":"+user.getGender());
}
session.close();
}