Hibernate-初学小结

Hibernate初学小结

前言:

       之前学习使用过Mybatis这个ORM持久层框架,使用配置文件替代了复杂的JDBC操作;使用它对开发的便利性不言而喻。之前听老师说过Hibernate是个自动化的ORM框架,且开发初衷就是为了让不会写sql的程序员也能走上开发,但现在主流的都转向了MyBatis。所以当时并没有想去学Hibernate,但最近我们在学SpringBoot敏捷开发这个框架,学到了SpringDataJPA-Hibernate的接口,因此,不得不去学习一下。(初学之后,真香~)

正文开始:

       首先,ORM是对象关映射;与MyBatis类似,Hibernate也是以XML配置文件来代替Java的dao层接口及其实现类;既然是对象关系映射,就先来谈谈实现这个映射:

实体对象应满足的条件:

        1:属性私有
        2:get/set方法
        3:要有一个唯一值(一般为ID)
        4:实体类属性不建议用基本数据类型,而用其对应的包装类 因为int不能为null,在表示学生没有参加考试时无法表示。

实体类对应的映射xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
<hibernate-mapping>
 <!--1配置实体类与表关联 -->
 <class name="com.qwx.entity.User" table="t_user">
 <!--2属性与字段相关联 
  要求实体类有个唯一属性值对应表的主键
  要求有字段作为唯一值
  ***************若无主键的表,要采用联合主键
  -->
  <id name="uid" column="uid">
  <!--设置库表id增长策略
   increment:用于long,int.short,增量为一自增,不能用在集群环境  ==mysql identity:采用底层数据库本身提供的主键生成标识符,条件是数据库支持自增长
   sequence:Hibernate根据底层数据库序列生成标识符,条件是数据库支持序列 ====oracal
   native:生成id值就是自动增长  根据底层数据库对自动生成标识符的能力来选择identity,sequence,hilo的一种 
   uuid:采用128位的UUID算法生成标识符,Hibernate为我们自动生成UUID
   hilo:通过hi/lo算法实现主键生成 机制,需要额外的数据库表或字段提供高位值来源
    -->
   <generator class="native"></generator>
  </id>
  <!--配置其他属性     column不屑则默认与name相同     type属性可不写,库表会自动与实体类对应 --> 
  <property name="username" column="username"></property>
  <property name="password" column="password"></property>
  <property name="address" column="address"></property>
 </class>
</hibernate-mapping>
Hibernate的全局配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
 "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
 <!--第一部分:数据库连接部分      必须的  -->
 <session-factory>
  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.url">jdbc:mysql:///hibernate1</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.connection.password">admin</property>
  
 <!--第二部分:配置hibernate信息      可选的  -->
  <!--输出底层sql  -->
  <property name="hibernate.show_sql">true</property>
  <property name="hibernate.format_sql">true</property>
  <!--hibernate帮创建表,update:如果有表,更新,没表,创建  -->
  <property name="hibernate.hbm2ddl.auto">update</property>
  <!--数据库方言  -->
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="connection.characterEncoding">utf8</property>
  <!--session绑定本地线程   底层时ThreadLocal  -->
  <property name="hibernate.current_session_context_class">thread</property>
 <!--第三部分:连接局部    必须  -->
  <!--引入映射文件  -->
  <mapping resource="com/qwx/entity/User.hbm.xml"></mapping>
  
 <!--Hibernate默认支持c3p0  -->
  <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
  <!--连接池中的连接耗尽时,Hibernate应该向数据库一次申请多少连接  -->
  <property name="hibernate.c3p0.acquire_increment">2</property>
  <!--连接池中最大连接数  -->
  <property name="hibernate.c3p0.max_size">20</property>
  <!--指定缓存池中缓存多少statement对象  -->
  <property name="hibernate.c3p0.max_statements">100</property>
  <!--连接池中最小连接数  -->
  <property name="hibernate.c3p0.min_size">2</property>
  <!--数据库连接超时就从数据库中销毁  -->
  <property name="hibernate.c3p0.timeout">5000</property>
  <!--检测数据库多久进行一次对象超时检测,检测后由该线程去销毁timeout中的连接。    以秒为单位  -->
  <property name="hibernate.c3p0.idle_test_period">3000</property>
 </session-factory>
