Hibernate 框架学习

Hibernate 复习

  • Hibernate 复习
    • 基本演示
      • hibernatecfgxml
      • Customerjava
      • Customerhbmxml
      • 测试
    • API 学习
      • Configuration对象
      • SessionFactory对象
      • Session对象
    • hibernate实体规则
      • 实体类创建的注意事项
      • 主键类型
        • 自然主键少见
        • 代理主键常见
      • 主键生成策略
        • 代理主键
        • 自然主键
    • hibernate中的对象状态
      • 对象分为三种状态
    • hibernate进阶-一级缓存
    • hibernate中的事务
      • 事务特性
      • 事务并发问题
      • 事务的隔离级别
        • 在hibernate中指定数据库的隔离级别
        • 在项目中如何管理事务
          • 注意1
          • 注意2
    • hibernate中的批量查询概述
      • HQL查询-hibernate Query Language
        • 基本查询
        • 条件查询
        • 排序查询
          • 号占位符
          • 命名占位符
        • 分页查询
        • 统计查询
        • 投影查询
        • 多表查询
      • Criteria查询
        • 基本查询
        • 条件查询
        • 分页 查询
        • 设置查询总记录数
        • 排序
        • 离线查询
          • 非离线
          • 离线
      • 原生SQL查询
        • 基本查询
          • 返回数组List
          • 返回对象List
        • 条件查询
        • 分页查询
    • 多表查询
      • 一对多多对一
        • 关系表达
          • 表中的表达
            • 表中的表达
            • 实体中的表达
          • orm元数据中表达
            • 一对多
            • 多对一
        • 操作
          • 操作关联属性
        • 进阶操作
          • 结论
          • 关系维护
            • 删除客户
      • 多对多
        • 关系表达
          • 表中
          • 对象中
          • orm元数据
            • 注意
            • 结论
          • 操作
          • 操作进阶
            • inverse属性
            • cascade属性
    • 查询优化
      • 类级别查询
      • 关联级别查询
        • 集合策略
        • 关联属性策略
        • 结论
        • 批量抓取

基本演示:

hibernate创建的 java 工程,需要的 jar包:

hibernate.cfg.xml



<hibernate-configuration>
   <session-factory>

       
       
       <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driverproperty>
       
       <property name="hibernate.connection.url">jdbc:mysql:///hibernate_demoproperty>
       
       <property name="hibernate.connection.username">rootproperty>
       
       <property name="hibernate.connection.password">rootproperty>
       
       <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialectproperty>


       
       
       <property name="hibernate.show_sql">trueproperty>
       
       <property name="hibernate.format_sql">trueproperty>
       
       <property name="hibernate.hbm2ddl.auto">updateproperty>
       
       <mapping resource="com/inke/domain/Customer.hbm.xml"/>

   session-factory>
hibernate-configuration>

Customer.java

public class Customer {

    /*
     * CREATE TABLE `cst_customer` (
      `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
      `cust_name` VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
      `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
      `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
      `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
      `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '联系人',
      `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定电话',
      `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移动电话',
      PRIMARY KEY (`cust_id`)
    ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
     */
    private Long cust_id;

    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_linkman;
    private String cust_phone;
    private String cust_mobile;

    set/get ...

    @Override
    public String toString() {
        return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
    }
}

Customer.hbm.xml



   
   
<hibernate-mapping package="com.inke.domain" >
    
    <class name="com.inke.domain.Customer" table="cst_customer" >
        
        <id name="cust_id"  >
            
            <generator class="native">generator>
        id>
        
        <property name="cust_name" column="cust_name" >
            
        property>
        <property name="cust_source" column="cust_source" >property>
        <property name="cust_industry" column="cust_industry" >property>
        <property name="cust_level" column="cust_level" >property>
        <property name="cust_linkman" column="cust_linkman" >property>
        <property name="cust_phone" column="cust_phone" >property>
        <property name="cust_mobile" column="cust_mobile" >property>
    class>
hibernate-mapping>

测试

public class Test1 {

   private static SessionFactory sessionFactory;

   @Test
   public void test1() {
       Configuration configuration = new Configuration();
       configuration.configure();
       sessionFactory = configuration.buildSessionFactory();
       Session session = sessionFactory.openSession();
       Transaction transaction = session.beginTransaction();

       Customer customer = new Customer();
       customer.setCust_name("百度");
       session.save(customer);

       transaction.commit();
       sessionFactory.close();
   }
}



API 学习

Configuration对象

// Configuration功能: 配置加载类.用于加载主配置,orm元数据加载
public class Demo {

    @Test
    public void fun1(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration();
        //2 读取指定主配置文件 => 空参加载方法,加载src下的hibernate.cfg.xml文件
        conf.configure();
        //3 读取指定orm元数据(扩展),如果主配置中已经引入映射配置.不需要手动加载
        //conf.addResource(resourceName);
        //conf.addClass(persistentClass);

        //4 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();

    }
}

