1 使用hibernate步骤 2 1.建立Hibernate的配置文件 3 2.建立持久化类 4 3.建立对象-关系映射文件 5 4.经过Hiberante api 编写访问数据库的代码 6 SessionFactory: 7 一个SessionFactory实例对应一个数据存储源,应用从SessionFactory中得到Session实例,SessionFactory有如下特色:它是线程安全,这意味着它一个实例能够被多个线程共享,这是重量级的。若是应用只访问一个数据库,只须要建立一个SessionFactory实例,在应用初始化时候建立一个该实例,若是应用同时访问多个数据库,则须要为每一个数据库建立一个单独的SessionFactory实例。主要用来建立session 8 9 Session接口: 10 Session接口是Hibernate应用使用最普遍的接口 11 session也被称为持久化管理器,它提供了和持久化相关的操做,如添加,更新,删除,加载和查询对象 12 它有如下特色:不是线程安全的,应该避免多个线程共享同一个Session实例 13 Session实例是轻量级的。session至关于一个数据库链接 14 注意:此session非彼Session(HttpSession) 15 java类型,Hibernate映射类型及sql类型之间的对应关系 16 17 java类型 Hibernate类型 Sql类型 18 java.lang.String string Varchar 19 int int int 20 char character char(1) 21 boolean boolean bit 22 java.lang.String text text 23 byte[] binary blob 24 java.sql.Date date date 25 java.sql.Timestamp timestamp timestamp 26 27 28 如下为构建SessionFactory及Session支持类 29 public class hibernateUtil { 30 static SessionFactory factory=null; 31 static Session session=null; 32 static{ 33 try { 34 //读取配置文件(hibernate.cfg.xml) 35 Configuration cfg=new Configuration().configure(); 36 //根据配置文件取得SessionFactory工厂 37 factory=cfg.buildSessionFactory(); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 42 } 43 44 45 //取得Session 46 public static Session getSession(){ 47 48 session=factory.openSession(); 49 return session; 50 } 51 public static void closeSession(Session session){ 52 if(session!=null){ 53 if(session.isOpen()) 54 session.close(); 55 } 56 57 } 58 59 } 60 hibernate的事务控制示例以下: 61 public void savePerson(Person person) 62 { 63 Session session = HibernateUtil.getSession(); 64 Transaction tx = session.beginTransaction(); 65 try 66 { 67 session.save(person); 68 tx.commit(); 69 }catch(Exception ex) 70 { 71 if(null!=tx) 72 { 73 tx.rollback(); 74 } 75 } 76 finally 77 { 78 HibernateUtil.close(session); 79 } 80 } 81 82 使用hql查询语句 83 Query query = session.createQuery("HQL语句"); 84 List<People> list = (List<People>)query.list(); 85 若是加入分页则能够用以下: 86 Query query = session.createQuery("HQL语句").setFirstResult("开始记录数").setMaxResult("每页多少条记录"); 87 把二进制文件从数据库里读出来: 88 byte[] buffer = people.getFile();//得到从查出来的属性 89 OutputStream os = new FileOutputStream("c:/"+people.getId+".pdf"); 90 os.write(buffer); 91 os.close(); 92 93 对于分页操做来讲,须要知道以下信息:当前正在操做的是第几页,每一页显示多少条记录。 94 95 对于query接口的list()方法与iterator()方法来讲,均可以实现获取查询的对象,可是list()方法返回的每一个对象都是完整的(对象中的每一个属性都被表中的字段填充上了),而iterator()方法所返回的对象中仅包含了主键值(标识符),只有当你对iterator()中的对象进行操做时,Hiberante才会向数据库再次发送SQL语句来获取该对象的属性值。实际上就是延迟加载 96 97 理解Session的缓存 98 1.当Session的save()方法持久化一个Customer对象时,Customer对象被加入到Session的缓存中,之后即便应用程序中的引用变量再也不引用Customer对象,只要Session的缓存尚未被清空,Customer对象仍然处于生命周期中。 99 2.当Session的load()方法试图从数据库中加载一个Customer对象时,Session先判断缓存中是否已经存在这个Customer对象,若是存在,就不须要再到数据库中检索。 100 3.减小访问数据库的频率,应用程序从内存中读取持久化对象的速度显然比到数据库中查询数据的速度快多了,所以Session的缓存能够提升数据访问的性能。 101 4.保证缓存中的对象与数据库中的相关记录保持同步。当缓存中持久化对象的状态发生了变化,Session并不会当即执行相关的SQL语句,这使得Session可以把几条相关的Sql语句合并成一条SQL语句,以便减小访问数据库的次数,从而提升应用程序的性能。 102 清理缓存(关闭session)是指按照缓存中对象的状态的变化同步更新数据库 103 104 Session会如下面的时间点清理缓存: 105 1.当应用程序调用org.hibernate.Transaction的commit()方法的时候,commit()方法先清理缓存,而后再向数据库提交事务 106 2.当应用程序显式调用Session的flush()方法的时候 107 108 hibernate自身一对多双向关系,能够表示无限的层级关系(任何孩子只有一个父母),在实体类里有一个对父对象的引用,还有一个集合表示它下面的孩子 109 110 Session级别的缓存叫作一级缓存;SessionFactory级别的缓存叫作二级缓存。一级缓存是没法改变的,二级缓存咱们能够本身去改变 111 112 Hibernate的二级缓存结构 113 1.Hibernate提供了两级缓存,第一级缓存是Session的缓存。因为Session对象的生命周期一般对应一个数据库事务或者一个应用事务,所以它的缓存是事务范围的缓存。第一级缓存是必须的,不容许并且事实上也没法被卸除。在第一级缓存中,持久化类的每个实例都具备惟一的OID. 114 2.第二级缓存是一个可插拔插件,它由SesssionFactory负责管理。因为SessionFactory对象的生命周期和应用程序的整个进程对应,所以第二级缓存是进程范围的缓存。这个缓存中存放的是对象的散装数据。第二级缓存是可选的,能够在每一个类或每一个集合的粒度上配置第二级缓存。 115 116 二级缓存EhCache 117 在sessionFactory中配置 118 <property name="hibernate.cache.user_second_level_cache">true</property> 119 <property name="hibernate.cache.provider_class">org.hibernate.EhCacheProvider</property> 120 二级缓存一对多,多的一方,在set中加<cache usage="read-only"/>在多的对方配置文件里也要加<cache usage="read-only"/> 121 在src目录下加一个ehcache.xml,常见配置以下 122 <ehcache> 123 <diskStore path="c:/ehcache"/>//在磁盘的存放目录 124 //默认的缓存策略 125 <defaultCache maxElementsInMemory="200"//内存最多存放多少个对象 126 eternal="false"//是否永恒 127 timeToIdleSeconds="50"// 128 timeToLiveSeconds="60"// 129 overflowToDisk="true" 130 /> 131 132 <cache name="com.hibernate.Student"/>//针对具体类的配置,它继承上面默认的也能够在里面设置覆盖。 133 134 </ehcache> 135 二级缓存: 136 1.transactional:必须在受管的环境下使用,保证可重复读的事务隔离级别,对于读/写比例大,不多更新的数据一般能够采起这种方式。 137 2.read-write:使用timestamp机制维护已提交事务隔离级别,对于读/写比例大,不多更新的数据一般能够采起这种方式。 138 3.nonstrict-read-write:二级缓存与数据库中的数据可能会出现不一致的状况。在使用这种策略的时候,应该设置足够短的缓存过时时间,不然就有可能从缓存中读取到脏数据。当一些数据不多改变(一天,两天都不改变的数据),而且这些数据若是出现数据库与缓存不一致的状况下影响并不大的时候,那么能够采起这种缓存策略。 139 4.read-only:当肯定数据不会被改变时,咱们可使用这种缓存策略 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 在Hibernate应用中Java对象的状态 160 1.临时状态(transient):刚刚用new语句建立,尚未被持久化,不处于Session的缓存中。处于临时状态的Java对象被称为临时对象 161 2.持久化状态(persistent):已经被持久化,加入到Session的缓存中。处于持久化状态的Java对象被称为持久化对象 162 3.游离状态(detached):已经被持久化,但再也不处于Session的缓存中。处于游离状态的Java对象被称为游离对象 163 164 Session的方法主要做用是把对象从游离状态转化为持久化状态 165 1.把customer对象从新加入到Session缓存中,使它变为持久化对象 166 2.计划执行一个update语句,值得注意的是,Session只有在清理缓存的时候才会执行update语句,而且在执行时才会把Customer对象当前的属性值组装到update语句中。所以,即便程序中屡次修改了Customer对象的属性,在清理缓存时只会执行一次update语句。 167 168 hibernate的检索策略: 169 1.当即检索策略 170 2.延迟检索策略 171 3.左外链接检索策略 172 173 在多对一关联级别使用左外链接检索策略 174 1.默认状况下,多对一关联使用左外链接检索策略 175 2.若是把Order.hbm.xml文件的<many-to-one>元素的outer-join属性高为true,老是使用左外链接检索策略,即在查询的时候就会把关联一的那一方查询出来,只用一条sql语句,从而节省了sql语句。 176 优势:对应用程序彻底透明,无论对象处于持久化状态,仍是游离状态,应用程序均可以方便的从一个对象导航到与它关联的对象。 使用了外链接,select语句数目减小。 177 缺点:可能会加载应用程序不须要访问的对象,白白浪费了许多内存空间。 复杂的数据库表链接也会影响检索性能。 178 适用范围: 179 1.多对一或才一对一关联 180 2.应用程序须要当即访问的对象 181 3.数据库系统具备良好的表链接性能 182 183 在程序中显式指定左外链接检索策略 184 1.在映射文件中设定的检索策略是固定的,要么是延迟检索,要么是当即检索,要么是外链接检索。 185 2.但应用逻辑是多种多样的,有些状况下须要延迟检索,而有些状况下须要外链接检索。 186 3.Hibernate容许在应用程序中覆盖映射文件中设定的检索策略,由应用程序在运行时决定检索对象力的深度。 187 如下Session的方法都用于检索OID为1的Customer对象: 188 session.createQuery("from Customer as c where c.id=1"); 189 session.createQuery("from Customer as c left join fetch c.orders where c.id =1"); 190 在执行第一个方法时,将使用映射文件配置的检索策略 191 在执行第二个方法时,在HQL语句中显式指定左外链接检索关联的Oreder对象,所以覆盖映射文件配置的检索策略。无论在Customer.hbm.xml文件中<set>元素的lazy属性是true仍是false,Hibernate都会执行如下select语句: 192 select*from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID = ORDERS.CUSTOMER_ID where CUTROMERS.ID =1; 193 194 195 一对一映射: 196 1.主键关联(主键值是同样的) 197 <id name="id" column="id" type="string"> 198 <generator class="foreign">//表示做为外键来使用,使用别的表的主键,只在一方这样配置,另外一方正常 199 <param name="property">student</param>//使用那个表的主键,使用本类中student对象的表的id 200 </generator> 201 </id>后面还要加上one-to-one配置,再配置上级联 202 一对一默认使用的是当即加载,如须要使用延迟加载,那么须要在one-to-one元素中将constrained属性设为true,而且将待加载的一方的class元素中的lazy属性高为true(或者不去设置,由于该属性默认值是true).一对一加载时默认使用左外链接,能够经过修改fetch属性为select修改为每次发送一条select语句的形式 203 204 fetch属性能够改变查询语句的方式,join表示链接查询,select表示单独select查询,one-to-one默认的是join链接查询 205 206 2.外键关联(特殊的一对多) 207 在一方使用many-to-one加上属性unique="true"表示惟一就变成了one-to-eon。 208 209 210 211 根据实体类配置生成数据库表结构 212 SchemaExport export = new SchemaExport(new Configuration().configuer()); 213 export.create(true,true);第一个参数打印出sql语句,第二个参数把sql语句导入到数据库 214 215 多对多关系相似于一对多,只是要在set中指定中间表,并在key与one-to-many中指定相关外键 216 217 map映射采用map标签,其实是采用另一张表的形式存储它 218 <map name="students" table="student"> 219 <key column="team_id"></key>//关联的表中的外键 220 <index column="name" type="string"></index>//指定map中的键存储 221 <element column="description" type="string"></element>//指定map中的值 222 </map> 223 若是map的值是另外一个实体 224 <map name="students" table="student"> 225 <key column="team_id"></key>//关联的表中的外键 226 <index column="name" type="string"></index>//指定map中的键存储 227 <one-to-many class="com.hibernate.Student"/>//必须有student映射文件 228 </map> 229 230 set集合中存放的全是简单属性(一个类对应两张表) 231 <set name="student" talbe="student"> 232 <key column="team_id"></key> 233 <element column="name" type="string"></element> 234 </set> 235 236 map与set标签中的element子标签映射的是原子类型(string,date,int,long...),即便可以直接映射到数据库表字段上的类型,而one-to-many映射的则是实体类型,指的是没法映射到表的某个字段,而是要映射到整张表的类型 237 238 list的元素是能够重复的,并且是有顺序的。set中的元素是不容许重复的 239 <list name="student" table="student" cascade="all"> 240 <key column="team_id"></key> 241 <index column="index_"></index>//数据库中存放顺序的字符(否则没法排序) 242 <one-to-many class="com.hibernate.Student"/> 243 </list>不能加inverse="true"由于多的一方是没法知道排序的,因此要在一的一方进行维护 244 245 Bag(结合了List与Set),能够重复且没有顺序的一种集合,是Hiberante提供的,能够用list来模拟bag 246 <bag name="student" table="student" cascade="all" inverse="true"> 247 <key column="team_id"></key> 248 <one-to-many class="com.hibernate.Student"/> 249 </bag> 250 251 查询排序(内存排序及数据库排序) 252 数据库排序使用order-by="name asc"(name是数据库里的字段),在set,list,bag,map标签上使用,order-by的内容能够指定多个字段排序,先按第一个字段排序,若是相等则按第二个字段排序 253 内存排序用sort="natural"按天然排序,它有三个属性(nusorted,natural),其中的natural指的是按照天然的升序排序。第三个属性值是咱们自定义的排序规则类(实现Comparator接口里的comparat方法),在sort中指定咱们排序规则类的全称 254 255 数据库联合主键 256 1.类中的每一个属性都对应到数据表中的每一个主键列 257 一个类有联合主键,则它必须实现serializable接口,而且重写equls和hashcode方法(两个方法中关联联合主键涉及到的属性)。hibernate要求具备联合主键的实体类实现Serializable接口,而且重写hashCode与equals方法,重写这两个方法的缘由在于Hibernate要根据数据库的联合主键来判断某两行记录是不是同样的,若是同样认为是同一个对象,若是不同,那么认为是不一样的对象,这反映到程序领域中就是根据hashCode与equals方法来判断某两个对象是否可以放到诸如Set这样的集合当中。 258 联合主键映射: 259 <composite-id> 260 <key-property name="cardId" column="cardId" type="string"></key-property> 261 <key-property name="name" column="name" type="string"></key-property> 262 </composite-id> 263 联合主键的实体类实现Serializable接口的缘由在于使用get或load方法的时候须要先构建出该实体的对象,而且将查询依据(联合主键)设置进去,而后做为get或load访求的第二个参数传进去便可 264 2.将主键所对应的属性提取出来一个类(称之为主键类),而且主键类须要实现Seriliable接口而且重写hashCode()与equlas接口,在主类中引用这个主键类 265 映射文件以下: 266 <composite-id name="stuentPrimaryKey" class="com.hibernate.StudentPrimaryKey"> 267 <key-property name="cardId" column="cardId" type="string"></key-property> 268 <key-property name="name" column="name" type="string"></key-property> 269 </composite-id> 270 这两种方式生成的表结构是同样的 271 272 组件映射(把一对一关联映射到一张表中也只是针对单向的如学生的家庭地址和学校地址组成一个地址类在学生表中的映射) 273 <component name="address" class="com.hibernate.Address"> 274 <property name="homeaddress" type="string"></property> 275 <property name="schooladdress" type="string"></property> 276 </component> 277 在set标签中也能够用组件映射,适用于单向 278 279 3.继承映射 280 1》每一个子类一张表(映射方式没有特别,把父类的属性映射到子类文件中) 281 Query query = session.createQuery("from com.hibernate.Person");//多态查询,会查询出它全部子类及它自己.若是没有映射文件,则必须加上包的名字 282 2》一张表存储体系中全部类的信息(数据库表实体上是继承体系中全部类的属性的并集构成的字段),映射用继承的父类来命名,须要在hbm文件中增长以下一行用来区分不一样子类 283 <discriminator column="personType" type="string"></discriminatro> 284 285 子类信息配置 286 <subclass name="com.hibernate.Student" discriminator-value="student">//类名 287 <property name="cardId" type="string"/>//属性 288 </subclass> 289 <subclass name="com.hibernate.Teacher" discriminator-value="teacher">//类名 290 <property name="salary" type="int"/>//属性 291 </subclass> 292 293 查询能够得到全部信息,经过instanceof比较子类,能够得到特定子类的信息 294 3》每一个类一张表,公共信息放在父类表中,独有信息放在子类表中,每一个子类对应一张表 295 其中有关联父类表的id 296 <joined-subclass name="com.hibernate.Student" table="student"> 297 <key column="id"></key>//主键关联父类 298 <property name="cardId" type="string"></property> 299 </joined-subclass> 300 301 <joined-subclass name="com.hibernate.Teacher" table="teacher"> 302 <key column="id"></key>//主键关联父类 303 <property name="salary" type="int"></property> 304 </joined-subclass> 305 306 Hibernate的查询 307 1.导航对象图检索方式:根据已经加载的对象,导航到其余对象,如,对于已经加载的Customer对象,调用它的getOrders().iterator()方法能够导航到全部关联的Order对象,假如在关联级别使用了延迟加载检索策略,那么首先执行此方法时,Hibernate会从数据库中加载关联的Order对象,不然就从缓存中取得Order对象 308 2.OID检索方式:按照对象的OID来检索对象。Session的get()和load()方法提供了这种功能。若是在应用程序中事先知道了OID,就可使用这种检索对象的方式 309 3.HQL检索方式: Hibernate提供了Query接口,这是Hibernate提供的专门的HQL查询接口,可以执行各类复杂的HQL查询语句 310 4.QBC查询方式:使用QBC(Query By Criteria)api来检索对象。这种api封装了基于字符串形式的查询语句,提供了更加面向对象的接口。 311 312 hql检索步骤: 313 //建立一个Query对象 314 Query query = session.createQuery("from Customer as c where 315 + "c.name=:customerName" 316 + "and c.age=:customerAge"); 317 //动态绑定参数 318 query.setString("customerName","tom"); 319 query.setInteger("customerAge",21); 320 //执行查询语句,返回查询结果 321 List result = query.list(); 322 323 qbc检索方式 324 采用hql检索方式时,在应用程序中须要定义基于字符串形式的hql查询语句 325 qbc api提供了检索对象的另外一种方式,它主要由Criteria接口,Criterion接口和Expression类组成,它支持在运行时动态生成查询语句 326 327 //建立一个Criteria对象 328 Criteria criteria = session.createCriteria(Custome.class); 329 //设定查询条件,而后把查询条件加入到Criteria中 330 Criterion creiterion1 = Expression.like("name","T%"); 331 Criterion creiterion2 = Expression.eq("age",new Interger(21)); 332 criteria=criteria.add(criterion1); 333 criteria=criteria.add(criterion2); 334 //执行查询语句,返回查询结果 335 List result = criteria.list(); 336 337 查询示例 338 339 查询部分属性 340 Query query = session.createQuery("select s.name,s.age from Student s");//查询部分属性 341 List list = query.list(); 342 for(int i=0;i<list.size();i++) 343 { 344 Object[] obj = (Object[])list.get(i);//查询出来的每一行数据都是以object[]形式返回 345 System.out.println(obj[0]+","+obj[1]);//第一个是name,第二个是age 346 } 347 348 Query query = session.createQuery("select new Student(s.name,s.age) from Student s"); 349 此时返回的是包含部分信息的student对象集合(但必须提供以上形式的构造函数) 350 351 链接操做 352 353 内链接,返回的是两个对象的数组的集合 354 Query query = session.createQuery("from Team t join t.students");//根据team与student内链接 355 List list = query.list(); 356 for(int i=0;i<list.size();i++) 357 { 358 Object[] obj =(Object[])list.get(i); 359 Team team = (Team)obj[0];//链接的第一张表 360 Student student =(Student)obj[1];//链接的第二张表 361 System.out.println(team.getName()); 362 } 363 364 进行表的链接查询,在hql中其实是覆盖了延迟加载,都变成了直接加载了,配置文件中的不起使用。 365 366 367 对实体进行参数绑定查询方法 368 Team team = (Team)session.get(Team.class,"id值"); 369 Query query = session.createQuery("from Student s where s.team=:team and s.age >20"); 370 371 query.setParameter("team",team,Hibernate.entity(Team.class));//1.命名参数名字去掉冒号2.参数所赋值对象3.对象转化成type类型 372 373 //以上也能够用这个简便方法 query.setEntity("team",team); 374 List<Student> list = query.list(); 375 376 377 Query query = session.createFillter("参数1","hql查询条件(where age>20) ");参数1为得到的持久化对象的集合,这就是hibernate的过滤查询 378 Query query = session.createFillter(tema.getStudents(),"where age>20"); 379 380 Criteria criteria = session.createCriteria(Student.class).add(Restrictions.between("age",new Integer(20),new Integer(30));查询年龄在20到30之间的学生 381 List<Student> list = criteria.list(); 382 between是包含小的也包含大的 383 384 Criteria criteria = session.createCriteria(Student.class).addOrder(Order.asc("age")).addOrder(Order.desc("cardId")); 385 hibernate官方推荐使用hql查询 386 387 数据库的事务与并发处理 388 事务transation是一组相互依赖的操做行为,如银行交易,股票交易或网上购物,事务的成功取决于这些相互依赖的操做行为是否都能执行成功,只要有一个操做行为失败,就意味着整个事务的失败。 389 数据库事务是对现实生活的模拟,它由一组在业务逻辑上相互依赖的SQL语句组成。 390 391 在实际系统中除了查询操做外,绝对不容许设计为自动提交 392 393 JTA java事务接口 394 395 每启动一个mysql.exe程序,就会获得一个单独的数据库链接,每一个数据库链接都有个全局变量@@autocommit,表示当前的事务模式,它有两个值: 396 0--:表示手工提交模式 397 1--:默认值,表示自动提交模式 398 若是要察看当前的事务模式,可以使用以下sql命令:select @@autocommit 399 若是要把当前的事务模式改成手工提交模式,可使用sql命令:set autocommit=0 400 在自动提交模式下,每一个sql语句都是一个独立的事务,mysql会自动提交这个事务 401 402 数据库事务的4个特性acid: 403 1.原子性atom 404 2.持久性consistence 405 3.隔离性isolation 406 4.持久性duration 407 在手工模式下,必须显式指定事务开始边界和结束边界: 408 事务的开始边界:begin 409 提交事务:commit 410 撤销事务:rollback 411 412 jdbc Connection提供了如下用于控制事务的方法 413 setAutoCommit(boolean autoCommit)设置是否提交事务 414 commit()提交事务 415 rollback()撤销事务 416 417 try{ 418 con=java.sql.DriveManager.getConnection(dbUrl,dbUser,dbPwd); 419 //设置手工提交模式 420 con.setAutoCommit(false); 421 stmt=con.createStatement(); 422 //数据库更新操做1 423 stmt.executeUpdate("更新语句"); 424 //数据库更新操做2 425 stmt.executeUpdate("更新语句"); 426 con.commit();//提交事务 427 }catch(Exception e){ 428 tyr{ 429 con.rollback();//操做不成功则撤销事务 430 }catch(Exception ex){ 431 //处理异常 432 。。。 433 } 434 //处理异常 435 }finally{...} 436 437 多个事务并发运行时的并发问题 438 1.丢失更新:撤销一个事务时,把其余事务已提交的更新数据覆盖 439 2.脏读:一个事务读到另外一个事务未提交的更新数据 440 3.虚读:一个事务读到另外一个事务已提交的新插入的数据 441 4.不可重复读:一个事务读到另外一个事务已提交的更新数据 442 5.第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另外一个事务已提交的更新数据 443 444 数据库的事务隔离级别: 445 ReadUncommited,ReadCommited,Repeatable Read,Serializable 446 查看当前的隔离级别: 447 select @@tx_isolation 448 设置隔离级别 449 set transaction isolation level read committed 450 451 在Hibernate的配置文件中中能够显式设置隔离级别。每一种隔离级别都对应一个整数: 452 1.Read Uncommitted 453 2.Read Committed 454 4.Repeatable Read 455 8.Serializable 456 在hibernate.cfg.xml文件中 hibernate.connection.isolation=2 457 458 使用悲观锁 459 Account account = (Account)session.get(Account.class,new Long(1),LockMode.UPGRADE); 460 hibernate执行的select语句为: 461 select * from ACCOUNTS where ID=1 for update;//多了个for update 462 463 乐观锁是由应用程序提供的一种机制,这种机制既能保证多个事务并发访问数据,又能防止第二类丢失更新问题 464 在应用程序中,能够利用Hibernate提供的版本控制功能来实现乐观锁。对象-关系映射文件中的<version>元素和<timestamp>元素都具备版本控制功能 465 -<version>元素利用一个递增的整数来跟踪数据库表中记录的版本 466 -<timestamp>元素用时间戳来跟踪数据库表中记录的版本 467 468 使用乐观锁 469 1.在实体类中加一个version属性,做为版本控制,映射该属性的时候用<version name="version" column="version" type="integer"> 470 时间戳方式控制 471 在实体类中另外一个日期类型的属性做为时间戳,配置文件中<timestamp name="lastDate" column="lastDate"/> 472 473 悲观锁是由数据库底层实现的,乐观锁是由应用程序来实现的 474 475 实现乐观锁的其余方法: 476 若是应用程序是基于已有的数据库,而数据库表中不包含表明版本或时间戳的字段,Hibernate提供了其余实现乐观锁的办法。把<class>元素的optimistic-lock属性设为“all”: 477 <class name="Account" table="ACCOUNTS" optimistic-lock="all" dynamic-update="true"> 478 hibernate会在update语句的where子句中包含Account对象被加载时的全部属性: 479 update ACCOUNTS set balanec=900 where id=1 and name="tom" and balance="1000"; 480 481 hibernate拦截器与事务 482 应用程序可以响应hibernate内部产生的特定事件是很是有用的。这样就容许实现某些通用的功能以及容许对hibernate功能进行扩展(监听事件) 483 持久层框架底层的拦截器机制是对诸如spring等业务管理容器拦截机制的有益的补充,使得咱们能够在更低层次,更广的对象范围上进行AOP操做(Spring虽然将hibernate归入到其容器管理的范围内,可是并无途径实现对其实体对象的管理)。这样就容许实现某些通知的功能,以及容许对hibernate功能进行扩展。 484 485 Interceptor接口提供了从会话(session)回调(callback)应用程序(application)的机制,这种回调机制能够容许应用程序在持久化对象被保存,更新,删除或是加载以前,检查并(或)修改其属性。其子类是EmptyInterceptor 486 487 你能够直接实现Interceptor接口,也能够(最好)继承自EmptyInterceptor. 488 拦截器有两种: 489 -session范围内的 490 -sessionFactory范围内的 491 492 使用拦截器按以下步骤进行: 493 1.定义实现Interceptor接口的拦截器 494 2.经过session启用拦截器,或者经过configuration启用全局拦截器 495 496 当使用某个重载的SessionFactory.openSession()使用Interceptor做为参数调用打开一个session的时候,就指定了Session范围内的拦截器。 497 Session session = factory.openSession(new AuditInterceptor()); 498 499 SessionFactory范围内的拦截器要经过configuration中注册,而这必须在建立SessionFactory以前。在这种状况下,给出的拦截器会被这个SessionFactory所打开的全部session使用了,除非session打开时明确指明了使用的拦截器。SessionFactory范围内的拦截器,必须是线程安全的,由于多个session可能并发使用这个拦截器,要所以当心不要保存与session相关的状态 500 501 事件系统(Event system) 502 若是须要响应持久层的某些特殊事件,你也可使用Hibernate3的事件框架。该事件系统能够用来替代拦截器,也能够做为拦截器的补充来使用。 503 基本上,Session接口的每一个方法都有相应的事件。如LoadEvent,FlushEvent等等(能够查询xml配置文件的dtd以及org.hibernate.event包来得到全部已定义的事件的列表) 504 505 当某个方法被调用时,Hibernate Session会生成一个相应的事件并激活全部配置好的事件监听器(观察者模式)。被监听的方法所作的其实仅仅是激活监听器,实际的工做是由监听器完成的。你能够自由地选择实现一个本身定制的监听器:好比,实现并注册用来处理LoadEvent的LoadEventListener接口,来处理全部的调用Session的load()方法的请求。 506 507 监听器在运行时被实例化为单例,也就是说,全部同类型的事件的处理共享同一个监听器实例,所以监听器不该该保存任何与请求相关的状态。 508 用户定制的监听器须要实现相关的事件监听器接口,或者从一个合适的基类继承(甚至是从Higbernate自带的默认事件监听器继承)。 509 510 用户定制的监听器能够经过编程使用Configuration对象来注册,也能够在Hibernate的xml格式的配置文件中进行声明。 511 512 你还须要修改一处配置,来告诉Hibernate,除了默认的监听器,还要附加选定的监听器。 513 <hibernate-configuration> 514 <session-factory> 515 .. 516 <event type="load"> 517 <listener class="com.eg.MyLoadListener"/> 518 <listener class="org.hibernate.event.def.DefaultLoadEventListener"> 519 </event> 520 </session-factory> 521 </hiberante-configuration> 522 523 经过编程的方式注册 524 Configuration cfg = new Configuration(); 525 LoadEventListener[] stack={new MyLoadListener(),new DefaultLoadEvnetListener()}; 526 cfg.getEventListeners().setLoadEventListeners(stack); 527 528 经过在xml配置文件声明而注册的监听器不能共享实例。若是在多个<listener/>节点中使用了相同类的名字,则每个引用都将会产生一个独立的实例。若是你须要在多个监听器类型之间共享监听器的实例,则你必须使用编程的方式来进行注册。 529 530 数据库链接池(connection pool),c3p0,apache的dbcp,hibernate内置支持c3p0链接池 531 能够主文件中用<property name="hibernate.c3p0.属性"></property>来设置链接池 532 533 hibernate tool与ant实现代码自动生成 534 在src目录下建build.xml与build.properties 535 build.properties内容 536 src=src 537 dbschema=dbschema//schema存储目录 538 libs=libs//ant所须要的架文件,hibernate.tool架包 539 bin=bin 540 541 build.xml文件内容 542 <project name="hibernate_tools" basedir=".">(.表示当前目录) 543 <property file="build.properties"></property>(指定属性文件位置) 544 <target name="init"> 545 <path id ="lib.path"> 546 <pathelement path="${bin}"/> 547 <fileset dir="${libs}"> 548 <include name="**/*.jar"/>(引用目录下全部文件) 549 </fileset> 550 </path> 551 </target> 552 553 <taskdef name="hibernatetools" classname="org.hibernate.tool.ant.HibernateTooTask" classpathref="lib.path">(定义任务) 554 </taskdef> 555 556 <target name="dbschema"> 557 <hibernatetools> 558 <configuration configurationfile="${src}/hibernate.cfg.xml"/> 559 <hbm2ddl destdir="${dbschema}" export="true" outputfilename="dbschema.sql"/> 560 <hbm2java jdk5="true" destdir="${src}"/> 561 </hibernatetools> 562 </target> 563 564 565 </project>