java深入探究12-框架之Hibernate

1.引入SSH框架

Struts框架,基于MVC 模式的应用层框架技术

Hibernate,基于持久层框架(数据访问层使用)

Dao代码编写的几种方式:

1.原始jdbc操作,Connection/Statement/ResultSet

2.自定义一个持久层框架,封装dao的通用方法

3.DbUtils组件,轻量级数据访问技术

4.Hibernate技术【最终执行的就是jdbc代码!】

2.开始学习SSH框架

a) ORM概念

Object Realtion Mapping:对象关系映射

用于:直接将对象存入数据库;直接从数据库拿到对象!

想要做到这点:映射,映射信息存入xml

映射中信息:1.对象与表,2.属性与字段,3.类型的对应

O R M三者间的关系

b) 组件的学习:源码引入jar文件

c) 环境搭建

我们也可以直接添加jar包,之后只有再添加一个连接数据库的包就好了

 

  1. 下载源码hibernate-distribution-3.6.0.Final
  2. jar包:实际上我们可以直接

hibernate3.jar核心+required必须引入的(6个)+jpa目录+数据库驱动包required中的jarantlr-2.7.6commons-collections-3.1dom4j-1.6.1javassist-3.12.0.GAjta-1.1slf4j-api-1.6.1

   Jpahibernate-jpa-2.0-api-1.0.0.Final.jar

               数据库驱动:mysql-connector-java-5.1.12-bin.jar

  1. 写对象和对象映射
    1. Employee.java 对象
    2. Employee.hbm.xml对象映射 我们可在源码中随便找个hbm文件再修改为自己的
  2. Src/hibernate.cfg.xml(源码project/etc/hibernate.cfg.xml可以找到再自行修改)
    1. 数据库连接配置;2.加载所用的映射(*.hbm.xml

例子:

  获取sessionFactory的工具类

public class HibernateUtils {
    private static SessionFactory sf;
    static{
        sf=new Configuration().configure().buildSessionFactory();    
    }
    public static  Session getSession(){
        return sf.openSession();
    }
}
View Code

  d) 核心API

    Configuration 配置管理类对象

      Config.configure(); 加载主配置文件的方法(hibernate.cfg.xml

      默认加载src/hibernate.cfg.xml

      Config.configure(cn/config/hibernate.cfg.xml);加载指定路径下的

      config.buildSessionFactory();创建session的工厂对象

    SessionFactory Session的工厂(或者说代表hibernate.cfg.xml文件)

      sf.openSession(); 创建一个session对象

      sf.getCurrentSession();创建一个session或者取出session对象

    Session session对象维护了一个连接(Connection,代表与数据库连接的回话,hibernate最重要的一个对象

      session.beginTransaction();开启一个事务,hibernate要求所有与数据库的操作都要有事务,否则报错

      更新:

        Session.save(obj)

        Session.update(obj)

        session.saveOrUpdate(obj);保存或者更新方法

          —》有主键执行更新

          —》没主键执行保存

          —》如果设置的主键不存在报错

      主键查询:

        Session.get(obj.class,1) 主键查询

        Session.load(obj.class,1)主键查询(支持懒加载)

      HQL查询:

        与SQL区别:SQL(结构化查询语句)面向表查询,不区分大小写

              HQL:Hebernate query language面向对象的查询语句,因为是对象,区分大小写

 

//适合有数据库基础的HQL面向对象,SQL面向表
    public static void HQLQuery(){
        /*这里的Hibernate的SQL与jdbc中的sql不同,对象对象的,jdbc是面向表字段的*/
        
            Query q=session.createQuery("from Employee where empId=1 or empId=2");
            List<Employee> list=q.list();
            System.out.println(list);
    }
View Code

 

      Criteria查询:没有数据库基础也可以用完全面向对象的查询

 

//QBC查询,query by criteria 完全面向对象的查询
    public static void CriteriaQuery(){
        Criteria criteria=session.createCriteria(Employee.class);
        //加条件
        criteria.add(Restrictions.eq("empId", 1));
        List<Employee > list=criteria.list();//开始查询
        System.out.println(list);
    }
View Code

 

      本地SQL查询:原生sql,缺点不能数据库跨平台

 

//本地事务查询sql直接查询
    public static void SqlQuery(){
        //把每一行数数据封装为对象数组,再添加list集合
        //SQLQuery sq=session.createSQLQuery("select * from employee");
        SQLQuery sq=session.createSQLQuery("select * from employee").addEntity(Employee.class);
        //想要封装对象-》
        List<?> list=sq.list();
        System.out.println(list);
    }
View Code

 

   e)可能会遇到的问题

    1.ClassNotFoundException...,缺少jar文件!

    2.如果程序执行,hibernate也生成sql语句,但数据没有结果影响:

      一般是事务没有提交。。

  f)Hibernate crud

    增删改查方法

package crud;

import hello.Employee;

import java.io.Serializable;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import utils.HibernateUtils;

public class EmployeeImpl implements EmployeeDao {
    