SessionFactory对象

// SessionFactory功能: 用于创建操作数据库核心对象session对象的工厂.
//                   简单说功能就一个---创建session对象
//注意:1.sessionfactory 负责保存和使用所有配置信息.消耗内存资源非常大.
//    2.sessionFactory属于线程安全的对象设计.
//结论: 保证在web项目中,只创建一个sessionFactory.
public class Demo2 {

    @Test
    public void fun1(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration();
        //2 读取指定主配置文件 => 空参加载方法,加载src下的hibernate.cfg.xml文件
        conf.configure();
        //3 读取指定orm元数据(扩展),如果主配置中已经引入映射配置.不需要手动加载
        //conf.addResource(resourceName);
        //conf.addClass(persistentClass);

        //4 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //--------------------------------------------------
        //5 获得session
        //打开一个新的session对象
        sf.openSession();
        //获得一个与线程绑定的session对象(明天讲解)
        sf.getCurrentSession();
    }
}

Session对象

//session对象功能: 表达hibernate框架与数据库之间的连接(会话).
//session类似于JDBC年代的connection对象. 还可以完成对数据库中数据的增删改查操作.
//session是hibernate操作数据库的核心对象
public class Demo3 {

    @Test
    //事务操作
    public void fun1(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration().configure();
        //2 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //3 获得session
        Session session = sf.openSession();
        //4 session获得操作事务的Transaction对象
        //获得操作事务的tx对象
        //Transaction tx = session.getTransaction();
        //开启事务并获得操作事务的tx对象(建议使用)
        Transaction tx2 = session.beginTransaction();
        //----------------------------------------------


        //----------------------------------------------
        tx2.commit();//提交事务
        tx2.rollback();//回滚事务
        session.close();//释放资源
        sf.close();//释放资源
    }

    @Test
    //session的新增
    public void fun2(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration().configure();
        //2 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //3 获得session
        Session session = sf.openSession();
        //4 session获得操作事务的Transaction对象
        //获得操作事务的tx对象
        //Transaction tx = session.getTransaction();
        //开启事务并获得操作事务的tx对象(建议使用)
        Transaction tx2 = session.beginTransaction();
        //----------------------------------------------
        Customer c = new Customer();
        c.setCust_name("谷歌");

        session.save(c);
        //----------------------------------------------
        tx2.commit();//提交事务
        session.close();//释放资源
        sf.close();//释放资源
    }

    @Test
    //session的查询
    //查询id为1的customer对象
    public void fun3(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration().configure();
        //2 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //3 获得session
        Session session = sf.openSession();
        //4 session获得操作事务的Transaction对象
        //获得操作事务的tx对象
        //Transaction tx = session.getTransaction();
        //开启事务并获得操作事务的tx对象(建议使用)
        Transaction tx2 = session.beginTransaction();
        //----------------------------------------------

        Customer customer = session.get(Customer.class, 1l);

        System.out.println(customer);
        //----------------------------------------------
        tx2.commit();//提交事务
        session.close();//释放资源
        sf.close();//释放资源
    }

    @Test
    //session的修改
    //修改id为1的customer对象的name属性为脸书
    public void fun4(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration().configure();
        //2 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //3 获得session
        Session session = sf.openSession();
        //4 session获得操作事务的Transaction对象
        //获得操作事务的tx对象
        //Transaction tx = session.getTransaction();
        //开启事务并获得操作事务的tx对象(建议使用)
        Transaction tx2 = session.beginTransaction();
        //----------------------------------------------
        //1 获得要修改的对象
        Customer c = session.get(Customer.class, 1l);
        //2 修改
        c.setCust_name("脸书");
        //3 执行update
        session.update(c);
        //----------------------------------------------
        tx2.commit();//提交事务
        session.close();//释放资源
        sf.close();//释放资源
    }

    @Test
    //session的删除
    //删除id为1的customer对象
    public void fun5(){
        //1 创建,调用空参构造
        Configuration conf = new Configuration().configure();
        //2 根据配置信息,创建 SessionFactory对象
        SessionFactory sf = conf.buildSessionFactory();
        //3 获得session
        Session session = sf.openSession();
        //4 session获得操作事务的Transaction对象
        //获得操作事务的tx对象
        Transaction tx = session.getTransaction();
        tx.begin();
        //开启事务并获得操作事务的tx对象(建议使用)
        Transaction tx2 = session.beginTransaction();
        //----------------------------------------------
        //1 获得要修改的对象
        Customer c = session.get(Customer.class, 1l);
        //2 调用delete删除对象
        session.delete(c);
        //----------------------------------------------
        tx2.commit();//提交事务
        session.close();//释放资源
        sf.close();//释放资源
    }
}



hibernate实体规则

