Hibernate-note03

hibernate03
• 一、
      一对一(单一的外键关联)
表的设计
t_person   
id   name
1.      tom
2.    jerry


t_card
  id(Fk)    cardnum
   1
因为card表的主键即是主键也是外键
一对一:所以card的id就只能是唯一的,并且这时有了个1了就不能再有一个1了
       并且也不能有个3,违反了外键约束,因为在t_person表中只有1和2,,写3就违反了外键约束


1.建立两个实体类生成set/get
    public class Person {


private int id;
private String name;
private Card card;
//set/get method
}




public class Card {


private int id;
private String cardNum;
private Person person;
//set/get method
}
2.配置one-to-one
 01.建立一个person.hbm.xml
<hibernate-mapping package="com.kaishengit.pojo">


<class name="Person" table="t_person">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<one-to-one name="card" class="Card"></one-to-one>
</class>
</hibernate-mapping>


   person中的id不受外键影响还是自动增长的,然后配置
   <one-to-one name="card" class="Card"></one-to-one>


 02.建立一个card.hbm.xml,这个注意的就是它的主键生成策略是foreign
<hibernate-mapping package="com.kaishengit.pojo">
<class name="Card" table="t_card">
<id name="id" column="id">
<generator class="foreign">//因为它是外键就不能是自动增长了,是外键生成策略
<param name="property">person</param>
//设置是指这个外键是哪里来的,是引用person这个属性所对应的表的主键
</generator>
</id>
<property name="cardNum" column="cardnum"></property>
<one-to-one name="person" class="Person"></one-to-one>
</class>
</hibernate-mapping>


 03.添加到hibernate.cfg.xml中
        <mapping resource="com/kaishengit/pojo/Person.hbm.xml"/>
<mapping resource="com/kaishengit/pojo/Card.hbm.xml"/>


3.OneToOne-----保存
        Person p = new Person();
p.setName("Alex");

Card card = new Card();
card.setCardNum("x001");
card.setPerson(p);

session.save(card);
session.save(p);
这个不论先save(card)还是先save(p),执行结果都是先存person再存card
并且就算你不存save(p),它也会需要先存person,这就是一对一中的强存


4.删除---级联删除
        Person p = (Person) session.get(Person.class, 9);
   session.delete(p);
这时会报错,与hibernate02note中的一样
这时可以在配置中加上:cascade="delete"
 <one-to-one name="card" class="Card" cascade="delete"></one-to-one>


这时候就会先查找,然后同时删除两个表中的这个记录


Hibernate: select person0_.id as id1_2_1_, person0_.name...._.id where person0_.id=?
Hibernate: delete from t_card where id=?
Hibernate: delete from t_person where id=?


5.查询
Person p = (Person) session.get(Person.class, 10);
System.out.println(p.getName());
System.out.println(p.getCard().getCardNum());
在一对一的时候,它查询的时候会直接查询出自己关联的那张表,并且这个特性是不能修改它
它总是这样查询,没有延迟加载这种情况,不论你是查card还是查询person


6.cascade属性
save-update:在执行保存和修改是进行级联操作,不常用
delete:在执行删除时进行级联操作
all:在所有情况下进行级联操作,指的就是save-update和delete同时
none:不进行级联操作(默认)


  总结:配置注意card主键生成策略,引用的是另外一张表的属性,可以级联删除,不支持懒加载


• 二、
     一对一(唯一外键关联)


1.表的设计
t_person   
id (fk)(unique)   name
1.                 tom
2.                jerry


t_card
  id(fk)(unique)   cardnum
   1                   000212
   2
这种是各存了对方的主键是自己的外键,算是多对一的一种特殊情况


一对一:为了保证双方都是一对一,所以要加上一个unique属性,(思考理解)
2.在配置的时候,因为是特殊的多对一
01.在Person.hbm.xml中
<hibernate-mapping package="com.kaishengit.pojo">
<class name="Person" table="t_person">
<id name="id">
<generator class="native"></generator>
</id>
<property name="userName"></property>
<many-to-one name="card" column="cardid" class="Card" cascade="delete" unique="true" />
//加了一个unique="true"
</class>
</hibernate-mapping>


