今日要点:

  1、继承关系映射

    一个表对应一个映射树(subclass)

    一个子类一个表,子类只有父类没有的字段(joined-subclass)

    鉴别器和内连接结合使用(subclass join)

    一个类对应一个表(union-subclass)

  2、懒加载

  3、缓存

  4、事务以及事务有关

  

1、j继承映射

  

1、一张表映射一个继承树。

Employee.hbm.xml的主要代码:

<class name="Employee" discriminator-value="0"> <!-- 鉴别属性值为0表示为普通雇员 -->
<id name="id" >
<generator class="native"/>
</id>
<discriminator column="type" type="int"/><!-- 鉴别器,缺省类型是字符串类型 ,鉴别器的位置放置在property前面,不写值的话表示属性值为类名的字符串形式-->
<property name="name"/>
<many-to-one name="depart" column="depart_id" cascade="save-update"/> <subclass name="Skiller" discriminator-value="1"> <!-- sub:替代品 ,表示子类 -->
<property name="skill"/>
</subclass> <subclass name="Sales" discriminator-value="2">
<property name="sell"/>
</subclass>
</class>

效率比较高
增加子类就会修改表结构。增加字段
特有字段都不能加上非空约束,应该可以为空,关系型来说不是很好的设计

package cn.itcast.domain;

import java.util.Set;
public class Department {
private int id;
private String name;
private Set<Employee> employees; public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
} public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department() {
super();
}
public Department(int id, String name) {
super();
this.id = id;
this.name = name;
} }

Department.java

package cn.itcast.domain;

public class Employee {
private int id;
private String name;
private Department depart;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepart() {
return depart;
}
public void setDepart(Department depart) {
this.depart = depart;
}
public Employee() {
super();
}
public Employee(int id, String name, Department depart) {
super();
this.id = id;
this.name = name;
this.depart = depart;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", depart=" + depart
+ "]";
}
}

Employee.java

package cn.itcast.domain;

public class Sales extends Employee{//销售
private int sell; public void setSell(int sell) {
this.sell = sell;
} public int getSell() {
return sell;
}
}

Sale.java

package cn.itcast.domain;

public class Skiller extends Employee{//技术
private String skill; public void setSkill(String skill) {
this.skill = skill;
} public String getSkill() {
return skill;
}
}

Skiller.java

<?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="cn.itcast.domain"> <class name="Department">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="employees" inverse="true">
<key column="depart_id" />
<one-to-many class="Employee" />
</set>
</class> </hibernate-mapping>

Department.hbm.xml

2、每一个子类一张表,通过外键关联

其他地方不用改变,只需要改变下employee.hbm.xml的代码(之后也是):

<class name="Employee"> <!-- 鉴别属性值为0表示为普通雇员 -->
<id name="id" >
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="depart" column="depart_id" cascade="save-update"/> <joined-subclass name="Skiller" table="skiller"><!-- join表示数据库表中使用连接与子类关联 -->
<key column="emp_id"/><!-- 连接使用的外键列 -->
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
</class>

表结构合理,子类和主类差距比较的的时候使用。
多态方式查询的话关联表过多效率低。不使用多态查询的话就比较优秀

3、鉴别器和内连接相结合

适用情况:雇员中销售的字段比较多但是技术的字段比较少

<class name="Employee" discriminator-value="0"> <!-- 鉴别属性值为0表示为普通雇员,注意:鉴别器的值如果不给的话,默认认为是类名的全名称 -->
<id name="id" >
<generator class="native"/>
</id> <discriminator column="type" type="int"/> <!-- 鉴别器,缺省类型是字符串类型 ,鉴别器的位置放置在property前面-->
<property name="name"/>
<many-to-one name="depart" column="depart_id" cascade="save-update"/>
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<join table="sales">
<key column="emp_id"/>
<property name="sell"/>
</join>
</subclass>
</class>

4、每一个类对应一个表,且每一个表中都具有完整信息,如果父类不是抽象的,也会有父类表