    @Override
    public void save(Employee emp) {
        // TODO Auto-generated method stub
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            
            //业务逻辑
            session=HibernateUtils.getSession();
            ts=session.beginTransaction();
            
            
        } catch (HibernateException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException();
        }finally{
            ts.commit();
            session.close();
        }
        
    }

    @Override
    public void update(Employee emp) {
        // TODO Auto-generated method stub
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
            
            //业务逻辑
            session.update(emp);
            
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
                
    }

    @Override
    public Employee findById(Serializable id) {
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //业务逻辑
            return (Employee)session.get(Employee.class, id);
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll() {
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //业务逻辑
            return session.createQuery("from Employee").list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll(String employeeName) {
        // TODO Auto-generated method stub
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //业务逻辑
            Query q =session.createQuery("from Employee where empName=?");
            q.setParameter(0, employeeName);
            return q.list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public List<Employee> getAll(int index, int count) {
        //获取session 
        Session session=null;
        Transaction ts=null;    
        try {
            session = HibernateUtils.getSession();
            ts = session.beginTransaction();    
                    
            //业务逻辑
            Query q= session.createQuery("from Employee");
            q.setFirstResult(index);//查询起始行
            q.setMaxResults(count);//查询的行数
            return q.list();
                    
            } catch (HibernateException e) {
            // TODO Auto-generated catch block
                throw new RuntimeException();
            }finally{
                ts.commit();
                session.close();
            }
    }

    @Override
    public void delete(Serializable id) {
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtils.getSession();
            tx = session.beginTransaction();
            // 先根据id查询对象,再判断删除
            Object obj=session.get(Employee.class, id);
            if(obj!=null){
                session.delete(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            tx.commit();
            session.close();
        }
    }

}
View Code

  g)配置文件

  主配置:hibernate.cfg.xml:主要配置数据库信息、其他参数、映射信息

<!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节点代表一个数据库 -->
    <session-factory>
    
        <!-- 1. 数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hib_demo</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!-- 
            数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        
        
        <!-- 2. 其他相关配置 -->
        <!-- 2.1 显示hibernate在运行时候执行的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 2.2 格式化sql -->
        <property name="hibernate.format_sql">true</property>
        <!-- 2.3 自动建表  -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        
        
        <!-- 3. 加载所有映射 --> 
        <mapping resource="hello/Employee.hbm.xml"/>
        
    </session-factory>
</hibernate-configuration>
View Code

  其中自动建表:就算数据库中的表被删了只要配置好就能自动在数据库中建表

  <property name="hibernate.hbm2ddl.auto">update</property>

#hibernate.hbm2ddl.auto create-drop 每次在创建sessionFactory时候执行创建表;
                                当调用sesisonFactory的close方法的时候,删除表!
#hibernate.hbm2ddl.auto create   每次都重新建表; 如果表已经存在就先删除再创建
#hibernate.hbm2ddl.auto update  如果表不存在就创建; 表存在就不创建;
#hibernate.hbm2ddl.auto validate  (生成环境时候) 执行验证: 当映射文件的内容与数据库表结构不一样的时候就报错!
View Code

  自动建表代码方式:SchemaExport export = new SchemaExport(config);主要是创建工具类对象

           export.creat(true,true),参数1显示在colsole,第二个是在数据库中执行

// 自动建表
    @Test
    public void testCreate() throws Exception {
        // 创建配置管理类对象
        Configuration config = new Configuration();
        // 加载主配置文件
        config.configure();
        
        // 创建工具类对象
        SchemaExport export = new SchemaExport(config);
        // 建表
        // 第一个参数: 是否在控制台打印建表语句
        // 第二个参数: 是否执行脚本
        
View Code

   映射配置:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">


<!-- 映射文件: 映射一个实体类对象;  描述一个对象最终实现可以直接保存对象数据到数据库中。  -->
<!-- 
    package: 要映射的对象所在的包(可选,如果不指定,此文件所有的类都要指定全路径)
    auto-import 默认为true, 在写hql的时候自动导入包名
                如果指定为false, 再写hql的时候必须要写上类的全名;
                  如:session.createQuery("from cn.itcast.c_hbm_config.Employee").list();
 -->
<hibernate-mapping package="cn.itcast.c_hbm_config" auto-import="true">
    
    <!-- 
        class 映射某一个对象的(一般情况,一个对象写一个映射文件,即一个class节点)
            name 指定要映射的对象的类型
            table 指定对象对应的表;
                  如果没有指定表名,默认与对象名称一样 
     -->
    <class name="Employee" table="employee">
        
        <!-- 主键 ,映射-->
        <id name="empId" column="id">
            <!-- 
                主键的生成策略
                    identity  自增长(mysql,db2)
                    sequence  自增长(序列), oracle中自增长是以序列方法实现
                    native  自增长【会根据底层数据库自增长的方式选择identity或sequence】
                            如果是mysql数据库, 采用的自增长方式是identity
                            如果是oracle数据库, 使用sequence序列的方式实现自增长
                    
                    increment  自增长(会有并发访问的问题,一般在服务器集群环境使用会存在问题。)
                    
                    assigned  指定主键生成策略为手动指定主键的值
                    uuid      指定uuid随机生成的唯一的值
                    foreign   (外键的方式, one-to-one讲)
             -->
            <generator class="uuid"/>
        </id>
        
        <!-- 
            普通字段映射
            property
                name  指定对象的属性名称
                column 指定对象属性对应的表的字段名称,如果不写默认与对象属性一致。
                length 指定字符的长度, 默认为255
                type   指定映射表的字段的类型,如果不指定会匹配属性的类型
                    java类型:     必须写全名
                    hibernate类型:  直接写类型,都是小写
        -->
        <property name="empName" column="empName" type="java.lang.String" length="20"></property>
        <property name="workDate" type="java.util.Date"></property>
        <!-- 如果列名称为数据库关键字,需要用反引号或改列名。 -->
        <property name="desc" column="`desc`" type="java.lang.String"></property>
        
    </class>
    

</hibernate-mapping>
View Code

   其中复合主键:一个表中主键除了一个可能还会有2个或更多在一起作为复合主键

      映射文件中配置复合主键<composite-id>

// 复合主键类
public class CompositeKeys implements Serializable{
    private String userName;
    private String address;
   // .. get/set
}
public class User {

    // 名字跟地址,不会重复
    private CompositeKeys keys;
    private int age;
}
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.d_compositeKey" auto-import="true">
    
    <class name="User">
        
        <!-- 复合主键映射 -->
        <composite-id name="keys">
            <key-property name="userName" type="string"></key-property>
            <key-property name="address" type="string"></key-property>
        </composite-id>
        
        <property name="age" type="int"></property>        
        
    </class>
    

</hibernate-mapping>

App.java

public class App2 {
    
    private static SessionFactory sf;
    static  {        
        // 创建sf对象
        sf = new Configuration()
            .configure()
            .addClass(User.class)  //(测试) 会自动加载映射文件:Employee.hbm.xml
            .buildSessionFactory();
    }

    //1. 保存对象
    @Test
    public void testSave() throws Exception {
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
        
        // 对象
        CompositeKeys keys = new CompositeKeys();
        keys.setAddress("广州棠东");
        keys.setUserName("Jack");
        User user = new User();
        user.setAge(20);
        user.setKeys(keys);
        
        // 保存
        session.save(user);
        
        
        tx.commit();
        session.close();
    }
    
    @Test
    public void testGet() throws Exception {
        Session session = sf.openSession();
        Transaction tx = session.beginTransaction();
        
        //构建主键再查询
        CompositeKeys keys = new CompositeKeys();
        keys.setAddress("广州棠东");
        keys.setUserName("Jack");
        
        // 主键查询
        User user = (User) session.get(User.class, keys);
        // 测试输出
        if (user != null){
            System.out.println(user.getKeys().getUserName());
            System.out.println(user.getKeys().getAddress());
            System.out.println(user.getAge());
        }
        
        
        tx.commit();
        session.close();
    }
}
View Code

hibernate执行周期:

-------------------------------------------------------------------------

关联映射

1. 集合映射  

  开发流程:需求分析/数据库设计、项目设计/ 编码/测试/实施部署上线/验收

  需求:一个用户有多个收货地址,通过映射创建映射表到数据库中,并对表数据做处理

     核心思路:配置文件中<property name="hibernate.hbm2ddl.auto">update</property>设置为自动生成表

          映射文件配置set映射配置:没有啥特殊之处,对应的只需外键就可以

                List集合映射:List有记住存储顺序的能力,处了外键,还需能记住存储顺序的list-index

                Map集合映射:map有兼职对的能力,处了外键还需键的存储,值得存储

     查询数据时,当查询Map映射信息,或list信息,都属于懒加载,代码写到时才会从数据库发送sql

 

<!-- 
            set集合属性的映射
                name 指定要映射的set集合的属性
                table 集合属性要映射到的表
                key  指定集合表(t_address)的外键字段
                element 指定集合表的其他字段
                    type 元素类型,一定要指定
         -->
         <set name="address" table="t_address">
             <key column="uid"></key>
             <element column="address" type="string"></element>
         </set>
View Code

 

完整代码:

// javabean设计
public class User {

    private int userId;
    private String userName;
    // 一个用户,对应的多个地址
    private Set<String> address;
    private List<String> addressList = new ArrayList<String>(); 
    //private String[] addressArray; // 映射方式和list一样     <array name=""></array>
    private Map<String,String> addressMap = new HashMap<String, String>();
    
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.a_collection">
    
    <class name="User" table="t_user">
        <id name="userId" column="id">
            <generator class="native"></generator>
        </id>    
        <property name="userName"></property>
        
        <!-- 
            set集合属性的映射
                name 指定要映射的set集合的属性
                table 集合属性要映射到的表
                key  指定集合表(t_address)的外键字段
                element 指定集合表的其他字段
                    type 元素类型,一定要指定
         -->
         <set name="address" table="t_address">
             <key column="uid"></key>
             <element column="address" type="string"></element>
         </set>
         
         <!-- 
             list集合映射
                 list-index  指定的是排序列的名称 (因为要保证list集合的有序)
          -->
          <list name="addressList" table="t_addressList">
                <key column="uid"></key>
                <list-index column="idx"></list-index>
                <element column="address" type="string"></element>
          </list>
          
          <!-- 
              map集合的映射
                  key  指定外键字段
                  map-key 指定map的key 
                  element  指定map的value
           -->
          <map name="addressMap" table="t_addressMap">
              <key column="uid"></key>
              <map-key column="shortName" type="string" ></map-key>
              <element column="address" type="string" ></element>
          </map>
          
         
    </class>
    

</hibernate-mapping>


    // 保存set
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //-- 保存
        Set<String> addressSet = new HashSet<String>();
        addressSet.add("广州");
        addressSet.add("深圳");
        // 用户对象
        User user = new User();
        user.setUserName("Jack");
        user.setAddress(addressSet);
        
        // 保存
        session.save(user);
        
        session.getTransaction().commit();
        session.close();
    }
    
    // 保存list/map
    @Test
    public void testSaveList() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        User user = new User();
        user.setUserName("Tom");
//        // 用户对象  --  list
//        user.getAddressList().add("广州");
//        user.getAddressList().add("深圳");
//        // 保存
//        session.save(user);
        
        // 用户对象  --  Map
        user.getAddressMap().put("A0001", "广州");
        user.getAddressMap().put("A0002", "深圳");
        
        // 保存
        session.save(user);
        
        session.getTransaction().commit();
        session.close();
    }
View Code 

2. 关联映射 (重点)

  有7中关联映射:1.单向一对一,单项一对一唯一外键关联;2.单向一对多,3.单向多对一,4.单向多对多,5.双向多对多,6.双向一对多,7.双向一对一

  1)引入:集合映射,映射的集合元素,都是普通的类型, 能否为对象类型

  2)需求1:

      部门与员工:一个部门有多个员工【一对多】;多个员工,属于一个部门【多对一】

   需求2:

      项目与开发员工:一个项目有多个开发人员,一个开发人员参与多个项目【多对多】

  3)多对一映射与一对多

    映射配置:

 

    <!-- 
            一对多关联映射配置  (通过部门管理到员工)
            casecade="save-update,delete";//关联表的联机影响          
            inverse=false  set集合映射的默认值; 表示有控制权
         -->
         <set name="emps" cascade="save-update,delete" table="t_employee" inverse="true">   <!-- table="t_employee" -->
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>
  

      <!--
        多对一关联映射配置
        
      -->
      <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>

 