</hibernate-configuration>
看看Hibernate怎么执行插入操作:
@Test
public void testAdd() {
  // 1加载hibernate配置文件    
   //hibernate封装了configuration对象   自动到src下找到名称是hibernate.cfg.xml
  Configuration cfg = new Configuration().configure();
  // 2创建SessionFactory
   //读取hibernate核心配置文件内容,创建sessionFactory-----可以维护二级缓存
   //在此过程中,根据黑心配置文件中局部配置文件的映射关系创建库表
   //因为要创建表,因此创建sessionFactory的过程特别损耗资源-----因此一个项目创建一个	sessionFactory对象----写工具类,静态代码块--类加载时执行,且值执行一次
   //其中包含着当前数据库的配置信息和所有的映射关系及预定义的SQL语句,同时负责维护Hibernate的二级缓存,一个sessionFactory对应一个数据库。
   //sessionFactory是重量级的,因此不能随意创建或销毁其实例。因此应在应用启动后加载完成。
   //sessionFactory需要一个较大的缓存,用于存放预定义的SQL语句和实体的映射信息,另可配一个缓存插件,这个插件称之为Hibernate的二级缓存,被多线程共享,这个插件可以是redis
  SessionFactory sessionFactory = cfg.buildSessionFactory();
  /*SessionFactory sessionFactory = HibernateUtil.getSessionFactory();*/
  // 3使用sessionFactory创建Session对象
   //类似于连接 Connection()
   //调用session中的不同方法实现crud操作
   //单线程对象,是Hibernate的运作中心
   //每个session都有自己的缓存,被称为Hibernate的一级缓存
   //每个操作对应一个session,需要及时关闭
  Session session = sessionFactory.openSession();
  // 4开启事务
   //Transaction代表一个原子操作,所有持久层都应在事务管理下操作,读操作也一样
   //四个特性:原子性,一致性,隔离性,持久性
   //原子性:所有操作不可再分,全部成功或全部失败
   //一致性:操作前后数据要一致,转账
   //隔离性:多个事务同时进行一个操作,相互不影响    不考录隔离性产生的问题:脏读,虚读,不可重复读     解决方式:设置事务隔离级别:MySQL默认隔离级别:repeatable read
   //持久性:操作commit后最终会生效(产生影响)
  Transaction tx = session.beginTransaction();
  // 5写具体CRUD操作
   //添加功能---调用session的save()
  User user=new User();
  user.setAddress("japen");
  user.setPassword("123");
  user.setUsername("jack");
   //调用session方法实现添加
  session.save(user);
  // 6提交事务
  tx.commit();
  // 7关闭资源
  session.close();
  //web应用中不用关闭,整个应用对应着一个sessionFactory
  sessionFactory.close();
  }
根据ID查询:

在这里引入Hibernate通过session管理Java对象的状态,在持久化生命周期中,Java对象的三种状态:
        1.持久状态( Persistent):与session有关联且有唯一标识ID —一般用于save操作

session.save(user);

        2.瞬时状态( Transient):与session没有关联且没用到唯一标识ID (对象由new操作符创建,且尚未与Hibernate Session关联的对象,该对象不会被持久化到数据库,也不会被授予持久化标识,程序中失去了对瞬时态对象的引用,则它将会被垃圾回收。使用Hibernate Session可将其变为持久态)

User user=new User();

        3.游离状态(托管态)( Detached): 用到唯一标识ID但与session没有关联 —update操作

User user=session.getUser(User.class,1);
@Test
public void testGetOne() {
  //读hibernate.cfg.xml的配置
  Configuration configure = new Configuration().configure();
  //联系到局部文件进行建表
  SessionFactory sessionFactory = configure.buildSessionFactory();
  //类似于打开连接
  Session session = sessionFactory.openSession();
  //开启事务
  Transaction tx = session.beginTransaction();
  //根据id查询
  User user = session.get(User.class, 2);
  System.out.println(user);
  //提交事务
  tx.commit();
  //关闭资源
  session.close();
  sessionFactory.close();
  }
执行update操作:

这里涉及到了Hibernate的一级缓存:
1.数据库本身就是文件操作系统,使用IO流操作文件效率不高----》数据存到内存,不使用流,直接读取内存数据
2.Hibernate缓存就是其优化方式:
2.1: 一级缓存默认打开
2.2:一级缓存使用范围(生命周期):session范围中 (从session创建到close)
2.3:一级缓存中存储数据必须是持久态数据
2.4:以内存为存储介质
2.5:介绍:一级缓存就是session(事务级)缓存,就是内存中存放相互关联的Java对象的一块内存空间。
       >session缓存是事务级缓存,伴随着事务的开启和关闭。
       >session缓存由hibernate管理,由hibernate内置,不能被取消。
       >只要使用hibernate就一定会用session缓存。
       >当调用session的load(),get(),save(),update()等时,hibernate会对实体对象进行缓存
       >当调用session的get()或load()时,会先到缓存中读,没有再访问DB,提高hibernate的使用效率。