<?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="cn.itcast.domain"> <class name="Employee"><!-- discriminator-value="0" --> <!-- 鉴别属性值为0表示为普通雇员,注意:鉴别器的值如果不给的话,默认认为是类名的全名称 -->
<id name="id" >
<!-- <generator class="native"/>--> <!-- mysql,选取nativehibernate帮你选择的主键生成器是identity -->
<generator class="hilo"/>
</id> <!--
<discriminator column="type" type="int"/>
-->
<!-- 鉴别器,缺省类型是字符串类型 ,鉴别器的位置放置在property前面-->
<property name="name"/>
<many-to-one name="depart" column="depart_id" cascade="save-update"/> <!--
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<property name="sell"/>
</subclass>
--><!-- sub:替代品 ,表示子类 -->
<!--
<joined-subclass name="Skiller" table="skiller">
<key column="emp_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
-->
<!-- join表示数据库表中使用连接与子类关联 --><!-- 连接使用的外键列 -->
<!--
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<join table="sales">
<key column="emp_id"/>
<property name="sell"/>
</join>
</subclass>
--> <union-subclass name="Skiller" table="skiller"> <!-- union表示结合的意思 -->
<property name="skill"/>
</union-subclass>
<union-subclass name="Sales" table="sales"> <!-- union表示结合的意思 -->
<property name="sell"/>
</union-subclass>
</class> </hibernate-mapping>

注意:父类和子类表的id是不能重复的,所以我们的主键生成策略不能再时native或者identity这种自增长了。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

懒加载:
懒加载就是返回的其实是一个代理对象,该代理对象是你的domain对象的一个子类。在你使用到该对象的时候,hibernate才进行数据库的查询
懒加载的对象在session关闭之后调用该属性会报错。no Session,无法访问数据库,解决方法:
1、在session未关闭之前就调用一下该对象的属性,使得hibernate访问数据库,将代理对象中的对应值填充好。
2、使用Hibernate.instialize(Object)方法实例化懒加载的对象,这样他就会查询数据库将该对象持久化。
懒加载使用的是:asm.jar和cglib.jar实现的,那两个jar能够动态修改装入内存的字节码

load方式的懒加载:

package cn.itcast.domain;

import java.util.Date;

public class User {
private int id;
private Name name;
private Date birth;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
} }
package cn.itcast.domain; public class Name {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
<?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="cn.itcast.domain"> <class name="User">
<id name="id">
<generator class="native"/>
</id>
<property name="birth"/>
<component name="name" class="Name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
</component>
</class> </hibernate-mapping>

准备工作的代码

这样使用会抛出异常:

package cn.itcast.test;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.Transaction; import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.Name;
import cn.itcast.domain.User; public class Demo2 { public static void main(String[] args) {
add();
User user=query(1);
System.out.println(user.getName());
}
static void add(){
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
User user=new User();
Name name=new Name();
name.setFirstName("zhang");
name.setLastName("san");
user.setName(name);
user.setBirth(new Date()); tx=s.beginTransaction();
s.save(user);
tx.commit();
}finally{
if(s!=null) s.close();
}
} static User query(int id){
Session s=null;
try{
s=HibernateUtil.getSession();
User user=(User) s.load(User.class, id);
return user;
}finally{
if(s!=null) s.close();
}
}
}
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
at cn.itcast.domain.User$$EnhancerByCGLIB$$aedc35f5.getName(<generated>)
at cn.itcast.test.Demo2.main(Demo2.java:17)

解决方式:

static User query(int id){
Session s=null;
try{
s=HibernateUtil.getSession();
User user=(User) s.load(User.class, id);
//user.getName(); 在session关闭前简单使用下该对象,就会立刻加载过来了。不建议这样使用,因为调用该对象的语句看起来没有意义可能被误删
Hibernate.initialize(user); //建议使用这种方式
return user;
}finally{
if(s!=null) s.close();
}
}

one-to-one(元素)懒加载
必须同时满足下面三个条件:
(主表不能有constrained=trur,所以主表没有懒加载)
1、lazy属性不等于false
2、constraint=true。有外键关联
3、fetch=select 抓取方式为select

例子:

package cn.itcast.domain;

public class Person {
private int id;
private String name;
private IdCard idcard;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IdCard getIdcard() {
return idcard;
}
public void setIdcard(IdCard idcard) {
this.idcard = idcard;
} } package cn.itcast.domain; import java.util.Date; public class IdCard {
private int id;
private Person person;
private Date startUseDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Date getStartUseDate() {
return startUseDate;
}
public void setStartUseDate(Date startUseDate) {
this.startUseDate = startUseDate;
} } <?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="cn.itcast.domain"> <class name="Person">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<one-to-one name="idcard"/>
</class> </hibernate-mapping> <?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="cn.itcast.domain"> <class name="IdCard">
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="startUseDate" column="start_use_date"/>
<one-to-one name="person" constrained="true" cascade="save-update" fetch="select"/>
</class> </hibernate-mapping> package cn.itcast.test; import java.util.Date; import org.hibernate.Session;
import org.hibernate.Transaction; import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.IdCard;
import cn.itcast.domain.Person; public class Demo3 {
public static void main(String[] args) {
Person p=add();
IdCard idc=(IdCard) query(p.getId());
} static Person add(){
Session s=null;
Transaction tx=null;
try{
Person p=new Person();
p.setName("zhangSan");
IdCard idcard=new IdCard();
idcard.setStartUseDate(new Date());
// p.setIdcard(idcard);
idcard.setPerson(p); s=HibernateUtil.getSession();
tx=s.beginTransaction();
// s.save(p);
s.save(idcard);
tx.commit();
return p;
}finally{
if(s!=null) s.close();
}
} static Object query(int id){
Session s=null;
try{
s=HibernateUtil.getSession();
// Person obj=(Person) s.get(Person.class, id);//查询主表直接将所有属性查出,使用left join。无法懒加载
Object obj=s.get(IdCard.class, id);//查询附表进行了懒加载
System.out.println(obj);
/*
* fetch如果设置成为了join,则抓取附表对象的时候进行的是连接查询,就使得懒加载失去了效果
*/
return obj;
}finally{
if(s!=null) s.close();
}
}
}

多对一、一对多、多对多进行懒加载的要求:
lazy属性不能为false,
fetch为select

能够进行懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载(代理)对象的属性(getId和getClass除外),hibernate会初始化这些代理,相关联的session关闭之后,再次访问懒加载对象将会出现异常。

hibernate中对于空集合和为null的集合是不进行区分的

---------------------------------------------------------------------------------------------------------------------------------------------------

缓存。

  我们知道从数据库中拿一条数据出来是成本高的操作,我们一般有这样的需求:比如读一个用户信息,前一个人读一个A的用户信息,读完后下一个人又读取A的用户信息,这个时候我们希望采取一些优化措施。我们希望第一个人从数据库中读取,第二个人可以使用第一个人的一些成果。

  缓存的简单模拟:

package cn.itcast.test;

import java.util.HashMap;
import java.util.Map; import cn.itcast.domain.User; public class CacheMoNiDemo {
private Map<String,Object> cache=new HashMap<String,Object>(); public void update(User user){
updateDB(user);
String key=User.class.getName()+user.getId();
cache.remove(key);//我这里采取的操作是删除该数据的缓存
} public User getUser(int id){
String key=User.class.getName()+id;
User user=(User) cache.get(key);
if(user!=null){
return user;
}
user=(User) getFromDB();
cache.put(key, user);
return user;
} public Object getFromDB(){
//空的,简单模拟
return null;
} public void updateDB(Object obj){ } }

  缓存当然不是我写的那么简单啦,可能遇到内存溢出,缓存超时,多台机器信息的交流等等。

hibernate中的缓存

一级缓存,Session级共享
存在于session的缓存。有点类似于我们前面模拟的缓存,不过存入缓存和删除缓存的操作比我们的更加人性。

例子:

package cn.itcast.test;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.Transaction; import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.Name;
import cn.itcast.domain.User; public class SessionCacheDemo { public static void main(String[] args) {
User user=add();
query(user.getId());
} static User add(){
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
User user=new User();
Name name=new Name();
name.setFirstName("first name");
name.setLastName("last name");
user.setName(name);
user.setBirth(new Date()); tx=s.beginTransaction();
s.save(user);
tx.commit();
// user=(User) s.get(User.class, user.getId()); 这里没有执行后查询语句,使用了一级缓存
return user;
}finally{
if(s!=null) s.close();
}
} static void query(int id){
Session s=null;
User user=null;
try{
s=HibernateUtil.getSession();
user=(User) s.get(User.class, id);
System.out.println("--------------------");
user=(User) s.get(User.class, id);
/*sql语句的输出可以看到只查询了一遍,使用了缓存,并且说明了缓存的范围是在session中*/ s.evict(user);//清除缓存中的指定对象所在键值对 user=(User) s.get(User.class, id);
System.out.println("--------------------"); s.clear();//清除session中的所有缓存 user=(User) s.get(User.class, id);
System.out.println("--------------------"); }finally{
if(s!=null) s.close();
}
} }

save、update、saveOrUpdate、load、get、list、iterate、lock这些方法都会将对象存入session一级缓存中,一级缓存不能控制缓存的数量,所以请注意大批量操作数据时可能造成内存溢出,可以用evict,clear方法清除缓存中的指定对象和清空缓存。

二级缓存:SessionFactory级共享
我们知道,一个完善的缓存机制是比较复杂的,hibernate提供的session级缓存不能满足我们的所有要求,于是我们需要使用其他缓存。hibernate提供了接口给我们,也就是说,我们可以在hibernate中配置其他的缓存插件,达到我们想要的效果。
配置缓存插件的步骤:
1、hibernate中开启二级缓存。
在hibernate.cfg.xml中配置<property name="cache.use_second_level_cache">true</property>。这个属性在缺省状态下为true,也就是默认hibernate是打开二级缓存的
2、指定二级缓存插件的提供者。这里我使用的是OSCache
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
使用该缓存插件的时候注意你有没有导入该缓存包
二级缓存插件确定了,如何进行一节二级缓存的基本配置呢?
我在src下面加入了一个oscache.properties文件,然后在该文件中配置了os缓存的一些信息
如何确定二级缓存会缓存那些内容呢?
加入你需要对user类的对象进行缓存,当然缓存的额是持久化对象。可以在hibernate.cfg.xml中配置:
<class-cache usage="read-only" class="cn.itcast.domain.User"/>
也可以在bean的映射文件中配置,比如:
<class name="User">
<cache usage="read-only"/>
。。。
其中usage属性是表示缓存的级别:
read-only则表示缓存的东西不会发生改变,比如缓存省市这种东西的时候就可以使用这个级别的缓存。如果缓存的对象发生了改变就会报错
read-write表示允许对缓存的对象进行读写操作,而且具有很好的并发性,缓存的内容基本不会出现问题
nonstrict-read-write表示允许对缓存的对象进行读写操作,并发要求不高,可能出现一些错误数据。在你允许一些缓存问题的情况下使用,比如评论。
transactional:级别不太清楚,有点麻烦

查看一些hibernate的状态:
可以在配置文件中打开统计信息,然后借助统计信息,得到你需要的信息。
<property name="hibernate.generate_statistics">true</property>

hibernate进行数据的查询,首先在一级缓存查找,然后如果没有在二级缓存查找,如果还没有再往数据库查找

例子:

    <property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<property name="hibernate.generate_statistics">true</property>