实体类创建的注意事项

  1. 持久化类提供无参数构造
  2. 成员变量私有,提供共有get/set方法访问.需提供属性
  3. 持久化类中的属性,应尽量使用包装类型(数据库 null 无法给 基本类型赋值)
  4. 持久化类需要提供主键.与数据库中的主键列对应,没有主键不能映射到 hibernate。
  5. 不要用final修饰class
    • hibernate使用cglib代理生成代理对象.代理对象是继承被代理对象.如果被final修饰.将无法生成代理.



主键类型

自然主键(少见)

表的业务列中,有某业务列符合,必须有,并且不重复的特征时,该列可以作为主键使用,例如身份证号,但是不推荐.

代理主键(常见)

表的业务列中,没有某业务列符合,必须有,并且不重复的特征时,创建一个没有业务意义的列作为主键,例如:id,uuid等,推荐



主键生成策略

代理主键

  • identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键.

    • sequence: Oracle中的主键生成策略.
    • increment(了解): 主键自增.由hibernate来维护.每次插入前会先查询表中id最大值.+1作为新主键值,具有线程安全问题.
    • hilo(了解): 高低位算法.主键自增.由hibernate来维护.开发时不使用.

    • native**(推荐)**:hilo+sequence+identity 自动三选一策略.

    • uuid**(推荐): 产生随机字符串作为主键. 主键类型必须为string 类型**.


<hibernate-mapping package="cn.itheima.domain" >
    <class name="Customer" table="cst_customer" >
        <id name="cust_id"  >
            
            <generator class="native">generator>
        id>
        <property name="cust_name" column="cust_name" >property>
        <property name="cust_source" column="cust_source" >property>
        <property name="cust_industry" column="cust_industry" >property>
        <property name="cust_level" column="cust_level" >property>
        <property name="cust_linkman" column="cust_linkman" >property>
        <property name="cust_phone" column="cust_phone" >property>
        <property name="cust_mobile" column="cust_mobile" >property>
    class>
hibernate-mapping>

自然主键

  • assigned:自然主键生成策略. hibernate不会管理主键值.由开发人员自己录入.



hibernate中的对象状态

对象分为三种状态

  • 瞬时状态
    • 没有id,没有在session缓存中
  • 持久化状态
    • 有id,在session缓存中
  • 游离|托管状态
    • 有id,没有在session缓存中

//测试对象的三种状态
public class Demo {

    @Test
    //查看三种状态
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        Customer c = new Customer(); // 没有id, 没有与session关联 => 瞬时状态

        c.setCust_name("联想"); // 瞬时状态

        session.save(c); // 持久化状态, 有id,有关联

        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }

    @Test
    //三种状态特点
    //save方法: 其实不能理解成保存.理解成将瞬时状态转换成持久状态的方法
    //主键自增 : 执行save方法时,为了将对象转换为持久化状态.必须生成id值.所以需要执行insert语句生成.
    //increment: 执行save方法,为了生成id.会执行查询id最大值的sql语句.
    public void fun2(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        Customer c = new Customer(); // 没有id, 没有与session关联 => 瞬时状态

        c.setCust_name("联想"); // 瞬时状态

        session.save(c); // 持久化状态, 有id,有关联

        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }

    @Test
    //三种状态特点
    // 持久化状态特点: 持久化状态对象的任何变化都会自动同步到数据库中.
    public void fun3(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作

        Customer c = session.get(Customer.class, 1l);//持久化状态对象

        c.setCust_name("微软公司");

        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }
}



hibernate进阶-一级缓存

缓存:提高效率.hibernate中的一级缓存也是为了提高操作数据库的效率.

提高效率手段1:提高查询效率

提高效率手段2:减少不必要的修改语句发送



hibernate中的事务

事务特性

a 原子性(像物理原子一样,最小而不可分隔,全成功或全失败)
c 一致性(例如,转账 相加总量不变)
i 隔离性(隔离级别)
d 持久性(数据写到硬盘等持久化的物质上)

事务并发问题

1.脏读(查询正在操作还没有提交的数据)
2.不可重复度(两次查询数据不一致,中间可能别人操作了数据)
3.幻|虚读(删除记录的同时,别人又插入记录了,产生幻觉了)

事务的隔离级别

(表示上面的并发问题代表的1,2,3)

读未提交- 123
读已提交 - 23
可重复读(mysql默认级别)-3
串行化 - 没有问题,效率太低,不考虑。

在hibernate中指定数据库的隔离级别

hibernate.cfg.xml



<hibernate-configuration>
    <session-factory>

        
         
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driverproperty>
         
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate_32property>
         
        <property name="hibernate.connection.username">rootproperty>
         
        <property name="hibernate.connection.password">1234property>
        
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialectproperty>


        
        
        <property name="hibernate.show_sql">trueproperty>
        
        <property name="hibernate.format_sql">trueproperty>
        
        <property name="hibernate.hbm2ddl.auto">updateproperty>
        
         
         <property name="hibernate.connection.isolation">4property>
         
         <property name="hibernate.current_session_context_class">threadproperty>

        <mapping resource="cn/itheima/domain/Customer.hbm.xml" />

    session-factory>