3.Hibernate的二级缓存
3.1:目前不用了,用redis技术代替
3.2:二级缓存默认关闭,需配置
3.3:二级缓存使用范围:sessionFactory中 (整个项目中)
3.4:以内存或硬盘为存储介质
3.5:介绍:二级缓存就是sessionFactory(应用级)缓存,是个可配置的第三方插件,hibernate默认是Ehcache。
    二级缓存又分两种:
       >内置缓存:Hibernate内置,通常在hibernate初始化阶段存放映射元数据和提前定义好的SQL语句,该内置缓存只读。
       >外置缓存:通常说的二级缓存也就是这个外置缓存,默认hibernate不会启用该插件,外置缓存中的数据就是数据库中数据的复制。
   适合放到二级缓存的数据:
       >1.修改频率低,2.不是很重要且允许偶尔出现并发,3.不会并发访问,4.参考性,只是用于展示或其他,等的数据。
   不适合放到二级缓存的数据:
       >1.经常被修改,2.绝对不允许并发(eg:财务型),3.与其他应用共享 ,等的数据
       
       
缓存的作用:
       1.减少访问数据库的频率。
       2.保证缓存中的对象与数据库的记录保持同步,当缓存中的对象改变后,session不会立即执行sql,而是将多个sql合并为一条SQL进行执行,提高效率。
       3.如果对同一个属性修改了多次,Hibernate的缓存不是执行多个update()语句,而是合并修改语句,以最后一个更新为准而发送一条最新的sql;

其工作原理大致就是:
       只有持久态数据会产生一级缓存,一级缓存会默认对应一个快照区(副本)。
       当使用get()时,会首先把数据存到session中,第二次get()时,到session中寻找id对应的数据,而这样可能就会造成数据库数据与一级缓存中的数据不一致,因此,session提供了flush().refresh(),clear();来保证数据一致性;

         flush():如果数据库中数据和缓存区的数据不一致,则发送sql语句,去修改数据库,以保证一致性。此方法会创建update语句不自动commit()即不会自动持久化到数据库,缓存数据会刷新,等待commit()真正持久化到数据库(commit会自动调用flush() )平常不使用,适用于批量操作,比如要执行两千条sql,为防止内存溢出,在事务commit()之前flush();(以缓存区为主导)

         refresh():如果缓存区中的数据和数据库中的数据不一致,则强制使用查询语句(get())强制刷新缓存区,以保证数据一致性。(以数据库为主导)

         clear():直接清空session缓存,再发起sql语句重新写入缓存区,直接了当保证数据一致性。

       在使用saveorupdate()或update时,会先修改一级缓存而不会修改快照区,当commit()时,Hibernate会将一级缓存和快照区对比,一致则不执行提交操作,不一致才执行。
       正因为此,update()操作不必显式调用update():获取一个持久态数据,调用实体对象的set(),最后commit();代码如下:

  --------获取sessionfactory
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //id查询
   //持久态对象不用调用update()
  User user = session.get(User.class, 6);
  //设置返回对象值
  user.setUsername("lilei");
  //调用方法实现
  
  tx.commit();
  --------关闭资源--------
get()和load()操作:

load:

  Person person= session.load(Person.class, 1);
  System.out.println(person);

get:

  Person person= session.get(Person.class, 1);
  System.out.println(person);

区别在于:
        当数据库的ID不存在时,load会抛异常,而get则返回null
        get()方法调用后会立即到数据库加载对象,load()调用后只有当使用到对象时才到数据库加载数据
        get()返回的是对象所处类的本身,load()返回的是Hibernate代理对象。

update()操作:
@Test
 public void testUpdate() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //修改操作
   //修改uid=2记录username的值
   //1.先查出来
  User user = session.get(User.class, 8);
   //2.向返回的user对象设置要修改的值 执行过程:先到user对象找uid的值,根据其修改
  user.setUsername("东方不败");
   //3。调用session的update       持久态会自动更新数据库
   //****持久态对象可不用调用update方法   set方法会修改一级缓存但不+会修改一级缓存对应的快照区    最后提交事务时将一级缓存与快照区比较是否一致,不一致就将一级缓存数据提交数据库,否则不提交;


  //session.update(user);	游离态--->持久态
  
  tx.commit();
  session.close();
  sessionFactory.close();
  }
delete()操作:

注意:删除后,对象变为瞬时态。是可以直接save()操作转化为持久态的,但其数据已经没了,这就是问题;解决办法:在Hibernate的配置文件中设置hibernate.use_identifer_rollback属性为true,这样删除后对象的)OID直接为null,就不会再被save()这种错误操作。

@Test
 public void testDelete() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //删除操作
   //第一种:先查再删
  User user = session.get(User.class, 4);
  session.delete(user);
   //第二种:赋id值删
  /*User user=new User();
  user.setUid(3);
  session.delete(user);*/
  
  
  tx.commit();
  session.close();
  sessionFactory.close();
  }