。。。
<class-cache usage="read-write" class="cn.itcast.domain.User"/> package cn.itcast.test; import java.util.Date; import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.stat.Statistics; import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.Name;
import cn.itcast.domain.User; public class SecondCacheDemo {
public static void main(String[] args) {
User user=add();
query(user.getId());
query(user.getId());
Statistics st= HibernateUtil.getSessionFactory().getStatistics();
System.out.println(st); System.out.println(""+st.getSecondLevelCachePutCount());//放入了多少次
System.out.println("miss:"+st.getSecondLevelCacheMissCount());//miss多少次
System.out.println("hit:"+st.getSecondLevelCacheHitCount());//击中多少次 } static User add(){
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
Name name=new Name();
name.setFirstName("first name");
name.setLastName("last name");
User user=new User();
user.setBirth(new Date());
user.setName(name); tx=s.beginTransaction();
s.save(user);
tx.commit(); return user;
}finally{
if(s!=null) s.close();
}
} static void query(int id){
Session s=null;
User user=null;
// Transaction tx=null;
try{
s=HibernateUtil.getSession(); user=(User) s.get(User.class, id); user=(User) s.get(User.class, id); s.clear(); // user=(User) s.get(User.class, id);
// tx=s.beginTransaction();
// user.setBirth(new Date());
// tx.commit();
}finally{
if(s!=null) s.close();
}
}
}

一些补充:

将信息放入二级缓存的方法:

save、update、saveOrUpdate、list、iterator、get、load、以及Query、Critria都会填充二级缓存,查询数据时session的iterator、get、load可以从缓存中读取数据

注意:session中的save方法不适合native生成方式的主键,也就是说native生成方式的主键使用save方法可能不会存入缓存

Query、Criteria(查询缓存)由于命中率低,所以hibernate缺省是关闭修改hibernate.cfg.xml中的property:

<property name="cache.use_query_cache">true</property>才能打开查询缓存

并且query和Criteria存储结果入二级缓存中需要使用query.setCacheable(true),Criteria.setCacheable(true)结果才能存入二级缓存

SessionFactory中使用evit()或者evitXXX()清除缓存内容

统计信息打开generate_statics用sessionFactory.getStatics()获取统计信息

    public static void main(String[] args) {
User user=add();
query1(user.getId()); } static void query1(int id){
Session s=null;
User user=null;
try{
s=HibernateUtil.getSession(); String hql="from User";
Query qy=s.createQuery(hql);
qy.setCacheable(true);
qy.list();
}finally{
if(s!=null) s.close();
} try{
s=HibernateUtil.getSession(); String hql="from User";
Query qy=s.createQuery(hql);
qy.setCacheable(true);
qy.list();
}finally{
if(s!=null) s.close();
}
System.out.println("will clear User in sessionFactory");
HibernateUtil.getSessionFactory().evict(User.class);
try{
s=HibernateUtil.getSession(); String hql="from User";
Query qy=s.createQuery(hql);
qy.setCacheable(true);
qy.list();
}finally{
if(s!=null) s.close();
}
}
}

缓存的再补充说明:

  

分布式缓存和中央缓存

分布式缓存:
我们知道大的系统可能会有很多台机器做处理,这种系统做缓存的话,如果每台机器都做一个缓存,这种缓存叫做分布式缓存。这样的话会出现负载均衡的问题。例子:1个人向第一台机器查询一个记录,然后拿到了,缓存中存入一个数据,返回给客户端。第2个人向第二台机器查询同一个记录,然后拿到了,缓存存入,返回给客户端。然后第三个人从第三台机器上,修改之前查询的数据,那么前面两台机器上的缓存数据就变成了废数据。
我们希望每一台服务器在更新对象信息的时候通知前面的服务器更新缓存,这种操作叫做集群。osCache支持这种操作。

从它的配置文件下弄过来的:

#cache.cluster.properties=UDP(mcast_addr=231.12.21.132;mcast_port=45566;ip_ttl=32;mcast_send_buf_size=150000;mcast_recv_buf_size=80000):PING(timeout=2000;num_initial_members=3):MERGE2(min_interval=5000;max_interval=10000):FD_SOCK:VERIFY_SUSPECT(timeout=1500):pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):pbcast.STABLE(desired_avg_gossip=20000):UNICAST(timeout=5000):FRAG(frag_size=8096;down_thread=false;up_thread=false):pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=true)
#cache.cluster.multicast.ip=231.12.21.132 (这个我不太懂啊)

分布式缓存的缺点:更新成本高