Inverse属性

  维护关系的时候起作用,表示控制权是否转移(只在一的一方有作用)

  inverse=false 不反转;当前方有控制权

  inverse-true 反转:当前方没有控制权,所以在以一对多存储(最后由一的结束控制关系)时就没有控制到关系了

影响

1. 保存数据 
        有影响。
       如果设置控制反转,即inverse=true, 然后通过部门方维护关联关系。在保存部门的时候,同时保存员工, 数据会保存,但关联关系不会维护。即外键字段为NULL
        
    2. 获取数据
        无。
    3. 解除关联关系?
        有影响。
        inverse=false,  可以解除关联
inverse=true,  当前方(部门)没有控制权,不能解除关联关系
(不会生成update语句,也不会报错)
    4. 删除数据对关联关系的影响?
        有影响。
        inverse=false, 有控制权, 可以删除。先清空外键引用,再删除数据。
        inverse=true,  没有控制权: 如果删除的记录有被外键引用,会报错,违反主外键引用约束!  如果删除的记录没有被引用,可以直接删除。
View Code

casecade属性 【可以设置到一的方和多的方】

  none          不级联操作, 默认值

  save-update     级联保存或更新

  delete   级联删除

  save-update,delete    级联保存、更新、删除

  all                 同上。级联保存、更新、删除

 