evict():从session中把指定对象移除
User user=new User(User.class,2);
session.evict(user);//此时user变为游离态。
doWork():提供了JDB连接数据库的方式,普遍用于执行存储过程等程序
@Test
public void doWork(){
  session.doWork(new Work(){
    public void execute(Connection connecion) throws SQLException{
    	connection.prepareStatement(sql);//调用sql
    	connection.prepareCall(sql);//调用存储过程
    }
});
}
save操作:
@Test
public void testSave(){
  User user=new User();
  user.setAge(12);
  user.setName("张三");
  //在save()方法之前设置主键ID无效,save()自动忽略。ID取决于自增值。(配置文件中的主键增长策略由数据库自主选择)
  //要自己设置主键ID,配置文件的主键增长策略要改为assigned;
  user.setUid(10);
  session.save();
  //user.setId(11);  save()之后修改id会抛异常
  session.commit();
}

save()和persist()的区别:
1.在persist()执行之前set了id,persist()会抛异常,不像save()自动忽略。

saveOrUpdate()操作:

set主键(游离态)则是 update()
没有set主键(瞬时态)则是save()
注意:若对象的OID不为null,但表中还没有与其相对应的记录,会抛异常。
OID的值等于局部配置文件中id的unsaved-value属性的值,也被认为是游离态。

@Test
 public void testSaveOrUpdate() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //瞬时态的实体(有session关联,没有主键ID) ---->添加save操作
  /*User u=session.get(User.class,2);
  u.setAddress("das");
  u.setPassword("rop");
  u.setUsername("oooo");*/
  
  //托管态的实体(有主键ID,没有session关联) ---->修改update操作
  /*User u=new User();
  u.setUid(5);
  u.setAddress("阿尔巴尼亚");
  u.setPassword("1313");
  u.setUsername("rose");*/
  
  //持久态的实体(有session关联且有ID) ---->修改update操作
  User u = session.get(User.class, 6);
  u.setAddress("你家");
  
  session.saveOrUpdate(u);
  
  tx.commit();
  session.close();
  sessionFactory.close();
  }
Query()操作:
@Test
 public void testQuery() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //1:创建Query对象
   //方法中写hql语句
  Query query = session.createQuery("from User");
  //2:调用Query对象中的方法得到结果
  List<User> list = query.list();
  for (User user : list) {
   System.out.println(user);
  }
  
  tx.commit();
  session.close();
  sessionFactory.close();
 }
Criteria()操作:

这种查询方式把查询条件封装为一个Criteria对象。在实际应用中,使用Session的createCriteria()方法构建一个org.hibernate.Criteria实例,然后把具体的查询条件通过Criteria的add()方法加入到Criteria实例中。这样,程序员可以不使用SQL甚至HQL的情况下进行数据查询,

@Test
 public void testCriteria() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  //创建对象
  Criteria criteria = session.createCriteria(User.class);
  criteria.add(Restrictions.eq("username", "jack"));//等价于where username=’jack’
  //调用方法
  List<User> list = criteria.list();
  for (User user : list) {
   System.out.println(user);
  }

  tx.commit();
  session.close();
  sessionFactory.close();
 }
SQLQuery()操作:
@Test
 public void testSQLQuery() {
  Configuration configure = new Configuration().configure();
  SessionFactory sessionFactory = configure.buildSessionFactory();
  Session session = sessionFactory.openSession();
  Transaction tx = session.beginTransaction();
  
  NativeQuery SQLQuery = session.createSQLQuery("select * from t_user");
  SQLQuery.addEntity(User.class);
  List<User> list = SQLQuery.list();
  for (User user : list) {
   System.out.println(user);
  }
  
  /*List list = SQLQuery.list();//默认数组形式
  for (Object object : list) {
   System.out.println((object));
  }*/
  
  tx.commit();
  session.close();
  sessionFactory.close();
  }
}
HQL 单表查询的10种常用方法
/**
	HQL单表查询:
	1.全表查询	2.别名查询	3.条件(占位)查询	4.条件(具名)查询	5.对象查询
	6.分页查询	7.查询排序	8.命名查询	9.投影查询	10.聚合查询
*/
public class HQLTest {
 Transaction tx =null;
 Session session =null;
 SessionFactory sessionFactory=null;
 
 @Before
 public void before() {
  Configuration configure = new Configuration().configure();
  sessionFactory = configure.buildSessionFactory();
  session = sessionFactory.openSession();
  tx = session.beginTransaction();
 }
 
 @After
 public void after() {
  tx.commit();
  session.close();
  sessionFactory.close();
 }
 
 
 /**   
  * @param:        1.全表查询 
  */
 @Test
 public void fullQueryTest() {
  String hql="from BuyOrder";
  Query<BuyOrder> query = session.createQuery(hql,BuyOrder.class);
  List<BuyOrder> list = query.list();
  for (BuyOrder o : list) {
   System.out.println(o.getOrderName());
  }
 }
 
 
 /**   
  *   2.别名查询
  */
 @Test
 public void alisaQueryTest() {
  String hql="select bo from BuyOrder as bo";
  List<BuyOrder> list = session.createQuery(hql,BuyOrder.class).list();
  for (BuyOrder o : list) {
   System.out.println(o.getOrderName());
  }
 }
 