中央缓存:
特意拿出一个服务器做缓存服务器,当每次获取数据,将缓存存入中央服务器。然后每一次读取数据都在中央服务器进行查询。这样做的特点:更新成本低,但是获取数据成本高,内存可以充分利用。
使用缓存的条件:
读取大于修改
数据量不能超过内存容量
对数据要有独享的控制
可以容忍出现无效数据

-----------------------------------------------------------

事务:
单个数据库(一个SessionFactory对应一个数据库),由JDBC实现
Session session=null;
Transaction tx=null;
try{
session=sessionFactory.openSession();
tx=session.beginTransaction();
//process
tx.commit();
}catch(HibernateException e){
if(tx!=null) tx.rollback(); throw e;
}finally{
if(session!=null) session.close();
}
hibernate本身是没有事务功能的,上面的额事务实质上是使用下面的代码实现的。
connection.setAutoCommit(false);
connection.commit();conn.rollback();

分布式事务:
可以简单理解成为跨数据库的事务,由应用JTA容器实现;使用JTATransaction需要配置hibernate.transaction.factory_calss参数
该参数缺省值是org.hibernate.transaction.JDBCTransactionFactory, 使用JTATransaction时需要将该参数改成org.hibernate.transaction.JTATransaction
,并配置jta.UserTransaction参数JNDI名
javax.transaction.UserTransaction tx=context.lookup("jndiName");
try{
tx.begin();
//多个数据库的session操作
//session1...
//session2...
tx.commit();
}catch(Exception e){
tx.rollback(); throw e;
}

事务的边界:
什么时候打开事务,什么时候提交事务,什么时候回滚
我们知道三层架构。事务边界应该由业务逻辑控制,但是我们如果在业务逻辑控制的话,事务的开关是 属于dao层的东西却在业务逻辑层,不合理。
后面我们可以使用spring容器解决这个问题。

openSessionInView模式:
我们知道sessionFactory有一个方法:getCurrentSession():就是获取当前线程内的session。这个方法默认hibernate是无法使用的。
需要在配置文件中进行配置current_session_context_class属性,单服务器其值为thread:ThreadLocal来管理Session实现多个操作共享一个session,避免反复获取session,并控制事务边界,此时session不能调用close或者commit和rollback方法,因为那些时候session会被关闭掉
多态服务器的时候配置其值为jta:由jta事务管理器来管理事务
threadLocal是一个容器,类似于map容器,键使用当前线程。
open session in view:
优势:在渲染页面的额时候,保持页面打开。可以简单处理事务边界问题和懒加载session被关闭问题。
劣势:扩大了session和transaction的生命周期,导致系统的并发处理能力下降了。

例子:

package cn.itcast.web.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import org.hibernate.Session;
import org.hibernate.Transaction; import cn.itcast.dao.HibernateUtil; public class OpenSessionInViewFilter implements Filter{ public void destroy() { } public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
/*
* 请求开始打开事务,请求完成提交事务,请求异常回滚事务,一个session对应一个请求
*/
Session session=null;
Transaction tx=null;
try{
session=HibernateUtil.getThreadSession();
tx=session.beginTransaction();
arg2.doFilter(arg0, arg1);
tx.commit();
}catch(Exception e){
if(tx!=null)
tx.rollback();
throw new RuntimeException(e.getMessage(),e);
}finally{
if(session!=null) HibernateUtil.closeCurrentSession();
} } public void init(FilterConfig arg0) throws ServletException { } }
package cn.itcast.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration; public final class HibernateUtil { private static SessionFactory sessionFactory;
private static ThreadLocal<Session> threadLocal=new ThreadLocal<Session>();;
private HibernateUtil(){
} static{
sessionFactory=new Configuration().configure().buildSessionFactory();
} public static SessionFactory getSessionFactory(){
return sessionFactory;
} public static Session getSession(){
return sessionFactory.openSession();
} public static Session getThreadSession(){
Session session=sessionFactory.getCurrentSession();
if(session==null){
session=getSession();
threadLocal.set(session);
}
return session;
} public static void closeCurrentSession(){
Session session=getThreadSession();
if(session!=null){
session.close();
threadLocal.set(null);
}
} } hibernate.cfg.xml中:
<property name="current_session_context_class">thread</property> dao中操作:
public void addUser(User u){
HibernateUtil.getThreadSession().save(u);
}