例子:

public class Dept {

    private int deptId;
    private String deptName;
    // 【一对多】 部门对应的多个员工
    private Set<Employee> emps = new HashSet<Employee>();
    
public class Employee {

    private int empId;
    private String empName;
    private double salary;
    // 【多对一】员工与部门
    private Dept dept;

Dept.hbm.xml    
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.b_one2Many">
    
    <class name="Dept" table="t_dept">
        <id name="deptId">
            <generator class="native"></generator>
        </id>    
        <property name="deptName" length="20"></property>
        
        <!-- 
            一对多关联映射配置  (通过部门管理到员工)
            Dept 映射关键点:
            1.  指定 映射的集合属性: "emps"
            2.  集合属性对应的集合表: "t_employee"
            3.  集合表的外键字段   "t_employee. dept_id"
            4.  集合元素的类型
            
         -->
         <set name="emps">   <!-- table="t_employee" -->
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>
         
         
    </class>
    

</hibernate-mapping>


Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.b_one2Many">
    
    <class name="Employee" table="t_employee">
        <id name="empId">
            <generator class="native"></generator>
        </id>    
        <property name="empName" length="20"></property>
        <property name="salary" type="double"></property>
        
        <!-- 
            多对一映射配置
            Employee 映射关键点:
            1.  映射的部门属性  :  dept
            2.  映射的部门属性,对应的外键字段: dept_id
            3.  部门的类型
         -->
         <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>
         
    </class>
    

</hibernate-mapping>

测试

public class App {
    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 测试时候使用
            .buildSessionFactory();
    }

    // 保存, 部门方 【一的一方法操作】
    @Test
    public void save() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 部门对象
        Dept dept = new Dept();
        dept.setDeptName("应用开发部");
        // 员工对象
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("张三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 关系
        dept.getEmps().add(emp_zs);
        dept.getEmps().add(emp_ls);

        // 保存
        session.save(emp_zs);
        session.save(emp_ls);
        session.save(dept); // 保存部门,部门下所有的员工  
        
        session.getTransaction().commit();
        session.close();
        /*
         *  结果
         *  Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: update t_employee set deptId=? where empId=?    维护员工引用的部门的id
            Hibernate: update t_employee set deptId=? where empId=?
         */
    }
    // 【推荐】 保存, 部员方 【多的一方法操作】
    @Test
    public void save2() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 部门对象
        Dept dept = new Dept();
        dept.setDeptName("综合部");
        // 员工对象
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("张三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 关系
        emp_zs.setDept(dept);
        emp_ls.setDept(dept);
        
        
        // 保存
        session.save(dept); // 先保存一的方法
        session.save(emp_zs);
        session.save(emp_ls);// 再保存多的一方,关系回自动维护(映射配置完)
        
        session.getTransaction().commit();
        session.close();
        /*
         *  结果
         *  Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            少生成2条update  sql
         */
    }
    
}
View Code

总结:多的一方来维护关系能提高效率

  一对多的保存:先保存员工,再保存部门,关系有部门来维护,多了些update代码,不推荐

  多对一的保存:先保存部门再保存员工,关系有员工来维护,效率高些

例子中两种保存都在测试App中save(),save2()

  配置一对多与多对一,这种叫“双向关联”

  只配置一对多,           “单项一对多”

  只配置多对一,           “单项多对一”

  注意:

  配置了哪一方,哪一方才有维护关联关系的权限! 

3. 多对多映射

项目与开发人员

  电商系统   OA系统   曹吉   王春

完整代码:

/**
 * 开发人员
 * 
 * @author Jie.Yuan
 * 
 */
public class Developer {
    private int d_id;
    private String d_name;
    // 开发人员,参数的多个项目
    private Set<Project> projects = new HashSet<Project>();
}

/**
 * 项目
 * 
 * @author Jie.Yuan
 * 
 */
public class Project {
    private int prj_id;
    private String prj_name;
    // 项目下的多个员工
    private Set<Developer> developers = new HashSet<Developer>();
    
Project.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_many2many">
    
    <class name="Project" table="t_project">
        <id name="prj_id">
            <generator class="native"></generator>
        </id>    
        <property name="prj_name" length="20"></property>
        <!-- 
            多对多映射:
            1.  映射的集合属性: “developers”
            2.  集合属性,对应的中间表: “t_relation”
            3. 外键字段:  prjId
            4. 外键字段,对应的中间表字段:  did
            5.   集合属性元素的类型
         -->
         <set name="developers" table="t_relation" cascade="save-update">
             <key column="prjId"></key>
             <many-to-many column="did" class="Developer"></many-to-many>
         </set>
         
    </class>
    

</hibernate-mapping>


Developer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_many2many">
    
    <class name="Developer" table="t_developer">
        <id name="d_id">
            <generator class="native"></generator>
        </id>    
        <property name="d_name" length="20"></property>
        
        <!-- 
            多对多映射配置: 员工方
                name  指定映射的集合属性
                table 集合属性对应的中间表
                key   指定中间表的外键字段(引用当前表t_developer主键的外键字段)
                many-to-many
                    column 指定外键字段对应的项目字段
                    class  集合元素的类型
         -->
        <set name="projects" table="t_relation">
            <key column="did"></key>
            <many-to-many column="prjId" class="Project"></many-to-many>
        </set>
        
         
    </class>
    

</hibernate-mapping>
View Code

 

多对多配置:

<set name="projects" table="t_relation">
            <key column="did"></key>
            <many-to-many column="prjId" class="Project"></many-to-many>
        </set>

  在多对多中设置关系维护inverse属性:

1) 保存数据
有影响。
     inverse=false ,有控制权,可以维护关联关系; 保存数据的时候会把对象关系插入中间表;
    inverse=true,  没有控制权, 不会往中间表插入数据。
2) 获取数据
    无。