 /**   
  *   3.条件(占位)查询
  */
 @Test
 public void conditionQueryTest() {
  String hql="from BuyOrder where orderId=?0"; //5.0之后在?后加对应位。 (jpaStyle)
  List<BuyOrder> list = session.createQuery(hql,BuyOrder.class)
    .setParameter(0 , 1)  //设置占位符
    .list();
  
  for (BuyOrder o : list) {
   System.out.println(o.getOrderName());
  }
 }
 
 
 /**   
  *   4.具名查询
  */
 @Test
 public void namedQueryTest() {
  String hql="from BuyOrder where orderId= :orderId";   //与占位查询对比
  List<BuyOrder> list = session.createQuery(hql,BuyOrder.class)
    .setParameter("orderId" , 1)  //设置占位符
    .list();
  
  for (BuyOrder o : list) {
   System.out.println(o.getOrderName());
  }
 }
 
 
 /**   
  *   5.对象查询   将对象入参,传入指定ID即可查询
  *   实际还是按照指定字段查询,其内部将对象转化为具体的字段
  */
 @Test
 public void objectQueryTest() {
  BuyOrder order = new BuyOrder();
  order.setOrderId(1);
  String hql="from OrderDetail where buyOrder= :orderJU";   
  List<OrderDetail> list = session.createQuery(hql,OrderDetail.class)
    .setParameter("orderJU" , order)  //设置传入的对象
    .list();
  
  for (OrderDetail od : list) {
   System.out.println(od.getGoodsName());
  }
 }
 
 
 /**   
  *   6.分页查询
  */
 @Test
 public void pageQueryTest() {
  String hql="from OrderDetail";   
  Query<OrderDetail> query = session.createQuery(hql,OrderDetail.class);
  //当前页,每页显示的行;
  int pageNo=2;
  int pageSize=1;
  query.setFirstResult((pageNo-1)*pageSize);//起始行数
  query.setMaxResults(pageSize); //查几条
  List<OrderDetail> list = query.list();
  for (OrderDetail od : list) {
   System.out.println(od.getGoodsName());
  }
 }
 
 /**   
  *   7.查询排序
  */
 @Test
 public void sortQueryTest() {
  //排序的是属性不是列
  String hql="from OrderDetail order by detailId desc";   
  Query<OrderDetail> query = session.createQuery(hql,OrderDetail.class);
  //当前页,每页显示的行;
  int pageNo=1;
  int pageSize=3;
  query.setFirstResult((pageNo-1)*pageSize);//起始行数
  query.setMaxResults(pageSize); //查几条
  List<OrderDetail> list = query.list();
  for (OrderDetail od : list) {
   System.out.println(od.getGoodsName());
  }
 }
 
 
 /**   
  *   8.命名查询
  *   在局部配置文件.hbm.xml中配置:
  *   <query name="tst"><![CDATA[select o from OrderDetail as o where detailId<2]]></query>
  *   CDATA用于转义xml标签
  */
 @Test
 public void nameQueryTest() {
  //用createNamedQuery();
  Query<OrderDetail> query = session.createNamedQuery("tst",OrderDetail.class);
  List<OrderDetail> list = query.list();
  for (OrderDetail o : list) {
   System.out.println(o.getGoodsName());
  }
 }
 
 
 /**   
  *   9.投影查询(只查询部分字段)
  *   在hql中的new需要在实体类提供与之对应的有参构造,同时无参构造也不能忘
  *   好处:将返回结果封装到一个类
  */
 @Test
 public void projectorQueryTest() {
  String hql="select new OrderDetail(o.goodsName) from OrderDetail as o";   
  Query<OrderDetail> query = session.createQuery(hql,OrderDetail.class);
  List<OrderDetail> list = query.list();
  for (OrderDetail o : list) {
   System.out.println(o.getGoodsName());
   System.out.println(o.getBuyOrder());//结果为null
  }
 }
 
 
 /**   
  *   10.聚合查询
  */
 @Test
 public void polymerQueryTest() {
  String hql="select o.buyOrder.orderId, count(1),sum(o.price),avg(o.price) from OrderDetail as o group by o.buyOrder.orderId";   
  Query query = session.createQuery(hql);
  List<Object[]> list = query.list();
  for (Object[] o : list) {
   System.out.println(Arrays.asList(o));
  }
  
 }
 }
多表查询:(内连接和迫切内连接)
public class BuyOrder {
	 private Integer orderId;
	 private String orderName;
	 private BigDecimal totalPrice;
	 private Set<OrderDetail> orderDetail=new HashSet<OrderDetail>();

	get/set....
}

public class OrderDetail {
	 private Integer detailId;
	 private String goodsName;
	 private Integer count;
	 private BigDecimal price;
	 
