hibernate复习第(三)天
今日要点:
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>
效率比较高
增加子类就会修改表结构。增加字段
特有字段都不能加上非空约束,应该可以为空,关系型来说不是很好的设计
![](/assets/blank.gif)
![](/assets/blank.gif)
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
![](/assets/blank.gif)
![](/assets/blank.gif)
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;}@Overridepublic String toString() {return "Employee [id=" + id + ", name=" + name + ", depart=" + depart+ "]";} }
Employee.java
![](/assets/blank.gif)
![](/assets/blank.gif)
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
![](/assets/blank.gif)
![](/assets/blank.gif)
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
![](/assets/blank.gif)
![](/assets/blank.gif)
<?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方式的懒加载:
![](/assets/blank.gif)
![](/assets/blank.gif)
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>
准备工作的代码
这样使用会抛出异常:
![](/assets/blank.gif)
![](/assets/blank.gif)
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();}} }
View Code
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Sessionat 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
例子:
![](/assets/blank.gif)
![](/assets/blank.gif)
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();}} }
View Code
多对一、一对多、多对多进行懒加载的要求:
lazy属性不能为false,
fetch为select
能够进行懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载(代理)对象的属性(getId和getClass除外),hibernate会初始化这些代理,相关联的session关闭之后,再次访问懒加载对象将会出现异常。
hibernate中对于空集合和为null的集合是不进行区分的
---------------------------------------------------------------------------------------------------------------------------------------------------
缓存。
我们知道从数据库中拿一条数据出来是成本高的操作,我们一般有这样的需求:比如读一个用户信息,前一个人读一个A的用户信息,读完后下一个人又读取A的用户信息,这个时候我们希望采取一些优化措施。我们希望第一个人从数据库中读取,第二个人可以使用第一个人的一些成果。
缓存的简单模拟:
![](/assets/blank.gif)
![](/assets/blank.gif)
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){}}
View Code
缓存当然不是我写的那么简单啦,可能遇到内存溢出,缓存超时,多台机器信息的交流等等。
hibernate中的缓存
一级缓存,Session级共享
存在于session的缓存。有点类似于我们前面模拟的缓存,不过存入缓存和删除缓存的操作比我们的更加人性。
例子:
![](/assets/blank.gif)
![](/assets/blank.gif)
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();}}}
View Code
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进行数据的查询,首先在一级缓存查找,然后如果没有在二级缓存查找,如果还没有再往数据库查找
例子:
![](/assets/blank.gif)
![](/assets/blank.gif)
<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();}} }
View Code
一些补充:
将信息放入二级缓存的方法:
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()获取统计信息
![](/assets/blank.gif)
![](/assets/blank.gif)
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();}} }
View Code
缓存的再补充说明:
分布式缓存和中央缓存
分布式缓存:
我们知道大的系统可能会有很多台机器做处理,这种系统做缓存的话,如果每台机器都做一个缓存,这种缓存叫做分布式缓存。这样的话会出现负载均衡的问题。例子: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的生命周期,导致系统的并发处理能力下降了。
例子:
![](/assets/blank.gif)
![](/assets/blank.gif)
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 {}}
View Code
![](/assets/blank.gif)
![](/assets/blank.gif)
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);}
View Code
乐观锁和悲观锁:
悲观锁和乐观锁
问题的引出:
我们在进行数据的修改往往是做两个步骤:1、获取初始数据显示在修改的表单项中 2、修改表单项数据并提高修改。 我们假设AB两个管理员几乎同时准备修改同一个用户信息,那么在显示修改页面得到的用户信息是一样的,A先提交,B提交的数据会将A提交的数据覆盖掉。而且B不会知道还有A也对该数据进行了一次更改。
解决方案:
1、悲观锁定。类似于同步。A读取到了用户信息的时候,给该用户加上锁,B用户无法读取等操作,知道A用户完成提交,释放锁,其他用户才可以针对该信息进行操作。这样做解决了前面的并发问题,但是效率慢,如果A锁定到释放锁的过程过长,不合适。对并发影响较大。
2、乐观锁定。对每一条记录加上一个标识列。当数据进行一次更改,标识列进行一次更改,标识列是唯一的,可以是int自增,也可以是时间戳。AB两个用户都得到了对象X,A先进行提交,验证标识列,通过,修改数据,标识列进行修改。然后B提交数据,验证标识列,原本的标识列发生改变,验证失败,抛出异常。
标识列可以使用version,版本号,也可以使用时间戳,timestamp,version可以指定多种类型,timestamp的类型只能是Date
例子:
![](/assets/blank.gif)
![](/assets/blank.gif)
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(100);//设置为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>
View Code
hibernate复习第(三)天相关推荐
- Hibernate复习之Hibernate基本介绍
众所周知.眼下流行的面向对象的对象关系映射的Java持久层框架有MyBatis和Hibernate.他们都是对象关系映射ORM. 解决的主要问题就是对象-关系的映射.域模型和关系模型都分别建立在概念模 ...
- 【Hibernate框架开发之五】Hibernate对象的三种状态Session常用方法
1. Hibernate对象的三种状态:(图解如下:) 如图所示,Hibernate对象有三种状态,1.Transient 2.Persistent 3.Detached; 三种状态的区别如下: 1 ...
- 菜鸟学SSH(八)——Hibernate对象的三种状态
前面写了几篇关于SSH的博客,但不是Struts就是Spring,Hibernate还从来没写过呢.说好是SSH的,怎么可以光写那两个,而不写Hibernate呢对吧.今天就先说说Hibernate对 ...
- Hibernate 对象的三种状态
Hibernate 对象的三种状态 瞬时(transient): 由new操作符创建,且尚未与Hibernate Session关联的对象被认定为瞬时(Transient)的.瞬时(Transient ...
- 分数怎么化成带分数_小升初数学总复习第三个基础模块:分数的认识
今天我们开始小升初数学总复习第三个基础模块的复习:分数的认识 分数的认识一共分为8个知识考点. 第一,分数的意义 把单位"1".平均分成若干份,表示这样的一份或者几份的数叫做分数. ...
- 算法复习第三章分治法
算法复习第三章分治法 循环日程表 最近点对 快速排序: 循环日程表 最近点对
- 计算机学硕专业课可以自学吗,考研专业课怎么考?高效复习法三条就够!
原标题:考研专业课怎么考?高效复习法三条就够! 充足的睡眠可以使你一天精力旺盛,状态在线.好的方法可以使学习更加有效,今天从考研专业课怎么考出发,总结三条高效复习法,帮助大家有效进行考研备考! 202 ...
- Hibernate中的三种数据持久状态和缓存机制
Java三大框架之--Hibernate中的三种数据持久状态和缓存机制 Hibernate中的三种状态 瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没 ...
- 计算机组成原理期末复习第三章-3(唐朔飞)
计算机组成原理期末复习第三章-3(唐朔飞) ✨欢迎关注
- hibernate二级缓存(三) 自定义实现一个简单的hibernate二级缓存
hibernate二级缓存(三) 自定义实现一个简单的hibernate二级缓存 前面我们已经提及过hibernate内部为二级缓存的扩展做了很多的实现.我们只需要实现RegionFactoryTem ...
最新文章
- php 负载监控_PHP监控linux服务器负载
- eset14 杀毒清除无提示
- Go 语言web 框架 Gin 练习8
- iOS连接linux服务器用什么,iOS实现通过SSH2协议链接Linux服务器,并执行相关指令...
- Linux 卷管理详解[ pv vg lv] —— 之三
- 前端学习(813):dom简介
- UDT源代码下载链接
- 计算机启动类型bios,UEFI还是Legacy BIOS?如何确定Windows启动类型
- 微服务模块综合管理(模块视图管理,自动化热部署,前端资源实时刷新......)
- pca 矩阵 迹_再谈协方差矩阵之主成分分析PCA
- SpringMvc-参数为数组
- VUE使用过滤器来格式化当前时间
- 最新emoji表情代码大全_由一个 emoji 引发的思考
- SpringMVC工作原理(含案例图解)
- 蒟蒻的做题录(时间)
- Python中的数学运算
- php入门,windows安装与环境配置,基础语法学习
- 如何改变hr标签的颜色
- PTA求100以内的素数
- 一种可能的投资策略和一种可能的模糊的快速股票估值方法
热门文章
- 解决SpringCloud客户端启动报错:“Field XXX required a bean of type XXX that could not be found”
- 童饰品,发夹、发卡ASTM F2923标准CPSIA测试报告办理
- 丽丽的redhat终于可以上网了
- MongoDB概念集合
- 微信再次重大更新,同时支持5个浮窗展示,你怎么看?
- 科普一下SM系列国密算法(从零开始学区块链)
- 51单片机学习笔记(郭天祥版)(5)——作业讲解、独立键盘、矩阵键盘
- Apache Log4j2详解
- nodejs Log4js v2.x配置使用
- 毫米波雷达测距/测速原理介绍_小七自学笔记