Hibernate基础知识

Hibernate

Hibernate的做用:java

一、         Hibernate解决ORM(对象关系映射)的问题,大大减小了持久层的代码量python

二、         hql方言,解决了可移植性问题mysql

三、         效率问题,频繁的链接和关闭,自动的封装JDBC,自动使用链接池。Sessionweb

四、         具备缓存功能,节省查询时间算法

五、         经过设定自动给数据加锁,事务的隔离级别sql

Hibernate工做在持久层。数据库

相似Hibernate的框架 apache的OJB,sun公司的JDO,Oracle的Toplink, apache 和 Google的Ibatis。apache

POJO:领域模型也叫实体类。包括表单bean和结果bean(不继承任何类)windows

<struts1.0的Form 不属于POJO,由于继承了ActionForm类>数组

1、为何要用Hibernate?

Java中的对象,数据库中的关系。Hibernate主要是实现对象模型和关系模型直接的转换。轻量级的封装。面向对象的思想。

由于Hibernate有映射文件,因此才能对数据save,query等操做。

映射文件:***.hbm.xml文件,也叫源文件,把对象模型的继承、关联映射成数据库中的主外键关系。

Hibernate自动的保持数据库在的记录和对象属性的值同步(保证一致)。

Hibernate的session是持久化管理器,工做在持久层。

 

数据库中的表的主键生成策略:

Native:Hibernate根据不一样的数据库生成不一样的主键,自动增加

Uuid:UUID.randomUUID().toString(),32位字符串

Asigned:用户本身生成的

Foreign:外键维护叫作参照完整性,维护关系。来自于对象里关联属性的值

GUID:increment,线程不安全,建议不要使用

Identity:使用数据库本身的自动增加策略,oracle数据库不支持

 

对象与对象的关系:关联(has a )和继承(is a )

属性映射:主键通常不从对象中拿,要么从数据库中生成,要么hibernate給。外键从对象的关联属性上拿。(关联属性:属性类型为自定义类型必定是关联属性,若是属性为集合类型,该属性有多是关联属性)

 

Hibernate3.2 的环境搭建:(测试环境—> 真实环境)

测试环境:(不占内存的,导入的包不在项目里面,只是作关联)

一、         建立java Project

二、         建立公共的jar包, windowsàjavaàUser Libraryà包名

三、         在建立的包下导入jar包,(主jar包和lib下的全部jar包+mysql的jar包)

四、         导入项目库,右键—>propertiesàjava buildPathà选择本身建立的存放jar包的包名

五、         建立source folder (src文件夹)

六、         把ext文件夹下的hibernate.cfg.xml.,log4j.properties文件放在src下

七、         在hibernate.cfg.xml文件中配置持久化数据库信息,配置方言(不一样的数据库有不一样的方言)<property>

八、         导入映射文件<mapping resource=”com/hibernate/User.htm.xml”/>:将User对象和数据库中的表进行ORM(对象关系映射)

 

真实环境:将jar包直接导入WEB-INF/lib下,配置信息与虚拟环境相同

 

Hibernate小案例

一、         建立实体类:POJO(表单bean、结果bean)User

二、         建立和实体类对应的元文件  User.hbm.xml(可能多个实体类对应一个元文件)

<hibernate-mapping>

      <class name=”类的全路径名 cn.bean.User”>//根据类名建立表 user

      <id name=”id”>//id映射表示该属性为数据库中的表的主键

<generator class=”uuid”>//generator 表示主键的生成策略。设置主键为uuid,Hibernate自动生成

</id>

<property  name=”name”/>//普通属性用property映射,在表中的字段也叫name

      </class>

</hibernate-mapping>

数据库中主键映射:

若是在对象中为String id,generator通常为uuid,32位码,Hibernate自动生成

若是在对象中为int id,generator通常为native,数据库中自动生成,自增。

 

Hibernate会自动根据表单bean建立和表单bean类名相同的表。

会根据表单bean中的属性自动建立表的字段,数据类型会自动解析。Int->int,Stringàvarchar,util Date à datetime 类型相对应。长度取默认值。

 

DDL:数据库定义语言、DCL数据库控制语言、DML:数据库操纵语言

DDL(data definition language):
DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的连接和约束等初始化工做上,他们大多在创建表时使用

DML(data manipulation language):
SELECT、UPDATE、INSERT、DELETE,这4条命令是用来对数据库里的数据进行操做的语言
DCL(Data Control Language):
是数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL

 

Hbm2ddl工具类:

Configuration cfg=new Configuration().configure();//将src下的hibernate.hbm.xml文件读到cfg中

Configuration cfg=new Configuration();//将src下的hibernate.properties文件读到cfg中

SchemaExport  export=new SchemaExport(cfg);

export.create(true,true);//按照User.hbm.xml文件中配置在数据库中生成对应的表

 

Hiberante的配置信息:

<hibernate-configuration>

<session-factory> 

<!—配置数据库的链接信息-- >

<property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_fist</property>

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password">mysql</property>

<!—配置mysql数据的方言 -- >

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!—配置显示sql语句,建表,查询之类的,为了方便跟踪sql执行 -- >

<property name="hibernate.show_sql">true</property>

<!—配置表的自动生成策略,若是表不存在就建立,存在就再也不建立-- >

<property name="hibernate.hbm2ddl.auto">update</property>

<!—配置映射文件信息,用于数据库中的表的生成-- >

<mapping resource="com/hibernate/User.hbm.xml"/>

</session-factory>

</hibernate-configuration>

 

JDK动态代理:对目标类实现同一个接口的代理(经过反射实现)

Cglib代理:代理类是目标类的子类               

 

Hibernate使用的是链接池,dbcp链接池(内建链接池)

autocommit(),自动提交事务默认为false,须要手动提交事务

 

测试类:把对象变成表中的记录

一、         Configuration cfg=new Configuration().configure();//读取配置文件,xml

二、         SessionFactory factory=cfg.buildSessionFactory();//建立工厂,session工厂是重量级的,线程安全,很是耗时,原则上只建立一次。最好是单例+同步。

SessionFactory的做用:生成Session对象,二级缓存,而且能够配置缓存策略

三、         Session session=null;

session=factory.openSession();//经过工厂拿到Session对象,session是持久化管理器,session是线程不安全的。能够拿到多个session。

四、         session.beginTransaction();//开启事务(从数据库的链接池中拿到链接),session开启的事务是session级别的事务,本地事务,局部事务。(经过SessionFactory建立的事务是全局事务,能够被多个SessionFactory对象使用)

什么是事务?事务是对数据库的操做(一致性,原子性,持久性,隔离性)

五、         建立User对象,给User对象赋值

当配置中的<id><generator class =”uuid”/></id>时,本身设定的主键是没有用的。赋值后的user对象是瞬时态

六、         session.save(user);//session会为user分配主标识,uuid,j将user对象归入session的缓存,临时集合insertron中。 保存数据

七、         session.getTransaction().commit();//session 拿到事务后提交事务。在这里生成sql语句,将user对象中的数据存入数据库中对应的字段里。(操纵数据库

八、         session.getTransaction().rollback();// 发生异常就catch而且回滚

                                                             

Hibernate的优势:

代码量少,提升生产力

更加面向对象,对象化

可移植性好(改方言,改配置就能够啦)

支持透明持久化:轻量级,没有侵入性

 

事务:

事务分为总事务和子事务,子事务预提交,总事务发现其中一个子事务出错就回滚。

公共请求事务对象代理:不是真正的操做只是代理对象。-- > 中间件(代理完成事务的提交)

事务的边界:session.beginTransaction();

JDNI:java的名称目录接口,对链接池进行管理。

Hibernate能够处理多种事务(本地事务,跨资源事务,局部事务),提供统一的事务边界,(事务边界由session获取)

SessionFactory能够生成全局事务,供多个session使用,而且具备二级缓存功能。

 

Configuration:封装和管理hibernate的主配置文件。

一、         封装和管理 :数据库的底层信息<property>

二、         封装和管理:映射文件元信息(元数据)<mapping-resources>

获取方式:

一、         new Configuration();//拿到hibernate.properties文件

二、         new Configuration().configure();//拿到hibernate.hbm.xml的配置信息

 

SessionFactory:(session工厂也叫会话工厂)

SessionFactory经过Configuration建立,建立特色:cfg.buildSessionFactory();

SessionFactory功能:

一、         取得持久化对象session(factory.openSession())

二、         生成全局性事务(Tansaction) 事物共享控制安全性

三、         缓存(缓存元数据和不常修改的数据) 缓存后,通常不释放。

缓存定义:第一次读取后,第二次访问时已经存入内存,直接读取效率会高。

缓存目的:提升对数据库的访问效率

SessionFactory性能:

一、         SessionFactory属于重量级,耗时,通常只在初始化的时候建立一次PlugIn

二、         原则上一个Hibernate能够管理多个数据库,每一个数据库都配置同一个SessionFactory(可是这样系统效率低,通常一个sessionFactory只管理一个数据库)

三、         sessionFactory是线程安全的(支持多线程并发访问操做)

 

Session:持久化管理器

做用:完成对数据库的操做 ORM(对象关系映射)

Session的功能:

一、         Session一级缓存,具备缓存功能(缓存后才能对对象进行持久化操做)

二、         保证所管理的对象中的属性和数据库中的记录同步(自动update)

三、         Session能够取得事务(对session中缓存的数据完成实际的存取)

四、         Session做为HQL查询à生成的Query对象,执行hql语句(session.createQuery())

Query query = session.createQuery(“from User”); 其中User是实体类。该语句将把数据库中t_user表中的所有查询出来放到query对象中。

五、         Session能够从链接池中拿到链接(关闭session,链接释放)

六、        Session的主要做用是对数据库进行操做

Session的性能:

一、         线程不安全(操做完数据库就关闭session)

        

Lifecycle,Validatable:能够对User请求作监听,不使用的缘由是由于须要实现接口,太过于重量级了。

UserType(转换器),Interceptor(拦截器):都属于轻量级接口,对对象自动拦截,不多用,不须要实现Hibernate的任何接口和类

 

实际测试环境的搭建:

测试类和被测试类在同一个包下 src

测试类的路径和被测试类相同

 

使用Junit测试原则:

一、         测试类继承TestCase(Junit包下的一个类)

二、         测试类的类名 suffix=*Test(eg:UserTest)

三、         测试类的方法名:test开头,后面是正常的方法名(eg:testLogin())

四、         测试类的方法不能有参数和任何返回值

 

调试时在debug—> Junit

Log4j.propeties:是为了测试的时候打印日志信息。

 

持久化对象的生命周期:

对象的三种状态:瞬时、持久、离线

瞬时态(transient):new User(),刚new 出类的对象处于瞬时态,没有归入session的管理,数据库中没有对象的记录,事务提交后这种对象没法提交到数据库(和数据库无关联)

持久态(presist):session. save(user),这时候user对象处于持久态,session管理,具备主标识,事务对持久态的对象作操做(和数据库同步)

离线态(Detached):session关闭后对象状态从持久态变为离线态。不归入session管理中,对象中的数据在数据库中有对应的记录,对象发生变化,数据库中的值不会发生变化。(和数据库不一样步)

 

Junit测试和Debug调试

测试类的代码和hibernate的代码在同一个包下

 

Session缓存的特色:

Session session=factory.openSession();//经过session工厂拿到session对象

Variable à 查看actionQueue: session队列中有三个临时集合(数组)

一、insertions:elementData session.save();存放要插入的对象

二、updates :elementData session.update();存放要修改的对象

三、deletions:elementData,存放要删除的对象

Session事务一提交,先到insertion数组中查看是否有对象,若是有对象,就发送有关插入数据的sql语句:insert  into()

再从deleteions数组中查看是否有对象,若是有对象就发送关于删除的sql语句:delete()

再从updates数组中查看是否有对象,若是有对象就发送关于更新的sql语句:update()

这三个集合都是临时集合,发送完数据就会清空。

 

PersistanceContext:持久化容器

存放真正缓存中的数据,实体化类对象必定要和数据库中记录的状态一致。

数据结构为entityEntries 是一个Map

里面有个Map集合,进入 entries 里面有个table ,真正归入session的中的数据存在table里面。

 

局部事务:

一、         session.openTransaction();开启事务,事务一提交,就去找session的actionQueue中的数组,发现哪一个数组中有对象就执行其对应的语句,

二、         User user=new User();建立user对象,user.setName(“zhangsan”);,这时候的user对象没有id,(若是主键的生成策略是uuid,手动设定的id是无效的)这时候还的user对象处于瞬时状态,没有归入session管理

三、         session.save(user);执行这条语句,通常不会发sql语句,只是将user对象归入session管理,语句执行完后:session中的临时数组:insertions数组中会有数据。User会分为两份存储:

一份是instance :实例

一份是 state:状态,session按照state状态发送数据(执行insert语句)

Insert以后,若是发现实例和状态的数据不同,会立刻执行update语句

四、         session.save(user)后,查看persistanceContext à entityEntries à map à table(table是HashMap的元素) value有值就继续进去, 查看如下几个状态值:

existsInDatabase的状态为false,表示数据库中没有数据

这时候id已经被赋值(uuid)

loaderSate:存储状态,数据都存在这里了(这里的数据必须和数据库中的数据同步)这就是session缓存的数据。

目前的user对象处于持久态

处于持久态对象的特色:必定有ID(不是全部持久态的数据会存放在临时数组中,可是持久态的数据必定会在map集合中)

五、         user.setName(“tom”);归入session管理以后,变为持久态以后,修改持久态对象的值,

六、         session.commit();修改后的数据提交,

actionQueue下的insertions数组中:  instance实例中的值为:tom;

state状态的值为:zhangsan

persisstanceContext底下的map:数值仍是zhangsan

会按照insertions数组的state(状态)的值发送insert into (zhagnsan)到数据库中

insert语句执行完后,发现instance  和state 中的值不同时,就会再发一条update语句,保证一致。发完sql语句后,临时集合中的数据会所有清空。