	 private BuyOrder buyOrder;

	get/set...
}
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2019-5-5 11:25:40 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.qwx.hql.BuyOrder" table="BUYORDER">
        <id name="orderId" type="java.lang.Integer">
            <column name="ORDERID" />
            <generator class="native" />
        </id>
        <property name="orderName" type="java.lang.String">
            <column name="ORDERNAME" />
        </property>
        <property name="totalPrice" type="java.math.BigDecimal">
            <column name="TOTALPRICE" />
        </property>
        <set name="orderDetail" table="ORDERDETAIL" inverse="true">
            <key>
                <column name="ORDERID" />
            </key>
            <one-to-many class="com.qwx.hql.OrderDetail" />
        </set>
    </class>
    <query name="tst"><![CDATA[select o from OrderDetail as o where detailId<2]]></query>
</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">
<!-- Generated 2019-5-5 11:25:40 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
    <class name="com.qwx.hql.OrderDetail" table="ORDERDETAIL">
    <!--缓存当前类(二级)  -->
    
    <cache usage="read-only"/>
    <id name="detailId" type="java.lang.Integer">
            <column name="DETAILID" />
            <generator class="native" />
     </id>
     <property name="goodsName" type="java.lang.String">
            <column name="GOODSNAME" />
     </property>
     <property name="count" type="java.lang.Integer">
            <column name="COUNT" />
     </property>
     <property name="price" type="java.math.BigDecimal">
            <column name="PRICE" />
     </property>
     <many-to-one name="buyOrder" class="com.qwx.hql.BuyOrder">
            <column name="ORDERID" />
     </many-to-one>
  </class>
</hibernate-mapping>
public class HQLTest {
 Transaction tx =null;
 Session session =null;
 SessionFactory sessionFactory=null;
 
 @Before
 public void before() {
  Configuration configure = new Configuration().configure();
  sessionFactory = configure.buildSessionFactory();
  session = sessionFactory.openSession();
  tx = session.beginTransaction();
 }
 
 @After
 public void after() {
  tx.commit();
  session.close();
  sessionFactory.close();
 }
 
 
/**   
  *   内连接
  */
 @Test
 public void innerjoinQueryTest() {
  //在使用多表连接时,如果不采用fetch,则只能够采用List<Object[]>接收
  //最好访问实体类内的关系属性
  String hql="from BuyOrder as b inner join b.orderDetail";   
  List<Object[]> list= session.createQuery(hql).list();
  for (Object[] o : list) {
   //可使用数组小标获取数据
   BuyOrder order=(BuyOrder)o[0];
   OrderDetail orderDetail=(OrderDetail)o[1];
   System.out.println("订单:"+order.getOrderName()+",订单明细:"+orderDetail.getGoodsName());
  }
  /*for (Object[] objects : list) {
   //循环执行三次
   BuyOrder order=(BuyOrder)objects[0];
   //没有使用fetch时,若采用一个对象去访问另一个对象,由于默认懒加载,会触发多次SQL。因此不建议
   Set<OrderDetail> orderDetail = order.getOrderDetail();
   for (OrderDetail o : orderDetail) {
    System.out.println(o.getGoodsName());
   }
  }*/
 }
 

/**   
  *   迫切内连接
  *  不是懒加载,会减少SQL的创建与发送
  *  推荐使用fetch
  */
 @Test
 public void innerjoinFetchQueryTest() {
  //最好访问实体类内的关系属性
  String hql="from BuyOrder as b inner join fetch b.orderDetail";   
  List<BuyOrder> list= session.createQuery(hql).list();
  for (BuyOrder order : list) {
   //查询使用fetch时,不会触发多次SQL
   Set<OrderDetail> orderDetail = order.getOrderDetail();
   for (OrderDetail od : orderDetail) {
    System.out.println(od.getGoodsName());
   }
  }
 }
 }
本地SQL查询:
public class HQLTest {
 Transaction tx =null;
 Session session =null;
 SessionFactory sessionFactory=null;
 
 @Before
 public void before() {
  Configuration configure = new Configuration().configure();
  sessionFactory = configure.buildSessionFactory();
  session = sessionFactory.openSession();
  tx = session.beginTransaction();
 }
 
 @After
 public void after() {
  tx.commit();
  session.close();
  sessionFactory.close();
 }
@Test
 public void localSQLQuery() {
  String sql="select * from buyorder where TOTALPRICE>?";
  NativeQuery<BuyOrder> query = session.createNativeQuery(sql, BuyOrder.class);
  //传参时,使用?做占位符,从1开始。
  //也可以用JPA-Style形式,?0
  //还可以使用具名形式,:属性名
  query.setParameter(1, 100);
  List<BuyOrder> list = query.list();
  System.out.println(list);
  }

