Hibernate4.x之Session--常用方法
接上篇文章继续学习Hibernate的Session(http://www.cnblogs.com/dreamfree/p/4111777.html)
持久化对象的状态;
站在持久化的角度,Hibernate把对象分为4种状态:持久化状态、临时状态、游离状态、删除状态。
Session的特定方法能使对象从一个状态转换到另一个状态。
临时对象(Transient):
在使用代理主键的情况下,OID通常为null
不处于Session的缓存中
在数据库中没有对应的记录
持久化对象(也叫“托管”)(Persist)
OID为null
位于Session缓存中
若在数据库中已经有和其对应的记录,持久化对象和数据库中的相关记录对应
Session在flush缓存时,会根据持久化对象的属性变化,来同步更新数据库
在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象
删除对象(Removed):
在数据库中没有和其OID对应的记录
不再处于Session缓存中
一般情况下,应用程序不该再使用被删除的对象
游离对象(也叫"脱管")(Detached):
OID不为null
不再处于Session缓存中
一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录
下面的图片说明了Hibernate中对象状态的转换:
Session的save方法
Session的save()方法使一个临时对象转变为持久化对象
Session的save()方法完成以下操作:
把News对象加入到Session缓存中,使它进入持久化状态
选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID,在使用代理主键的情况下,setId()方法为News对象设置OID是无效的
计划执行一条insert语句:在flush缓存的时候
Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当News对象处于持久化状态时,不允许程序随意修改它的ID
persist()和save()的区别:
当对一个OID不为null的对象执行save()方法时,会把该对象以一个新的OID保存到数据库中;但执行persist()方法时会抛出一个异常
Session的get()和load()方法
都可以根据给定的OID从数据库加载一个持久化对象
区别:
当数据库中不存在与OID对应的记录时,load()方法抛出ObjectNotFoundException异常,而get()方法返回null
两者采用不同的延迟检索策略:load()方法支持延迟加载策略。而get不支持
Session的update()方法
Session的update()方法使一个游离对象转变为持久化对象,并且计划执行一条update语句
若希望Session仅当修改了News对象的属性时,才执行update()语句,可以把映射文件中<class>元素的select-before-update设为true。该属性的默认值为false
当update()方法关联一个游离对象时,如果在Session的缓存中已经存在相同的OID的持久化对象,会抛出异常
当update()方法关联一个游离对象时,如果数据库中不存在相应的记录,也会抛出异常
Session的saveOrUpdate()方法
Session的saveOrUpdate()方法同时包含了save()与update()方法的功能
判定对象为临时对象的标准
Java对象的OID为null
映射文件中的<id>设置了unsaved-value属性,并且Java对象的OID取值与这个unsaved-value属性值匹配
Session的merge()方法
Session的delete()方法
Session的delete()方法既可以删除一个游离对象,也可以删除一个持久化对象
Session的delete()方法处理过程
计划执行一条delete语句
把对象从Session的缓存中删除,该对象进入删除状态
Hibernate的cfg.xml配置文件中有一个hibernate.use_identifier_rollback属性,其默认值为false,若把它设为true,将改变delete()方法的运行行为:delete()方法会把持久化对象或游离对象的OID设置为null,使它们变为临时对象
通过Hibernate调用存储过程
work接口:直接通过JDBC API来访问数据库的操作
Session的doWork(Work)方法用于执行Work对象指定的操作,即调用Work对象的execute()方法。Session会把当前使用的数据库连接传递给execute()方法
Hibernate与触发器协同工作
Hibernate与数据库中的触发器协同工作时,会造成两类问题
触发器使Session的缓存中的持久化对象与数据库中对应的数据不一致;触发器运行在数据库中,它执行的操作对Session是透明的
session的update()方法盲目地激发触发器;无论游离对象的属性是否发生变化,都会执行update语句,而update语句会激发数据库中相应的触发器
解决方案:
在执行完Session的相关操作后,立即调用Session的flush()方法和refresh()方法,迫使Session的缓存与数据库同步(refresh()方法重新从数据库加载对象)
在映射文件的<class>元素中设置select-before-update属性;当Session的update或saveOrUpdate()方法更新一个游离对象时,会先执行select语句,获得当前游离对象在数据库中的最新数据。只有在一致的情况下才会执行update语句
测试类:
1 package com.yl.hibernate.entities; 2 3 import static org.junit.Assert.*; 4 5 import java.sql.CallableStatement; 6 import java.sql.Connection; 7 import java.sql.SQLException; 8 import java.util.Date; 9 10 import org.hibernate.Session; 11 import org.hibernate.SessionFactory; 12 import org.hibernate.Transaction; 13 import org.hibernate.cfg.Configuration; 14 import org.hibernate.jdbc.Work; 15 import org.hibernate.service.ServiceRegistry; 16 import org.hibernate.service.ServiceRegistryBuilder; 17 import org.junit.After; 18 import org.junit.Before; 19 import org.junit.Test; 20 21 public class HibernateTest { 22 23 private SessionFactory sessionFactory; 24 private Session session; 25 private Transaction transaction; 26 27 @Before 28 public void init() { 29 Configuration configuration = new Configuration().configure(); 30 ServiceRegistry serviceRegistry = 31 new ServiceRegistryBuilder().applySettings(configuration.getProperties()) 32 .buildServiceRegistry(); 33 34 sessionFactory = configuration.buildSessionFactory(serviceRegistry); 35 36 session = sessionFactory.openSession(); 37 38 transaction = session.beginTransaction(); 39 } 40 @After 41 public void destory() { 42 transaction.commit(); 43 44 session.close(); 45 46 sessionFactory.close(); 47 } 48 49 @Test 50 public void testSessionCache() { 51 News news = (News)session.get(News.class, 21); 52 System.out.println(news); 53 session.flush(); 54 News news2 = (News)session.get(News.class, 21); 55 System.out.println(news2); 56 57 System.out.println(news == news2);//true 58 } 59 60 /** 61 * flush:使数据库表中的记录和Session缓存中的对象的状态保持一致。为了保持一致,则可能会发送对应的SQL语句。 62 * 1.在Transaction的commit()方法中:先调用session的flush方法,再提交事务 63 * 2.flush()方法可能会发送SQL语句,但不会提交事务 64 * 3.注意:在为提交事务或显示调用session.flush()方法 之前,也有可能会flush()操作。 65 * 1).执行HQL或QBC查询,会先进行flush操作,以得到数据表的最新纪录 66 * 2).若记录的ID是由底层数据库使用自增的方式生成的,则在调用save方法时,就会立即发送INSERT语句 67 * 因为save方法后,必须保证对象的ID是存在的! 68 */ 69 @Test 70 public void testFlush2() { 71 News news = new News("Hibernate", "oracle", new Date()); 72 session.save(news); 73 } 74 75 @Test 76 public void testFlush() { 77 /*News news = (News)session.get(News.class, 21); 78 news.setAuthor("Bruce Eckel"); 79 80 News news2 = (News)session.createCriteria(News.class).uniqueResult(); 81 System.out.println(news2);*/ 82 News news = (News)session.get(News.class, 22); 83 session.flush(); 84 System.out.println(news); 85 } 86 87 /** 88 * refresh():会强制发送SELECT 语句,以使Session缓存中对象的状态和数据表中对应的记录保持一致! 89 */ 90 @Test 91 public void testRefresh() { 92 News news = (News) session.get(News.class, 22); 93 System.out.println(news); 94 session.refresh(news); 95 System.out.println(news); 96 } 97 98 /** 99 * clear():清理缓存 100 */ 101 @Test 102 public void testClear() { 103 News news1 = (News) session.get(News.class, 21); 104 session.clear(); 105 News news2 = (News) session.get(News.class, 21); 106 } 107 108 /** 109 * save()方法: 110 * 1.使一个临时对象变为持久化对象 111 * 2.为对象分配ID 112 * 3.在flush缓存时会发送一条insert语句 113 * 4.在save方法之前的id是无效的 114 * 5.持久化对象的ID是不能被修改的! 115 */ 116 @Test 117 public void testSave() { 118 News news = new News(); 119 news.setTitle("AA"); 120 news.setAuthor("aa"); 121 news.setNewsDate(new Date()); 122 //news.setId(100);//不起效果 123 System.out.println(news); 124 //news.setId(101);//抛出异常 125 session.save(news); 126 System.out.println(news); 127 } 128 /** 129 * persist():也会执行insert操作 130 * 和save()的区别: 131 * 在调用persist方法之前,若对象已经有ID了,则不会执行insert,而抛出异常 132 */ 133 @Test 134 public void testPsesist() { 135 News news = new News(); 136 news.setTitle("DD"); 137 news.setAuthor("dd"); 138 news.setNewsDate(new Date()); 139 //news.setId(200);//抛出异常 140 System.out.println(news); 141 session.save(news); 142 System.out.println(news); 143 } 144 145 /** 146 * 147 */ 148 @Test 149 public void testGet() { 150 News news = (News) session.get(News.class, 21); 151 System.out.println(news); 152 } 153 154 /** 155 * get 和 load 156 * 157 * 1.执行get方法,会立即加载对象。 158 * 而执行load方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象 159 * 160 * get是立即检索,load是延迟检索 161 * 162 * 2.load方法可能会抛出LazyInitializationException(懒加载异常):在需要初始化代理对象之前已经关闭了session 163 * 164 * 3.若数据表中没有对应的记录,且session也没有被关闭。同时需要使用对象时 165 * get 返回null 166 * load 若不使用该对象的任何属性,则不会出问题;若要初始化了,抛出异常 167 * 168 * 169 */ 170 @Test 171 public void testLoad() { 172 News news = (News) session.load(News.class, 21); 173 System.out.println(news); 174 } 175 176 /** 177 * update: 178 * 1.若更新一个持久化对象,不需要显式的调用update对象。因为在调用Transaction的commit方法时,会先执行session的flush方法。 179 * 2.更新一个游离对象,需要显式的调用session的update方法。可以把一个游离对象变为持久化对象 180 * 181 * 需要注意的: 182 * 1.无论要更新的游离对象和数据表中的记录是否一致,都会发送update语句。 183 * 如何能让update方法不再盲目的发送update语句,在.hbm.xml文件的class节点设置select-before-update的值为true,默认为flase。但通常不需要设置该属性 184 * 185 * 2.若数据表中没有对应的记录,但还调用了update方法,会抛出异常 186 * 187 * 3.当update()方法关联一个游离对象时,如果在Session的缓存中已经存在相同OID的持久化对象,会抛出异常。因为在Session缓存中不能有两个OID相同的对象 188 */ 189 @Test 190 public void testUpdate() { 191 /*News news = (News) session.get(News.class, 22); 192 news.setAuthor("Sun");//此时news的author字段已经更新到数据库中,因为在执行commit语句时会先执行flush,flush将session缓存中的修改同步到数据库中。 193 */ 194 195 News news = (News) session.get(News.class, 22); 196 transaction.commit(); 197 session.close(); 198 199 session = sessionFactory.openSession(); 200 transaction = session.beginTransaction(); 201 202 203 //news.setAuthor("Sun"); 204 session.update(news); 205 206 } 207 208 /** 209 * 注意: 210 * 1.若OID不为空,但数据表还没有和其对应的记录。会抛出一个异常。 211 * 2.了解:OID值等于id的unsaved-value属性值的对象,也被认为是一个游离对象 212 */ 213 @Test 214 public void testSaveOrUpdate() { 215 News news = new News("FF", "ff", new Date()); 216 news.setId(41); 217 session.saveOrUpdate(news); 218 219 } 220 221 /** 222 * delete: 执行删除操作,只要OID和数据表中一条记录对应,就会准备执行delete操作 223 * 若OID在数据表中没有对应的记录,则抛出异常 224 * 225 * 可以通过设置一个Hibernate配置文件hibernate.use_identifier_rollback 为true,使删除对象后,把其OID置为null 226 */ 227 @Test 228 public void testDelete() { 229 /*//游离对象删除 230 News news = new News(); 231 news.setId(42); 232 233 session.delete(news);*/ 234 235 //持久化对象删除 236 News news = (News) session.get(News.class, 41); 237 session.delete(news);//计划删除,并不立即执行。在commit中的flush时执行delete 238 System.out.println(news);//依然可以打印 239 } 240 241 /** 242 * evict:从session缓存中把指定的持久化对象移除 243 */ 244 @Test 245 public void testEvict() { 246 News news1 = (News) session.get(News.class, 22); 247 News news2 = (News) session.get(News.class, 41); 248 249 news1.setTitle("AA"); 250 news2.setTitle("BB"); 251 252 session.evict(news1); 253 } 254 255 /** 256 * 调用存储过程 257 */ 258 @Test 259 public void testDoWork() { 260 session.doWork(new Work() { 261 262 @Override 263 public void execute(Connection connection) throws SQLException { 264 System.out.println(connection); 265 //调用存储过程 266 267 //创建存储过程的对象 268 CallableStatement c = connection.prepareCall("{call getsum(?,?)}"); 269 270 //给存储过程的第一个参数设置值 271 c.setInt(1,100); 272 273 //注册存储过程的第二个参数 274 c.registerOutParameter(2,java.sql.Types.INTEGER); 275 276 //执行存储过程 277 c.execute(); 278 279 connection.close(); 280 } 281 }); 282 } 283 }