02.在Card.hbm.xml中


<hibernate-mapping package="com.kaishengit.pojo">
<class name="Card" table="t_card">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="cardNum"></property>
<many-to-one name="person" class="Person" column="personid" unique="true“ />
</class>
</hibernate-mapping>


它的持久化类还是不变
它的好处是:它是manytoone的一种特殊形式,在加载person时候就不会去主动加载card,
            有了懒加载的特性
这个要根据业务来选择,
           要是需要person的时候总是需要card就可以使用第一种
           如果需要person偶尔需要card,就可以用这一种。


• 三、
多对多(学生对应老师)
多对多的时候就必然需要第三张表来维护这种关系
 
t_student → t_student_teacher →  t_teacher


 t_student
id         name


t_student_teacher
studentid(FK)   teacherid(fk)
 
t_teacher
id          name


1.中间的表关系表不用建实体类(持久化类),但是两个实体表需要建实体类
   并且都要放入对方实体类的set集合
    public class Teacher {
private int id;
private String name;
private Set<Student> studentSet;
   //get/set method
    }
    public class Student {
private int id;
private String name;
private Set<Teacher> teacherSet;
//get/set method
    }
2.配置映射文件
 01.student.hbm.xml配置
<hibernate-mapping package="com.kaishengit.pojo">
<class name="Student" table="t_student">
<id name="id" column="id">
<generator class="native">//生成策略仍是自动增长
</generator>
</id>
<property name="name" column="name"></property>
<set name="teacherSet" table="t_teacher_student">
//set中要加一个table来对应的关系表(中间表)
<key column="stu_id" />//这个表即student在关系表中对应的外键的名字


<many-to-many class="Teacher" column="tea_id" />
//这里面的column写的也是对应的表在关系表中的外键的名字
</set>
</class>
</hibernate-mapping>


 02.teacher.hbm.xml配置,和student中相对应
<hibernate-mapping package="com.kaishengit.pojo">
<class name="Teacher" table="t_teacher">
<id name="id" column="id">
<generator class="native">
</generator>
</id>
<property name="name" column="name"></property>
<set name="studentSet" table="t_teacher_student">
<key column="tea_id" />
<many-to-many class="Student" column="stu_id" />
</set>
</class>
</hibernate-mapping>
 03.在hibernate.cfg.xml加上映射文件
<mapping resource="com/kaishengit/pojo/student.hbm.xml"/>
<mapping resource="com/kaishengit/pojo/teacher.hbm.xml"/>
3.保存
01.在ManyToMany中
Student s1 = new Student();
s1.setName("s1");

Student s2 = new Student();
s2.setName("s2");

Teacher t1 = new Teacher();
t1.setName("t1");

Teacher t2 = new Teacher();
t2.setName("t2");

Set<Student> studentSet = new HashSet<Student>();
studentSet.add(s1);
studentSet.add(s2);

t1.setStudentSet(studentSet);
t2.setStudentSet(studentSet);

session.save(s1);
session.save(s2);
session.save(t1);
session.save(t2);

Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)

s1s2t1t2保存,然后他们的关系保存,8条记录
这时候即使s1s2和t1t2发生位置变化,也是有8条,都是先存进去--4条,再4条维护关系

02.
   Set<Student> studentSet = new HashSet<Student>();
studentSet.add(s1);
studentSet.add(s2);

t1.setStudentSet(studentSet);
t2.setStudentSet(studentSet);

Set<Teacher> teacherSet = new HashSet<Teacher>();
teacherSet.add(t1);
teacherSet.add(t2);
//假如是student.hbm.xml放弃关系,那下面的学生维护关系就没有用了
s1.setTeacherSet(teacherSet);//不起作用
s2.setTeacherSet(teacherSet);