 @Test
 public void localInsert() {
  String sql="insert into buyorder (ORDERNAME,TOTALPRICE) values(?,?)";
  NativeQuery query = session.createNativeQuery(sql)
    .setParameter(1, "美的")
    .setParameter(2, new BigDecim##al(100));
  //返回影响行数
  int n = query.executeUpdate();
  System.out.println("影响行数:"+n);
  }
 }
Hibernate组件关系映射:

业务场景:两个实体对象映射在一张表中,B对象作为A对象的一部分(AB对象为组合关系)
需求:一辆Car,拥有name,color,wheel_count,wheel_size
关系:此时可抽取Car和Wheel两个对象:Wheel时Car的一部分,作为Car的组件。

public class Car {
 private Integer cid;
 private String cname;
 private String color;
 
 private Wheel wheel;

  getter...setter...
 }
public class Wheel {
 //组件表中没有id主键,且不生成映射文件
 private Integer count;
 private Integer size;
 }
<hibernate-mapping package="com.qwx.domain">
    <class name="Car" table="CAR">
        <id name="cid" type="java.lang.Integer">
            <column name="CID" />
            <generator class="native" />
        </id>
        <property name="cname" type="java.lang.String">
            <column name="CNAME" />
        </property>
        <property name="color" type="java.lang.String">
            <column name="COLOR" />
        </property>
        
        <!--不是one-to-many  -->
        <!--组件  -->
        <component name="wheel" class="Wheel">
          <!--属性  -->
         <property name="count" column="whell_count"></property>
         <property name="size" column="wheel_size"></property>
        </component>
    </class>
</hibernate-mapping>
Hibernate三种检索策略

       Hibernate的Session在加载一个Java对象时,可以将这个对象相关联的其他对象都加载到缓存中,以便程序及时调用。但是有时我们不需要加载太多无用对象到缓存。而那样会撑爆内存,且增加数据库的访问次数。因此为了合理利用缓存,hibernate提供了三种检索策略。
       1.立即检索:将被检索的对象,以及和这个对象相关联的一对多对象都加载到缓存中,如常用的session.get()。
              优点:1.频繁使用的关联对象内被加入到缓存,方便调用。
              缺点:1.占用内存        2.select语句过多
              适用范围:单表查询
       2.延迟加载:不会加载关联对象的内容,直到第一次调用关联对象时才加载关联对象。
         在不涉及关联类操作时,此种策略只适用于session的load()
         涉及关联类操作时,此种策略也能够适用于 get()和list()
         在类级别操作时,此种加载策略只加载类的OID不加载类的其他属性,只有当第一次访问时,才访问数据库加载内容(这里使用了CGLIB生成类的代理类)
         在关联级别操作时,此种策略只加载类本身不加载关联类,到调用时才加载,Hibernate默认采用延迟加载,需要显示指定使用延时加载策略,设置如下:

<class lazy="true">
<set lazy="true">extra(增强延迟)
<many-to-one proxy="proxy"> 或no-proxy

              优点:由程序决定加载那些类和内容,避免了大量无用SQL和内存消耗。
              缺点:在Session关闭后就不能访问内联类对象,要在session.close()方法前调用。
              适用范围:1.一对多或多对多关联
                               2.应用程序不需要立即访问或根本不会访问的对象。
       3.迫切左外连接检索:能够使用Sql的外连接查询,将需要加载的关联对象加载到缓存中,设置方法:

<set fetch="join">
<many-to-one fetch="join">

          优点:
              1.对应用程序完全透明,无论对象时持久态还是游离态,应用程序都可以方便的从一个对象导航到与其关联的对象。
              2.使用外连接,select语句减少
          ;缺点:
              1.可能会加载应用程序不需要访问的对象,浪费内存空间。
              2.复杂的数据表连接会影响检索性能
          适用范围:
              1.多对一或一对一关联。
              2.应用程序需立即访问到的对象。
              3.数据库系统有良好的表连接性能。

batch-size属性:无论是立即检索还是延迟检索,都可以指定关联查询的数量,以减少批量检索的数目条数
配置EhCache
  1. 在hibernate.cfg.xml中开启并指定jar路径
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property>
  1. 在某个类中声明开启
    1.在hibernate.cfg.xml中<mapping resource=“xxxx”》下面配置:
    不配置region属性则采用默认数据过期策略(defaultCache);而region属性的值是在ehcache.xml中配置的。