hibernate-configuration>

在项目中如何管理事务

业务开始之前打开事务,业务执行之后提交事务. 执行过程中出现异常.回滚事务.

dao层操作数据库需要用到session对象.
service控制事务也是使用session对象完成.
我们要确保dao层和service层使用的使用同一个session对象

在hibernate中,确保使用同一个session的问题,hibernate已经帮我们解决了. 我们开发人员只需要调用sf.getCurrentSession()方法即可获得与当前线程绑定的session对象,底层还是 ThreadLocal

注意1:

调用getCurrentSession方法必须配置


<property name="hibernate.current_session_context_class">threadproperty>
注意2:

通过getCurrentSession方法获得的session对象.当事务提交时,session会自动关闭.不要手动调用close关闭,手动 close 会报异常。



hibernate中的批量查询(概述)

HQL查询-hibernate Query Language

(多表查询,但不复杂时使用)
Hibernate独家查询语言,属于面向对象的查询语言

基本查询

//测试HQL语句
public class Demo {

    @Test
    //基本查询
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        //-------------------------------------------
        //1> 书写HQL语句
//      String hql = " from cn.itheima.domain.Customer ";
//      只有一个类名的话,那么可以不写包名。
        String hql = " from Customer "; // 查询所有Customer对象
        //2> 根据HQL语句创建查询对象
        Query query = session.createQuery(hql);
        //3> 根据查询对象获得查询结果
        List list = query.list(); // 返回list结果
        //query.uniqueResult();//接收唯一的查询结果

        System.out.println(list);
        //-------------------------------------------
        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }
}

条件查询

@Test
    //条件查询
    //HQL语句中,不可能出现任何数据库相关的信息的
    public void fun2(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        //-------------------------------------------
        //1> 书写HQL语句
        String hql = " from Customer where cust_id = 1 "; // 查询所有Customer对象
        //2> 根据HQL语句创建查询对象
        Query query = session.createQuery(hql);
        //3> 根据查询对象获得查询结果
        Customer c = (Customer) query.uniqueResult();

        System.out.println(c);
        //-------------------------------------------
        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }

排序查询

@Test
    //排序
    public void fun2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql1 = " from  cn.itcast.domain.Customer order by cust_id asc ";//完整写法
        String hql2 = " from  cn.itcast.domain.Customer order by cust_id desc ";//完整写法

        Query query = session.createQuery(hql2);

        List list = query.list();

        System.out.println(list);
        //----------------------------------------------------
        tx.commit();
        session.close();

    }
?号占位符
@Test
//条件查询
//问号占位符
public void fun3(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //1> 书写HQL语句
    String hql = " from Customer where cust_id = ? "; // 查询所有Customer对象
    //2> 根据HQL语句创建查询对象
    Query query = session.createQuery(hql);
    //设置参数
    //query.setLong(0, 1l);
    query.setParameter(0, 1l);
    //3> 根据查询对象获得查询结果
    Customer c = (Customer) query.uniqueResult();

    System.out.println(c);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联 
}
命名占位符
@Test
    //条件查询
    //命名占位符
    public void fun4(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        //-------------------------------------------
        //1> 书写HQL语句
        String hql = " from Customer where cust_id = :cust_id "; // 查询所有Customer对象
        //2> 根据HQL语句创建查询对象
        Query query = session.createQuery(hql);
        //设置参数
        query.setParameter("cust_id", 1l);
        //3> 根据查询对象获得查询结果
        Customer c = (Customer) query.uniqueResult();

        System.out.println(c);
        //-------------------------------------------
        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }

分页查询

@Test
    //分页查询
    public void fun5(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        //-------------------------------------------
        //1> 书写HQL语句
        String hql = " from Customer  "; // 查询所有Customer对象
        //2> 根据HQL语句创建查询对象
        Query query = session.createQuery(hql);
        //设置分页信息 limit ?,?
        query.setFirstResult(1);
        query.setMaxResults(1);
        //3> 根据查询对象获得查询结果
        List<Customer> list =  query.list();

        System.out.println(list);
        //-------------------------------------------
        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }

统计查询

@Test
//统计查询
//count 计数
//sum   求和
//avg   平均数
//max
//min
public void fun5(){
    Session session = HibernateUtils.openSession();
    Transaction tx = session.beginTransaction();
    //----------------------------------------------------
    String hql1 = " select count(*) from  cn.itcast.domain.Customer  ";//完整写法
    String hql2 = " select sum(cust_id) from  cn.itcast.domain.Customer  ";//完整写法
    String hql3 = " select avg(cust_id) from  cn.itcast.domain.Customer  ";//完整写法
    String hql4 = " select max(cust_id) from  cn.itcast.domain.Customer  ";//完整写法
    String hql5 = " select min(cust_id) from  cn.itcast.domain.Customer  ";//完整写法

    Query query = session.createQuery(hql5);

    Number number  = (Number) query.uniqueResult();

    System.out.println(number);
    //----------------------------------------------------
    tx.commit();
    session.close();

}