session.save(s1);
session.save(s2);
session.save(t1);
session.save(t2);


Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_teacher (name) values (?)
Hibernate: insert into t_teacher_student (stu_id, tea_id) values (?, ?)
Hibernate: insert into t_teacher_student (stu_id, tea_id) values (?, ?)
Hibernate: insert into t_teacher_student (stu_id, tea_id) values (?, ?)
Hibernate: insert into t_teacher_student (stu_id, tea_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)
Hibernate: insert into t_teacher_student (tea_id, stu_id) values (?, ?)


在一对多,和多对一的时候,在出现双方都去维护,这是对结果没有影响,
只是多执行两条update
但是在多对多的时候,热心群众过多,这时候就会出错,就会出现重复的关系记录出现
解决方案:
我只需要在配置的时候,任意选择一方,在set里面配置加上,inverse="true"


<set name="teacherSet" table="t_teacher_student" inverse="true">
<key column="stu_id" />
<many-to-many class="Teacher" column="tea_id" />
</set>


4.查询
Student s3 = (Student) session.get(Student.class, 19);
System.out.println(s3.getName());
for (Teacher t : s3.getTeacherSet()) {
System.out.println(t.getName());
}
同样是懒加载,用不到teacher的时候,不执行第二条语句
但是也可以用抓取策略
<set name="teacherSet" table="t_teacher_student" inverse="true" fetch="join">
这时候就是执行一个连接表查询
5.关闭懒加载lazy="false"
这时候是不管你需要不需要都会进行查询,就是都是有两条查询语句,
它比着fetch="join",就是它是执行两条语句,而fetch是执行一条join查询
这样性能会降低


6.延迟加载的范围,是session关闭之前


Student s3 = (Student) session.get(Student.class, 19);
System.out.println(s3.getName());

session.getTransaction().commit();
for (Teacher t : s3.getTeacherSet()) {
System.out.println(t.getName());
}
这时候就会报错no session
solution:
   • 关闭延迟加载功能----lazy="false"
   • 修改抓取策略
   • 使用Hibernate对象的initialize方法将关联对象进行预加载
   预加载:
   就是让第二条SQL在关闭之前执行一次,保存在内存里
    Student s3 = (Student) session.get(Student.class, 19);
System.out.println(s3.getName());
//这是预加载
Hibernate.initialize(s3.getTeacherSet());
session.getTransaction().commit();
for (Teacher t : s3.getTeacherSet()) {
System.out.println(t.getName());
}


但是这三种解决方案都不是hibernate推荐的,会让性能下降。
note:但是在用的时候经常会session关闭了,页面还在用它的懒加载
eg: findById(){ return session.get(User.class,1);}
session关闭了
页面:${user.address}
这时候会用Spring的一个过滤器OpenSessionInView来延长session的存活周期,
会让session在全部做完了响应了,再关闭


• 四、缓存


1.一级缓存(内置的,本来就有),范围就是在session关闭之前


 01.一级缓存在Session中实现,当Session关闭时一级缓存就失效了。
eg01:session.get(User.class,10);
session.get(User.class,10);
session.get(User.class,10);
session.getTransaction().commit();
当在session中连续多次获得同一个对象,这时候只执行一次sql。
hibernate会在一级缓存中查看下,若是有了,就直接输出,要是没有就到数据库中查询
这比用jdbc性能高,jdbc会用几次查几次,这明显高很多
eg02:session.get(User.class,10);
session.get(User.class,10);
session.getTransaction().commit();
Session session2 = HibernateUtil.getSession();
session2.beginTransaction();
session2.get(User.class, 10);
session2.getTransaction().commit();
这时候就会执行两条sql,因为在session2查询的时候,session关闭了,一级缓存已经没有


02.contains方法
判断对象是否存在于一级缓存中
User user = (User) session.get(User.class, 10);
System.out.println(session.contains(user));//true
User user2 = (User) session.get(User.class, 10);
System.out.println(session.contains(user2));//true
03.clear方法和evict方法
clear方法用于将所有对象从一级缓存中清除
evict方法用于将指定对象从一级缓存中清除
session.beginTransaction();
User user = (User) session.get(User.class, 2);
//session.clear();
session.evict(user);
User user2 = (User) session.get(User.class, 2);
session.getTransaction().commit();