3) 解除关系
    // 有影响。
    // inverse=false ,有控制权, 解除关系就是删除中间表的数据。
    // inverse=true, 没有控制权,不能解除关系。
4) 删除数据
    有影响。
    // inverse=false, 有控制权。 先删除中间表数据,再删除自身。
    // inverse=true, 没有控制权。 如果删除的数据有被引用,会报错! 否则,才可以删除
View Code

   映射总结:

一对多:
	<set name="映射的集合属性" table="(可选)集合属性对应的外键表">
	   <key column="外键表的,外键字段" />
	   <one-to-many class="集合元素的类型" />
	</set>

多对一:
    <many-to-one name="对象属性" class="对象类型" column="外键字段字段" />

多对多
    <set name="" table="">
        <key column="" />
        <many-to-many column="" class="">
    </set>

  -------------------------------------------------------------------

1.对象状态

   临时状态、持久化状态、游离状态

  临时状态:new创建出来的,不由session管理,数据库中没有对应的值

  持久化状态:session来调用save()方法,管理对象,数据库中有数据

        session.save(user); //持久化状态后再对user处理就会反应到数据库中

          user.setUserName("Jack333333"); // 会反映到数据库

  游离状态:session管理,数据库中有值管理,一般都是持久化对象session关闭后

 

  对对象的处理,不同结果是由对象当前的状态决定的!!!

    user = (User) session.get(User.class, 5);// 先检查缓存中是否有数据,如果有不查询数据库,直接从缓存中获取

  对象转换例子:

//1. 对象状态的转换
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 创建对象                        【临时状态】
        User user = new User();
        user.setUserName("Jack22222");
        // 保存                            【持久化状态】
        session.save(user);        
        user.setUserName("Jack333333");  // 会反映到数据库
        
        
        // 查询
        /*User user = (User) session.get(User.class, 5);
        user.setUserName("Tomcat");// hibernate会自动与数据库匹配(一级缓存),如果一样就更新数据库
*/        
        session.getTransaction().commit();
        session.close();
        
        
        user.setUserName("Jack444444444");
        // 打印                            【游离状态】
        System.out.println(user.getUserId());
        System.out.println(user.getUserName());
    }
View Code

  

2.缓存

  缓存目的:减少对数据库的访问,从而提供hibernate的执行效率

  1)一级缓存

    session的缓存就是一级缓存;

    1.如何将对象存入缓存:session调用方法save/saveOrUpdate/get/load/list/iterator方法时会将对象存入缓存

    2.缓存感觉:小数据感觉不到,当操作数据库次数多时就能体现出来了

    3.如何处理session的缓存:用户不能操作缓存数据,如果想要操作它的缓存数据必须通过hibernate提供的evit(),clear()方法

    4.缓存的几个方法:

        session.flush();//让一级缓存与数据库同步,同步后就相当事务提交,session就失去管理,对象处于游离状态

        session.evit(arg0);//清空缓存中指定的对象

        session.clear();//清空缓存中所有的对象

     5.什么情况用上面的方法:批量操作的时候

         Session.flush();   // 先与数据库同步

         Session.clear();   // 再清空一级缓存内容

    6.面试题:

      一:session是否会共享缓存数据?不会!

      二:list,iterator查询区别

          list查询会将所有的查询的记录都放入缓存,但不会缓存中获取数据

          iterator:查询了N+1次,N是所有的查询记录,先将所有记录的主键(1)存入缓存,之后再通过缓存中的主键获得每条记录

 

/**
     * list与iterator区别
     * 1. list 方法
     * 2. iterator 方法
     * 3. 缓存
     * @throws Exception
     */
    //1.  list 方法
    @Test
    public void list() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查询
        Query q = session.createQuery("from User ");
        // list()方法
        List<User> list = q.list();
        
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        
        session.getTransaction().commit();  
        session.close();
    }
    
    //2. iterator 方法
    @Test
    public void iterator() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        // HQL查询
        Query q = session.createQuery("from User ");
        // iterator()方法
        Iterator<User> it = q.iterate();
        while(it.hasNext()){
            // 得到当前迭代的每一个对象
            User user = it.next();
            System.out.println(user);
        }
        
        
        
        session.getTransaction().commit();  
        session.close();
    }
View Code

3.懒加载:

  1)session.get()不是懒加载,session.load()默认是懒加载

  2)懒加载对于set集合的配置:

      <!-- 
            集合属性,默认使用懒加载 
            lazy
                true 懒加载
                extra 懒加载(智能)
                false 关闭懒加载
        
        -->
         <set name="emps" lazy="extra">
              <key column="dept_id"></key>
              <one-to-many class="Employee"/>
         </set>

  3)懒加载运行路线:

    在真正使用数据的时候才向数据库发送查询的sql

    如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!

  4)懒加载异常

     Session关闭后,不能使用懒加载数据!

 

     如果session关闭后,使用懒加载数据报错:

 

         org.hibernate.LazyInitializationException: could not initialize proxy - no Session

     如何解决session关闭后不能加载数据:

        1.在关闭之前使用下数据;//dept.getDeptName();

        2.强迫代理初始化://.Hibernate.initialize(dept);

        3.直接关闭懒加载://lazy=false//在配置文件中直接修改

  5)例子:

//1. 主键查询,及区别
    @Test
    public void get_load() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = new Dept();
        // get: 及时查询
        //dept = (Dept) session.get(Dept.class, 1);
        //System.out.println(dept.getDeptName());
        
        // load,默认懒加载, 及在使用数据的时候,才向数据库发送查询的sql语句!
        dept = (Dept)session.load(Dept.class, 1);
        // 方式1: 先使用一下数据
        dept.getDeptName();
        // 方式2:强迫代理对象初始化
        //Hibernate.initialize(dept);
        // 方式3:关闭懒加载
        
        session.getTransaction().commit();
        session.close();
        
        // 在这里使用
        System.out.println(dept.getDeptName());
    }


    //1. 主键查询,及区别
    @Test
    public void set() {
        Session session = sf.openSession();
        session.beginTransaction();
        Dept dept = (Dept) session.get(Dept.class, 10);
        System.out.println(dept.getDeptName());
        System.out.println("------");
        System.out.println(dept.getEmps().isEmpty());  //  SQL,不是真正的查询
        
        session.getTransaction().commit();
        session.close();
        
    }