投影查询

就是查询默写字段名字,不是全部* 。

@Test
//投影查询
public void fun6(){
    Session session = HibernateUtils.openSession();
    Transaction tx = session.beginTransaction();
    //----------------------------------------------------
    String hql1 = " select cust_name from  cn.itcast.domain.Customer  ";
    // 字段查询到自动封装Object[] 中
    String hql2 = " select cust_name,cust_id from  cn.itcast.domain.Customer  ";
    // 字段查询到自动封装到 Model中,同时Customer需要创建2个参数的构造函数。
    String hql3 = " select new Customer(cust_id,cust_name) from  cn.itcast.domain.Customer  ";

    Query query = session.createQuery(hql3);

    List list = query.list();

    System.out.println(list);

    //----------------------------------------------------
    tx.commit();
    session.close();

}

多表查询

//学习HQL语法(不常用) - 多表查询语法
public class Demo2 {
    //回顾-原生SQL
    // 交叉连接-笛卡尔积(避免)
//      select * from A,B 
    // 内连接
//      |-隐式内连接
//          select * from A,B  where b.aid = a.id
//      |-显式内连接
//          select * from A inner join B on b.aid = a.id
    // 外连接
//      |- 左外
//          select * from A left [outer] join B on b.aid = a.id
//      |- 右外
//          select * from A right [outer] join B on b.aid = a.id
//---------------------------------------------------------------------
//HQL的多表查询
        //内连接(迫切)
        //外连接
//          |-左外(迫切)
//          |-右外(迫切)

    @Test
    //HQL 内连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun1(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c inner join c.linkMens ";

        Query query = session.createQuery(hql);

        List list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

    @Test
    //HQL 迫切内连接 => 帮我们进行封装.返回值就是一个对象
    public void fun2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c inner join fetch c.linkMens ";

        Query query = session.createQuery(hql);

        List list = query.list();

        System.out.println(list);
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

    @Test
    //HQL 左外连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c left join c.linkMens ";

        Query query = session.createQuery(hql);

        List list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }
    @Test
    //HQL 右外连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun4(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c right join c.linkMens ";

        Query query = session.createQuery(hql);

        List list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }   
}

Criteria查询

(单表条件查询)
Hibernate自创的无语句面向对象查询

基本查询

//测试Criteria查询
public class Demo {

    @Test
    //基本查询
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 控制事务
        Transaction tx = session.beginTransaction();
        //3执行操作
        //-------------------------------------------

        //查询所有的Customer对象
        Criteria criteria = session.createCriteria(Customer.class);

        List list = criteria.list();

        System.out.println(list);

//      Customer c = (Customer) criteria.uniqueResult();

        //-------------------------------------------
        //4提交事务.关闭资源
        tx.commit();
        session.close();// 游离|托管 状态, 有id , 没有关联
    }
}

条件查询

@Test
//条件查询
//HQL语句中,不可能出现任何数据库相关的信息的
// >                gt
// >=               ge
// <                lt
// <=               le
// ==               eq
// !=               ne
// in               in
// between and      between
// like             like
// is not null      isNotNull
// is null          isNull
// or               or
// and              and
public void fun2(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //创建criteria查询对象
    Criteria criteria = session.createCriteria(Customer.class);
    //添加查询参数 => 查询cust_id为1的Customer对象
    criteria.add(Restrictions.eq("cust_id", 1l));
    //执行查询获得结果
    Customer c = (Customer) criteria.uniqueResult();
    System.out.println(c);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联
}

分页 查询

@Test
//分页查询
public void fun3(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //创建criteria查询对象
    Criteria criteria = session.createCriteria(Customer.class);
    //设置分页信息 limit ?,?
    criteria.setFirstResult(1);
    criteria.setMaxResults(2);
    //执行查询
    List<Customer> list = criteria.list();

    System.out.println(list);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联
}

设置查询总记录数

@Test
//查询总记录数
public void fun4(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //创建criteria查询对象
    Criteria criteria = session.createCriteria(Customer.class);
    //设置查询的聚合函数 => 总行数
    criteria.setProjection(Projections.rowCount());
    //执行查询
    Long count = (Long) criteria.uniqueResult();

    System.out.println(count);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联
}

排序

@Test
//排序语法 
public void fun4(){
    Session session = HibernateUtils.openSession();
    Transaction tx = session.beginTransaction();
    //----------------------------------------------------

    Criteria c = session.createCriteria(Customer.class);

    c.addOrder(Order.asc("cust_id"));
    //c.addOrder(Order.desc("cust_id"));

    List list = c.list();

    System.out.println(list);

    //----------------------------------------------------
    tx.commit();
    session.close();

}

离线查询

区别就是创建的时候是否需要 session

非离线

离线