2.二级缓存(外置)
  在Hibernate中二级缓存在SessionFactory中实现,由一个SessionFactory的所有Session
实例所共享。Session在查找一个对象时,会首先在自己的一级缓存中进行查找,如果没有找到,
则进入二级缓存中进行查找,如果二级缓存中存在,则将对象返回,如果二级缓存中也不存在,
则从数据库中获得。Hibernate并未提供对二级缓存的产品化实现,而是为第三方缓存组件的使
用提供了接口,当前Hibernate支持的第三方二级缓存的实现如下:
• EHCache//用的最多的,java中一个比较好的缓存框架
• Proxool
• OSCache
• SwarmCache
• JBossCache
  所有的操作需要用到的session都是SessionFactory产生的,不管是session1还是session3,
  二级缓存在这个范围里都是可以用的。


3.Ehcache的使用
01.导入包
在hibernate-release-4.2.4.Final\hibernate-release-4.2.4.Final\lib\optional\ehcache中
在lib的可选择lib的ehcache中有几个jar包,导入项目
02.添加ehcache.xml
  在etc源代码包里添加这个文件,复制下面内容进去即可
 <ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache 
maxElementsInMemory="10000" 
eternal="false" 
timeToIdleSeconds="120" 
timeToLiveSeconds="120"
overflowToDisk="true" />
</ehcache>
说明:
<ehcache>
//当内存不足的时候,它就把这些对象以序列化的形式存到磁盘里
//写到磁盘的这个目录里,是java的一个临时文件夹,用的时候通过反序列化取出来
<diskStore path="java.io.tmpdir"/>


<defaultCache
maxElementsInMemory=“10000“ →缓存中最大允许保存的对象数量
eternal=“false“ →缓存中数据是否为常量
timeToIdleSeconds=“120“ →缓存数据钝化时间,单位为秒
timeToLiveSeconds=“120“ →缓存数据生存时间,单位为秒
overflowToDisk=“true“ →内存不足时,是否启用磁盘缓存
/>
</ehcache>


03.开启二级缓存
在hibernate.cfg.xml,中的</session-factory>节点中添加下面这段
<!-- 开启二级缓存 -->
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>


note:这个配置在hibernate提供的hibernate.properties中可以查到,但是里面把chcache的要映射
 的包名写错了,
hibernate.cache.region.factory_class// 这是对的
org.hibernate.cache.internal.EhCacheRegionFactory//这是错的
可以在web applib中/hibernate-ehcache-4.2.4.Final.jar/org.hibernate.cache.ehcache中
查找的到
04.哪一个持久化类支持二级缓存,需要在它的映射文件xxx.hbm.xml中的class节点里面添加缓存配置
添加一个缓存节点<cache usage="read-write" />
    
    <hibernate-mapping package="com.kaishengit.pojo">
<class name="User" table="t_user">
<cache usage="read-write" />
<id name="id" column="id">
....
</class>
</hibernate-mapping>


  EHCache支持以下三种同步策略:
read-only:只读。对于不会发生改变的数据,可以使用只读性缓存。这种性能最高
read-write:可读写缓存。用于对数据同步要求严格的情况。
//性能最低,时刻与数据库同步,所以性能低
nonstrict-read-write:如果程序对并发访问下的数据同步要求不是很严格,
且数据更新操作不频繁时可采用该缓存策略//性能次之


05.让刚才映射缓存的pojo实体类去实现序列化接口
public class User implements Serializable {
private static final long serialVersionUID = 1L;
......
}
  这个是推荐不是,必须。当我们内存够用的时候,不实现Serializable接口没有事,但是当内存不足
  存到磁盘上的时候,如果没有实现序列化接口,它就会报错,所以我们一般都推荐实现。
  切记。


06.使用二级缓存


用法很简单,就是可以跨session了,只要在同一个sessionFactory中
session.get(User.class,10);
session.get(User.class,10);
session.get(User.class,10);

session.getTransaction().commit();
Session session2 = HibernateUtil.getSession();
session2.beginTransaction();
session2.get(User.class, 10);
session2.getTransaction().commit();
这时候是只执行一条sql语句