乐观锁和悲观锁:

悲观锁和乐观锁
问题的引出:
我们在进行数据的修改往往是做两个步骤:1、获取初始数据显示在修改的表单项中 2、修改表单项数据并提高修改。 我们假设AB两个管理员几乎同时准备修改同一个用户信息,那么在显示修改页面得到的用户信息是一样的,A先提交,B提交的数据会将A提交的数据覆盖掉。而且B不会知道还有A也对该数据进行了一次更改。
解决方案:
1、悲观锁定。类似于同步。A读取到了用户信息的时候,给该用户加上锁,B用户无法读取等操作,知道A用户完成提交,释放锁,其他用户才可以针对该信息进行操作。这样做解决了前面的并发问题,但是效率慢,如果A锁定到释放锁的过程过长,不合适。对并发影响较大。
2、乐观锁定。对每一条记录加上一个标识列。当数据进行一次更改,标识列进行一次更改,标识列是唯一的,可以是int自增,也可以是时间戳。AB两个用户都得到了对象X,A先进行提交,验证标识列,通过,修改数据,标识列进行修改。然后B提交数据,验证标识列,原本的标识列发生改变,验证失败,抛出异常。
标识列可以使用version,版本号,也可以使用时间戳,timestamp,version可以指定多种类型,timestamp的类型只能是Date

例子:

package cn.itcast.domain;

import java.util.Date;