//学习离线Criteria
public class Demo2 {

    @Test
    public void fun1(){
        //Service/web层
        DetachedCriteria dc  = DetachedCriteria.forClass(Customer.class);

        dc.add(Restrictions.idEq(6l));//拼装条件(全部与普通Criteria一致)

        //----------------------------------------------------
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        Criteria c = dc.getExecutableCriteria(session);

        List list = c.list();

        System.out.println(list);
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

}
演示

原生SQL查询

(复杂的业务查询)

基本查询

返回数组List
@Test
//基本查询
public void fun1(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //1 书写sql语句
    String sql = "select * from cst_customer";

    //2 创建sql查询对象
    SQLQuery query = session.createSQLQuery(sql);

    //3 调用方法查询结果
    List list = query.list();
    //query.uniqueResult();

    for(Object[] objs : list){
        System.out.println(Arrays.toString(objs));
    }

    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联


}
返回对象List
@Test
//基本查询
public void fun2(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //1 书写sql语句
    String sql = "select * from cst_customer";

    //2 创建sql查询对象
    SQLQuery query = session.createSQLQuery(sql);
    //指定将结果集封装到哪个对象中
    query.addEntity(Customer.class);

    //3 调用方法查询结果
    List<Customer> list = query.list();

    System.out.println(list);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联
}

条件查询

@Test
//条件查询
public void fun3(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //1 书写sql语句
    String sql = "select * from cst_customer where cust_id = ? ";

    //2 创建sql查询对象
    SQLQuery query = session.createSQLQuery(sql);

    query.setParameter(0, 1l);
    //指定将结果集封装到哪个对象中
    query.addEntity(Customer.class);

    //3 调用方法查询结果
    List<Customer> list = query.list();

    System.out.println(list);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联
}

分页查询

@Test
//分页查询
public void fun4(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 控制事务
    Transaction tx = session.beginTransaction();
    //3执行操作
    //-------------------------------------------
    //1 书写sql语句
    String sql = "select * from cst_customer  limit ?,? ";

    //2 创建sql查询对象
    SQLQuery query = session.createSQLQuery(sql);

    query.setParameter(0, 0);
    query.setParameter(1, 1);
    //指定将结果集封装到哪个对象中
    query.addEntity(Customer.class);

    //3 调用方法查询结果
    List<Customer> list = query.list();

    System.out.println(list);
    //-------------------------------------------
    //4提交事务.关闭资源
    tx.commit();
    session.close();// 游离|托管 状态, 有id , 没有关联


}



多表查询

一对多|多对一

关系表达

表中的表达
表中的表达

实体中的表达

orm元数据中表达
一对多

Customer.hbm.xml



<hibernate-mapping package="cn.itcast.domain" >
    <class name="Customer" table="cst_customer" >
        <id name="cust_id"  >
            <generator class="native">generator>
        id>
        <property name="cust_name" column="cust_name" >property>
        <property name="cust_source" column="cust_source" >property>
        <property name="cust_industry" column="cust_industry" >property>
        <property name="cust_level" column="cust_level" >property>
        <property name="cust_linkman" column="cust_linkman" >property>
        <property name="cust_phone" column="cust_phone" >property>
        <property name="cust_mobile" column="cust_mobile" >property>

        
        
         
          
        <set name="linkMens" inverse="true" cascade="save-update"  >
            <key column="lkm_cust_id" >key>
            <one-to-many class="LinkMan" />
        set>


    class>
hibernate-mapping>
多对一

LinkMan.hbm.xml



<hibernate-mapping package="cn.itcast.domain" >
    <class name="LinkMan" table="cst_linkman" >
        <id name="lkm_id"  >
            <generator class="native">generator>
        id>
        <property name="lkm_gender"  >property>
        <property name="lkm_name"  >property>
        <property name="lkm_phone"  >property>
        <property name="lkm_email"  >property>
        <property name="lkm_qq"  >property>
        <property name="lkm_mobile"  >property>
        <property name="lkm_memo"  >property>
        <property name="lkm_position"  >property>

        
        
          
          
        <many-to-one name="customer" column="lkm_cust_id" class="Customer"  >
        many-to-one>
    class>
hibernate-mapping>

操作

操作关联属性

使用级联操作

//一对多|多对一关系操作
public class Demo {
    @Test
    //保存客户 以及客户 下的联系人
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        Customer c = new Customer();
        c.setCust_name("百度公司");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("李彦宏");

        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("小度");

        //表达一对多,客户下有多个联系人
        c.getLinkMens().add(lm1);
        c.getLinkMens().add(lm2);

        //表达对对对,联系人属于哪个客户
        lm1.setCustomer(c);
        lm2.setCustomer(c);


        session.save(c);
//      使用级联操作,不用互相保存
//      session.save(lm1);
//      session.save(lm2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