七、         session.commit()状态:persistanceContext à entityEntries à map àtableà[0]àvalues

existsInDatabase的状态为true,表示数据库中已经有了相应的记录。这时候map中的name会改成tom(由于update了)

八、         session.close(),关闭session,把session中存放的实体对象所有清除,这时候user对象处于离线状态,user对象还有主键,并且这个主键和数据库中的主键同样。离线态的对象在数据库中有对应的记录

瞬时态:没有归入session的管理,数据库中没有对应的记录

离线态:没有归入session的管理,数据库中 有对应的记录。(只要主标识和数据库中的主键相同,主标识的class为asigned,就为离线态(new出来的对象也能够是离线态)),离线态的对象不能直接save()进入持久态。

九、        将离线态变为持久态:update(),saveOrUpdate(),lock(),从新开启session,拿到事务,session.update(user); <若是new 出来的对象,本身设定的id和数据库中主键相同,id标识为assigned,那么就只能用update方法,让该对象变为持久态>

十、      对象从离线态变为持久态,临时集合(insertions/updates/deletions)中 都 没有数据。persistanceContext--àMap集合有数据,可是map中只有一个id记录,数据库中也会有相应的数据。

十一、      session.commit();就算没有修改user对象的属性值,hiberante也会发送update语句。Update以后,user也被归入session管理

十二、      session.commit()后,再进入persistanceContext à entityEntries à map à table à[0]àvalues

loadedSate会有相应的user实体对象中的属性的记录了

瞬时态—》持久态:插入(save)

离线态—》持久态:更新( update )

 

加载:(查询)将数据库中的记录加载到对象中

一、         拿到session(经过静态方法 自定义的getSession())

二、         User user=(User)Session.get(User.class,”这里写数据库中的主键值”);//把关系模型转换为对象模型。经过User类中找到对应的表。(这里是由于session经过sessionFactory建立,sessionFactory经过Configuration建立,Configuration能够读取整个hibernate.hbm.xml文件中的配置信息,因此session能够找到对应的user.hbm.xml文件)找到User.hbm.xml文件。Session经过hbm.xml元数据找到对应的表。经过主键找到表中对应的那条记录,自动生成对象,而且自动将数据库中记录的字段设定到user对象中对应的属性中。

这里发送了一条sql语句:select * from user where id=”上面的id参数值”;

这就叫作加载(load())。这时候的user对象处于持久态。(和数据库同步)

三、         user对象已经进入持久态,归入session管理。这时候缓存域(三个集合 insertions/deletions/updates)中是没有数据的,persistanceContextà底下的map 的 value中会有数据。

四、         user.setName(“lisi”);//加载后,修改user对象中值。这时候session缓存的临时集合中没有数据,persistanceContext中的map中的数据也没有变化,user对象中的数据被修改了。 Session发现user对象中的值和map中的值不同时,会自动发update语句,保证数据同步

五、        session.commit();//修改后提交数据。Update以后,数据会变为和对象中的值同样。Session缓存中的数据也会变成和user对象中的值同样。

持久态对象特色:一提交就发update.肯定保证对象和数据库中数据同步。

处于持久态的对象在数据库中不必定有对应的记录。

session.commit()后:数据库中记录必定要和persistanaceContextàlocalState中的数据一致

使用get()方法加载数据的缺点:

一、若是从数据库中找到了指定ID对应的记录?

若是不须要使用User对象,会浪费内存

二、没有从数据库中找到ID对应的记录?

Hiberante就不会建立对应的User对象,会返回null。

再调用该对象的方法时,会出现NullPointerException

 

根据以上问题,修改方案:使用Load();

User user=(User)Session.load(User.class,”这里写数据库中的主键值”);//从这里引入懒加载。执行load方法后,不会生成sql语句,意味着尚未访问数据库。可是会生成Cglib 代理的User对象,这个User对象是目标User对象的一个子类对象。因此这里的User类不能定义为final类型。

Cglib代理的原理:建立目标类的子类对象。

这时候代理User对象的target 值为null;

user.getName();//调用代理对象的getName()方法,若是不为null,代理对象会调用父类的getName()方法。而后à发送sql语句à建立User对象。

 

本周做业:

理解hiberante:juint,debug,生命周期,缓存,

理解物料管理项目

 

Get方法查询数据:session.get(User.class,”对应的数据库中的表的ID”);

一、经过类找表

二、经过主键找记录

三、记录映射

无论数据存不存在都会发送select语句,若是找到了就会生成user对象,将对应的记录映射到对象的属性中,若是没有对应记录就返回null。(不支持懒加载)

使用get查询,会立刻发select语句,select * from user where id=””;

若是后面再也不修改User对象的值,就不会再发update语句了

若是修改user对象的值就会发送update语句。

这里主要比较user对象里面的值和loadedState里面的值,若是相同就不会发送update语句,若是不一样就会发送update语句。

 

懒加载:(最大的问题就是会产生懒加载异常)

使用时再访问数据库,不使用数据的时候就不会访问数据库

懒加载有类上的懒加载,集合上的懒加载,单端上的懒加载

懒加载不支持多态查询

悲观锁不支持懒加载

 

Load方法查询数据:session.load(User.class,”对应的数据库中的表的ID”);

不会立刻就发select语句,使用的时候才会发送select语句,若是不使用就不发送

一、         建立目标对象的cglib代理对象(目标对象的子类对象)

目标类User必需要有明确的无参构造方法,(为目标类建立代理对象,建立子类对象要先调用父类的无参构造方法),目标类不能是final类型

二、         对应的cglib代理对象下,有个target标识,标明目标对象是否生成,没有生成目标对象的时候 target=null;

三、         执行代理对象的user.getName()时,;先看目标对象产生了没有(判断target的值),若是产生了就执行目标对象的getName()方法,若是没有就从数据库中查出主键所标识的记录,根据记录生成目标User对象,再调用该User对象的getName()方法。(生成目标对象后,target=user(目标对象的地址))

Load()方法在数据库中找不到主键所标识的记录时,会产生异常 :ObjectNotFoundException,而不是NullPointerException。这个异常在load()的时候产生这个异常,可是使用目标对象的时候就会抛出这个异常。

Get()方法在数据库中找不到主键所标识的记录时,会返回null,再在使用目标对象时,会抛出NullPointerException.

 

Get和load:

共同点:经过ID(主键)去数据库中找相同的记录,生成对应的User 对象

不一样点:

一、         Get:不支持懒加载,立刻发送select语句

Load:支持懒加载,使用的时候才发送select语句

二、         若是使用get,发现主键在数据库中找不到对应的记录,不会生成user对象,也不会抛出异常,会返回null.(使用对象是抛出NullPointerException.)

若是使用load,发现主键在数据库中找不到对应的记录时, 使用时会抛出ObjectNotFoundException。

通常使用load,不多使用get.

 