07.将对象从二级缓存中清除
Cache cache = HibernateUtil.getSessionFactory().getCache();
cache.evictEntityRegion(User.class);
eg:
User user = (User) session.get(User.class, 2);
session.getTransaction().commit();
Cache cache = HibernateUtil.getSessionFactory().getCache();
cache.evictEntityRegion(User.class);
Session session2 = HibernateUtil.getSession();
session2.beginTransaction();
User u = (User) session2.get(User.class, 2);
session2.getTransaction().commit();


4.关于缓存
01.缓存在web中非常重要
在有的语言,例如php就没有缓存
例如在一个应用程序app连接到一个数据库D,但是这个数据库同时能提供的连接数量是有限的(900)。
当一瞬间有很多(1000)用户访问你的程序app,页面只发出一条select语句,但是app就会做出
1000次数据库的查询,这时候数据库就挂掉了。就算连接池也不行。这时候就得靠缓存。


02.缓存的理解
例如:当查询出来的一个结果,存到一个map集合,键值对,map集合的key是个String型,value是
一个Object型这时我们给map起个名字叫cache,存在内存里面的。
当第一次做出来一个select的时候,我们调用cache.put(key,value);key就为index,value把这个
list结果存进value
当第二select到来的时候,我们就可以判断,如果cache中有,就直接取出来,如果没有就再从数据库中
查,同时存到cache中。这就是缓存。即使同时有1000人来查询也没问题。这样就提高了性能。
这就是缓存的设计,但是实现是很复杂的,远没有这么简单。


03.这里面涉及的东西
整个系统里缓存的东西很多,所以cache-map中需要放的东西很多,这个东西需要占内存,并且占的
内存很大。所以我们要把长时间不用的键值对清理掉。这时就需要做一个定时器,定时检查把一些给
剔除掉,但是哪些能剔除掉呢?这就需要缓存算法。
1.FIFO,先进先出,先进来的先清掉
2.LRU, 最近最少使用的,先清理掉。把索引记录放到缓存器中,在缓存器中有一个计数器count,当
被用一次count就加1;这时候它就定期会把使用次数排在倒数的,剔除掉。


04.这时候就算使用缓存算法,内存再大还是不够用,例如淘宝,推特。这时候会用分布式缓存。
可以用多台电脑去做缓存,这时候假如在机器A中的存有index的数据,B中存有home中的数据,C中存
有另外的数据,这时候请求的人就找不到了。
这时候就会
APP → 一个缓存总的调度的机器 → 调用各个缓存的多台机器 →如果没有 → 到数据库中查
查出来后再放到缓存服务器上
    
    一个缓存总的调度的机器指的就是服务器组件,叫Memcache
Mamcache,这个只能在linux上,是一个开源的项目,当在linux上加了这个组件,它会让配置缓存服务
器的ip地址,*,*,*,01;02;03,....;这时候假如主服务器是100,它的作用就是调用,分发。
当app查的时候,只需要调用Memcache,Memcache会自动去在所有的server上找,找不到了会去数据库中
查找,查找到后,返回结果并存一份到缓存server中。
基本上所有的互联网公司都用到这个。单机访问一天一千万这已经是一个极致。


Memcache是为Php做的,php中没有缓存,java中可以用map和set,和cache实现。所以有人写了一个这个
组件供php用。但是后来被流行。


5.分布式缓存
1.memcache---:http://memcached.org/


Cache Results


function get_foo(foo_id)
   foo = memcached_get("foo:" . foo_id)
   return foo if defined foo


   foo = fetch_foo_from_database(foo_id)
   memcached_set("foo:" . foo_id, foo)
   return foo
end


在java中有一个对于memcache的jar包叫:Memcached-Java-Client


2.redis---http://redis.io/
一个键值对的数据库(现在也有人把它当缓存数据库用)
1.Redis简介
redis Redis是一个key-value存储系统。和Memcached类似,但是解决了断电后
数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还
支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型。这些数
据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且
这些操作都是原子性的。


http://www.cnblogs.com/shanyou/archive/2012/01/28/2330451.html






































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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值