public class User {
private int id;
private Name name;
private Date birth;
// private int ver;
private Date ver;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
// public void setVer(int ver) {
// this.ver = ver;
// }
// public int getVer() {
// return ver;
// }
public Date getVer() {
return ver;
}
public void setVer(Date ver) {
this.ver = ver;
} } package cn.itcast.dao; import org.hibernate.Session;
import org.hibernate.Transaction; import cn.itcast.domain.Name;
import cn.itcast.domain.User; public class UserDao {
public static void main(String[] args) {
// HibernateUtil.getSessionFactory().getCurrentSession();
User user=add(); Session s1=null;
Session s2=null;
try{
s1=HibernateUtil.getSession();
s2=HibernateUtil.getSession(); User u1=(User) s1.get(User.class, user.getId());
u1.getName().setFirstName("firstName2"); User u2=(User) s2.get(User.class, user.getId());
u2.getName().setFirstName("firstName3"); Transaction t1=s1.beginTransaction();
Transaction t2=s2.beginTransaction();
s1.update(u1);
s2.update(u2);
t1.commit();
try {
Thread.sleep();//设置为timestamp形式因为两个事务提交时间几乎一样,检测不到,所以睡眠一下
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.commit();
}finally{
if(s1!=null) s1.close();
if(s2!=null) s2.close();
} } static User add(){
Session s=null;
Transaction tx=null;
try{
User user=new User();
Name name=new Name();
name.setFirstName("firstName1");
user.setName(name); s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(user);
tx.commit(); return user;
}finally{
if(s!=null) s.close();
} } } <?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="cn.itcast.domain"> <class name="User" table="user">
<!--
<cache usage="read-write"/>
--> <id name="id">
<generator class="increment"/>
</id> <version name="ver"/> <!-- 缺省状态是int类型 --> <!-- <timestamp name="ver"/>--><!-- 类型只能是Date类型 -->
<property name="birth"/>
<component name="name" class="Name">
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
</component>
</class> </hibernate-mapping>

hibernate复习第(三)天的更多相关文章

  1. Java三大框架之——Hibernate中的三种数据持久状态和缓存机制

    Hibernate中的三种状态   瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过 ...

  2. hibernate对象的三种状态

    对于hibernate,我想不在这里讲解了,我们就直接进入主题 在这里我将要说的是"hibernate对象的三种状态",对象是我们十分熟悉的,对吧!而对于对象它有三种状态 分别是瞬 ...

  3. Hibernate 对象的三种状态

    hibernate对象的三种状态: (一) 瞬时(临时)状态:   对象被创建时的状态,数据库里面没有与之对应的记录! (二) 持久状态:   处于session的管理中,并且数据库里面存在与之对应的 ...

  4. Hibernate基础学习(三)—Session

    一.概述      Session接口是Hibernate向应用程序提供的操纵数据库最主要的接口,它提供了基本的保存.更新.删除和加载Java对象的方法.      Session具有一个缓存,位于缓 ...

  5. Hibernate中的三种数据状态

    Hibernate中的三种数据状态(临时.持久.游离) 1.临时态(瞬时态) 不存在于session中,也不存在于数据库中的数据,被称为临时态. 比如:刚刚使用new关键字创建出的对象. 2.持久态 ...

  6. 菜鸟学SSH(八)——Hibernate对象的三种状态

    前面写了几篇关于SSH的博客,但不是Struts就是Spring,Hibernate还从来没写过呢.说好是SSH的,怎么可以光写那两个,而不写Hibernate呢对吧.今天就先说说Hibernate对 ...

  7. 浅谈Hibernate中的三种数据状态

    Hibernate中的三种数据状态:临时.持久.游离 1.临时态(瞬时态) 不存在于session中,也不存在于数据库中的数据,被称为临时态. 数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器 ...

  8. Hibernate复习之Hibernate基本介绍

    众所周知.眼下流行的面向对象的对象关系映射的Java持久层框架有MyBatis和Hibernate.他们都是对象关系映射ORM. 解决的主要问题就是对象-关系的映射.域模型和关系模型都分别建立在概念模 ...

  9. 【Hibernate框架】三种继承映射

    一.综述 大家都知道,hibernate作为ORM框架的一个具体实现,最大的一个优点就是是我们的开发更加的能体现出"面向对象"的思想.在面向对象开发中,类与类之间是可以相互继承的( ...

随机推荐

  1. 正负小数点后两位浮点数--jquery

    背景:项目中需要做个对两位小数点的正负浮点数的处理, 要求:非数字或者.字符自动清除,并对.12自动修补.前的0 原理:在输入框中加入两个事件,keyup与blur,keyup处理字符串中非要求的字符 ...

  2. 小图拼接大图MATLAB实现

    小图拼接大图MATLAB实现 1.实现效果图 原图 效果图 2.代码 files = dir(fullfile('D:\document\GitHub\homework\digital image p ...

  3. php党 强烈推荐TIPI:深入理解PHP内核

    深入理解PHP内核(Thinking In PHP Internals) TIPI项目是一个自发项目, 项目主要关注PHP的内部实现, 以及PHP相关的方方面面, 该项目包括<深入理解PHP内核 ...

  4. 什么是Java Server Pages?

    JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java servlet ...

  5. jmeter java 请求 payload

    1.注册页面抓包看见内容如下: POST http://test.nnzhp.cn/bbs/forum.php?mod=post&action=edit&extra=&edit ...

  6. iOS拓展:react-native创建工程的坑和解决方法

    正文 首先,在创建工程之前,你需要有环境.这个网上很多,不赘述. 我们直接从创建工程开始,一步步记叙这过程. 坑一 一.打开终端,cd到你放文件的位置(我放在桌面了): yuqideMacBook-A ...

  7. Java之Filter

    一.何为Filter? Filter也称之为过滤器,它是Servlet技术中比較激动人心的技术.WEB开发者通过Filter技术.对webserver管理的全部web资源.换句话说其主要用于前台向后台 ...

  8. centos7.0 tomcat9.0 ip访问 manager

    版本:Tomcat 9.0 问题:新安装的tomcat,用其他机器访问tomcat的Server Status.Manager App.Host Manager三个页面均显示403(本机访问没有问题) ...

  9. php之 人员的权限管理

    1.想好权限管理的作用? 2.有什么权限内容? 3.既然有权限管理那么就会有管理员? 4.登录后每个人员的界面会是不一样的? 一.想好这个权限是什么? 就做一个就像是vip的功能,普通用户和vip用户 ...

  10. HDFS上传机制