删除:(删除的内容:数据库中的数据

先查 à 后删

User user=(User)session.load(User.class,”ID”);//对象处于持久态

session.delete(user);//在session的缓存persistanceContext中的map集合中有该对象,existInDatabase=true,loadedState有该对象;

临时集合中的deletions中有该对象,

User代理对象中也有数据。target=user;

 

session.getTransaction().commit();//这里发送delete语句(进入瞬时态)

临时集合deletions中数据被清空

Session里面的persistanceContextàmap中没有数据

数据库中的数据也没有

User对象依旧存在,target=user.

删除了User对象对应的数据库中的表的记录,删除后对象进入瞬时态。(不归入session管理,数据库中的记录也没有了)

(课堂做业:完成hiberante的简单的增删改查)

 

查看表中的全部记录:(Query

Query query=session.createQuery(“from User”);//hql语句,from后面跟的是类名。经过session建立Query对象,实参为hql语句。从User类所映射的user表中拿到每一条记录,封装到Query对象中。

Query对象的做用是:执行hql语句。

List list=query.list();//Query中的list方法,把Query封装的每条记录放到List集合中。

 

Hibernate的基本映射

若是主键为String类型,通常配置为uuid<generator class=”uuid”/>

关联映射:

<hiberante-mapping  package=”com.hibernate”>//多个实体类在同一个包下,这里配置了包名,使用时直接写具体类名就能够了

<class name=”User” table=”t_user”>//根据类名为User的实体类生成表,table:指定数据库中的表名为t_user

<id name=”id” column=”user_id” length=”33”>//定义主键名在数据库的表中的字段名为 user_id,长度位33,(最好不要修改主键字段长度,使用默认长度255

<generator class=”uuid”/>//标识主键生成策略为uuid

</id>

 

/* Name:标识在User类的属性

Unique:标识该字段在数据库的表中必须惟一(不能重复)

Not-null:标识该字段在数据库的表中不能为null

为空验证:user.setName(“”);这里不会产生异常,空串不为空。

若是直接不给name赋值,String类型默认值为null,就会产生NulPointerException

Length:指定该字段的最大长度

Column:指定该属性映射在数据库中的表中的字段名为 t_name

*/

<property name=”name” unique=”true” not-null=”true” length=”232” column=”t_name”>

 

</class>

<hibernate-mapping>

 

主键生成策略:(做用/目的:一旦实体类归入session的管理,session根据实体类的主键生成策略生成主标识,这个主标识就是数据库中的记录的主键

主标识:

业务主键和业务标识:(有意义的惟一标识)--学号

逻辑主键和逻辑标识:(没有意义的惟一标识),由数据库或者是Hibernate生成uuid

 

根据实体类的ID类型肯定主键生成策略:

若是是字符串,hibernate生成 。uuid

若是是int/long,由数据库生成。native

用<id>标签来标识主键,后面必需要跟<generator class=” 主键生成策略

”>

 

1、由数据库生成:

native:根据不一样的数据库自动使用不一样的主键生成策略。(不是真正的生成策略,只是选择对应的生成策略)

identity:(mysql,sqlserver)

sequence:(DB2,Oracle)

2、由程序生成:

increment(递增):多台应用服务器,可能会主键相冲,不多使用

uuid.hex:Hibernate采用这种方式生成主键,多态服务器同时生成主键,不会重复;

一、         IP地址

二、         JVM的启动时间(1/4秒)

三、         系统时间

四、         随机数(和指令计数器有关)

以上四种数据进行随机组合,最后生成32 uuid 码

3、用户本身维护主键:

assigned:用户本身输入

4、外部引用

foreign:依赖于另外一个实体类的主键。

 

主键生成策略为uuid: (主属性类型为String类型)

1三、      session.openTransaction();开启事务,事务一提交,就去找session的actionQueue中的数组,发现哪一个数组中有对象就执行其对应的语句,

1四、      User user=new User();建立user对象,user.setName(“zhangsan”);,这时候的user对象没有id,(主键的生成策略是uuid,手动设定的id是无效的)这时候还的user对象处于瞬时状态,没有归入session管理

1五、      session.save(user);执行这条语句,通常不会发sql语句,只是将user对象归入session管理,语句执行完后:session中的临时数组:insertions数组中会有数据。User会分为两份存储:

一份是instance :实例

一份是 state:状态,session按照state状态发送数据(执行insert语句)

Insert以后,若是发现实例和状态的数据不同,会立刻执行update语句

1六、      session.save(user)后,查看persistanceContext à entityEntries à map à table(table是HashMap的元素) value有值就继续进去, 查看如下几个状态值:

existsInDatabase的状态为false,表示数据库中没有数据

这时候id已经被赋值(uuid)

loadedSate:存储状态,数据都存在这里了(这里的数据必须和数据库中的数据同步)这就是session缓存的数据。

目前的user对象处于持久态

处于持久态对象的特色:必定有ID(不是全部持久态的数据会存放在临时数组中,可是持久态的数据必定会在map集合中)

1七、      user.setName(“tom”);归入session管理以后,变为持久态以后,修改持久态对象的值,

1八、      session.commit();修改后的数据提交,

actionQueue下的insertions数组中:  instance实例中的值为:tom;

state状态的值为:zhangsan

persisstanceContext底下的map:数值仍是zhangsan

会按照insertions数组的state(状态)的值发送insert into (zhagnsan)到数据库中

insert语句执行完后,发现instance  和state 中的值不同时,就会再发一条update语句,保证一致。发完sql语句后,临时集合中的数据会所有清空。

session.commit()状态:persistanceContext à entityEntries à map àtableà[0]àvalues

existsInDatabase的状态为true,表示数据库中已经有了相应的记录。这时候map中的name会改成tom(由于update了)

1九、      session.close(),关闭session,把session中存放的实体对象所有清除,这时候user对象处于离线状态,user对象还有主键,并且这个主键和数据库中的主键同样。离线态的对象在数据库中有对应的记录

瞬时态:没有归入session的管理,数据库中没有对应的记录

离线态:没有归入session的管理,数据库中 有对应的记录。(只要主标识和数据库中的主键相同,主标识的class为asigned,就为离线态(new出来的对象也能够是离线态)),离线态的对象不能直接save()进入持久态。

 

主键生成策略为native: (主属性类型为 int类型)

先看使用的数据库,而后根据数据库选择主键生成方式,若是是mysql数据库:identityà auto_increment

数据库自动生成主键:首先是主属性为int类型,大部分都是从1开始(基本上没有从0开始的数据)。

session.save(user);//就发送sql语句了,insert语句中不会插入ID,ID由数据库自动生成,人为设定的sql语句无效。发送sql语句:insert into(),目的是生成主键

一旦发送sql语句了,数据库中必定会有对应的记录。

主键生成策略为native,save后的特色:(进入持久态)

一、         session的临时集合中没有数据,(临时集合是为了发sql语句而存在的)

二、         session的缓存persistanceContext下的map下的existInDatabase=true;表示数据库中有数据,可是是脏数据。(查不到)

脏数据:未经证实和检测的数据

事务提交特色:先插入数据,再对数据进行验证,若是正确就提交到数据库中,若是有误就回滚。没有提交的数据叫作脏数据,mysql事务的传播特性是可提交读,看到的数据是提交后的数据,看不到脏数据,因此提交前在数据库的表中查不到数据。

session.getTransaction().commit();//commit()后才会检查数据的正确性。(对数据库中的脏数据进行确认,若是正确就将脏数据变为可用数据《在数据库中能够查到》,若是不正确就将脏数据回滚。)

主键生成策略为assigned(用户提供)

主键标识 为String类型

一、         不设置对象的ID值,直接save(),会从user对象中共取ID,发现没有会抛出异常

二、         2.一、设置ID值

2.二、   session.save(user);从瞬时态进入持久态:不发送sql语句,user对象归入session缓存,存放在session的临时集合中的insertions中,session的缓存persistanceContext下的map的loadedState下也会有数据。数据库中没有数据,existInDatabase=false;

2.三、   session.getTransaction().commit();临时集合中的数据清空,数据库中有数据。existInDatabase=true;

 

经过配置让数据库自动生成表:

<property  name=” hiberante.hbm2ddl.auto”>update</property>

 

 

做业:分析三种主键生成策略在session缓存中的异同:

主键生成策略为native和uuid,assigned的异同:

native根据数据库不一样而自动选择对应数据库的主键生成策略。好比:mysql数据库à主键生成策略为:identity.主属性类型为int类型。(用户手动设定的ID无效)

uuid将IP地址,JVM的启动时间,系统时间以及和指令相关的随机数相结合随机生成32位uuid码。主属性类型为String类型。(用户手动设定的ID无效)

assigned用户在建立对象的时候,手动设定主键(ID的值)。主属性类型为String类型,若是不设定ID 就会抛出异常。

session.getTransaction();//开启事务

 

User user=new User();//建立User对象

user.setID(“123”);

user.setName(“zhangSan”);

这时候三种主键生成方式(native,uuid,assigned)都相同: user对象处于瞬时态,没有归入session管理(临时集合和session缓存map中都没有user对象的相关数据)

 

session.save(user);

相同点:将user对象归入session管理,进入持久态。

native:(发送insert 语句)。临时集合中没有数据,session缓存的map下的table中有数据,loadedState中有user对象值,existInDatabase=true,user对象依旧存在,user对象的ID值变为数据库中自增的ID值。

uuid:临时集合insertions中的instance和state中都有数据,ID为hibernate自动生成的32位码,session缓存的map下的table中有数据,loadedState中有user对象值,existInDatabase=false,user对象依旧存在(ID为32位码,其他属性值不变)

assigned;临时集合insertions中的instance和state中都有数据,ID为用户手动设定的值,session缓存的map下的table中有数据,loadedState中有user对象值,existInDatabase=false,user对象依旧存在(值和设定的同样)。

 

user.setName(“Tom”);//修改进入持久态后的数据

相同点:user对象中的name属性值变为Tom,session 缓存的map下的table中的值不变。

native:临时集合中依旧没有数据。

uuid: 临时集合insertions中的instance 指定的name属性改变为 Tom, state指定的name属性依旧为zhangSan,

assigned: 临时集合insertions中的instance 指定的name属性改变为 Tom, state指定的name属性依旧为zhangSan。

 

session.getTransaction().commit();//提交事务

相同点:session缓存的map下的table中有数据,loadedState中user对象的name属性值为Tom,existInDatabase=true,

native:(发送update语句), user对象依然存在。

uuid: (发送两条sql语句,先insert,后update)临时集合insertions的数据清空, user对象依然存在。

assigned: (发送两条sql语句,先insert,后update)临时集合insertions的数据清空, user对象依然存在。

 

session.close();//关闭事务

相同点:session缓存map下的table中的数据被清空, user对象依然存在。user对象处于瞬时态。

native:临时集合中一直没有数据,

uuid:临时集合中数据被清空,

assigned:临时集合中数据被清空

 

 

Many-to-one

关联属性:多对一:(Many-to-one

关联属性的特色:

若是属性的类型时自定义类型,那么这个属性就是关联属性

若是属性的类型时集合或者数组,那么这个属性有多是关联属性

对象模型经过关联属性创建关联关系

关系模型的关联关系经过外键创建关系

谁往外键里存放数据,谁就在维护关系。

 

核心技术:配置*.hbm.xml

 

关联属性的映射:(外键)

<many-to-one name=”group”  column=”groupid” cascade=””/>

column=”groupid”:这个值的设定参照group属性对应的Group对象的id属性。

这是怎么知道引用的表是group表的主键?

由于User实体类中,有Group类型的group属性。根据关联属性找到引用的外键表。

 

存储数据:(主键生成策略都为native)

Group group=new Group();//先建立Group对象,

group.setName(“zte”);// 给该对象的属性赋值,(id自动分配)

session.save(group);//发送insert into 语句,取得id

 

将Group对象的地址赋值给User对象的group属性中。

User user=new User();//再建立User对象,

user.setName(“zs”);// 给该对象的属性赋值,(id自动分配)

user.setGroup(group);//经过user拿到group属性所指的Group对象,取出该Group对象的id值,将取到的id设定到groupid字段中。(groupid是 hbm.xml文件中 many-to-one 标签中的name指定的字段名)

session.save(user);//数据库发送sql语句,insert into ,生成id。将user对象属性的属性值设定到数据库中的user表中的对应的记录的各个字段中。

 

cascade(级联属性): all,update-save,delete,none

 

加载:(将关系模型转换为对象模型)

User user=(User)session.load(User.class,1);

将从数据库中查出来的记录,将字段依次赋值给User对象的对应属性。

Many-to-one:经过外键(groupid)找到对应的 Group对象对应的group表中的记录,根据记录再建立Group对象赋值,将Group对象设定到User对象的group属性中。(经过外键找主键)

 

user.getName();//zs,这叫类上的懒加载,只发送一条sql语句,只查询user表

user.getGroup().getName();//这就叫单端上的懒加载,用到两张表的时候查询两张表

单端上的懒加载在外键设定的标签上,<set><many-to-one>

外键能够为空,若是不为空,必定是所关联表的有效值(通常为关联表的主键)

TransientObjectException:持久态对象引用了瞬时态对象

若是不给Group对象的id属性设值,那么默认值为0 ,

不save Group对象。Group对象为瞬时态。id依旧为默认值0

建立User对象,给user对象的各个属性赋值。

session.save(user);//发送sql语句,insert into

将Group对象的id属性值(0)设置到User对象中的group属性所指Group对象的id ,对应数据库中的字段为groupid上。

Mysql数据库的主键不能为0 ,因此这就违反了参照完整性。

session.getTransaction().commit();

提交,就检查数据,发现错误就回滚。(commit之前的数据都是脏数据

这里的user对象处于持久态,user对象使用了瞬时态数据 group对象,瞬时态的对象的id是无效值。因此会出现TransientObjectExecption.

主要缘由:持久态对象引用了瞬时态对象,就会出现TransientObjectException

结论:持久态对象不能引用瞬时态对象。

解决办法:

  1. 1.   建立group对象的时候就session.save(group);

2、添加级联属性。

<many-to-one name=”group” column=”groupid” cascade=”all”>//持久态对象引用瞬时态对象,加了cascade时,会自动先把瞬时态对象save进入持久态。在执行session.save(user)这条语句时,会先执行 insert into group(),再执行insert into user();

 

cascade:(级联属性)

cascade的取值:all、none、save-update,delete

cascade的做用:存储,而不是为加载服务的。

级联:两个对象中的操做联动性,对一个对象操做后,对其指定的级联对象也须要执行相同的操做。(对象的连锁操做)

all:全部状况下执行级联操做

none:任何状况下都不执行级联操做

delete:在删除的时候执行级联操做(容易出错)

save-update:在保存和更新的时候执行级联操做

 

Hql语句对全部的数据库都同样:

Query query=session.createQuery(“from User user where user.group.name=’zte’ ”);//从数据库中找到User类对应的t_user表,从user表中找到user对象的group属性所指定的Group对象对应的t_group表,找出t_group表中的name=‘zte’的那条记录。

至关于sql语句;

select * from t_user ,t_group

where t_user.groupid=t_group.id

and  t_group.name=’zte’;

 

One-to-one:(一对一)

一对一单向关联:

对象模型:

Person: (类)

int id;

String name;

IdCard idCard;//关联属性

 

IdCard:(类)

int id;

String cardNo;

 

关系模型:

Person表和idCard表的主键相同,一 一对应

(两个表的主键相同并不意味着两个表有关系)

 

三大问题:(在*.hbm.xml文件中体现)

一、如何创建关系

二、如何存储关系

三、如何加载关系

 

创建关系:(在数据库中建表时)

t_person:(表)

具备关联属性的那个对象生成对应的表时,须要设定主键生成策略为 foreign:t_person表中这样设定,使得对象一 一对应。

<id name=”id”>

<generator class=”foreign”>

<param name=”property”>idCard</param>//引用的是idCard属性的所指的IdCard对象的主标识 (id属性值)

</generator>

 </id>

一旦归入session的管理,person对象的id须要和IdCard的id相同。(经过session保证一对一关系:执行save语句的时候将IdCard对象的id赋值给person对象的id)

必须加约束: <one-to-one name=”idCard” constrained=”true”/> 建立t_person表的时候不会建立idCard字段,这里表示引用的外键是当前t_person表的主键,外键参照idCard属性对应的IdCard对象所映射的t_idCard表。

一、        创建关系:表示当前person类所映射的表的主键就是外键,

二、        存储关系:外键参照idCard属性对应的类所映射的表生成

三、        加载关系:one-to –one ,经过主找主,为当前映射属性idCard赋值

若是不添加 constrained=true,在建立表的时候会缺乏 foreign key(id) references t_idCard (id)语句,就不会有外键。这样t_person表和t_idCard表就没有任何关系了。

 

t_idCard:(表)

和普通表同样,没有外键,主键为id.<id name=id>

<generator class=”native” /></id>

 

存储关系:(在生成对象时创建存储关系)

一、         先建立IdCard对象,

IdCard idCard=new IdCard();

idCard.setCardNo(“123”);

session.save(idCard);//发送sql语句,生成id主标识,归入session管理

二、         建立Person对象

Person person =new Person();

person.setName(“tom”);

person.setIdCard(idCard);

session.save(person);//这里不发送sql语句,(由于不须要数据库为之生成id)直接从Person对象的idCard属性所指的IdCard对象中拿到主标识(IdCard对象中的id属性值)设为Person对象的主标识(id属性)。从这里创建了对象和对象之间的一对一关系。临时集合中有数据(insertions),数据库中没有数据。

commit()就把Person的id属性映射到t_person的id字段上。

主标识:为对象中的id

主键:为数据库表中的id

 

三、        t_person表在数据库中存储Person对象的idCard(关联)属性的时候:把关联属性的值存在数据库的表的外键上。 往对应的外键设值,经过映射发现,存储idCard属性的时候,外键就是主键 id。往主键id上设定值,t_person表的主键id值来自于idCard属性所指的IdCard对象映射的t_idCard表的主键(id).

 

One-to-one:主键关联,自动级联。cascade=”all”

倘若在session.save(person);语句执行以前,不执行session.save(idCard)语句,

IdCard对象尚未归入session管理,也没有为 IdCard对象分配主标识,IdCard对象处于瞬时态。

一、         执行session.save(person)时,发现IdCard没有主标识,Person对象没法拿到主标识。

二、         自动先发送IdCard的sql语句,insert into t_idCard (),先为IdCard对象分配主标识(IdCard对象归入session管理,进入持久态,IdCard在数据库中有数据)

三、         将IdCard的主标识赋给Person对象的主标识,Person对象归入session管理,进入持久态。Person在数据库中没有数据,由于person尚未发送sql语句,因此Person对象在临时集合insertions中有数据。

 

加载关系: one-to-one(主找主)

Session.load(Person.class,1);

经过t_person表的主键找到所关联的t_idCard表的主键对应的记录,建立IdCard对象,并将该IdCard对象赋值给Person对象的idCard属性。其他属性直接赋值。

 

Session不容许同一个类的不一样对象使用相同的ID

若是两个不一样的对象引用同一个对象的id,会抛出异常。

例如:

IdCard idCard=new IdCard();//建立Idcard对象

idCard.setCardNo(“123”);

session.save(idCard);//发送sql语句,生成id主标识,归入session管理

 

Person person =new Person();//建立第一个Person对象

person.setName(“tom”);

person.setIdCard(idCard);

      session.save(person);//不会发送sql语句,将IdCard对象的主标识id给该Person对象

 

Person person1 =new Person();//建立第二个Person对象

person.setName(“lisa”);

person.setIdCard(idCard);//引用同一个IdCard对象

      session.save(person1);// //不会发送sql语句,将IdCard对象的主标识id给该Person对象,因此在这里会抛出异常,由于one-to-one不一样对象不能引用同一个对象的id.

 

一对一主键双向关联: 两张表中的数据能够相互查询

IdCard.hbm.xml中:主键生成策略为native

这里的person在实体类中为Person类型。

<one-to-one name=”person”>:单独使用不会在建立表的时候不会指定外键,也不能往外键中设值,只会加载关系,给Person属性赋值。

person 不是关联属性,

 

Person.hbm.xml中:主键生成策略为foreign

这里的idCard在实体类中为IdCard类型。

<one-to-one name=” idCard” constrained=”true”>:指定外键就是主键,外键的生成参照idCard属性对应的IdCard类映射的idCard表。还能够加载。

idCard为关联属性,

 

创建关系:两张表的关系依靠Person类中的idCard属性创建关系。

维护关系:存储Person的idCard属性时,外键就是t_person的主键,维护关系。

加载关系:

session.load(IdCard.class,1);

IdCard类中的person属性经过one-to-one(主找主),到t_person表中找到对应的记录,建立Person对象给IdCard类的person 属性赋值。

 

不是全部的类中的自定义类型都是关联属性,当前IdCard类中的person属性就不是关联属性,

 

 

一对一惟一外键单向关联:(外键 unique=”true”

让外键惟一,两条记录即是一 一对应。

Person的配置:使用 many-to-one.配置

<generator class=”native”/>

<many-to-one name=”idcard” unique=”true”>//在映射关联属性建立关系的时候会在表中建立字段idCard,关系经过many-to-one :建立外键,外键参照idCard属性的类所映射的t_idcard表。 设定unique=”true”,使得外键惟一。

<many-to-one name=”idcard”>:必定要先save所引用的对象,它不会自动级联,在数据库的表中会生成idcard字段。

<one-to-one name=”idcard”>指定主键就是外键,能够自动级联,在数据库的表中不会生成idcard字段。

 

创建关系:

<one-to-one name=”idcard”>至关于:<many-to-one unique=”true” name=”idcard” cascade=”all”> 由many-to-one创建关系,指定idcard为外键。

 

加载关系:

Many-to-one:找到idCard在t_idcard表中对应的记录,生成IdCard对象,设定到Person对象中的idcard属性上。

 

存储关系:

Person存储关系:存储Person的idcard属性值,往数据库的idcard字段设值是存储,由于idcard字段是外键。(若是往不一样的对象的idcard属性中存同一个的idcard对象,因为设定了unique=”true”,就会抛出异常,外键重复。)

在这时候 持久态对象引用瞬时态对象,因为瞬时态对象的默认id值为0,不影响数据库的存储,在save()的时候不会产生异常,commit()的时候数据库就会检测0为非法数据就会抛出异常。

 

一对一惟一外键的双向关联:

Person表的映射不变:

int id;

IdCard idcard;//关联属性,创建关系,维护关系,加载关系(外键

 

<id name=”id”><generator class=”native”/><id>

<many-to-one name=”idcard” unique=”true”>

 

 

IdCard表的映射:

int id;

String cardNo;

Person person;//不创建关系,不维护关系,只加载关系(非外键

 

<id name=”id”><generator class=”native”/><id>

<property name=”cardNo”/>

<one-to-one name=”person” property-ref=”idcard”/ >

property-ref=”idcard”:将位置指针挪到t_person表中的idcard属性上。

property-ref表示引用person属性对应的Person对象所映射的t_person表中的idcard字段。

 

加载关系:

给IdCard的person属性设值,one-to-one

主找主找不到对应的数据,property-ref=”idcard”,位置指针挪到idcard字段上,找到idcard字段对应的那条记录,生成Person对象,将该person对象赋值到person属性中,

 

MyEclipse快捷键:

Ctrl+shift+T:查找类

Ctrl+F:查找类中的具体方法

 

 

one-to-many

one-to-many 单向关联:

一对多:由一的一方来创建关系,存储关系,使用关系(加载)。

外键必定在多的一方。

一怎么管理多:创建集合,管理多方(Set集合)

为何不是List集合和map集合,由于Hibernate为Set集合重写了懒加载。

一个班级多个学生

Classes 类:(一方)外键关系建立方、维护方、使用方

int id;

String name;

Set students;

 

Student类:(多方)外键关系存放方

int id;

String name;

 

Set 集合能够存听任意类型的数据。(关联属性)

 

创建关系:

t_classes表在映射关联属性的时候,会建立外键,而且把外键设定在t_student表中,因此t_student表中有classesid字段。可是这个外键的关系由Classes维护。

 

Student.hbm.xml

<id name=”id”><generator class=”native”/></id>

<property name=”name”/>

 

Classes.hbm.xml

<id name=”id”><generator class=”native”/></id>

<property name=”name”/>

<set name=”students”>

       <key column=”classesid”/>

       <one-to-many class=”cn.hiberante.bean.Student”/>

</set>

一、         用set标签映射关联属性,当前Classes对象中的students属性

二、         建立Classes对象的时候在Set集合中存放 Student类型的数据

三、         建立外键字段 key column=”classesid”,加在<one-to-many>指定的class àStudent类所映射的t_student表中

四、         增长的字段classesid为外键,经过 one-t-many:外键与当前表创建关联,外键参照t_classes表

 

经过上面的映射:

t_student表中有三个字段, id,name,classesid;

t_classses表中有两个字段:id,name

 

 

维护关系:(关系存储,往外键里面设值) native

1、建立多个学生对象(两个或者两个以上)

Student stu=new Student();

stu.setName(“tom”);

session.save(stu);//id 生成,name为tom,外键classesid为空,进入持久态,归入session管理(外键能够为空) insert into t_student(name) values(?);

 

Student stu1=new Student();

stu.setName(“Jam”);

session.save(stu1);//id 生成,name为Jam,外键classesid为空,进入持久态,归入session管理(外键能够为空)insert into t_student(name) values(?);

 

 

2、建立Set集合,将Student对象添加到集合中

Set students =new HashSet();

students.add(stu);

student.add(stu1);

3、建立Classes对象

Classes classes=new Classes();

classes.setName(“java1”);

classes.setStudents(students);//将建立的Set集合students设到Set集合类型属性students

session.save(classes);//发送sql语句,id自动生成,把Classes的name属性存在t_classes表中的name字段上。insert into t_classes(name) values(?);

关联属性的存储:(外键设值)

一、         从students这个Set集合中拿到第一个元素stu,取得stu对象

二、         拿到stu对象主标识,(由于stu已经save过了,因此能取到该对象的主标识)

三、        经过stu对象的主标识找到数据库中t_student表中对应的记录

四、         存储的时候拿到Classes对象的主标识id,(由于classes对象已经save)

五、        将Classes对象的主标识(id值)设定到stu对象对应的t_student表中的对应记录的classesid字段上。

第二个元素stu1和第一个同样;

 

session.beaginTransaction().commit();//提交后会发送update语句,有多少个学生对象会发送多少条update语句,update t_student set classesid=?where id=?;

 

加载关系:(将数据库中查出来的记录给对象赋值)

session.load(Classes.class,1);

classes.getName();//直接取出记录中的name字段的值设置到name属性中

classes.getStudents();

one-to-many,主找外:(一个对应多个)

一、         经过主键到对应的t_student表中找到外键,找到对应的第一条记录

二、         建立Student对象,

三、         建立Set集合(有记录时才会建立)

四、         将Student对象add到Set集合中

五、         而后继续经过主找外,依次找到全部的Student对象都分别add到Set集合中

六、         最后把Set集合设置到Classes对象的students属性上。(students属性为Set类型)

One-to-many单向映射的缺点:

一、         若是t_student表中的classesid字段设置为非空,就没法保存数据

二、         由于不在Student这一端维护关系,全部Student不知道是哪一个班的

三、         须要发出多余的update语句(数据库负载大)

 

one-to-many 双向关联:

 

创建关系:

Student

int id;

String name;

Classes classes;

 

Student.hbm.xml文件:

<id class=”id”><generator class=”native”/></id>

<property name=”name”/>

<many-to-one name=”classes” column=”classesid”>

让外键重名,保证关系维护的一致性。

 

Classes 类:

 int id;

String name;

Set students;

 

Classes.hbm.xml文件:

<id class=”id”><generator class=”native”/></id>

<property name=”name”/>

<set name=”students” inverse=”true” cascade=”all”>

<key column=”classesid”/>

<one-to-many class=”cn.hiberante.bean.Student”/>

</set>

 

Key指定的字段名必须和<many-to-one name=”classes”  column=”classesid”>指定的同样

外键的关系由一端维护,两头使用关系。

双向关联时,由多的一方存储关系,维护关系。

<set name=”students”  inverse=”true” cascade=”all”>

<key column=”classesid”/>

<one-to-many class=”cn.hiberante.bean.Student”/>

</set>

让多的一方(Student)维护关系。添加inverse=”true”

 

维护关系:(关系存储,往外键里面设值)

1、建立多个学生对象(两个或者两个以上)

Student stu=new Student();

stu.setName(“tom”);

session.save(stu);//id 生成,name为tom,外键classesid为空,进入持久态,归入session管理(外键能够为空)Student能够维护关系可是不维护关系。Classes不设值就为null. insert into t_student(name,classesid) values(?,?);

 

Student stu1=new Student();

stu.setName(“Jam”);

session.save(stu1);//id 生成,name为Jam,外键classesid为空,进入持久态,归入session管理(外键能够为空)Student能够维护关系可是不维护关系。Classes不设值就为null. insert into t_student(name,classesid) values(?,?);

 

2、建立Set集合,将Student对象添加到集合中

Set students =new HashSet();

students.add(stu);

student.add(stu1);

3、建立Classes对象

Classes classes=new Classes();

classes.setName(“java1”);

classes.setStudents(students);//将建立的Set集合students设到Set集合类型属性students

session.save(classes);//发送sql语句,id自动生成,把Classes的name属性存在t_classes表中的name字段上。insert into t_classes(name) values(?);

这时候Classes想维护关系,可是看到inverse=”true”,就没法维护关系,Student能够维护关系可是不维护。建立的表 t_student 中的 classesid列为null.

Inverse的做用:(值为true/false

一、         指定谁反转另外一方来维护关系

二、         主要使用在set集合标签上

三、         主要用于存储关系

 

正确流程:

先建立classes对象,只给name属性设值;(id,set集合都不设值)

再建立Student对象,每一个值都设值(id,name,classes)

再建立Set集合将Student对象add到set集合中

session.save(classes);//insert into t_classses(name) values(?);

 

inverse=true:

设定由另外一端往外键中设值。(对方来维护关系)

用在Set标签上

用在存储上(加载用不着inverse)

一般inverse后面会加 cascade 级联属性

cascade=all

session.save(classes)当Classes对象准备维护关系的时候,inverse属性为true,让Student属性维护关系.因此就自动发送session.save(stu);就建立Student对象的 id主标识,往外键设值(Student对象的classid属性)à拿到classes所指的Classes对象的主标识设到classesid上。

优势:没有发送多余的sql语句(update

 

Inverse和cascade

一、         都是进行存储的时候使用

二、         inverse à翻转,指定由对方来存储关系

三、         cascade在存储数据之前,若是须要存储另外一种数据,就会自动的发送save语句,先存储另外一种数据再save当前对象。

四、         inverse:是关联关系的控制方向,用在set标签上

五、         cascade操做上的连锁反应,用在 one-to-one,many-to-one,one-to-many标签上

 

 

周末任务:

一、         整理出Hibernate案例,把笔记分析都添加到对应的案例上

二、         Many-to-one *

三、         One-to-one *

四、         One-to-many *

五、         Many-to-many

六、         单继承映射关系

七、         看Oracle视频,学会Oracle数据库的基本操做 建表以及增删改查

八、         复习一下struts1.0,尽可能完善本身的struts框架

九、         找到Hibernate源码,看看源码,加强本身对映射关系的理解

 

Many-to-many:(多对多)

多对多---单向关联:

示例:一我的能够有多个角色,一个角色能够由多我的担当

从关系模型中体现多对多关系:建立中间表

中间表的特色:

至少有两个外键,外键分别参照两张表的主键

这两个外键共同生成主键à联合主键

两个外键的值都由一个对象的关联属性设定,关联属性为Set集合类型。

 

创建关系:

Role:(类)

int id;

String  name;

Role.hbm.xml

<id name=”id”>

<generator class=”native”/></id>

<property name=”name”>

 

User (类)

int id;

String  name;

Set roles

 

Roles属性映射指定:

一、         Set里面放什么类型的数据

二、         建立第三张表 t_user_role

三、         往第三张表中添加两个字段(两个外键分别参照两张表)

 

User.hbm.xml

<id name=”id”>

<generator class=”native”/></id>

<property name=”name”>

<set name=”roles” table=”t_user_role”>

<key column=”userid”/>

<many-to-many class=”cn.hibernate.Role” column=”roleid”>

</set>

一、         指定roles的Set集合中存放cn.hibernate.Role类型的数据

二、         Many-to-many, 须要建立中间表,映射时roles属性时,建立t_user_role表

三、        <key column=”userid”/>指定外键字段userid,把这个字段添加到t_user_role表中

四、         userid这个外键字段 参照当前对象所对应的表t_user表

五、         在many-to-many 后面的column=”roleid” 指定外键字段 roleid, 把这个字段添加到t_user_role表中

六、         roleid这个外键字段参照class指定的Role所映射的t_role表

七、         userid和roleid为 t_user_role表的联合主键,分别参照t_user和t_role表

 

存储关系:

先建立Role对象:

Role role1=new Role();

role1.setName(“zs”);

session.save(role1);//insert   into t_tole(name) values (?);

Role role2=new Role();

role2.setName(“ls”);

session.save(role2); //insert   into t_tole(name) values (?);

 

再建立User对象

User  user=new User();

user.setName(“wife”);

 

建立Set集合,将Role对象添加到set集合中

Set set=new HashSet();

set.add(role1);

set.add(role2);

 

user.setRoles(set);//把存储Role对象的Set集合设定到roles属性中

session.save(user);//发送三条sql语句,在t_user_role表中建立两条记录

//insert   into t_user (name) values (?);

//insert   into t_user_role(userid,roleid) values (?,?);

//insert   into t_user_role(userid,roleid) values (?,?);

 

在当前对象Set集合中有几个元素,就会在t_user_role表中建立几条记录

 

id自动生成,name字段直接映射

存储User对象的关联属性roles:

从roles的Set集合中拿出第一个元素role1,拿到role1对象的主标识,

将role1对象的(主标识)id,设到t_user_role的roleid字段上

拿到当前User对象的id, 设到t_user_role的userid字段上

 

从roles的Set集合中拿出第二个元素role2,拿到role2对象的主标识,

将role2对象的id主标识,设到t_user_role的roleid字段上

拿到当前User对象的id, 设到t_user_role的userid字段上

 

加载关系:

关联属性的加载,给关联属性roles赋值:

一、         many-to-many:分红一对多(one-to-many),和多对一(many-to-one);

二、         one-to-many(主找外),经过t_user表中 id 找找t_user_role表中对应的userid

三、         经过userid自动找到 roleid字段

四、         many-to-one(外找主),经过roleid字段找到t_tole表中的对应的id指定的行生成记录

五、         根据记录生成Role对象,设到Set集合中

六、         继续重复2-5(直到找完t_user表中指定id对应的t_user_role表中的全部记录)

七、         将赋完值后的Set集合设定到roles属性中

 

 

多对多---双向关联:

 

创建关系:

Role:(类)

int id;

String  name;

Set users

Role.hbm.xml

<id name=”id”>

<generator class=”native”/></id>

<property name=”name”>

<set name=”users” table=”t_user_role”>

<key column=”roleid”/>

<many-to-many class=”cn.hibernate.User” column=”userid”>

</set>

 

User (类)

int id;

String  name;

Set roles

 

Roles属性映射指定:

四、         Set里面放什么类型的数据

五、         建立第三张表 t_user_role

六、         往第三张表中添加两个字段(两个外键分别参照两张表)

 

User.hbm.xml

<id name=”id”>

<generator class=”native”/></id>

<property name=”name”>

<set name=”roles” table=”t_user_role”>

<key column=”userid”/>

<many-to-many class=”cn.hibernate.Role” column=”roleid”>

</set>

 

<set name=”” table=”t_user_role”>//这里table指定的表必须同样

两张表均可以维护关系,

User维护关系,set集合中存放Role类型的数据,

key column=”userid”,参照 t_user表

<many-to-many class=”cn.hibernate.Role” column=”roleid”>

roleid参照 class指定的类Role对应的t_role表

 

Role维护关系,set集合中存放User类型的数据,

key column=”roleid”,参照 t_role表

<many-to-many class=”cn.hibernate.User” column=”userid”>

user参照 class指定的类User对应的t_user表

 

建立关系能够双方建立,

维护关系只能一方维护

使用关系能够双方使用

 

存储关系:

存储的时候,只能由一方维护,只让一方给关联属性设值,设值的一方就是在维护关系。

 

加载关系:

和单向关联相似

关联属性的加载,给关联属性roles赋值:

一、         many-to-many:分红一对多(one-to-many),和多对一(many-to-one);

二、 one-to-many(主找外),经过t_user表中 id 找找t_user_role表中对应的userid

三、         经过userid自动找到 roleid字段

四、         many-to-one(外找主),经过roleid字段找到t_tole表中的对应的id指定的行生成记录

五、         根据记录生成Role对象,设到Set集合中

六、         继续重复2-5(直到找完t_user表中指定id对应的t_user_role表中的全部记录)

七、         将赋值后的Set集合设定到roles属性中

 

 

单表继承映射

创建关系:

一个父类,两个子类

一个hbm.xml文件

Hibernate默认 lazy=”true”

Extends.hbm.xml文件的配置:

<!— Animal为父类,具备id,name,sex三个基本属性-->

<class name=”Animal” table=”t_animal” lazy=”true”>

<id name=”id”><generator class=”native”/></id>

 

<!--注意元素的顺序:discriminator要在property的上面-->

<!—discriminator :为鉴别器,指定type为鉴别器字段(not-null=true)。string是Hibernate定义的String类型。在t_animal中增长type字段。类型为string,在数据库中会转换为varchar类型。自动把 discriminator-value的值自动设定到该字段上。

-->

<discriminator column=”type”  type=”string”/>

<property name=”name”/>

<property name=”sex”/>

 

<!—Pig继承了Animal,为子类,Pig子类有新增的weight属性

discriminator-value:指定鉴别值,设到鉴别器字段中

-->

<subclass name=”Pig” discriminator-value=”P”>

<property name=”weight”/>

</subclass>

 

<!—Bird继承了Animal,为子类,Bird子类有新增的height属性

discriminator-value:指定鉴别值,设到鉴别器字段中

-->

<subclass name=”Bird” discriminator-value=”B”>

<property name=”height”/>

</subclass>

</class>

 

Discriminator(鉴别器)的做用:加载时,自动识别记录生成对应的对象类型。

 

存储关系:(识别子类类型,自动往鉴别器字段加鉴别值)

Pig pig=new Pig();

pig.setName(“xiaohua”);

pig.setSex(true);

pig.setWeight(100);

session.save(pig);//id自动生成

一、经过Pig这个类,找到映射文件中的子类subclass name=”Pig”

二、经过subclass子类找到父类class=”Animal”,

三、根据class=”Animal”找到对应的表 t_animal,

四、往Pig对象设定到t_animal表中id指定的记录上的的各个字段。没有height属性的就设为null,

五、type字段的设值:看到是subclass,就往type鉴别器字段自动设 discriminator-value指定的值。(这个值Hibernate根据配置信息自动设定)

 

 

加载关系:

 

加载子类对象:

session.load(Pig.class,1);// Sql:select * form  t_animal where id=1 and type=’P’.

经过*.hbm.xml文件中找,经过Pig找到t_animal表

到表中找到id=1对应的记录

在查找的时候会自动添加查找条件 type=’P’.

生成的记录对应的对象为Pig类型

 

加载父类对象:

Animal animal =(Animal)session.get(Animal.class,1);

if(animal instanceof Pig) ==true

到t_animal表中查出id=1的记录,拿到全部字段,

发现type=”P”,知道应该建立Pig类型的对象。

 

load方法查询不支持多态查询,get方法查询支持多态查询。

Load支持懒加载,不支持多态查询:由于load拿到的是cglib代理对象,代理类不是真正的实例 没法用instenceof判断类的归属

Query支持多态查询,

session.createQuery(form Animal);

拿出全部的记录而且生成对应子类对象。(指定类型)

 

多态查询:单表查出数据,Hibernate能够自动鉴别类的数据类型(指定生成对象类型)。

单表继承惟一缺点是:多了冗余字段。

若是关闭懒加载,load就支持多态查询。(直接发送sql语句)lazy=false,默认状况下lazy为true

 

多表继承映射:

继承关系:每个具体的类映射成一张表

Animal为父类,Pig为子类。

创建关系:(创建三张表,t_animal,父类对应的表中有三个字段,t_pig,t_bird子类对应的表中只有两个字段,主类的主键也为外键,参照父类的主键。对应子类和父类主键保持一致)

映射文件:

<class name=”Animal” table=”t-animal”>

<id name=”id”>

<generator class=”native”/>

</id>

<property name=”name”/>

<property name=”sex”/>

 

<joined-subclass name=”Pig” table=”t_pig”>

<!—pid是当前表t_pig的主键,也是外键参照父类的t_animal表。-- >

<key column=”pid”/>

<property name=”weight”/>

</joined-subclass>

</class>

 

存储关系:

Pig pig=new Pig();

pig.setName(“hua”);

pig.setSex(“male”);

pig.setWeight(100);

session.save(pig);

一、经过子类Pig找到父类Animal,再找到Animal对应的表t_animal

二、先往父类的表中存数据,t_animal(id,name,sex)

三、再往子类的表中存数据,将t_animal的id设到t_pig表中的pid字段。t_pig(weight,pid)

<从t_animal中拿到主键做为当前t_pig表的主键(由于pid即便主键也是外键)>

 

加载关系:

Pig pig=(Pig)session.load(Pig.class,1);

一、先从t_pig中找对应记录,拿到该记录的主键(先找子表),找到weight字段,设到weight属性中。

二、再经过外找主,找到t_animal中对应的记录,将查到的记录设到pig对象中。Sex,name.

 

优势:结构清晰,一个类一张表

缺点:查找效率低,(须要多个表关联查询)

 

继承映射:每一个子类一张表,父类没有对应的表

缺点:主键生成策略不能为native,只能为assigned

Animal为父类,Pig为子类。

创建关系:

<!-- abstract=”true”:表示父类不生成表,映射的属性字段被子类继承,让子类应用-- >

<class name=”Animal” abstract=”true”>

<id name=”id”>

<!—这里只能为assigned-- >

<generator class=”assigned”/>

</id>

<property name=”name”/>

<property name=”sex”/>

 

<union-subclass name=”Pig” table=”t_pig”>

<property name=”weight”/>

</union-subclass>

</class>

只有t_pig表,子类的映射的表不只有本身的属性对应的字段,还会有父类的属性对应的字段。(weight+name,sex)

 

存储关系:

Pig pig=new Pig();

pig.setId(2);

pig.setName(“hua”);

pig.setSex(“male”);

pig.setWeight(100);

session.save(pig);

/*这里不会发送sql语句,由于主键生成策略为assigned,发sql语句是为了拿到主键。这里的主键已经本身手动指定,全部不须要发送sql语句。

Pig对象已经归入session管理,id为2.。*/

若是再建立一个Bird,

Bird bird=new Bird();

bird.setId(2);

session.save(bird);//这里就会抛出异常,由于id已经在session中存在。

同一个类的不一样对象,一旦归入session管理,id必须不一样。

同一个类的不一样子类对象,id也必须不一样。不然会出异常。

 

 

关于主键生成策略:

父类的属性会被子类继承,映射在子类的字段中,

父类的主键生成策略会被子类继承。

若是为native或者是uuid,有数据库或者hibernate自动生成,只生成一次。

session.save()的时候,会为父类对象生成主标识

同一个id会被父类的全部子类都继承做为子类的主标识id,就会形成多个子类对象引用相同的主标识,就会抛出异常。同一个类的不一样子类对象,id必须不一样。

 

单表继承:

优势:效率高

缺点是数据冗余

 

一个类映射一张表:

优势:结构清晰,一个类一张表

缺点:查找效率低,(须要多个表关联查询)

 

一个子类映射一张表:

优势:结构清晰,效率高

缺点:主键不能自动生成,必须制定为assigned(不符合hibernate

建议使用第一种,单表继承.

 

物料项目:

持久层:继承映射和关联映射同时使用

用户需求: 主键生成策略为assigned

鉴别器字段 discriminator column=”category” type=”string”

 

存储关系:(继承关系的表中的数据的存储)

经过子类对象找到子类所在的映射文件,拿到鉴别器值 discriminator-value=pig”

在映射文件中,经过子类找到父类,经过父类找到对应的表 t_data_dict

往父类的表(t_data_dict)中设值。

 

加载关系:(继承关系的表中的数据的加载)

session.createQuery(from ItemCategory);

至关于sql语句:select *from t_data_dict where category=itemCategory;

由于查询的是子类对象,多态查询,会自动加上鉴别器的值category=itemCategory。

将查询出来的记录封装到Query对象中,再经过.list()方法,将Query对象中的记录存到list集合中。

 

 

关联映射:(关于关联属性的映射- Item对象)

Item:

Private ItemCategory category;

Category为关联属性,经过配置hbm.xml文件

<many-to-one name=”category”>

代表category为外键,参照的是category属性所属的类ItemCategory所映射的表,可是因为ItemCategory为子类而且在单表继承映射中,全部参照的是ItemCategory的父类所映射的表t_data_dict。

 

存储关系:(关联映射--存Item对象)

在Action中:

一、         从表单中拿到表单中的参数(普通属性以及关联属性的值)

二、         建立Item对象,将普通属性设到Item对象中(Item处于瞬时态)

三、         建立ItemCategory对象,ItemCategory category=new ItemCategory();(从数据库中取出来的数据是离线态,在数据库中有对应的记录)

四、         category.setId (categoryId);// categoryId为从表单中取出来的数据

五、         item.setItemCategory(category);//给关联属性赋值

六、         建立ItemUnit对象,ItemUnit unit=new ItemUnit();//离线态

七、         unit.setId(unitId);//unitId为从表单中取出来的数据

八、         item.setUnitItem(unit);// //给关联属性设置

 

持久态对象能够引入离线态对象,不会有异常,由于离线态对象在数据库中有对应的id.

 

加载(关联映射—给关联属性赋值):

session.load(Item.class,itemNo);

一、         找到hbm文件

二、         找到对应的表 t_items

三、         根据itemNo找到对应的记录

四、         将对应的记录设到Item对象中

五、         给关联属性ItemCategory赋值,many-to-one,外找主,找到t_data_dict中对应的记录

六、         根据记录生成对象,将对象赋值给Item对象中的category属性

 

懒加载(Lazy)

懒加载概念:只有真正使用该对象时,才会建立对象;对于Hibernate而言,真正使用对象的时候才会发送sql语句查询数据库。

懒加载--lazy主要用于加载数据。

lazy使用的标签:

<class>:

<peroperty>:

<set><list>:

<one-to-one><one-to-many>:

懒加载的实现原理:产生cglib代理

一、         生成目标类的子类(cglib代理类)

二、         一旦须要使用对象的方法时,判断target是否等于null(target为代理对象中的一个状态标识,默认为null,对象建立后就会等于对象的类型 target=Group)

三、         若是target不等于null就直接使用

四、         若是target等于null 就发sql语句查询数据库根据记录生成对象再使用。

懒加载的分类:

一、类上的懒加载<class>::true/false,默认为true,开启懒加载

二、集合上的懒加载<set>:: true/false/extra 默认为true

三、单端关联懒加载<one-to-one><one-to-many>::

:false/proxy/noproxy,默认为proxy

 

类上的懒加载:(默认开启lazy=”true”

<class name=”Group” table=”t_group” lazy=”true”>

Group gourp=session.load(Group.class,1);//执行这条语句时不会立刻发送sql语句

建立的Group对象是cglib代理对象,target=null;

group.getId();//取得id的时候也不会发送sql语句,由于上面查询条件中已经给出id的值。group依旧是cglib代理对象,target=null

group.getName();//这时候group仍是cglib代理对象。而后发送sql语句。

cglib代理对象是目标对象的子类,会继承目标对象的方法,会先判断目标对象是否存在(target==null)。若是存在就会调用目标对象的方法;若是不存在就会发送sql语句,从数据库中查询记录,根据记录建立目标对象,执行目标对象的getName()方法。target=Group。

再执行一次 group.getName();这里仍是代理对象,目标对象是由cglib代理对象调用。会判断target==null,发现不等于null,就直接调用目标对象的getName()方法。

 

加载能够不须要事务,事务只是用在存储。

Hibernate支持三种事务:局部事务,全局事务,代理事务

 

懒加载异常:

懒加载开启,建立的是代理对象,先不使用代理对象(就不会发送sql语句建立目标对象),执行commit()方法后,执行session.close()方法把session关闭。而后再使用代理对象,须要发送sql语句建立目标对象时,发现链接已经关闭,就会抛出异常LazyInitializationException:懒加载初始化异常。

Session是线程不安全的,因此用完了要立刻关闭。

 

解决方案:

一、         关闭懒加载:可是效率会大大下降

二、         使用ThreadLocal

 

懒加载的实际应用:物料项目

一、load()方法在lazy=true的状况下查询出来的对象都是代理对象,在持久层是不会使用对象的

二、将代理对象下传到业务层(struts的Action类中)

三、在Action类中经过request.setAttribute(“item”,item);传到jsp页面

四、在jsp页面经过EL表达式输出。经过item属性名拿到对应的属性值 item代理对象。

五、发现要使用的时候就会发送sql语句建立目标对象,可是session已经关闭因此会抛出异常。

 

正确处理方法:

在持久层不关闭session,使用Filter来管理session的建立和关闭

Public class HibernateFileter implements Filter{}

ThreadLocal hibernateHolder=new ThreadLocal();

一、加载类建立工厂类型的静态变量(全局)

二、Tomcat一启动就建立过滤器对象,一旦建立过滤器对象就会执行该对象的init()方法

三、init()方法中,读取Hibernate.hbm.xml文件,建立SessionFactory对象。

四、把工厂对象赋值给工厂类型的静态全局变量

五、在HibernateFilter类中,建立getSession静态方法,在此方法中获得session

public static Session getSession(){

Session session=(Session)hibernateHolder.get();//取得当前线程绑定的session

if(session==null){

session=factory.openSession();

hibernateHolder.set(session);//把session和当前线程绑定

}

return session.

}

 

六、持久层经过HibernateFilte类中的的静态方法 getSession()拿到session。

七、在实现Filter接口的类中,doFilter方法中:chain.doFilter()执行后对结果进行拦截,关闭session。先判断当前线程是否已经绑定session,

Session session=(Session)hibernateHolder.get();//取得当前线程绑定的session

若是绑定了,取出的session不为null,就执行判断session.isOpen(),若是为true就继续执行session.close()方法关闭session,而后再将session从map集合中移除(解除当前线程与session的绑定关系)

try {

chain.doFilter(servletRequest, servletResponse);

} finally {

Session session = (Session)hibernateHolder.get();//从Map中取出session

if (session != null) {

if (session.isOpen()) {

session.close();//关闭session

}

hibernateHolder.remove();//移除session

}

}

 

ThreadLocal类的分析:

原理:在ThreadLocal类中有一个线程安全的Map,用于存储每个线程的变量的副本。

public class ThreadLocal{

private Map values = Collections.synchronizedMap(new HashMap());;//线程安全

}

ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>

Thread thraed=Thread.currentThread();//获得当前线程对象

ThreadLocal:是线程作并发用的技术,主要方法get(),set(T),initialValue(),remove()

 

1set(T):--设置到当前线程的局部变量

将当前线程和给session作绑定,线程做为key,T(session)做为value存入一个加锁的线程安全的map集合中。values.put(thread,session);

public void set(Object newValue) {

values.put(Thread.currentThread(), newValue);

}

2T get():返回当前线程的局部变量

从线程安全的map中集合中取出线程对应的session,若是有就直接用,若是没有就建立session,再绑定到当前线程上  values.put(thread,session);

public Object get() {

Thread curThread = Thread.currentThread();

Object  obj=values.get(currentThread);//先从map集合中取

if (obj == null && !values.containsKey(curThread)){//发现map集合中没有当前线程做为key对应的value,就建立再添加到map

 obj = initialValue();

values.put(curThread, obj);

}

return obj;

}

 

3T initialValue():---返回当前线程的局部变量的初始值

protected Object initialValue(){

 return null;

}

4remove():--移除当前线程的局部变量

map.remove(key);//根据map集合的移除元素的原则,根据key移除,

public void remove() {

values.remove(Thread.currentThread());

}

 

为何必定要用线程安全的map?

多个线程并发的拿到session并发的访问数据库会形成数据混乱。线程安全的map,使得一次只能有一个线程访问数据库。并行变串行

资源共享:

对于多线程资源共享的问题,同步机制采用了以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不一样的线程排队访问,而ThreadLocal为每个线程都提供了一份变量,所以能够同时访问而互不影响。

Hibernate和struts的集成,使用拦截器(Filter)继承。

 

OpenSessionInview模式:一直开启session,直到数据输出完再关闭。

 

集合上的懒加载:(extra,true,false)

在<set>标签上使用,使用set标签的主要有<one-to-many><many-to-many>

<class>标签上的懒加载,不影响<set>上的懒加载。

普通属性的加载是使用类上的懒加载

关联属性是使用集合上的懒加载

1、集合上的懒加载开启lazy=true,类上的懒加载是开启的 lazy=true

session.load(Classes.class,1);//生成的是代理对象,target=null,不会发送sql语句

classes.getName();//只发一条sql语句,访问t_classes,只给普通属性设值

Set students=classes.getStudents();//不会发送sql语句访问t_student表,说明当前students是PersistantSet(相似cglib)代理对象,不是真正存放Student对象的Set集合。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素时才会发送sql语句访问t_student

}

 

2、集合上的懒加载关闭lazy=false,类上的懒加载是开启的 lazy=true

加载Set集合类的students属性和加载普通属性同样。

session.load(Classes.class,1);//生成的是代理对象,target=null,不会发送sql语句

classes.getName();//发送两条sql语句,访问t_classes,t_student表(后面都不发送sql语句了)

 

3、集合上的懒加载关闭lazy=false,类上的懒加载关闭lazy=false

session.load(Classes.class,1);//生成的是目标对象,会发送两条sql语句,分别访问t_classes,t_student表

 

4、集合上的懒加载开启lazy=true,类上的懒加载关闭lazy=false

session.load(Classes.class,1);//生成的是目标对象,会发送一条sql语句,访问t_classes表

Set students=classes.getStudents();//不会发送sql语句访问t_student表,说明当前students是PersistantSet(相似cglib)代理对象,不是真正存放Student对象的Set集合。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素时才会发送sql语句访问t_student

}

 

5、集合上的懒加载lazy=extra,类上的懒加载关闭lazy=true

session.load(Classes.class,1);//生成的是代理对象,不会发送sql语句,

classes.getName();//会发一条sql语句,访问t_classes表

Set students=classes.getStudents();//不会发送sql语句访问t_student表,说明当前students是PersistantSet(相似cglib)代理对象,不是真正存放Student对象的Set集合。

students.size();//这时候会使用函数从数据库中查询数据,select count(*)form t_student where classesid=1;,若是集合上的lazy为true,就会先发送select *from student where classesid=1语句,查询数据库中的全部记录,再计算出记录的条数。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素时才会发送sql语句访问t_student

}

 

lazy=true和lazy=extra的功能差很少,可是extra更加智能,它会根据用户的的条件发送比较智能的sql语句。

 

单端上的懒加载:(proxy,false,no-proxy

般用在<many-to-one><one-to-one>标签上面

默认为lazy=”proxy”,开启状态

<many-to-one name=”group”  lazy=”proxy”>

主要是在加载group(关联)属性的时候有用,

1、单端上的懒加载开启lazy=proxy,类上的懒加载是开启的 lazy=true

session.load(User.class,1);//生成的是代理对象,target=null,不会发送sql语句

user.getName();//发送sql语句,访问t_user表

Group group=user.getGroup();//拿到的而是Group的代理对象

Group.getName();发送sql语句访问t_group.

 

2、单端上的懒加载关闭lazy=false,类上的懒加载关闭 lazy=false

session.load(User.class,1);//发送两条sql语句,访问t_user和t_group

user.getName();

Group group=user.getGroup();

Group.getName();

 

3、单端上的懒加载开启lazy=proxy,类上的懒加载关闭lazy=false

session.load(User.class,1);//发送一条sql语句,只访问t_user

user.getName();

Group group=user.getGroup();//不会发送sql语句,group为代理对象

Group.getName();//发送sql语句访问t_group

 

4、单端上的懒加载开启lazy=no-proxy,类上的懒加载关闭lazy=false

session.load(User.class,1);//发送一条sql语句,只访问t_user

user.getName();

Group group=user.getGroup();//不会发送sql语句,group为代理对象

Group.getName();//发送sql语句访问t_group

 

Lazy的取值为proxy和no-proxy功能和性能上都没有很大的区别:

proxy:作懒加载是使用cglib代理,no-proxy使用的是加强的字节码工具,在字节码生成的时候增长了懒加载策略。

 

今天的任务:

将老师讲的案例本身写一遍(懒加载) *

晚上投简历 *

作>=1道算法题 *

查看hibernate源码

 

Session_Flush

Session_flush的做用:

一、         清理缓存(清理session临时集合中的数据)

二、         生成sql语句(发送sql)

三、         将session的persistanceContext中的map->existsInDatabase的标记置为true。

Flush:用在存储上。

 

主键生成策略为uuid(session.flush()的特色)

session.save(user)前—>> 对象处于瞬时态

执行save(user)方法后,通常不会发送sql语句,由于主键是由hibernate自动生成。

session.save(user)后-->> 对象处于持久态。existsInDatabase=false

session.flush();//主要作了如下三件事:

一、         清理缓存(清理session临时集合中的数据)

二、         生成sql语句(发送sql)

三、         将session的persistanceContext中的map->existsInDatabase的标记置为true

数据库中会有相应记录,可是会看不到。(由于是 脏数据:没有提交的数据)

session.commit();

一、         执行commit()方法的时候会执行flush()方法,因此能够不显示的写出flush()方法。

二、        就算是显示写了flush()方法,commit()的时候仍是会再执行一次flush()

三、        执行flush()方法的步骤:

3.一、   先判断临时集合中有没有数据

3.二、   若是没有的话就直接commit().

3.三、   若是临时集合中有数据的话就发送sql语句再清空临时集合中的数据,而且将existsInDatabase标记置为true,而后再执行commit()

 

主键生成策略为native(session.flush()的特色)

session.save(user)前—>> 对象处于瞬时态

执行save(user)方法后,通常会发送sql语句,由于主键是由数据库自动生成。

session.save(user)后-->> 对象处于持久态。existsInDatabase=true

session.flush();//这时候flush()什么都不作,由于临时集合中没有数据,sql语句已经发送,existsInDatabase=true

session.commit();//会再次执行flush()方法,依旧什么都不作。commit()方法对提交到数据库的数据作验证,成功就添加到数据库,不成功就回滚。

 

主键生成策略为uuid(session.evict()的特色)

session.save(user)前—>> 对象处于瞬时态

执行save(user)方法后,一 般不会发送sql语句,由于主键是由hibernate自动生成。

session.save(user)后-->> 对象处于持久态临时集合中有数据existsInDatabase=false

session.evict(user);//将user对象从session-- >> persistanceContext下的map中逐出。临时集合中依旧有数据.

session.commit();//抛出异常,(persistanceContext下的map)EntityEntries属性中的table中的数据不存在,找不到数据,没法提交数据。(缓存使用不当)

产生异常的缘由:

主键生成策略为uuid

session.save(user);//在临时集合中有数据,session的map中有数据,existsInDatabase=true,loadedState中存放了user对象

session.evict(user);// 把user从map中逐出(session缓存),为了管理缓存

而后 临时集合中有数据,可是map中没有数据

 

执行commit()方法前会先调用session.flush(),作如下三步操做:

  1. 从临时集合中拿到数据,清空临时数据
  2. 发送sql语句
  3. 3.   将map下的existsInDatabase的标记置为true,可是user的map已经被清除,找不到map也找不到map里面的existsInDatabase标记,就会抛出异常。

 

解决办法:(调用session.evict(user)之前先显示调用session.flush())

session.save(user);

显示的写flush方法

session.flush();//根据临时集合中的数据发送sql语句,而后清空临时集合,到session缓存中将existsInDatabase标记置为true

session.evict(user);//将user对象从session缓存的map集合中移除

最后session.commit();//这时候依旧会执行session.flush()方法,可是不会抛出异常,由于flush()方法,是先查看临时集合中有没有数据,发现临时集合是空的没有数据就不会发送sql语句,也不用再次清空临时集合,也不须要将existsInDatabase标记置为true;直接执行commit()方法。

 

主键生成策略为native(session.evict()方法的特色)

session.save(user)前—>> 对象处于瞬时态。

执行save(user)方法后,通常会发送sql语句,由于主键是由数据库自动生成。

session.save(user)后-->> 对象处于持久态。临时集合中没有数据。existsInDatabase=true

session.evict(user);//自动把数据从session缓存的map中逐出

session.commit();//执行flush()方法,可是因为主键生成策略为native,临时集合中已经没有数据了,就不要用发送sql语句也不用将existsInDatabase标记置为true了。直接执行commit()方法。 commit()方法对提交到数据库的数据作验证,成功就添加到数据库,不成功就回滚。这里不会抛出异常,由于这里的flush()方法什么都不用作。

 

主键生成策略为assigned(session.evict()的特色)

session.save(user)前—>> 对象处于瞬时态。

执行save(user)方法后,通常不会发送sql语句,由于主键是由用户手动生成。

session.save(user)后-->> 对象处于持久态。existsInDatabase=false,临时集合中有数据。

session.evict(user);//自动把数据从缓存中逐出

session.commit();//无论assinged知道的主标识类型是int仍是String,执行session.evict(user)后执行session.commit()必定会抛出异常

解决办法,在调用sessionn.evict(user)方法前显示的调用session.flush()

 

Sql语句在数据库中的执行顺序:

发送sql语句的顺序:

建立两个对象 user,user1;

session.save(user);

user.setName(“ls”);

session.save(user1);

session.commit();

先发送insert into  user () 存储的对象是user

再发送 insert into user() 存储的对象是user1

再发送 update user set name=ls 更新的对象是 user

 

可使用flush()方法调整语句的发送顺序

建立两个对象 user,user1;

session.save(user);

user.setName(“ls”);

session.flush();

session.save(user1);

session.commit();

先发送insert into  user ()存储的对象是 user

再发送 update user set name=ls user(由于flush()方法)

再发送 insert into user()存储的对象是 user1

 

悲观锁和乐观锁:

一、         为何要加锁?

在数据库中不加锁会丢失更新

缘由:用户同时访问数据库,查看的数据相同,一方对数据作了修改可是另外一方不知道,因此就在原来的数据上修改。就数据不同。

 

不能并发的访问数据库中的数据,否则数据库会崩溃的哦

保证数据库的安全,并行变串行

 

悲观锁:(悲观锁不支持懒加载)

测试示例:

两个方法串行执行:(load1(),load2())money=1000

一、load1();---- session.load(Test.class,1);

二、Debugà 查完数据后修改数据库中的值 money-200可是 不执行commit()方法

三、执行另外一个查询方法load2()-----ssession.load(Test.class,1);

四、修改数据 money-200而且一次执行完.

五、再接着执行第一个方法,commit()。更新后数据库中的数据就是错误的。最后结果为money=800。更新丢失

 

session.load(Test.class,1,LockMode.UPGRADE);//悲观锁,使用数据库自身的锁

LockMode.UPGRADE//利用数据库的for  update 子句加锁—---记录锁(会出现幻读)

锁的分类:记录锁,表锁,库锁

记录锁:只给当前查询出来的记录加锁,其余记录依旧没有变化

 

加悲观锁后的示例:(悲观锁中懒加载自动失效,load方法执行后就会发送sql语句)

两个方法串行执行:(load1(),load2())money=1000

一、load1();---- session.load(Test.class,1,LockMode.UPGRADE);//立刻发sql语句查询

二、使用debug调试à 查看记录money=1000,而后修改数据库中该记录的的money字段, ,money=money-200可是 不执行commit()方法

三、执行另外一个查询方法load2()-----ssession.load(Test.class,1,LockMode.UPGRADE); //发送sql语句查询的时候发现查不出来数据。id=1的记录已经被上了锁。

四、修改数据 money值,money=money-200,执行commit()方法,会停留在记录外面,查询不出来记录,由于前面已经上了锁,要等前面那个锁释放才能够访问那条记录。

五、再接着执行第一个方法,commit()。更新后数据库中的数据money=800;

六、这时候load2()方法执行,查询出来的数据money=800,以后才能对记录作修改。

悲观锁中的记录锁的特色:避免了不可重复读,可是存在幻读。(效率低)

 

乐观锁:(乐观锁支持懒加载)

表中必须添加一个版本控制字段version,version默认值为0,用户读到的记录是同样的 version和money。version在User类中定义为 private int version;

version在映射文件中使用<version>标签配置,version是版本控制字段,由Hibernate自动管理。

<version name=”version”/>:标识对乐观锁的控制

Load1()方法(debug调试,执行commit()方法之前的代码)

User user=(User)session.load(User.class,1);//不会发送sql语句,生成代理对象

int money= user.getMoney();//使用到对象方法时发送sql语句select  * from user where id=1 and version=0),

user.setMoney(money-200);

 user.setVersion(1); //查询出数据后,修改表中记录的值money = money-200;version=1。

session.update(user);//发送update语句,update user set money=money-200 and version=1 where id=1 and version=0;

暂不执行session.commit()这条语句。

 

Load2()方法(一次性执行完)

User user1=(User)session.load(User.class,1);//不会发送sql语句,生成代理对象

int money= user.getMoney();//使用到对象方法时发送sql语句select  * from user where id=1 and version=0),

user.setMoney(money-200);

 user.setVersion(1); //查询出数据后,修改表中记录的值money = money-200;version=1。

session.update(user);//发送update语句,update user set money=money-200 and version=1 where id=1 and version=0;

执行session.commit()方法。修改数据库中id=1对应的记录

 

再继续执行load1()方法中的session.commit()语句,发送sql语句update user set money=money-200 and version=1 where id=1 and version=0;

可是版本号version的值已经改为1,因此commit()的时候就会抛出异常。

 

 

事物的隔离级别:

一、         未提交读:没有提交就能在数据库中读到数据

二、         可提交读:提交后才能在数据库中读到数据,解决不可重复读的方法是加悲观锁。(mysql和oracle的默认处理级别)

三、         可重复读:加了悲观锁(记录锁)

四、         序列化读:不会出现幻读,加表锁(不会出现幻读,不会出现脏读,也不会出现不可重复读)

五、         脏读:读取未提交到数据库中数据。

六、         不可重复读:读取数据时,若是有人修改了数据就发现不是原来的数据,会出错(加悲观锁解决问题)

七、         幻读:每次读取数据的时候读取到的记录数一直在变化(解决方法应该加表锁)

安全性强的使用使用悲观锁

并发性强的时候使用乐观锁

 

缓存

缓存的分类:

一级缓存:session级别,生存周期和session同样

session.save(obj);将数据归入session管理。load()get(),iterator()方法都使用一级缓存。若是iterator()方法使用不当就会形成n+1问题。

Query 的list()方法不使用一级缓存,可是往一级缓存中放数据。(可能有n+1问题)

 

二级缓存:sessionFactory级别,工厂不会轻易销毁,通常只建立一次。放到二级缓存中的数据特色:查询次数频繁,可是修改次数少

 

二级缓存:一级缓存能够控制对二级缓存的使用get(),save(),load() 既要往一级缓存中存数据,也要往二级缓存中存数据。

查询缓存:为了提升Query list的查找效率

 

一级缓存和二级缓存主要是缓存实体对象,不会缓存属性

查询缓存只缓存实体属性

 

一级缓存:(load,get,list,iterator都支持一级缓存)

直接查询load()或者get()

Session级别的缓存,通常不须要配置也不须要专门的管理,直接使用就能够了。

Student stu=(Student)session.load(Student.class,1);

stu.getName();//先从一级缓存session中查看有没有id=1的Student对象,有的话就直接使用,没有的话就发sql语句访问数据库根据记录生成对象再使用。<load支持一级缓存>

stu.getSex();//再次使用Student对象的时候,先从一级缓存session中查有没有该对象,发现有就直接使用,再也不建立了。

Student stu1=(Student)session.get (Student.class,1);//这里也不会发送sql语句,由于一级缓存中有id=1的Student对象,因此直接使用。<get()也支持一级缓存>

 

先save后查询get()或者load(),查到的是save()过的数据

主键生成策略为:native

Student stu=new Student();

stu.setName(“ls”);

stu.setSex(“male”);

Serializable id=session.save(stu);//save()方法的返回值是Integer类型的数据。获得的id是对象的主标识。这里save()后Student对象归入session管理,进入缓存。

Serializable:序列化接口,String ,Integer都支持序列化协议。

Student stu1=(Student)session.get(Student.class,1);//这里不会发送sql语句,由于先从缓存中查找数据,发现有id=1的Student对象,就直接使用。

缺点: 可能会读到脏数据。

 

Query的iterate()方法

Student stu1=(Student)session.createQuery(“from Student s where s.id=1”).iterate().//执行到iterator()方法才会发送sql语句

一、        经过id先从数据库中查对象的id(select id from t_stu where id=1)

二、        根据id到session中查对象

三、        若是没有就再发sql语句访问数据库 (select* from t_stu where id=1)

Student stu2=(Student)session.createQuery(“from Student s where s.id=1”).iterate().//再执行一次,仍是会发查询id的sql语句(select id from t_stu where id=1)

而后发现session中有对象,就不发sql语句(select* from t_stu where id=1)了,直接从session中拿到对象使用。

 

N+1问题:(iterator()缓存使用不当)

session.createQuery(“from Student ).iterate();

一、        先发一条sql语句取出全部的id(select id from t_stu

二、        查询session中有没有id对应的对象

三、        根据id依次发送sql语句访问数据库(select * from t_stu),数据库中有多少条记录就会发送多少条sql语句,若是数据库中的记录特别的多,这里就会出现数据库访问拥塞,效率降低,形成n+1问题。

session.createQuery(“from Student ).iterate();//再查一次,这里会先发sql语句查id,而后发现以及缓存session中有id对应的对象,就直接使用。不会再发送sql语句访问数据库了。

 

list()方法:只会往一级缓存中放数据,不会从一级缓存中取数据

List stus=session.createQuery(“from Student”).list();//直接发sql语句访问数据库,不会先查id,一次性直接把数据库中的全部记录放到一级缓存session

Stus= session.createQuery(“from Student”).list();//list()只放不拿,仍是会再次发送sql语句访问数据库,查出全部记录放到session中。

 

N=1问题的解决方法:

第一次使用list(),第二次使用iterator()方法

一、        list()先把从数据库中查询出全部数据放到一级缓存session

二、         再使用iterator()方法使用一级缓存session中的数据

三、        iterator()会先发送sql语句查全部的id,而后去一级缓存session中查找id分别对应的对象

四、         发现对象已经存在一级缓存session中就直接使用。不会再发送多条sql语句访问数据库了。这样就只发送了两条sql语句,解决了N+1问题。

 

一级缓存的管理:(evict(),clear(),close()

使用flush()清除session中的缓存

session.flush();//清除临时集合中的数据

session.clear();//清除persistanceContext下的map中的数据

必定要先flush(),而后再clear(),否则可能会产生异常

session.evict(user);//清除一条

session.clear();//清除多条数据

clear()和evict()的区别:clear()方法清除多条,evict()只能清除一条。

 

一级缓存只缓存对象,不缓存对象的属性。

若是sql语句查询的结果是记录(一条或者多条),就会根据记录生成对象(一个或多个)放到一级缓存session中。

若是sql语句查询的结果是记录的字段,就不会把字段映射成属性放入一级缓存session中了。

 

一级缓存里面的数据不能共享:session关闭后全部数据都销毁。

Hibernate的缺点:Hibernate不会保证批量数据的同步,只能保证单条数据的同步。

 

二级缓存:(缓存的内容是对象,load,get,save都支持一级缓存)

二级缓存只有一个,生成周期很长,和SessionFactory的生命周期同样。

缓存策略的做用是: 提供对缓存的方式

Hibernate通常使用Hcache这种缓存策略。

 

 

二级缓存的配置:

1、准备缓存策略的配置文件,在配置文件中写缓存策略

在ehcache.xml中配置缓存策略:(配置在src下)

默认的缓存策略:

<defaultCache

maxElementsInMemeory=”10000”//缓存的最大空间

eternal=”false”//设置没有任何对象常驻二级缓存中

timeToldleSeconds=”120”//若是120秒不使用就移除缓存

timeToLiveSeconds=”120”//最多能在二级缓存中呆120秒

overflowToDisk=”true”//若是缓冲存的数据超过10000,就将数据存到硬盘中

/>

 

配置指定的缓存策略:

<cache name=”cache1”

maxElementsInMemeory=”10000”//缓存的最大空间

eternal=”true”//设置全部对象都常驻二级缓存中

timeToldleSeconds=” 0”//无论多长时间没有使用都不会移除缓存

timeToLiveSeconds=” 0” //无论使用多长时间都不会移除缓存

overflowToDisk=”false”//溢出的时候不支持将数据存到硬盘上

/>

 

二级缓存经过缓存策略设定缓存中能存放多少个对象,存放多久,有多长的生存时间。

 

2、在Hibernate.cfg.xml文件中配置哪一个类的对象来管理二级缓存

<!-- 指定缓存产品提供类-->

<property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</property>

org.hibernate.cache. EhCacheProvider:建立sessionFactory时,读取Hibernate.cfg.xml文件,建立EhCacheProvider对象。经过插件执行对象的相应的方法--init(),读取ehcache.xml配置文件。经过读取ehcache.xml配置文件对缓存进行管理。

3、配置哪些对象使用二级缓存

<class-cache class=”cn.Student” usage=”read-only”/>

4、指定使用二级缓存使用对象的使用方式

也能够在实体类的hbm.xml文件中配置 <cache usage=”read-only”/>

缓存策略的集中方式:read-only(经常使用)read-write , nonstrict-read-write

五、        开启二级缓存:

<!-- 开启二级缓存 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

 

二级缓存的使用:(只存放配置了的对象,get,load,save

load()方法的二级缓存解析:

Student stu=(Student)session.load(Student.class,1);

Stu.getName();//发送sql语句从数据库中拿到id=1的记录生成对象,存入二级缓存也会存入一级缓存中。

Session.close();//关闭session,一级缓存中的数据被清空

Student stu=(Student)session.load(Student.class,1);

Stu.getName();再次访问时,先从一级缓存session中查看有没有对象,没有再从二级缓存中查,发现有就直接使用。

 

get()方法的二级缓存解析:

Student stu=(Student)session.get(Student.class,1); //发送sql语句从数据库中拿到id=1的记录生成对象,存入二级缓存也会存入一级缓存中

stu.getName();

session.close();//关闭session,一级缓存中的数据被清空

Student stu=(Student)session.get(Student.class,1); 再次访问时,先从一级缓存session中查看有没有对象,没有再从二级缓存中查,发现有就直接使用。

stu.getName();

结论:在二级缓存开启的状态下,load()和get()不只使用一级缓存,还会使用二级缓存。

 

二级缓存的管理:(没有临时集合,基本上都是map

Student stu=(Student)session.get(Student.class,1); //发送sql语句从数据库中拿到id=1的记录生成对象,存入二级缓存也会存入一级缓存中

stu.getName()

session.close();//关闭session

factory =HibernateUtils.getSessionFactory();//拿到sessionFactory对象

factory.evict(Student.class);//清除二级缓存中的全部Student对象

factory.evict(Student.class,1);//只把二级缓存中id=1的Student对象

Student stu=(Student)session.get(Student.class,1); //再次发送sql语句从数据库中拿到id=1的记录生成对象,存入二级缓存也会存入一级缓存中(由于一级缓存和二级缓存中的数据都被清空了)

SessionFactory的做用:

一、         建立Session对象

二、         管理二级缓存

 

一级缓存和二级缓存的交互:

一级缓存能够控制如何使用二级缓存

1、设定二级缓存只能读不能写

session.setCacheMode(CacheMode.GET);//设定仅从二级缓存中读数据,而不向二级缓存中写数据

Student stu=(Student)session.get(Student.class,1); //发送sql语句从数据库中拿到id=1的记录生成对象,只将数据存入一级缓存中。

stu.getName()

session.close();//关闭一级缓存,这时候一级缓存和二级缓存中都没有数据,下一次使用对象时,会发送sql语句。

2、设定对二级缓存不作任何约束

Student stu=(Student)session.get(Student.class,1); //发送sql语句从数据库中拿到id=1的记录生成对象,将数据存入一级缓存和二级缓存中。

stu.getName()

session.close();//关闭一级缓存,这时候一级缓存没有数据,二级缓存中有数据

3、设定二级缓存只能写不能读

session.setCacheMode(CacheMode.PUT); //设定只往二级缓存中写数据,可是不能读取二级缓存中的数据。

Student stu=(Student)session.get(Student.class,1); //虽然二级缓存中有数据,可是由于二级缓存中的数据不能读取,因此要发送sql语句从数据库中拿到id=1的记录生成对象,将数据存入一级缓存和二级缓存中。

Stu.getName();

Session.close();/关闭session,一级缓存中的数据被清空。二级缓存中的数据还在。

4、设定对二级缓存不作任何约束

Student stu=(Student)session.get(Student.class,1); //不会发送sql语句,由于二级缓存中有数据,能够读取。

Stu.getName();

Session.close();/关闭session,一级缓存始终都没有数据,二级缓存中的数据依旧在。

 

查询缓存:(只能缓存属性,不能缓存对象,只有list可使用查询缓存)

list经过查询缓存使用二级缓存

查询缓存的配置:(不须要配置缓存策略)

一、         在Hibernate.cfg.xml中配置开启查询缓存:(二级和查询缓存都开启 true)

二、         在使用查询缓存前要启动查询缓存

query.setCacheable(true);

 

一、        开启查询缓存(true),关闭二级缓存(false) 使用list()方法

Query query=session.createQuery(“select s.name form Student s);//查出全部的name属性对应的字段

query.setCacheable(true);//启动查询缓存

List name=query.list();//发送sql语句,将查到的name属性放到查询缓存中

 

Query query=session.createQuery(“select s.name form Student s);//查出全部的name属性对应的字段

query.setCacheable(true);//启动查询缓存,查询缓存配置后也是默认开启的

List name=query.list();//不会再发送sql语句,由于list()方法使用了查询缓存,查询缓存中已经有数据

结论:查询缓存,值缓存属性,不缓存对象;只有list()方法能使用查询缓存

 

二、        查询缓存不受一级缓存的影响

 

三、        开启查询缓存(true),关闭二级缓存(false) 使用iterate()方法

Query query=session.createQuery(“select s.name form Student s);//查出全部的name属性对应的字段

query.setCacheable(true);//启动查询缓存

List name=query.iterate();//发送sql语句,由于是iterate()方法,不能使用查询缓存,因此没法将查到的name属性放到查询缓存中

 

Query query=session.createQuery(“select s.name form Student s);//查出全部的name属性对应的字段

query.setCacheable(true);//启动查询缓存

List name=query.iterator();//仍是再次发送sql语句,由于iterator()方法不能使用查询缓存,查询缓存中没有数据

结论:iterator()不使用查询缓存。

四、        关闭查询缓存(false),关闭二级缓存(false) 使用list()方法

Query query=session.createQuery(“select s form Student s);//查出全部的Student对象

查询两次,查询完后关闭session,第二次查询依旧会发送sql语句

 

五、        开启查询缓存(true),关闭二级缓存(false) 使用list()方法

Query query=session.createQuery(“select s form Student s);//查出全部的Student对象

query.setCacheable(true);//启动查询缓存

List stus=query.list();//查出的全部对象都存入一级缓存中,查出全部对象的id放入查询缓存中。

Session.close();//关闭session,一级缓存中的数据被清空

 

Query query=session.createQuery(“select s form Student s);//查出全部的Student对象

query.setCacheable(true);//启动查询缓存

List stus=query.list();//先到查询缓存中查看存放的全部对象的id,再到二级缓存中找id分别对应的对象,可是二级缓存已经关闭,全部查询缓存会发送sql语句:slelect * from t_stu where id= id;有多个id就会发送多少条语句。这样就会形成数据库访问拥塞,N+1问题。

 

6开启查询缓存(true),开启二级缓存(true) 使用list()方法

就不会出现N+1问题,由于开启二级缓存,二级缓存中就会有数据,直接使用就能够了,不用再次发sql语句访问数据库拿到记录生成对象。

 

N+1问题:

iterator()是对一级缓存使用不当,形成N+1问题

list()是对二级缓存的使用不当,形成N+1问题。

 

结论:Query的list方法不能使用一级缓存,可使用二级缓存,可是要开启查询缓存才有用。

Iterate()也会使用二级缓存。

 

 

注意:

list()方法:直接取出全部对象

iterate()方法:先取出全部的记录的id,再根据id到一级缓存中查看有没有对应的对象,没有的话就发送sql语句访问数据库拿到记录生成对象。查询出了对少个id就发送多少条sql语句。

 

 

今天的任务:

将缓存示例写一遍(尤为是二级缓存和查询缓存)

写>=1道算法题

看python文档

复习一遍Hibernate

Lunix的指令

数据结构,算法

 

Hibernate查询语句:hql

hibernate的查询语言种类:

一、         标准化对象查询(Criteria Query:彻底面向对象可是不够成熟,不支持投影也不支持统计函数。

二、         原生的sql查询语句(Native SQL Queries) :直接使用标准SQL语言和特定的数据库相关联的sql进行查询。<和数据库耦合性太强>

三、        Hibernate查询语言(Hibernate Query Language HQL

使用sql的语法,以面向对象的思想完成对数据库的查询,独立于任何数据库

以类和属性来代替表和数据列

支持多态,支持各类各样关联

减小了SQL的冗余, hql:不区分大小写

 

Hql支持的数据库操做:

链接(joins),笛卡尔积(cartesian products),投影(projection),聚合(max,avg),排序(ordering),子查询(subquering),sql函数,分页

 

HQL导航查询示例:

Query query=session.createQuery(“from User user where user.group.name=’zte’ ”);

//查找User类对象的属性group,group属性所属的类型Group对象的name

至关于sql语句:

select  * from t_user ,t_group where t_group.name=’zte’ and t_group.id =t_user.groupid;

 

randomDate(“2017-1-1”,”2017-2-9”);//随机生成一个“2017-1-1”,”2017-2-9”之间的日期

 

两个或者两个以上的普通属性查询

使用Object[]数组接收查询出来的属性

List students=session.createQuery(“select id ,name from Student”).list();

Iterator it=students.iterator();

While(it.hasNext()){

      Object[] obj=(Object) it.next();

      System.out.println(obj[0]+”---”+obj[1]);

}

先建立一个Object类型的数组对象Object[]

Object[]数组的长度和查询的属性的个数相同,一 一对应,obj[0]存放从数据库中取出来的id值,obj[1]存放从数据库中取出的name值。

Object[]数组元素的类型和Student实体类的属性类型一 一对应。

List集合students中存放的元素是Object[]类型的数据,查询出来多少条记录就有多少个元素,有多少个Object[]数组。

 

使用Object[]数组接收查询出来的属性(添加别名,更加清晰)

List students=session.createQuery(“select s.id ,s.name from Student (as) s”).list(); //as能够省略也能够显示的写出来

Iterator it=students.iterator();

While(it.hasNext()){

      Object[] obj=(Object) it.next();

      System.out.println(obj[0]+”---”+obj[1]);

}

 

把从数据库查到的记录生成对象,再查出id和name属性(不建议使用)

List students=session.createQuery(“select new Student( id ,name) from Student”).list();//自动调用Student类的有参的构造方法,建立Student对象

Iterator it=students.iterator();

While(it.hasNext()){

      Student stu=(Student) it.next();

      System.out.println(stu.getId()+”---”+stu.getName());

}

 

实体对象的查询:(Select 查找对象 不能使用*)error: unexpected token *

List students=session.createQuery(“from Student”).list();//正确

List students1=session.createQuery(“from Student s”).list();//正确

List students2=session.createQuery(“select * from Student”).list();//这是错误的

List students3=session.createQuery(“select s from Student  as s”).list();//这才是正确的,as能够省略

Iterator it=students.iterator();

While(it.hasNext()){

      Student stu=(Student) it.next();

      System.out.println(stu.getId()+”---”+stu.getName());

}

 

条件查询:

一、写定查询条件

List students=session.createQuery(“select s.id,s.name from Student s where s.name like ‘%1%’ ”).list();//查询出全部名字包含1的学生id和name

二、使用填充符,动态查询条件

List students=session.createQuery(“select s from Student s where s.name like ”).setParameter(0,”%1%”).list();

三、使用参数,把条件设到参数中

List students=session.createQuery(“select s from Student s where s.name like :myname ”).setParameter(“myname”,”%1%”).list();

 

四、使用多个参数,把多个条件分别设到参数中

List students=session.createQuery(“select s from Student s where s.name like :myname and s.id= : myid ”).setParameter(“myname”,”%1%”). setParameter(“myid”,1).list();

参数顺序不同没有关系。只和参数名有关。

 

五、使用一个参数,把多个条件设到一个参数中(setParameterList

List students=session.createQuery(“select s from Student s where s.id in (:myids )”).setParameterList(“myids”,new Object[]{1,2,3}).list();

使用参数数组对象,查询出id 为1 ,2,或者3的学生对象

 

六、日期类型数据做为条件的查询(使用数据库中的date_format()函数)

List students=session.createQuery(“select s from Student s where date_format(s.createTime,’%Y-%m’)=?”).setParameter (0,”2017-2”).list();

先把字符串转换为日期类型

再把日期和数据库中日期作比较

 

六、日期类型数据做为条件的查询(使用自定义的日期转换函数 sdf.parse())

SimpleDateFormat sdf=new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);

List students=session.createQuery(“select s from Student s where sdf.parse (s.createTime,’%Y-%m’)=?”).setParameter (0,”2017-2-12 11:22:11”).list();

先把字符串转换为日期类型

再把日期和数据库中日期作比较

 

若是非要使用select *语句 就要使用原生的sql语句

List students=session.createSQLQuery(“select * from Student).list();

 

分页查询:

List students=session.createQuery(“select s from Student s )

.setFirstResult(5)//查询从第五条记录开始的数据 ((pageNo-1)*pageSize)

.setMaxResult(2)//每页最多显示两条(pageSize)

.list();

 

对象导航查询:

List students=session.createQuery(“select s.name  from Student s where s.classes.name like ‘%1%’ “).list();

 

链接查询:

内链接

List students=session.createQuery(“select s.name ,c.name  from Student s join s.classes c”).list();//经过学生类的关联属性把学生类和班级类作内链接。(找出既有学生又有班级的记录)

外链接:

左链接:

List students=session.createQuery(“select s.name ,c.name  from Classes c left join c.student s”).list();拿到有学生的班以及没有学生的班级(班级所有显示,学生只显示有班级的)

右链接:

List students=session.createQuery(“select s.name ,c.name  from Classes c right join c.students s”).list();拿到有学生的班以及没有班级的学生(学生所有显示,班级只显示有学生的)

 

分组查询:

List students=session.createQuery(“select s.name ,count(*)  from Classes c left join c.Student s group by s.name order by s.name”).list();

 

查询过滤器:

使用场合:要求数据录入人员只能看到本身录入的数据时 ---使用查询过滤器

使用条件:

在Student.hbm.xml映射文件中定义过滤器参数:

<filter-def name=”filtertest”>//查询过滤器的名字

<filter-param name=”myid” type=”integer”/>//参数名 参数类型为integer

</filter-def>

 

使用步骤:(先在Student.hbm.xml文件中)

<filter name=”filtertest”  condition=”id &lt; :myid”/>//condition配置条件,id< myid(参数)

在查询方法中:session.enableFilter(“filtertest”) .setParameter(“myid”,10)//启用查询过滤器,而且设定参数。 表示查询id<10的数据记录。

 

外置命名查询:

在Student.hbm.xml映射文件中采用<query>标签来定义hql:

<query name=”searchStudents”>

<![CDATA[SELECT s FROM Student s where s.id<?]]>

</query>

List students=session.getNameQuery(“searchStudents”).setParameter(0,5).list();//安全性比较差,显示写出sql语句在配置文件中,容易被黑。

 

DML风格:(批量更新,批量操做)和缓存不一样步

session.createQuery(“update Student s set s.name=? where s.id<?”)

.setParameter(0,”小仙女”)//更新name都为小仙女

.setParameter(1,5)//id<5的记录都被更新

.executeUpdate();

Hibernate的缺点:不支持批量更新,不是说不能批量更新,而是批量更新的操做不能保证缓存中的数据和数据库中的数据同步。

 

Struts1Hibernate的集成:

一、         建立web项目

二、         导入两个框架的jar包以及jstl的jar包—--WEB-INF/lib

三、         在src下存放国际化资源文件

四、         在web.xml中配置struts1.0  <ActionServlet>,配置编码过滤器

五、         配置struts-config.xml(一般用DispatchAction),放在WEB-INF下

六、         在src下配置hibernate.cfg.xml,log4j.properties

七、         在web.xml文件中配置HibernateFilter,一启动就建立SessionFactory

八、         也能够在struts-config.xml文件中使用插件配置Hibernate的启动,可是建议在Filter中配置,由于filter还要处理openSessionInview问题。

九、         模型层:包括实体类和类对应的*hbm.xml文件.

相关文章
相关标签/搜索