    	 **<class-cache usage="read-only" class="com.qwx.hql.OrderDetail"/**
    

    2’.在xxx.hbm.xml中配置:
    <cache usage=“read-only”/

  2. 测试代码:

@Test
 public void secondCacheTest() {
  OrderDetail orderDetail = session.get(OrderDetail.class, 1);
  System.out.println(orderDetail.getGoodsName());
  tx.commit();
  session.close();
  
  session=sessionFactory.openSession();
  tx=session.beginTransaction();
  //如果sql只执行一次,说明二级缓存配置成功。
  //数据检索路径:先到session中检索。如果没有,则去sessionFactory(二级)缓存中检索,如果还没有再发sql
  //因为第一次查出数据后session关闭,缓存消失,第二次重新打开session,重新调用get(),
  //先到session中检索,此时没有数据,到sessionFactory中,因为它是应用级的,且处于开启状态,因此会有数据。
  OrderDetail orderDetail2 = session.get(OrderDetail.class, 1);
  System.out.println(orderDetail2.getGoodsName());
  tx.commit();
  }

4.EhCache.xml配置文件解析
在这里插入图片描述
5.类中开启ehcache配置的二级缓存并发访问策略:
在这里插入图片描述

配置查询缓存:

当二级缓存要和HQL配合使用时,就需要配置查询缓存了:

  1. 在hibernate.cfg.xml中配置:
<property name="hibernate.cache.use_query_cache">true</property>
  1. 在使用HQL的session.createQuery();的对象.setCacheable(true);
Query<Order> createQuery=session.createQuery(hql,Order.class);
createQuery.setCacheable(true);
时间戳缓存区域:

       时间戳缓存区存放了对于查询结果相关的表进行插入,更新,删除的时间戳,
       hibernate通过时间戳缓存区来判断被缓存的查询结果是否过期,运行过程如下:
          1.T1时刻执行查询操作,结果放在QueryCache区域,记录该区域的时间戳为T1;
          2.T2时刻对查询结果对应的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域;
          3.T3时刻再次执行查询操作,先比较QueryCache的时间戳和UpdateTimestampCache的时间戳,若T2>T1,则丢弃原来存放在QueryCache区域的查询结果,重新到数据库查询结果,再把结果放到QueryCache缓存区;若T2<T1,直接从QueryCache中获得查询结果。
在这里插入图片描述

QBC检索 不常用
/**   
  *   QBC检索:不常用
  */
 @Test
 public void criteriaQuery() {
  //构造一个创建器
  CriteriaBuilder builder = session.getCriteriaBuilder();
  //创建查询语句体
  CriteriaQuery<BuyOrder> query = builder.createQuery(BuyOrder.class);
  //得到root节点
  Root<BuyOrder> root = query.from(BuyOrder.class);
  //指定需要返回的属性
  query.multiselect(root.get("orderName"));
  //需要封装条件
  Path<Object> path = root.get("orderId");
  //hibernate将所有sql封装为方法
  Predicate predicate1=builder.gt(root.get("totalPrice"), 100);
  Predicate predicate2= builder.equal(root.get("orderId"), 1);
  Predicate predicate3=builder.equal(root.get("orderName"), "京东订单");
  
  //where totalPrice>100 and orderId=1
  Predicate hqls = builder.and(predicate2,predicate1);
  //where totalPrice>100 and orderId=1 or orderName='京东订单'
  Predicate finals = builder.or(hqls,predicate3);
  //最终将所有的条件放入query
  CriteriaQuery<BuyOrder> express = query.where(finals);
  Query<BuyOrder> list = session.createQuery(express);
  
  
 }
附::Hibernate事务操作的规范写法:
@Test
 public void testTransaction() {
  SessionFactory sessionFactory =null;
  Session session =null;
  Transaction tx =null;
  try {
   /*sessionFactory = HibernateUtil.getSessionFactory();
   session = sessionFactory.openSession();*/
   //本地线程绑定的
   session = HibernateUtil.getCurrentThreadSession();
   tx = session.beginTransaction();
   
   User u=new User();
   u.setAddress("美国usa");
   u.setPassword("qwer");
   u.setUsername("xioasdd");
   
   session.save(u);
   
   tx.commit();
  } catch (Exception e) {
   tx.rollback();
   e.printStackTrace();
  }finally {
   session.close(); //当与本地线程绑定时就无需手动关闭session
   //sessionFactory.close();
  }
 }
附上文提及的工具类:
public class HibernateUtil {
 //创建SessionFactory
 //读取hibernate核心配置文件内容,创建sessionFactory
 //在此过程中,根据黑心配置文件中局部配置文件的映射关系创建库表
 //因为要自动创建表,因此创建sessionFactory的过程特别损耗资源-----因此一个项目创建一个sessionFactory对象----写工具类,静态代码块--类加载时执行,且值执行一次
 
 private static final Configuration cfg;
 private static final SessionFactory sessionFactory;
 
 static {
  cfg = new Configuration().configure();
  sessionFactory = cfg.buildSessionFactory();
 }
 
 public static SessionFactory getSessionFactory() {
  
  return sessionFactory;
 }
 
 
 //Hibernate绑定Session----》先在配置文件中配置
 //session是非线程安全的,要让其每次都访问本地线程,确保其线程的安全性
 public static Session getCurrentThreadSession() {
  
  return sessionFactory.getCurrentSession();
 }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值