View Code

4.映射

  多对一;一对多;多对多;一对一(多对一的特例);组件;继承

  1)一对一映射

    例如案例:用户和身份证信息

    

    基于外键设计:

      需要注意点:user对象的idcard属性:没有外键方只有主键方用<one-to-one name="idCard" class="IdCard"></one-to-one>

            IdCard对象的user属性:有外键方用 <many-to-one name="user" column="user_id" class="User" unique="true" cascade="save-update"></many-to-one> 

              一对一是特殊的多对一:idCard的user_id外键对应唯一一个主键;

// 身份证
public class IdCard {

    // 身份证号(主键)
    private String cardNum;// 对象唯一表示(Object Identified, OID)
    private String place; //  身份证地址
    // 身份证与用户,一对一的关系
    private User user;
    
// 用户
public class User {

    private int userId;
    private String userName;
    // 用户与身份证信息, 一对一关系
    private IdCard idCard;
    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one">
    
    <class name="IdCard" table="t_IdCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>    
        <property name="place" length="20"></property>
        
        <!-- 
            一对一映射,有外键方
            unique="true"   给外键字段添加唯一约束
         -->
         <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
            
    </class>
    

</hibernate-mapping>


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one">
    
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>    
        <property name="userName" length="20"></property>
        <!-- 
            一对一映射: 没有外键方
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one>
             
    </class>
    

</hibernate-mapping>
View Code

    基于主键设计: 

      需要注意点:主键是Icard表user_id是主键,也将是User表的外键

            IdCard的主键user_id的设置:,并且需要给IdCard的user对象设置其外键约束<one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>

            User的配置就简单了idCard配置:<one-to-one name="idCard" class="IdCard"></one-to-one>

<id name="user_id">
            <!-- 
                id 节点指定的是主键映射, 即user_id是主键
                主键生成方式: foreign  即把别的表的主键作为当前表的主键;
                        property (关键字不能修改)指定引用的对象     对象的全名 cn..User、  对象映射 cn.User.hbm.xml、   table(id)
             -->
             <!-- 这里讲user表的主键作为Idcard的外键 -->
            <generator class="foreign">
                <!-- 通过他能找到User表的主键 -->
                <param name="property">user</param>
            </generator>
        </id>    
View Code
// 身份证
public class IdCard {

    private int user_id;
    // 身份证号
    private String cardNum;
    private String place; //  身份证地址
    // 身份证与用户,一对一的关系
    private User user;
    
    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.c_one2one2">
    
    <class name="IdCard" table="t_IdCard">
        <id name="user_id">
            <!-- 
                id 节点指定的是主键映射, 即user_id是主键
                主键生成方式: foreign  即把别的表的主键作为当前表的主键;
                        property (关键字不能修改)指定引用的对象     对象的全名 cn..User、  对象映射 cn.User.hbm.xml、   table(id)
             -->
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>    
        <property name="cardNum" length="20"></property>
        <property name="place" length="20"></property>
        
        <!-- 
            一对一映射,有外键方
            (基于主键的映射)
             constrained="true"  指定在主键上添加外键约束
         -->
        <one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>
            
    </class>
    

</hibernate-mapping>
View Code

  2)组件映射,继承映射

    类关系分为:组合关系:,继承关系;

    1.组件映射:例如Car ,Wheel就属于组件映射

      主要Car的映射配置:

<component name="wheel" class="Wheel"><property name="count"></property><property name="size"></property></component>

完整例子:

public class Car {

    private int id;
    private String name;
    // 车轮
    private Wheel wheel;
}
// 车轮
public class Wheel {

    private int count;
    private int size;
}
<hibernate-mapping package="cn.itcast.d_component">
    
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>    
        <property name="name" length="20"></property>
        
        <!-- 组件映射 -->
        <component name="wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>
        
                     
    </class>
    

</hibernate-mapping>
View Code

    2.继承映射

      1)简单继承映射:

          直接写子类映射就可以了,父类的属性直接写上;

          简单继承映射,有多少个子类,写多少个映射文件!

// 动物类
public abstract class Animal {

    private int id;
    private String name;
    
<!-- 
    简单继承
 -->
<hibernate-mapping package="cn.itcast.e_extends1">
    
    <class name="Cat" table="t_Cat">
        <!-- 简单继承映射: 父类属性直接写 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        <property name="catchMouse"></property>                     
    </class>
    

</hibernate-mapping>

@Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 保存
//        Cat cat = new Cat();
//        cat.setName("大花猫");
//        cat.setCatchMouse("抓小老鼠");
//        session.save(cat);
        
        // 获取时候注意:当写hql查询的使用,通过父类查询必须写上类的全名
        Query q = session.createQuery("from cn.itcast.e_extends1.Animal");
        List<Animal> list = q.list();
        System.out.println(list);
        
        session.getTransaction().commit();
        session.close();
        
    }
    
View Code

      2)复杂继承映射

          需求:猫,狗,动物

          1.所有子类映射到一张表

            1)运用场景:子类较多只有个别属性;

            2)好处缺点:只有一个映射文件,但是不符合数据库的设计原则

            3)保存在数据库中的表样子:

            4)配置文件:

              <subclass name="Cat" discriminator-value="cat_">

               每个子类节点都用subclass节点映射,discriminator-value鉴别器字段

<class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <!-- 指定鉴别器字段(区分不同的子类) -->
        <discriminator column="type_"></discriminator>
        
        <property name="name"></property>
        
        <!-- 
            子类:猫
                每个子类都用subclass节点映射
                注意:一定要指定鉴别器字段,否则报错!
                鉴别器字段:作用是在数据库中区别每一个子类的信息, 就是一个列
            discriminator-value="cat_"
                指定鉴别器字段,即type_字段的值
                如果不指定,默认为当前子类的全名
         -->
         <subclass name="Cat" discriminator-value="cat_">
             <property name="catchMouse"></property>
         </subclass>
         
         <!-- 
             子类:猴子
          -->
          <subclass name="Monkey" discriminator-value="monkey_">
              <property name="eatBanana"></property>
          </subclass>
        
    </class>