    @Test
    //为客户增加联系人
    public void fun2(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 获得要操作的客户对象
        Customer c = session.get(Customer.class,1l);
        //2> 创建联系人
        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("郝强勇");
        //3> 将联系人添加到客户,将客户设置到联系人中
        c.getLinkMens().add(lm1);
        lm1.setCustomer(c);
        //4> 执行保存
        session.save(lm1);
        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

    @Test
    //为客户删除联系人
    public void fun3(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 获得要操作的客户对象
        Customer c = session.get(Customer.class,1l);
        //2> 获得要移除的联系人
        LinkMan lm = session.get(LinkMan.class, 3l);
        //3> 将联系人从客户集合中移除
        c.getLinkMens().remove(lm);
        lm.setCustomer(null);
        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

}


"customer" column="lkm_cust_id" class="Customer"  >




//测试级联操作
public class Demo2 {
    @Test
    //保存客户 以及客户 下的联系人
    //cascade:save-update
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        Customer c = new Customer();
        c.setCust_name("百度公司");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("李彦宏");

        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("小度");

        //表达一对多,客户下有多个联系人
        c.getLinkMens().add(lm1);
        c.getLinkMens().add(lm2);

        //表达对对对,联系人属于哪个客户
        lm1.setCustomer(c);
        lm2.setCustomer(c);


        session.save(c);
//      session.save(lm1);
//      session.save(lm2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

    @Test
    //测试删除客户时,级联删除客户下的联系人
    //cascade:delete
    public void fun2(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 获得要操作的客户对象
        Customer c = session.get(Customer.class,1l);
        //2>调用delete删除客户
        session.delete(c);
        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

    @Test
    //保存联系人以及联系人对应的客户
    //cascade:save-update
    public void fun3(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        Customer c = new Customer();
        c.setCust_name("北大青鸟");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("刘总");

        //表达一对多,客户下有多个联系人
        c.getLinkMens().add(lm1);

        //表达多对一,联系人属于哪个客户
        lm1.setCustomer(c);


        session.save(lm1);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }       
}



进阶操作

结论:

简化操作.一定要用:save-update,不建议使用delete,delete 会把外键关联的记录也删除,太危险了.

关系维护

inverse属性: 性能优化,提高关系维护的性能。
原则: 无论怎么放弃,总有一方必须要维护主键关系。
inverse=”true”放弃维护。

一对多关系中: 一的一方放弃,也只能一的一方放弃。
多的一方不能放弃,外键在多方的表中。

<set name="linkMens" inverse="true" cascade="save-update"  >
  <key column="lkm_cust_id" >key>
  to-many class="LinkMan" />
set>
//操作进阶--关系维护属性
public class Demo3 {
    @Test
    //保存客户 以及客户 下的联系人
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        Customer c = new Customer();
        c.setCust_name("百度");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("李彦宏");

        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("小度");

        //表达一对多,客户下有多个联系人. 
        // 如果客户放弃维护与联系人的关系. 维护关系的代码可以省略
        //c.getLinkMens().add(lm1);
        //c.getLinkMens().add(lm2);

        //表达多对一,联系人属于哪个客户
        lm1.setCustomer(c);
        lm2.setCustomer(c);


        session.save(c);
        session.save(lm1);
        session.save(lm2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }   
}
删除客户

因为联系人表外键了客户表,所以不能直接删除客户。

  • 方式一:级联:cascade=”delete” ,删除客户表同时也会删除联系人表。
  • 方式二:外键关系:inverse=”false” ,删除客户表,同时联系人表外键变为 null。
@Test
//删除客户
public void fun2(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 开启事务
    Transaction tx = session.beginTransaction();
    //-------------------------------------------------
    //3操作
    Customer customer = session.get(Customer.class, 1l);

    session.delete(customer);
    //-------------------------------------------------
    //4提交事务
    tx.commit();
    //5关闭资源
    session.close();
}



多对多

关系表达

表中

对象中

orm元数据

Role.hbm.xml



<hibernate-mapping package="cn.itcast.domain" >
    <class name="Role" table="sys_role" >
        <id name="role_id"  >
            <generator class="native">generator>
        id>
        <property name="role_name"  >property>
        <property name="role_memo"  >property>

            
        <set name="users" table="sys_user_role" inverse="true" >
            <key column="role_id" >key>
            <many-to-many class="User" column="user_id" >many-to-many>
        set>
    class>
hibernate-mapping>

User.hbm.xml



<hibernate-mapping package="cn.itcast.domain" >
    <class name="User" table="sys_user" >
        <id name="user_id"  >
            <generator class="native">generator>
        id>
        <property name="user_code"  >property>
        <property name="user_name"  >property>
        <property name="user_password"  >property>
        <property name="user_state"  >property>

        
        
         
        <set name="roles" table="sys_user_role" cascade="save-update" >
            <key column="user_id" >key>
            <many-to-many class="Role" column="role_id" >many-to-many>
        set>

    class>
hibernate-mapping>
注意:

中间表报错,必须只能一方维护外键关系,如果双方都维护外键关系,那么中间表会插入练体能一样的记录,造成中间表的主键重复。

结论:

将来在开发中,如果遇到多对多关系.一定要选择一方放弃维护关系.
一般谁来放弃要看业务方向. 例如录入员工时,需要为员工指定所属角色.
那么业务方向就是由员工维护角色. 角色不需要维护与员工关系.角色放弃维护.

操作

操作关联属性

//多对多关系操作
public class Demo {

    //为郝强勇解除一个角色
    @Test
    public void fun4(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 获得郝强勇用户
        User user = session.get(User.class, 1l);
        //2> 获得要操作的角色对象(保洁,保安)
        Role r1 = session.get(Role.class, 1l);
        Role r2 = session.get(Role.class, 2l);
        //3> 将角色从用户的角色集合中移除
        user.getRoles().remove(r1);
        user.getRoles().remove(r2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }
}
操作进阶
inverse属性
  • true: 放弃维护外键关系
  • false(默认值):维护关系
//保存员工以及角色
@Test
public void fun1(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 开启事务
    Transaction tx = session.beginTransaction();
    //-------------------------------------------------
    //3操作
    //1> 创建两个 User
    User u1 = new User();
    u1.setUser_name("郝强勇");

    User u2 = new User();
    u2.setUser_name("金家德");

    //2> 创建两个 Role
    Role r1 = new Role();
    r1.setRole_name("保洁");

    Role r2 = new Role();
    r2.setRole_name("保安");
    //3> 用户表达关系
    u1.getRoles().add(r1);
    u1.getRoles().add(r2);

    u2.getRoles().add(r1);
    u2.getRoles().add(r2);

    //4> 角色表达关系
    r1.getUsers().add(u1);
    r1.getUsers().add(u2);

    r2.getUsers().add(u1);
    r2.getUsers().add(u2);

    //5> 调用Save方法一次保存
    session.save(u1);
    session.save(u2);
    session.save(r1);
    session.save(r2);
    //-------------------------------------------------
    //4提交事务
    tx.commit();
    //5关闭资源
    session.close();
}

注意:
中间表报错,必须只能一方维护外键关系,如果双方都维护外键关系,那么中间表会插入练体能一样的记录,造成中间表的主键重复


cascade属性
  • cascade级联操作:
  • save-update: 级联保存更新
  • delete:级联删除
  • all:级联保存更新+级联删除
//为郝强勇新增一个角色
@Test
public void fun3(){
    //1 获得session
    Session session = HibernateUtils.openSession();
    //2 开启事务
    Transaction tx = session.beginTransaction();
    //-------------------------------------------------
    //3操作
    //1> 获得郝强勇用户
    User user = session.get(User.class, 1l);
    //2> 创建公关角色
    Role r = new Role();
    r.setRole_name("男公关");
    //3> 将角色添加到用户中
    user.getRoles().add(r);
    //4> 将角色转换为持久化
    //session.save(r);//增加级联操作可以省略这一行
    //-------------------------------------------------
    //4提交事务
    tx.commit();
    //5关闭资源
    session.close();
}

结论: cascade简化代码书写.该属性使不使用无所谓.
建议要用只用save-update.
如果使用delete操作太过危险.尤其在多对多中.不建议使用.



查询优化

类级别查询

  • get方法:没有任何策略.调用即立即查询数据库加载数据.

  • load方法: 应用类级别的加载策略

    • lazy=”true”(默认值), 查询类时,会返回代理对象。会在使用属性时,根据关联的session查询数据库.加载数据.

    • lazy=”false”。load方法会与get方法没有任何区别.调用时即加载数据.
      结论:为了提高效率.建议使用延迟加载(懒加载)

      • 注意:使用懒加载时要确保,调用属性加载数据时,session还是打开的.不然会抛出异常

Customer.hbm.xml 属性:lazy="false"



<hibernate-mapping package="cn.itcast.domain" >
    <class name="Customer" table="cst_customer" lazy="false" >
        <id name="cust_id"  >
            <generator class="native">generator>
        id>
        <property name="cust_name" column="cust_name" >property>
        <property name="cust_source" column="cust_source" >property>
        <property name="cust_industry" column="cust_industry" >property>
        <property name="cust_level" column="cust_level" >property>
        <property name="cust_linkman" column="cust_linkman" >property>
        <property name="cust_phone" column="cust_phone" >property>
        <property name="cust_mobile" column="cust_mobile" >property>

    
     
        <set name="linkMens" batch-size="3" fetch="join" lazy="proxy" >
            <key column="lkm_cust_id" >key>
            <one-to-many class="LinkMan" />
        set>


    class>
hibernate-mapping>

关联级别查询

集合策略

关联属性策略

结论:

为了提高效率.fetch的选择上应选择select. lazy的取值应选择 true. 全部使用默认值.

批量抓取



参考自某某培训机构。

你可能感兴趣的:(javaee)