Hibernate3.2(四)生命周期示例、CRUD操作、Junit 3单元测试
抽时间 总结junit3和junit4的相关测试方法 ,junit4采用的是annotations
junit3 命名规范: 测试类 xxxTest 测试方法void testxxx() ,方法中可以用断言assertEquals(expected, actual).
测试类需要继承TestCase , 在工程中新建一个source folder,在其中建好包,包名最好和要测的类所在的包一样。
1.用Junit 3 编写一个测试类SessionTest来测试 & 探索Hibernate 三种对象的生命周期
熟悉hibernate的CRUD操作,主要方法有:get()、load()、save()、delete()、update()
package com.wyx.hibernate; import java.util.Date; import junit.framework.TestCase; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import com.wyx.hibernate.utils.HibernateUtils; /** * 使用junit做测试多个方法比较方便,junit中还有setup()和Deardown(),用来初始化和销毁 * setup()一般放的是 * @author bird */ public class SessionTest extends TestCase{ //public 无return public void testHello(){ System.out.println("----------SessionTest.testHello()----------"); //throw new java.lang.RuntimeException(); //this.assertEquals("hello", "no hello"); } public void testSession(){ Session session = HibernateUtils.getSession(); Transaction ts = session.beginTransaction(); User u = new User(); try { //Transient状态 //uid不需要赋值,自动赋值 u.setName("李四"); u.setPassword("123"); u.setCreateTime(new Date()); u.setExpireTime(new Date()); session.save(u);//单步调试表明,在执行完save()后,uid的值才产生 System.out.println("---------mark-----------"); //save()后变成persistent状态 u.setName("王五"); //persistent状态的时候,不需要手动update,hibernate会自动和数据库同步 //session.update(u); ts.commit(); } catch (HibernateException e) { e.printStackTrace(); ts.rollback(); }finally{ HibernateUtils.closeSession(session); } //Detached状态 u.setName("张三"); try { session = HibernateUtils.getSession(); session.beginTransaction(); session.update(u); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); ts.rollback(); }finally{ HibernateUtils.closeSession(session); } } }
运行结果:
----------SessionTest.testHello()----------
---------mark-----------
Hibernate:insertintoUser(name,password,createTime,expireTime,id)values(?,?,?,?,?)
Hibernate:updateUsersetname=?,password=?,createTime=?,expireTime=?whereid=?
Hibernate: update User set name=?, password=?, createTime=?, expireTime=? where id=?运行结果表明,一个对象处于Persistent状态时,当属性发生改变的时候,Hibernate会自动和数据库同步,Hibernate只是把这些数据放在缓存中处理,而且是在commit()提交的时候才执行所有的数据库操作。
产生多条sql语句的原因是:hibernate在修改数据之前要先照一个快照,接下来修改了对象u.setName("王五"),在commit的时候,需要清理缓存(内从中的对象),脏数据是指没有提交的数据,commit()之前清理缓存的这个过程中要执行脏数据对比,脏数据对比的主要工作是处理哪些数据要发成insert,哪些数据要发成update语句。
当session关闭的时候,session已经不再管理u对象,u对象虽然变成了Detached状态,但是数据库中该条记录依然存在,当执行update的时候,数据库中u对应的那条记录name字段又变成了“张三”,update之后,该u对象又变成了Persistent状态的。
接着在SessionTest中写一个方法,运行:
public void testReadByGetMethod(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); //将数据库中对应id的那条记录取出来存在User对象中,此时立即产生sql语句 User u =(User)session.get(User.class, "402881fa2edaffe9012edaffea5c0001"); System.out.println("-------------mark----------------"); //修改u , persistent状态的时候,不需要手动update,hibernate会自动和数据库同步 u.setName("jeff"); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
运行结果:
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.password as password0_0_, user0_.createTime as createTime0_0_, user0_.expireTime as expireTime0_0_ from User user0_ where user0_.id=?
-------------mark----------------
Hibernate: update User set name=?, password=?, createTime=?, expireTime=? where id=?public void testReadByGetMethod1(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); User u =(User)session.get(User.class, "adfsafa"); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
运行结果:Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.password as password0_0_, user0_.createTime as createTime0_0_, user0_.expireTime as expireTime0_0_ from User user0_ where user0_.id=?调试跟踪表明,u对象值为null,但是单元测试没有报错,所以采用get()加载数据,如果数据中不存在相应的数据,返回null。
public void testReadByLoadMethod1(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); //不会发出查询sql,因为load方法实现了lazy() //懒加载 or 延迟加载,真正使用这个对象的时候,才加载(发出sql语句) //hibernate延迟加载实现原理是代理方式 User u =(User)session.load(User.class, "402881e62ed3df84012ed3df85510001"); System.out.println("----------mark------------"); System.out.println("u.name: " + u.getName()); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
运行结果:
----------mark------------
Hibernate:selectuser0_.idasid0_0_,user0_.nameasname0_0_,user0_.passwordaspassword0_0_,user0_.createTimeascreateTime0_0_,user0_.expireTimeasexpireTime0_0_fromUseruser0_whereuser0_.id=?
u.name: jeff代理类是使用CGLIB工具生成代理类,代理类继承了User。
public void testReadByLoadMethod2(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); //采用load加载数据,如果数据库中没有相应数据, //那么抛出ObjectNotFoundException的异常 User u =(User)session.load(User.class, "adadfafdf"); System.out.println("----------mark------------"); System.out.println("u.name: " + u.getName()); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
运行结果:
----------mark------------
Hibernate:selectuser0_.idasid0_0_,user0_.nameasname0_0_,user0_.passwordaspassword0_0_,user0_.createTimeascreateTime0_0_,user0_.expireTimeasexpireTime0_0_fromUseruser0_whereuser0_.id=?
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.wyx.hibernate.User#adadfafdf]从上述get和load的测试方法可见:
1. get()不支持lazy, load()是支持lazy的(懒加载);
2. get()查询数据的时候,查不到返回null。而load()抛出org.hibernate.ObjectNotFoundException。注意,执行load方法的时候是不会报错的,在调用保存load的那个对象时才会抛出异常。
测试update():
public void testUpdate1(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); //虽然是我们自己new出来的,但是id在数据库中存在,所以这属于手动构造的 Detached 对象 User u = new User(); u.setId("402881e62ed40ec1012ed40ec2f10001"); u.setName("小明"); u.setPassword("123"); session.update(u); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
测试结果:
Hibernate: update User set name=?, password=?, createTime=?, expireTime=? where id=?
虽然手动构造的Detached对象可以update到数据库,但是,万一有字段没setvalue就会更新空数据进DB,所以最好的做法还是操作一个从数据库get或load的对象,因为该对象已经填充了值。
测试Delete():
public void testDelete(){ Session session = HibernateUtils.getSession(); try { session.beginTransaction(); User u = (User)session.load(User.class, "402881e62ed40ec1012ed40ec2f10001"); session.delete(u); session.getTransaction().commit(); } catch (HibernateException e) { session.getTransaction().rollback(); e.printStackTrace(); }finally{ HibernateUtils.closeSession(session); } }
测试结果:
Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.password as password0_0_, user0_.createTime as createTime0_0_, user0_.expireTime as expireTime0_0_ from User user0_ where user0_.id=?Hibernate: delete from User where id=?
在删除一条记录的时候,操作的也是对象,所以把对象根据id用get或load获取到,然后session.delete(u);
小结:
transient状态的特征:
1. 在数据库中没有与之匹配的记录;
2. 没有纳入session的管理;
Persistent状态的特征:
1. 对象在数据库中有与之匹配的记录;
2. 纳入了session的管理;
3. 在清理缓存(脏数据检查)的时候,会和数据库同步(内存检查、清理缓存的目的是:看缓存发生有没有变化,从而判断到底发什么hql语句,eg:在save完了后,修改了对象,发了两条:一条insert一条update,这就是脏数据检查的好处);
Detached状态的特征:
1. 在数据库中有与之匹配的记录;
2. 没有纳入session的管理;
在实际应用中,我们经常需要根据表中的非id字段查询一个对象(一条记录),或者全部的数据,就要用到HQL。