View Code

          2.每个类映射到一张表【3张表】

            1)配置文件;

              joined-subclass关键字表示子类

<class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子类:猫  t_cat
            key 指定_cat表的外键字段
        -->
        <joined-subclass name="Cat" table="t_cat">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
        </joined-subclass>
        
        <!-- 子类:猴子  t_monkey -->
        <joined-subclass name="Monkey" table="t_monkey">
            <key column="t_animal_id"></key>
            <property name="eatBanana"></property>
        </joined-subclass>
        
    </class>
View Code

            2)优点缺点:一个映射文件存储所有子类;子类父类都有映射;但表结构比较复杂,子类插入一条数据执行2条sql

          3.(推荐)每个子类映射一张表,父类不对应表(2张表)

            1)配置文件:

              父类设置abstract="true"指定实体对象不对应表,即在数据库中不生成表

              union-subclass节点存储子类

<!-- 
         abstract="true"  指定实体类对象不对应表,即在数据库段不生成表
     -->
    <class name="Animal" abstract="true">
        <!-- 如果用union-subclass节点,主键生成策略不能为自增长! -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子类:猫  t_cat
            union-subclass  
                table 指定为表名, 表的主键即为id列
        -->
        <union-subclass name="Cat" table="t_cat">
            <property name="catchMouse"></property>
        </union-subclass>
        
        <!-- 子类:猴子  t_monkey -->
        <union-subclass name="Monkey" table="t_monkey">
            <property name="eatBanana"></property>
        </union-subclass>
        
    </class>
View Code

 -------------------------------------------------------------------------------------------

1.hibernate查询

  1)Get/load主键查询

    Dept dept =  (Dept) session.get(Dept.class, 12);

    Dept dept =  (Dept) session.load(Dept.class, 12);

  2)对象导航查询

Dept dept =  (Dept) session.get(Dept.class, 12);

    System.out.println(dept.getDeptName());

    System.out.println(dept.getEmps());

  3)HQL查询Hibernate Query language  hibernate 提供的面向对象的查询语言。

    // 注意:使用hql查询的时候 auto-import="true" 要设置true,

            如果是false,写hql的时候,要指定类的全名

        Query q = session.createQuery("from Dept");

      System.out.println(q.list());

查询的几种例子:函数查询,连接查询

// a. 查询全部列
//        Query q = session.createQuery("from Dept");  //OK
//        Query q = session.createQuery("select * from Dept");  //NOK, 错误,不支持*
//        Query q = session.createQuery("select d from Dept d");  // OK
//        System.out.println(q.list());

        // b. 查询指定的列  【返回对象数据Object[] 】
//        Query q = session.createQuery("select d.deptId,d.deptName from Dept d");  
//        System.out.println(q.list());
        
        // c. 查询指定的列, 自动封装为对象  【必须要提供带参数构造器】
//        Query q = session.createQuery("select new Dept(d.deptId,d.deptName) from Dept d");  
//        System.out.println(q.list());
        
        // d. 条件查询: 一个条件/多个条件and or/between and/模糊查询
        // 条件查询: 占位符
//        Query q = session.createQuery("from Dept d where deptName=?");
//        q.setString(0, "财务部");
//        q.setParameter(0, "财务部");
//        System.out.println(q.list());
        
        // 条件查询: 命名参数
//        Query q = session.createQuery("from Dept d where deptId=:myId or deptName=:name");
//        q.setParameter("myId", 12);
//        q.setParameter("name", "财务部");
//        System.out.println(q.list());
        
        // 范围
//        Query q = session.createQuery("from Dept d where deptId between ? and ?");
//        q.setParameter(0, 1);
//        q.setParameter(1, 20);
//        System.out.println(q.list());
        
        // 模糊
//        Query q = session.createQuery("from Dept d where deptName like ?");
//        q.setString(0, "%部%");
//        System.out.println(q.list());
        

        // e. 聚合函数统计
//        Query q = session.createQuery("select count(*) from Dept");
//        Long num = (Long) q.uniqueResult();
//        System.out.println(num);
        
        // f. 分组查询
        //-- 统计t_employee表中,每个部门的人数
        //数据库写法:SELECT dept_id,COUNT(*) FROM t_employee GROUP BY dept_id;
        // HQL写法
//        Query q = session.createQuery("select e.dept, count(*) from Employee e group by e.dept");
//        System.out.println(q.list());

//1) 内连接   【映射已经配置好了关系,关联的时候,直接写对象的属性即可】
//        Query q = session.createQuery("from Dept d inner join d.emps");
        
        //2) 左外连接
//        Query q = session.createQuery("from Dept d left join d.emps");

        //3) 右外连接
        Query q = session.createQuery("from Employee e right join e.dept");
        q.list();
View Code

迫切连接

//1) 迫切内连接    【使用fetch, 会把右表的数据,填充到左表对象中!】
//        Query q = session.createQuery("from Dept d inner join fetch d.emps");
//        q.list();
        
        //2) 迫切左外连接
        Query q = session.createQuery("from Dept d left join fetch d.emps");
        q.list();
View Code

HQL优化:HQL放到映射文件中

  映射文件:

<!-- 存放sql语句 -->
    <query name="getAllDept">
        <![CDATA[
            from Dept d where deptId < ?
        ]]>
        
    </query>
// HQL 放到映射文件中
        Query q = session.getNamedQuery("getAllDept");
        q.setParameter(0, 10);
        System.out.println(q.list());
View Code

  4)Criteria查询,完全面向对象查询(Query By Criteria  ,QBC)

Criteria criteria = session.createCriteria(Employee.class);
        // 构建条件
        criteria.add(Restrictions.eq("empId", 12));
//        criteria.add(Restrictions.idEq(12));  // 主键查询
View Code

  5)SQLQuery本地查询

SQLQuery q = session.createSQLQuery("SELECT * FROM t_Dept limit 5;")
            .addEntity(Dept.class);  // 也可以自动封装
        System.out.println(q.list());
