hibernate 3 笔记

Configuration cfg = new Configuration().configure() ;//加载配置文件默认路径为/hibernate-cfg.xml 

SessionFactory factory = cfg.buildSessionFactory() ;

Session session = factory.openSession() ;

1关于Session的管理的注意事项:(sessionactionQueuepersistenceContext是重点) 

User u = new User () ;

u.setName("小强3") ;

Transaction t =session.beginTransaction()  ; 

session.save(u) ;

u.setName("小二强3") ;

t.commit() ;

发生如下代码

1Hibernate: insert into User (name, password, createTime, expireTime, id) values (?, ?, ?, ?, ?)

2Hibernate: update User set name=?, password=?, createTime=?, expireTime=? where id=?

   观察发现: save以后,又手动改了uname ,没有再进行保存commit时仍然进行了update,说明此时的u对象被Session对象管理着(放在一个map里),commit后逐一检查是否更改(实际采用listener监听,扫描listener数组,观察是否发生改变),若更改则进行保存 .

然后再进行了一次测试 (接着上面的)

User user = new User() ;

user.setId(u.getId()) ;

user.setName(u.getName()) ;

user.setPassword("111") ;

           session.save(user) ;

//session.update(user) ; //update也是一样的异常

然后发生如下错误:

a different object with the same identifier value was already associated with the session: [com.pqh.entity.User#5a437a293eef6ef9013eef6efb5e0001]

(一开始以为 session管理是记录实体的id,后来看源代码发现是map)此时知道了 new出来的对象 ,即使id相同 ,也无法对应同一条记录 (u所对应的那条数据库记录),但实际上 这两个User对象 的key是相同的,都是[com.pqh.entity.User#5a437a293eef6ef9013eef6efb5e0001]  

(可能是先通过key取出对象 ,再进行地址的比较判断是不是同一个对象)

Session.get(object o)  Session.load(object o) 的区别 

   Get 是发出sql 查询,而loadlazy加载)是不发出sql去查询

而是用cglib生成一个继承User代理类

只有在真正使用的时候 才发出sql去加载

2Detached 状态注意点: 

   Detached 状态的对象 ,原先处于Session管理中,然后因为session.close ()clear() 被清除

   但是此时可以对该对象进行update saveOrUpdate(推测是在sessionFactory中保留了一些信息进行user对象的验证)

若对detached对象进行update,可以修改原来的数据

但若对detached对象进行save操作 ,会存进一条新的数据( 而被session管理的对象则会更新,因为save内部其实调用了saveOrUpdate方法)

User u = new User () ;

Transaction t =session.beginTransaction()  ; 

session.save(u) ;

u.setName("小二强3") ;

t.commit() ;

   session.close() ;

Session session2 = factory.openSession() ;

session2.beginTransaction()  ; 

session2.save(u) ;

u.setName("小强3") ;

session2.getTransaction().commit() ;

而把session2.save(u) ; 改成session2.update(u) ; 只会

3对于hibernate oid  <id>

Hibernate 3中 ,

<id name="id">

<generator class="org.hibernate.id.IncrementGenerator"/>

</id>

该字0段仅对hibernate程序有效 ,在mysql中不会自增,insert时不指定id则出错

4、对于manyToOne 等标签的一个小注意,

    如user的外键(在hibernate中变成外键的类对象)为group 

save一个user时 (在没有级联时)如果grouptrasient状态(即也是刚new出来,还没存的)

  则会出错 ,要先存group

  而manyToOne 中先指定了casecade=   “all” 、”save-update” 、”delete”、”none”的前2个,则不用再先保存group

如果是用已有的groupid insert一个user ,先要查询出group 设进user中,再进行插入,否则会新建group 或出错(把groupid设成数据库中一个grou记录的id时,因为不是被session管理的group,即使id相同,也会出错(新建了重复idgroup))

5、one2many many2one标签 在数据库添加的外键是一样的 

比如在user中设置了<manytoone class=group column=groupid>

这样会在user表中添加一个groupid 的字段 

而只是用onetomany标签,在group中添加

<set name=users>

<key column=groupid />

<onetomany class=User />

<set/>   

一样是在user表中加了一个groupid字段  ,两个标签的区别就是可以在group中取到set<User>  而数据库表是没区别的

 

6、many2many

  例子:Student course表是多对多的

单向: 

   只在student中加入

<set name="courses" table="t_students_courses">

    <key column="studentid"></key>

    <many-to-many     class="com.pqh.entity.Course"column="courseid">

</many-to-many>

    

</set>

生成的表  

  

双向:

       几乎相同(两个类中都有对方的set , 其中table名一定要一样t_students_courses,不然会多生成出一个中间表)

7、Session flush : 

        作用:

             1、清理缓存(save update 等操作后,在session中有一个临时集合存储对象,还有一个map 用来管理对象 ,flush就是移除临时集合里的对象 ,而evict是移除map里被管理的对象)

             2、发送sql (在移除临时集合的同时,发送sql到数据库,但还没有commit)

             3修改EntityEntriessentinel中的ExistsInDatabasetrue

临时集合:

        

管理的map(对象在collectionEntries):

        

   数据库的隔离级别 :

   Read uncommitted 

   Read -committed     // 其他数据库常用 ()

   Repeatable read    //mysql 默认 (可重复读)

   Serializable 

  对于id native uuid 

   Native: 

         (Mysql为自增),此时用session.save 就发送了sql 

   Uuid 

          Session.save 还不发送sql ,直到session.flush 调用才发送sql  commit方法会调用session.flush

   

如果我们数据库的隔离级别为未提交读( Read uncommitted ),我们可以看到flush后的数据 ,(commit之前)

8、Hibernate lazy 策略

Class上的lazy策略 ,load方法得到一个cglib生成的代理实体类(假设是user)

,在getName等方法调用以后 会向数据库发送sql取得相应的实体对象(getId不行,因为查询时id已经自己设进去了,不会再去数据库中查询) 

若是在session关闭以后才调用getName等方法去触发sql,就会发生异常

Collection上的lazy策略, load方法得到group对象 ,group对象有set<User>

但直到调用

Set<User> users = group.getUsers() ;  //在该方法调用以后才发出了查询全部数据的SQL  ,说明集合是支持lazy加载的

   

Lazy =false 标签

  1、 Class上的lazy ,lazy设为false 

   <class name="com.pqh.entity.Groups"  lazy="false">

   在调用load方法以后立刻发出了sql 但只加载了普通属性set 仍然没有加载(many2one  one2one 对应的对象也都没有加载)

 

  2、集合上的lazy设为false以后 :

<set name="users"  lazy="false">

    <key column="groupid"></key>

    <one-to-many class="com.pqh.entity.User" />

</set>

   load  group类时 立刻把set<User> 加载进来了

   3、集合上的lazy设为extra以后 :

 <set name="users"  lazy="extra">

    <key column="groupid"></key>

    <one-to-many class="com.pqh.entity.User" />

</set>

  其他方面和lazy =true 相似 ,但调用users.size() 时,

  发出的sql是 select count(*) from user  where user.id = ?

  而不是直接把所有的user 都加载进来   , 效率会比lazy =true 高一些

  (但是访问任意一个users中的user对象 ,仍然会把所有的user都加载进来)

   4、把many2one one2one lazy

          设为false以后 :不会进行延迟加载

          设为proxy以后 :进行延迟加载  (和普通的lazy = true相同)

           Noproxy 一般不用

注意 : 

class(group) 设置Lazy = false 以后 ,不会再生成class的代理类,

那么这对类内部的其他属性(如set) 设置的lazy=true有影响吗??   

(由图得 :其中的set<User>因为设置了lazy=true , 所以仍然生成了代理类presistentSet  ,没有影响)

9、继承映射

1每棵继

 

承树映射成一张表

t_animal

id

name

sex

weight

height

type

1

猪猪

true

100

P

2

鸟鸟

false

50

B

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC 

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.bjsxt.hibernate">

<class name="Animal" table="t_animal" lazy="false">

<id name="id">

<generator class="native"/>

</id>

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

<property name="name"/>

<property name="sex"/>

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

<property name="weight"/>

</subclass>

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

<property name="height"/>

</subclass>

</class>

</hibernate-mapping>

多态查询:在hibernate加载数据的时候能鉴别出正真的类型(instanceOf

1get支持多态查询

2load只有在lazy=false,才支持多态查询  

    而用load(lazy=true)查询animal时,得到的结果即使是pig type =p ),但是 animal对象 instanceof Pig   == false ,因为该instanceanimal的代理子类 ,字节码是不同的

3hql支持多态查询

2、每个继承类映射成一张表

<hibernate-mapping package="com.bjsxt.hibernate">

<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">

<key column="pid"/>

<property name="weight"/>

</joined-subclass>

<joined-subclass name="Bird" table="t_bird">

<key column="bid"/>

<property name="height"/>

</joined-subclass>

</class>

</hibernate-mapping>

       共生成3张表 ,t_animal  t_pig  ,t_bird , 后2着的主键分别为pidbid,是t_animal id的外键

3、每个类映射成一张表(子类中加入父类的字段,这时最好设置父类的abstract=true)

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC 

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.bjsxt.hibernate">

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

<id name="id">

<generator class="assigned"/>

</id>

<property name="name"/>

<property name="sex"/>

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

<property name="weight"/>

</union-subclass>

<union-subclass name="Bird" table="t_bird">

<property name="height"/>

</union-subclass>

</class>

</hibernate-mapping>

 设置abstract=true以后得到2张表(不设置则为3张,但父表是没用的)

 字表中直接有父表的字段,不用再像第2种生成方式那样通过join id =id 得到完整的一条记录

 此时要注意:分成2张表以后 <generator class="assigned"/>,也可以是uuid,但不能是native  ,虽然在数据库中native是可用的,但在hibernatesession中, 若是native ,则插入一个pig 和一个bird数据 ,两个类会都得到id=1 ,这样会出错(同是animal的子类  ,(但这个解释仍然不是非常理解))

10、 复合主键映射   (composite component映射相似,但前者是主键,后者是普通字段)

11、 Map List 等集合的映射 (生成了其他表)

12 、悲观锁 :

      加锁以后读取一条记录,在事务完成前,别的事务不能再读取该记录了  。  

      在数据库中是select * from XX for update;

      在hibernate中使用方法是 :

            load (XX.class ,id ,LockModel.UPGRADE) ;

            使用悲观所以后,loadlazy会失效

13、乐观锁 :

      

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC 

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.pqh.entity.Student"   optimistic-lock="version">  <!--添加该字段表示乐观锁的控制方式为版本控制 -->

<id name="id">

<generator class="native"/>

</id>

  <version name="version"></version>  <!--加入一个版本的字段  该字段由hibernate自动进行维护 -->

  <property name="st_name" />

  <property name="st_classNumber" />

</class>

</hibernate-mapping>

public class Student {

private int id ; 

private String st_name ;

private int st_classNumber ;

   //  ....getter  setter  .....

}

测试

  线程1取得了一条Student数据并修改,但没有提交  

  此时线程2也取得该数据 修改并提交 ,再对1进行提交 ,

  此时出现如下错误

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.pqh.entity.Student#1]

  

  因为原先 \

   Version 。两线程取得的version都为线程2修改以后,version变为,而线程1又提交了version0的数据,此时hibernate可以判断出线程1的记录为脏数据,所以出错

14、hibernate  cascade=true 的设置

<hibernate-mapping>

<class name="com.pqh.entity.Groups">

<id name="id">

<generator class="org.hibernate.id.IncrementGenerator"/>

</id>

<property name="name"/>

 <set name="users" >

    <key column="groupid" />  <!-- 告诉hibernate groups的id字段要设到user哪个字段上去 -->

    <one-to-many class="com.pqh.entity.User" />

</set> 

</class>

</hibernate-mapping>

public class Groups {

private int id ; 

private String name ;

private Set<User> users ; 

}

public class User {

private int id;

private String name;

private Groups group ;

}

<hibernate-mapping>

<class name="com.pqh.entity.User">

<id name="id">

<generator class="org.hibernate.id.IncrementGenerator"/>

</id>

<property name="name"/>

<many-to-one name="group" column="groupid" cascade="all">

          </many-to-one>

</class>

</hibernate-mapping>

测试:

在many2one 标签上设置了cascade以后 ,

   1save User对象时,会同时save trasient状态的group ,

   2save Group对象时 ,会出错object references an unsaved transient instance

说明 cascade是单向的,若想groupuser 能互相保存trasient状态的对方,many2one one2many上都要设置cascade=true

15、Hibernate HQL 

  首先,query是支持lazy策略的

List list = session.createQuery(" from User u where u.id=0").list() ;

 User u=   (User) list.get(0);

 System.out.println(u.getBelonggroup().getName());

   查询得一个User对象,其中的belonggroup是代理类,

hibernate查询语言hql

hql中关键字不区分大小写,但是属性和类名区分大小写

1、简单属性查询【重要】

单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致

多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致

  数组的长度取决与select中属性的个数

如果认为返回数组不够对象化,可以采用hql动态实例化Student对象

参见:SimplePropertyQueryTest.java  

2、实体对象查询【重要】

* N + 1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题

  所谓的N+1是在查询的时候发出了N+1sql语句

  1: 首先发出一条查询对象id列表的sql

  N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句

* listiterate的区别?

* list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据

* iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题

参见:SimpleObjectQueryTest1.java/SimpleObjectQueryTest2.java

3、条件查询【重要】

可以采用拼字符串的方式传递参数

可以采用 ?来传递参数(索引从0开始)

可以采用 :参数名 来传递参数

如果传递多个参数,可以采用setParamterList方法

hql中可以使用数据库的函数,如:date_format

参见:SimpleConditionQueryTest.java 

  

4hibernate也支持直接使用sql进行查询

参见:SqlQueryTest.java

5、外置命名查询

在映射文件中采用<query>标签来定义hql

在程序中采用session.getNamedQuery()方法得到hql查询串

参见:Student.hbm.xmlNameQueryTest.java

<query name="getUserByID">

    <![CDATA[

    from User u where u.id =? 

    ]]>

</query>

6、查询过滤器

在映射文件中定义过滤器参数

在类的映射中使用这些参数

在程序中启用过滤器

参见:Student.hbm.xmlFilterQueryTest.java

7、分页查询【重要】

* setFirstResult(),从0开始

* setMaxResults,每页显示多少条数据

参见:PageQueryTest.java

//每页3条记录 

for (int i =0 ;i<5; i++){

List<User> list = session.createQuery("from User     ").setFirstResult(i*3)

.setMaxResults(3).list() ;

System.out.println("第"+(i+1)+"页");

Iterator<User> iter = list.iterator() ;

while (iter.hasNext()){

User u = iter.next();

System.out.println(u.getName()+ ","+u.getCreateTime());

}

}

8、对象导航查询,在hql中采用 进行导航【重要】

参见:ObjectNavQueryTest.java

9、连接查询【重要】

内连

外连接(左连接/右连接)

参见:JoinQueryTest.java

10、统计查询【重要】

参见:StatQueryTest.java

11DML风格的操作(尽量少用,因为和缓存不同步)

参见:DMLQueryTest.java

session.createQuery("update Student s set s.name=? where s.id < ?")

.setParameter(0, "李四")

.setParameter(1, 5)

.executeUpdate();

16、Hibernate 一级缓存 

 一级缓存很短和session的生命周期一致,一级缓存也叫session级的缓存或事务级缓存

那些方法(的查询)支持一级缓存:

* get()

* load()

* iterate(查询实体对象)

如何管理一级缓存:

* session.clear(),session.evict()

如何避免一次性大量的实体数据入库导致内存溢出

flush,再clear

如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具

Evict(object ) 会清除object 的缓存 ,

Clear () 会清除当前没在使用的缓存(EntityEntries) 

Flush() 会对比actionQueueEntityEntries 中对象的区别,从而进行数据库更新,设置ExistsInDatabase =true ;

17、Hibernate 二级缓存 

二级缓存的配置和使用:

echcache.xml文件拷贝到src

开启二级缓存,修改hibernate.cfg.xml文件

<property name="hibernate.cache.use_second_level_cache">true</property>

指定缓存产品提供商,修改hibernate.cfg.xml文件

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

指定那些实体类使用二级缓存(两种方法)

在映射文件中采用<cache>标签

hibernate.cfg.xml文件中,采用<class-cache>标签

二级缓存是缓存实体对象的

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

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

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

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

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

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

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

<!-- 开启二级缓存 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

<!-- 指定缓存产品提供商 -->

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

<mapping resource="com/pqh/entity/Groups.hbm.xml"/>

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

<class-cache class="com.pqh.entity.User" usage="read-only"/>

</session-factory>

</hibernate-configuration>

User Groups 两个对象,用两个Session查询同一个idUser,

 u  =(User) session.get(User.class, 0) ;

System.out.println("name = "+u.getName());

System.out.println("  group ="+u.getBelonggroup().getName());

第二次读取 :虽然u.getName时没有再发sql,但getBelonggroup时发出了查询groupsql ,(可以说明,如果只配置了User,那么Userone2many ,one2one等映射的对象,不会被缓存起来 )

session和二级缓存的交互 : (默认为CacheMode.Normal)

     session.setCacheMode(CacheMode.GET) ;//表示该sessiongetload操作只从二级缓存中读,不会写进二级缓存

     

  CacheMode.PUT    CacheMode.IGNORE   CacheMode.NORMAL   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值