本文共 9591 字,大约阅读时间需要 31 分钟。
Session是Hibernate的核心,我们在创建了SessionFactory对象后,有两种方式可以获取Session实例
openSession();
采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session实例,并且在使用完成后需要调用close方法进行手动关闭。getCurrentSession();
而getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。我们通过Session开启事务,获取Transaction
对象
//开启事务Transaction transaction = session.beginTransaction();// 提交事务transaction.commit();// 回滚事务transaction.rollback();
Session执行完数据库操作后,要使用Transaction接口的commit()方法进行事务提交,才能真正的将数据操作同步到数据库中。发生异常时,需要使用rollback()方法进行事务回滚,以避免数据发生错误。
由于Session对象属于线程不安全,当一个线程有多个Session时,无法保证事务。所以一个线程要保障只有一个Session对象。
为了保证一个线程中只有一个Session,所以在实际开发中,我们将Session绑定到当前线程中
true
(1)实体表和映射文件
Customer.javapublic class Customer { private Long custId;// 客户编号 private String custName;// 客户名称 private String custSource;// 客户信息来源 private String custIndustry;// 客户所属行业 private String custLevel;// 客户级别 private String custAddress;// 客户联系地址 private String custPhone;// 客户联系方式 //一对多关系映射:一个客户可以对应多个联系人 private Setlinkmans = new HashSet (); ......}
Customer.hbm.xml
LinkMan.java
public class LinkMan implements Serializable { private Long lkmId;//联系人编号(主键) private String lkmName;//联系人姓名 private String lkmGender;//联系人性别 private String lkmPhone;//联系人办公电话 private String lkmMobile;//联系人手机 private String lkmEmail;//联系人邮箱 private String lkmPosition;//联系人职位 private String lkmMemo;//联系人备注 private Customer customer;//对应联系人表中的外键 ......}
LinkMan.hbm.xml
(2)dao类
public class CustomerDao { /** * 根据id查询客户信息 get立即加载 */ public static Customer findOneById(long id){ Session session = null;//Session对象 try{ // 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.get(Customer.class, id); return newCustomer; }catch(Exception e){ e.printStackTrace(); }finally{ session.close();//释放资源 } return null; } /** * 根据id查询客户信息 load延迟加载 */ public static Customer loadOneById(long id){ Session session = null;//Session对象 try{ // 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); return newCustomer; }catch(Exception e){ e.printStackTrace(); }finally{ session.close();//释放资源 } return null; }}
(3)工具类获取session
HibernateUtilpublic class HibernateUtil { private static SessionFactory factory; // 使用静态代码块获取SessionFactory static { //细节:Hibernate把所有可预见的异常,都转成了运行时异常(工具中不会提示要添加异常块) try { // 加载hibernate.cfg.xml配置文件 Configuration config = new Configuration().configure(); factory = config.buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取一个新的Session对象(每次都是一个新对象) * @return */ public static Session openSession(){ return factory.openSession(); } /** * 获取一个新的Session对象(每次都是一个新对象) * @return */ public static Session getCurrentSession(){ return factory.getCurrentSession(); }}
(4)测试get方法
@Testpublic void testGet(){ Customer oneById = CustomerDao.findOneById(1L); System.out.println("-------------------------"); System.out.println(oneById);}
测试结果如下:
我们发现确实有执行sql,但最后还是报错了,报错的位置在Customer的toString方法
中,在获取Customer中的linkmans属性时报错,这是因为在执行get方法
时,查询的sql,只针对于cst_customer表(等于select * from cst_customer where cust_id = ?),所以不会填充linkmans属性,而在CustomerDao方法的最后,我们手动关闭了session
,所以在test方法中,再想通过session去懒加载就加载不了了 解决方式:
public static Customer findOneById(long id){ Session session = null;//Session对象 try{ session = HibernateUtil.openSession(); Customer newCustomer = session.get(Customer.class, id); System.out.println("---------------------------------"); System.out.println(newCustomer); return newCustomer; }catch(Exception e){ e.printStackTrace(); }finally{ session.close();//释放资源 } return null;}
(5)测试load方法
@Testpublic void tesLoad(){ Customer customer = CustomerDao.loadOneById(1L); System.out.println(customer);}
执行结果:我们发现这次连sql语句都没有,报错的情况也是no session,所以load方法获取的对象,也是在使用时才去执行sql
当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。public static Customer loadOneById(long id){ Session session = null;//Session对象 try{ // 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); System.out.println(newCustomer.getCustId()); return newCustomer; }catch(Exception e){ e.printStackTrace(); }finally{ session.close();//释放资源 } return null;}
这个查询结果也证明了刚刚说的话
解决方式:和get方法一样,在session未关闭前,使用他们 可以使用 Hibernate.initialize(xx);方法,不过这个方法需要对类中的属性对象也要使用public static Customer loadOneById(long id){ Session session = null;//Session对象 try{ // 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); Hibernate.initialize(newCustomer); Hibernate.initialize(newCustomer.getLinkmans()); return newCustomer; }catch(Exception e){ e.printStackTrace(); }finally{ session.close();//释放资源 } return null;}
总结:
Hibernate的一级缓存就是指Session缓存
在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。(session关闭,缓存消失)
Hibernate的一级缓存的作用就是减少对数据库的访问次数(降低IO读写次数)。
Hibernate的一级缓存有如下特点:
存储的数据必须都是持久态数据
@Testpublic void testCache(){ //从Hibernate封装的工具类中获取Session对象 Session session=HibernateUtil.openSession(); //开启事务 Transaction tx=session.beginTransaction(); //第一次执行get方法:一级缓存中无数据,会去数据库中查询 Customer c1=session.get(Customer.class, 100L); System.out.println("One : "+c1); //第二次执行get方法:一级缓存中有数据,直接获取缓存中的数据Customer c2=session.get(Customer.class, 100L); System.out.println("Two : "+c2); System.out.println(c1==c2);//结果为true tx.commit(); session.close();}一级缓存执行过程 一级缓存的特性:快照机制
public void testUpdateName(){ //从Hibernate封装的工具类中获取Session对象 Session session=HibernateUtil.openSession(); //开启事务 Transaction tx=session.beginTransaction(); //第一步:先查询出客户信息(根据ID查询) Customer c=session.get(Customer.class, 95L); //第二步:对查询出的客户实体进行修改(修改名称) c.setCustName("传智.黑马程序员"); //第三步:调用Hibernate方法实现更新操作 //session.update(c); //常规方式是要调用update方法,但此处省略看看执行结果 tx.commit(); session.close();}解释:以上java程序中没有直接调用update方法,同样也对数据修改成功。主要是借助了Hibernate的快照功能
Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。
结论:Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
在 Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定 Session 管理方式, 可选值包括:
thread
获取当前线程的session
public static Session getCurrentSession(){ return factory.getCurrentSession();}
cst_customer表:表中cust_id并非主键,只是不能为null
Customer.hbm.xml,配置中custId为主键,且生成策略为native(使用本地数据库的策略)此时当我们在保存对象时:
public static void findOne(Customer c){ Session session = null;//Session对象 Transaction tx = null ;//事务对象 try{ //使用Hibernate工具类获取Session对象 session = HibernateUtil.openSession(); tx = session.beginTransaction();//开启事务 //保存客户 session.save(c);//save方法返回实体保存后的主键值 //提交事务 tx.commit(); }catch(Exception e){ tx.rollback();//事务回滚 e.printStackTrace(); }finally{ session.close();//释放资源 }}
执行报错,我们看执行的sql,sql中并没有cust_id的值,而表中cust_id的值不能为空,所以报了这个错
那么将表中cust_id改为可以为空,能成功吗 还是报错, 所以我们在开发中,一定要保证表中字段的生成策略和映射中一致转载地址:http://zwnjn.baihongyu.com/