View Code

  6)分页:

 Query q = session.createQuery("from Employee");
         
         // 从记录数
         ScrollableResults scroll = q.scroll();  // 得到滚动的结果集
         scroll.last();                            //  滚动到最后一行
         int totalCount = scroll.getRowNumber() + 1;// 得到滚到的记录数,即总记录数
         
         // 设置分页参数
         q.setFirstResult(0);
         q.setMaxResults(3);
         
         // 查询
         System.out.println(q.list());
         System.out.println("总记录数:" + totalCount);
View Code

2.Hibernate对连接池的支持

  1)hibernate自带连接池和配置文件和c3p0配置

Hibernate 自带的也有一个连接池,且对C3P0连接池也有支持!

Hbm 自带连接池:
    只维护一个连接,比较简陋。
    可以查看hibernate.properties文件查看连接池详细配置:

#################################
### Hibernate Connection Pool ###     
#################################

hibernate.connection.pool_size 1        【Hbm 自带连接池: 只有一个连接】



###########################
### C3P0 Connection Pool###           【Hbm对C3P0连接池支持】
###########################

#hibernate.c3p0.max_size 2                最大连接数
#hibernate.c3p0.min_size 2                最小连接数
#hibernate.c3p0.timeout 5000           超时时间
#hibernate.c3p0.max_statements 100     最大执行的命令的个数
#hibernate.c3p0.idle_test_period 3000    空闲测试时间
#hibernate.c3p0.acquire_increment 2     连接不够用的时候, 每次增加的连接数
#hibernate.c3p0.validate false
View Code

  2)如何选择自己的连接池:可以在Hibernate.cfg.xml中配置

<!-- 【连接池配置】 -->
        <!-- 配置连接驱动管理类 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <!-- 配置连接池参数信息 -->
        <property name="hibernate.c3p0.min_size">2</property>
        <property name="hibernate.c3p0.max_size">4</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_statements">10</property>
        <property name="hibernate.c3p0.idle_test_period">30000</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>
View Code

3.二级缓存

  1)引入二级缓存i

    一级缓存Session只在当前的有效,Sesssion关闭就会失效,而二级缓存作用了应用程序,可以跨越多个session

  2)优点:享用二级缓存在配置文件中配置下就好,不想用删了就可以;也可以换成自己的缓存框架

  3)配置二级缓存

    hibernate.properties中对二级缓存的配置

##########################
### Second-level Cache ###
##########################

#hibernate.cache.use_second_level_cache false【二级缓存默认不开启,需要手动开启】
#hibernate.cache.use_query_cache true      【开启查询缓存】

## choose a cache implementation        【二级缓存框架的实现】

#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默认实现
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
View Code

    二级缓存的使用步骤:

    打开二级缓存-》选择缓存框架-》指定哪些类加入缓存-》测试

    其中指定哪些类加入缓存可以用缓存策略:

    HQL查询  【setCacheable  指定从二级缓存找,或者是放入二级缓存】

<class-cache usage="read-only"/>     放入二级缓存的对象,只读; 
    <class-cache usage="nonstrict-read-write"/>  非严格的读写
    <class-cache usage="read-write"/>    读写; 放入二级缓存的对象可以读、写;
    <class-cache usage="transactional"/>   (基于事务的策略)
View Code

例子:

集合缓存:

<usage="read-wirte" collection="Dept.emps">集合缓存的元素对象也加入二级缓存

查询缓存:list()默认存入缓存不会存入一级,只能存入二级中

Hibernate.cfg.xml
<!--****************** 【二级缓存配置】****************** -->
        <!-- a.  开启二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- b. 指定使用哪一个缓存框架(默认提供的) -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
        <!-- 开启查询缓存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- c. 指定哪一些类,需要加入二级缓存 -->
        <class-cache usage="read-write" class="cn.itcast.b_second_cache.Dept"/>
        <class-cache usage="read-only" class="cn.itcast.b_second_cache.Employee"/>
        <!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
        <collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
        
App  测试类

public class App {
    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 测试时候使用
            .buildSessionFactory();
    }
    // 1. 测试二级缓存的使用
    // 没有/有用 二级缓存
    @Test
    public void testCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // a. 查询一次
        Dept dept = (Dept) session1.get(Dept.class, 10);
        dept.getEmps().size();// 集合
        session1.getTransaction().commit();
        session1.close();
        
        System.out.println("------");
        
        // 第二个session
        Session session2 = sf.openSession();
        session2.beginTransaction();
        // a. 查询一次
        dept = (Dept) session2.get(Dept.class, 10);  // 二级缓存配置好; 这里不查询数据库
        dept.getEmps().size();
        
        session2.getTransaction().commit();
        session2.close();
    }
    
    
    @Test
    public void listCache() {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        // HQL查询  【setCacheable  指定从二级缓存找,或者是放入二级缓存】
        Query q = session1.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());
        session1.getTransaction().commit();
        session1.close();
        
        
        Session session2 = sf.openSession();
        session2.beginTransaction();
        q = session2.createQuery("from Dept").setCacheable(true);
        System.out.println(q.list());  // 不查询数据库: 需要开启查询缓存
        session2.getTransaction().commit();
        session2.close();
    }
}
View Code

  4)Session的管理方式:

    可以sf.openSession()方式创建;

    可以通过线程的方式创建 :首先要在配置文件中配置:

<property name="hibernate.current_session_context_class">thread</property>

之后:通过sf.getCurrentSession()获得当前的线程的session或者存入session

@Test
    public void testSession() throws Exception {
        //openSession:  创建Session, 每次都会创建一个新的session
        Session session1 = sf.openSession();
        Session session2 = sf.openSession();
        System.out.println(session1 == session2);
        session1.close();
        session2.close();
        
        //getCurrentSession 创建或者获取session
        // 线程的方式创建session  
        // 一定要配置:<property name="hibernate.current_session_context_class">thread</property>
        Session session3 = sf.getCurrentSession();// 创建session,绑定到线程
        Session session4 = sf.getCurrentSession();// 从当前访问线程获取session
        System.out.println(session3 == session4);
        
        // 关闭 【以线程方式创建的session,可以不用关闭; 线程结束session自动关闭】
        //session3.close();
        //session4.close(); 报错,因为同一个session已经关闭了!
    }
View Code

4.Hibernatestruts小案例

      

posted @ 2017-05-26 17:31  王吉平  阅读(185)  评论(0编辑  收藏  举报