Hibernate笔记

JavaEE的三层结构

  1. web层:struts2框架
  2. service层:spring框架
  3. dao层:hibernate框架
    MVC思想
  4. m:模型
  5. v:视图
  6. c:控制器

1 Hibernate概述

1.1 框架

1.1.1 通俗理解

就是少写部分代码实现功能

1.2 Hibernate框架

  1. hibernate框架应用在JavaEE三层结构中的dao层
  2. 在dao层hibernate实现CRUD操作,hibernate底层就是JDBC,对JDBC进行封装,好处是不需要写复杂的
    JDBC代码了,不需要写SQL语句实现
  3. hibernate开源轻量级框架
  4. hibernate 版本
    Hibernate3.x
    Hibernate4.x
    Hibernate5.x(2017最新)

1.3 底层实现

1.3.1 ORM思想
  1. hibernate使用ORM思想对数据库进行CRUD操作
  2. 在web阶段学习Javabean更正确的叫法交:实体类
  3. ORM:Object Relational Mapping 对象关系映射
    实体类数据库 表进行一一对应关系
    实体类数据库 表进行对应
    实体类 属性 里面 字段 对应
    不需要操作数据库而是直接操作对应的实体类对象

2 Hibernate使用

2.1 入门

2.1.1 hibernate开发环境搭建
1)导入jar包

下载hibernate-release-5.2.10.Final
①压缩包中的lib中的required文件夹内的jar包
②jpa的jar包也在required内
③日志jar包.hibernate本身没有日志输出jar包,需导入其他日志输出jar包 log4j,slf4j-log4j,slf4j-api
④mysqk驱动jar包mysql-connector-java

2)创建实体Bean类

hibernate 要求有个实体类有一个属性唯一
(可以手动在mysql中创建表与bean类属性对应,也可以由hibernate自己创建)

package cn.wanxp.entity;

public class User {
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    /*hibernate 要求有个实体类有一个属性唯一*/
    private int uid;
    private String username;
    private String password;
    private String address;
}

3) 配置实体类和数据库表一一对应关系(映射关系)

实用配置文件实现映射关系
(1) 创建xml格式的配置文件
映射文件的名称和位置没有固定要求
-- 建议在实体类的所在包内 实体类名称.hbm.xml
(2) 配置文件是xml格式,在配置文件中引入xml约束
-- hibernate里面引入的约束是dtd格式
(3)在包内创建映射文件User.hbm.xml
约束可以是本地文件在hibernate-release-5.2.10.Final内可搜索到,复制与User放一起然后写

<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >  

也可以是网上公用的DTD文件,如果是使用网上的dtd约束则

<!DOCTYPE hibernate-mapping PUBLIC   
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"   
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">  
<!-- 上面这段约束可在hibernate-mapping-3.0.dtd中开头部分找到 -->      
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
    <!-- 1配置类和表对应
        class标签
            name 属性: 实体类全路径
            table 属性:数据库名称(随意名称)
     -->
     <class name="cn.wanxp.entity.User" table="t_user">
        <!-- 2配置实体类id和表id一一对应
            要求两id在各自位置具有唯一性
         -->
         <!-- 
            id标签
                name属性: 实体类里面id属性名称
                column属性:生成表字段名称(名称随意)
          -->
         <id name="uid" column="uid">
            <!--设置数据表id增长策略  
                native 生成表id是主键和自增长
            -->
            <generator class="native"></generator>
         </id>
         <!-- 配置其他属性与字段对应 
            name属性:实体类属性名称  
            column属性:数据表字段名称
         -->
         <property name="username" column="username"></property>
         <property name="password" column="password"></property>
         <property name="address" column="address"></property>
     </class>
    
</hibernate-mapping>
4)创建hibernate的核心配置文件

(1)核心配置文件格式xml,配置文件名称和位置固定
-位置: 必须src下面
-名称: 必须hibernate.cfg.xml
(2)引入约束

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

(3)hibernate 操作过程中只会加载核心配置文件,其它配置文件不会加载
第一部分:配置数据库信息
第二部分:配置hibernate信息
第三部分:映射文件放在核心配置文件中
注意
①方言
注意下面的这句:是MySQL5Dialect,不是properties文件中的MySQLDialect因为版本不一样了,
不然无法创建表。原因就是使用没有5的会用创建命令type=,而不是engine=

  <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

②字符集
-1- 如要使用字符集utf8,想要支持中文时:
先创建数据库database加上字符集:create database hibernate_day01 charset utf8 ;
然后在hibernate.cfg.xml加上下面标签属性,表示和数据库传递数据采用utf8编码

<property name="connection.useUnicode">true</property>
<property name="connection.characterEncoding">UTF-8</property>

hibernate.cfg.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
    <!-- 第一部分:配置数据库信息 
        在下载的hibernate-release压缩包文件夹内搜索hibernate.properties打开后可看到对应的数据库需要配置的属性
    -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_day01</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <property name="connection.useUnicode">true</property>
    <property name="connection.characterEncoding">UTF-8</property>
    <!-- 第二部分:配置hibernate信息,可选的,可以配置也可以不用配置
        同样能在hibernate.properties搜索到相应的值
     -->
        <!-- 如:输出底层sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 如:格式化底层sql语句 -->
        <property name="hibernate.format_sql">true</property>
        <!-- 如:hibernate创建表,需要配置后
            update:如果已经存在表则更新,若不存在则创建
         -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 配置数据库方言
            mysql里实现分页关键字 limit 只能在mysql里面
            Oracle数据库,实现分页rownum
            在hibernate框架识别不同数据库自己特有的语句
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        
    <!-- 第三部分:配置映射文件 -->
        <!-- resource引入路径根目录是src -->
        <mapping resource="/cn/wanxp/entity/User.hbm.xml" />
    </session-factory>
</hibernate-configuration>
5)测试 添加操作 1-4,6,7写法基本都是固定的,变化的只有第5步

(1)加载hibernate核心配置文件
(2)创建SessionFactory对象
(3)使用SessionFactory创建session对象
(4)开启事务
(5)写具体逻辑
(6)提交事务
(7)关闭资源
示例如下

package cn.wanxp.hibernatetest;


import java.io.UnsupportedEncodingException;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.wanxp.entity.User;

public class HibernateDemo {
    @Test
    public void testAdd() {
//      (1)加载hibernate核心配置文件  
            //在hibernate里面封装对象  
            //将会到src下找到hibernate.cfg.xml的文件
        Configuration cfg = new Configuration();
        cfg.configure();
//      (2)创建SessionFactory对象 
            //读取hibernate核心配置文件,创建SessionFactory
            //过程中根据配置文件中的属性以及映射关系xml文件创建表
        SessionFactory sessionFactory=cfg.buildSessionFactory();
//      (3)使用SessionFactory创建session对象
            //类似于JDBC连接
        Session session = sessionFactory.openSession();
//      (4)开启事务  
        Transaction tx = session.beginTransaction();
//      (5)写具体逻辑编写CRUD操作  
         User user = new User();
         String username="张三";
         user.setUsername(username);
         user.setPassword("password1");
         user.setAddress("上海");
         session.save(user);
//      (6)提交事务  
        tx.commit();
//      (7)关闭资源 
        session.close();
        sessionFactory.close();
    }
}

效果如下:

hibernate-first-test

2 hibernate详解

2.1 hibernate配置文件

2.1.1 hibernate映射配置文件
  • 映射文件没有固定位置
    映射配置文件中,标签name属性写实体类中的属性名;column可以不用写,字段名就会命名为实体类属性同一名字。
    property标签type属性可以设置数据表的字段属性。
2.1.2 核心配置文件

标签位置要求:

<hibernate-configuration>
    <session-factory>
  ...
  </session-factory>  
</hibernate-configuration>

配置的三部分要求

  • 数据库部分--必须
  • hibernate部分--可选
  • 映射文件--必须

2.2 hibernate核心api

2.2.1 Configuration
Configuration cfg = new Configuration();
cfg.configure();

上面代码的过程:从src下找到hibernate.cfg.xml文件并根据里面的配置,创建对象并加载

2.2.2 SessionFactory
  1. 根据配置文件中的属性创建表
<!-- 如:hibernate创建表,需要配置后
  update:如果已经存在表则更新,若不存在则创建  
 -->
<property name="hibernate.hbm2ddl.auto">update</property>
  1. 创建SessionFactory过程,特别耗费资源,所以一般一个项目只创建一次
  2. 具体实现
    (1)写工具类,写静态代码块
package cn.wanxp.utils;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {
    private static Configuration cfg = null;
    private static SessionFactory sessionFactory = null;
    static {
        cfg = new Configuration();
        cfg.configure();
        sessionFactory = cfg.buildSessionFactory();
        
    }
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}
2.2.3 Session
  1. Sesssion不同方法实现CRUD操作
    (1) 添加:save
    (2) 修改:update
    (3) 删除:delete
    (4) 根据id查询:get
  2. session对象单线程对象
    (1) session对象不能共用,只能自己使用
2.2.4 Transaction
  1. 事务对象
Transaction tx =  session.beginTransaction();
  1. 事物提交和回滚方法
tx.commit();
tx.rollback();
  1. 事物
    (1) 事物四个特性
    原子性、一致性、隔离性、持久性
2.3 配置文件没有提示

复制下面的uri

http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd

Eclipse->Preferences->XML Catalog->Add->

location---找到hibernate-configuration-3.0.dtd    
uri----http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd

这样就能不用联网就能使用http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd

3 hibernate

3.1 实体类编写

3.1.1 编写规则

1) 私有域
2) set-get方法
3) 实体类有属性有唯一值
4) 实体类建议不使用基本类型,使用基本数据类型的包装类
(1) int--Integer,char--Character
(2) 比如表示学生分数用int就只能表示分数大小,不能使用null表示没参加,而是用Integer就能解决这个问题了

3.2 Hibernate主键生成策略

1) hibernate要求实体类厘米吗必须有一个属性作为唯一值,对应表主键,主键可以不同生成策略
2) hibernate主键生成策略有很多值
3) 在class里面的值
主要: native,uuid
UUID生成策略的实体类属性表id的类型是String类型

private String uid;
<generator>

3.3 Hibernate CRUD

3.3.1 增
User user = new User();
user.setUsername("李四");
user.setPassword("123456");
user.setAddress("上海");
session.save(user);
3.3.2 删

方法1 查询-删除

User user =session.get(User.class , 3);
session.delete(user);

方法2 设置-删除

User user = new User();
user.setId(3);
session.delete(user);
3.3.3 改

方法1

方法2 查询-修改

//李四换成王五
package cn.wanxp.hibernatetest;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;

public class HibernateDemoDay02 {
    @Test
    public void testAdd() {
//      直接调用SessionFactory调用静态调用方法   
        SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
//      (3)使用SessionFactory创建session对象
            //类似于JDBC连接
        Session session = sessionFactory.openSession();
//      (4)开启事务  
        Transaction tx = session.beginTransaction();
//      (5)写具体逻辑编写CRUD操作  
         User user = new User();
         String username="李四";
         user.setUsername(username);
         user.setPassword("password1");
         user.setAddress("上海");
         session.save(user);   
         
         System.out.println(user);
         
         user = session.get(User.class , 3);
         user.setUsername("王五");
         session.update(user);  
         user = session.get(User.class , 3);
         System.out.println(user);
//      (6)提交事务  
        tx.commit();
//      (7)关闭资源 
        session.close();
        sessionFactory.close();
    }
}

3.3.4 查
User user = session.get(User.class,2);//查询并返回id=2的user

3.3.5 save,update,saveOrUpdate

save:id没有效果 ,只会直接添加
update:id的对应记录更新所有内容

3.4 实体类对象状态

3.4.1实体类状态三种状态

1)瞬时态,对象里面没有id值,对象与session没有关联

User user = new User();
user.setUsername("Jack");
user.setPassword("password");
user.setAddress("上海");

2)持久态,对象里有id值,与session相关联

User user = session.get(User.class , 3);
  1. 托管态,对象里有id值,与session没有关联
user.setId(3);
3.4.2演示操作实体类对象的方法
  1. saveOrUpdate方法:
  • 实体类状态瞬时态,这个方法做的是添加操作
  • 实体类状态托管态,这个方法做的是修改操作
  • 实体类状态持久态,这个方法做的是修改操作

4 hibernate的优化

4.1 缓存

hibernate有两级缓存:一级缓存,二级缓存

4.1.1 一级缓存

1)第一类 hibernate一级缓存特点
(1)hibernate的一级缓存默认打开的
(2)hibernate的一级缓存适用范围,是session范围,从session创建到session关闭范围
(3)hibernate的一级缓存中,存储数据必须持久态数据
2)第二类hibernate的二级缓存
(1)目前 已经不适用了,替代技术redis
(2)二级缓存默认不是打开的,需要配置
(3)二级缓存使用范围,是SessionFactory范围

4.1.2 验证一级缓存的存在

1)验证方式
(1)首先根据uid=1查询,返回对象
(2)再次根据uid=1查询,返回对象
第二次查询将不会查询数据库而是直接拿出数据

4.1.3 一级缓存过程

session.get(),会先去一级缓存中查询,若没有则去数据库,若有直接拿出。
session范围是指从创建到close();

4.1.4 一级缓存的特点

持久态会自动执行

User user = session.get(User.class , 3);//将对象保存至一级缓存和快照区
user.setUsername("赵武");   //会同时修改:修改user对象里的值,修改持久态对象的值,修改一级缓存中的内容,不会修改快照区内容
tx.commit();//执行到这里即使没有update,也会修改数据库
//commit做的内容:提交事务,做的事情,比较一级缓存和快照区内容是否相同,若不同则将一级缓存内容写入数据库,若相同则不修改    

效果图如下

效果图

4.2 事务详解

4.2.1 什么是事务
4.2.2 事务的特性

原子性,一致性,隔离性,持久性

4.2.3 不考虑隔离性产生的问题
  1. 脏读
    2)不可重复读
    3)虚读
4.2.4 设置事务隔离级别

1)mysql默认隔离级别 repeateable read
2)级别设置 在hibernate.cfg.xml中

<!--
  1-Read uncommited isolation
  2-Read commited isolation
  4-Repeatable read isolation
  8-Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
4.2.5 hibernate规范的事务写法
try{
  //开启事务
  //提交事务
}catch(){
  //回滚事务
}final{
  //关闭操作
}
@Test
public void testRollback() {
  SessionFactory sessionFactory = null;
  Session session = null;
  Transaction tx = null;
  try {
    sessionFactory = HibernateUtils.getSessionFactory();
    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    User user = session.get(User.class , 3); 
    user.setUsername("毛毛");
    tx.commit();
  }catch(Exception e) {
    e.printStackTrace();
    tx.rollback();
  }finally{
    session.close();
    sessionFactory.close();
  }
}

4.3 hibernate绑定session线程

4.3.2 介绍

为了保证session是单线程的,而不会被其它人使用。
注意 由于与本地线程绑定,所以不需要关闭session了,因为本地线程一完成,session自动关闭

4.3.2 配置
  1. hibernate.cfg.xml中配置
<property name="hibernate.current_session_context_class">thread</property>
  1. 调用SessionFactory对象里的的方法得到
public static Session getSessionObject(){
  return sessionFactory.getCurrentSession();
}

5 hibernate的api使用

5.1 查询

Query对象,Criteria对象 SQLQuery对象

5.1.1 Query对象

1)使用Query对象不需要写sql语句,但是写hql语句
(1)hql:hibernate query language,hibernate提供查询语句
这个hql语句和普通sql很相似
(2) hql和sql语句区别
sql操作表和字段
hql操作实体类和属性
2)查询所有hql语句:
(1)from实体类名称
3) Query对象使用
(1)创建 Query对象
(2)调用Query对象里的方法得到结果

Query query = session.createQuery("from User");
List<User> list = query.list();
5.1.2 Criteria对象

1) Query对象使用
(1)创建 Query对象
(2)调用Query对象里的方法得到结果

Criteria criteria = session.createCriteria(User.class);
List<User> list = query.list();
5.1.3 SQLQuery对象
  1. 使用SQL底层语句
    2) SQLQuery对象使用
    (1)创建 Query对象
    (2)调用Query对象里的方法得到结果(默认是数组)
SQLQuer sqlQuery = session.createSQLQuery("select * from t_user where username='张三'");
//默认返回的数组
List list = query.list();

(3)调用Query对象里的方法得到结果为对象的做法

SQLQuer sqlQuery = session.createSQLQuery("select * from t_user where username='张三'");
sqlQuery.addEntity(User.class);
List<User> list = query.list();

6 hibernate的表关系操作

6.1 表的关系

1)一对多
一个分类有多个商品
2)多对多
订单与商品,一个订单多个商品,一个商品多个订单
3)一对一
丈夫与妻子

6.2 一对多

6.2.1 一对多表创建

1)创建“一”实体类并在里面创建“多”的Set(集合)如HashSet
2)创建“多”实体类并在里面创建“一”的实体的对象作为私有域
3)创建“一”实体类映射表,将基本的私有域建立好映射
4)创建“多”实体类映射表,将基本的私有域建立好映射
5)在配置文件中配置一对多关系
6)创建核心配置文件hibernate.cfg.xml
下面是一个例子,客户(企业)类,每个(客户)企业都有很多员工的联系方式,客户表-员工联系方式表就是一对多的关系

①----客户实体类 Customer.java

package cn.wanxp.entity;

import java.util.HashSet;
import java.util.Set;


public class Customer {

    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCustomerName() {
        return customerName;
    }
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }
    public String getCustomerLevel() {
        return customerLevel;
    }
    public void setCustomerLevel(String customerLevel) {
        this.customerLevel = customerLevel;
    }
    public String getCustomerSource() {
        return customerSource;
    }
    public void setCustomerSource(String customerSource) {
        this.customerSource = customerSource;
    }
    public String getCustomerPhone() {
        return customerPhone;
    }
    public void setCustomerPhone(String customerPhone) {
        this.customerPhone = customerPhone;
    }
    public String getCustomerMobile() {
        return customerMobile;
    }
    public void setCustomerMobile(String customerMobile) {
        this.customerMobile = customerMobile;
    }
    
    //在客户实体类中表示多个联系人,一个客户对应多个联系人   
    //hibernate要求使用集合表示多的数据,使用Set集合
    private Set<Linkman> setLinkman = new HashSet<Linkman>();
    
    
    public Set<Linkman> getSetLinkman() {
        return setLinkman;
    }
    public void setSetLinkman(Set<Linkman> setLinkman) {
        this.setLinkman = setLinkman;
    }

    private Integer cid;
    private String customerName;
    private String customerLevel;
    private String customerSource;
    private String customerPhone;
    private String customerMobile;
}

②----”多“的表实体类Linkman.java

package cn.wanxp.entity;

public class Linkman {
    public Integer getLid() {
        return lid;
    }
    public void setLid(Integer lid) {
        this.lid = lid;
    }
    public String getLinkmanName() {
        return linkmanName;
    }
    public void setLinkmanName(String linkmanName) {
        this.linkmanName = linkmanName;
    }
    public String getLinkmanGender() {
        return linkmanGender;
    }
    public void setLinkmanGender(String linkmanGender) {
        this.linkmanGender = linkmanGender;
    }
    public String getLinkmanPhone() {
        return linkmanPhone;
    }
    public void setLinkmanPhone(String linkmanPhone) {
        this.linkmanPhone = linkmanPhone;
    }
    
    //在联系人实体类里表示所属客户,一个联系人属于一个客户
    private Customer customer = new Customer();
    
    
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    private Integer lid;
    private String linkmanName;
    private String linkmanGender;
    private String linkmanPhone;
}

③----Customer实体配置文件Customer.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
     <class name="cn.wanxp.entity.Customer" table="t_customer">
         <id name="cid" column="cid">
            <generator class="native"></generator>
         </id>
         <property name="customerName" column="customerName"></property>
         <property name="customerLevel" column="customerLevel"></property>
         <property name="customerSource" column="customerSource"></property>
         <property name="customerPhone" column="customerPhone"></property>
         <property name="customerMobile" column="customerMobile"></property>
         <!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人 
                set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
         -->
         <set name="setLinkman">
            <!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键 
                column属性值:外键名称
            -->
            <key column="cid"></key>
            <one-to-many class="cn.wanxp.entity.Linkman"/>
         </set>
     </class>
</hibernate-mapping>

④----Linkman配置文件 Linkman.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
     <class name="cn.wanxp.entity.Linkman" table="t_linkman">
        <id name="lid" column="lid">
            <generator class="native"></generator>
         </id>
         <property name="linkmanName" column="linkmanName"></property>
         <property name="linkmanGender" column="linkmanGender"></property>
         <property name="linkmanPhone" column="linkmanPhone"></property>
         <!-- 在联系人中配置所属客户 
            name属性:因为在联系人实体类使用Customer对象表示。写customer名称
            class属性:Customer全路径
            column属性是生成的表格存储Customer的id的列名称要与Customer.hbm.xml中key一致
         -->
         <many-to-one name="customer" class="cn.wanxp.entity.Customer" column="cid"></many-to-one>
     </class>
</hibernate-mapping>

⑤----同样需要将配置文件加入到hibernate核心配置文件中

<mapping resource="/cn/wanxp/entity/Customer.hbm.xml" />
<mapping resource="/cn/wanxp/entity/Linkman.hbm.xml" />

⑥----测试效果

package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import cn.wanxp.entity.User;
public class HibernateDemoDay03 {
    @Test
    public void testOneToMany() {
        Configuration cfg = new Configuration();
        cfg.configure();
        SessionFactory sessionFactory=cfg.buildSessionFactory();
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        tx.commit();
        session.close();
        sessionFactory.close();
    }
}

⑦----输出结果

Hibernate: 
    
    create table t_customer (
       cid integer not null auto_increment,
        customerName varchar(255),
        customerLevel varchar(255),
        customerSource varchar(255),
        customerPhone varchar(255),
        customerMobile varchar(255),
        primary key (cid)
    ) engine=MyISAM
Hibernate: 
    
    create table t_linkman (
       lid integer not null auto_increment,
        linkmanName varchar(255),
        linkmanGender varchar(255),
        linkmanPhone varchar(255),
        cid integer,
        primary key (lid)
    ) engine=MyISAM
Hibernate: 
    
    alter table t_linkman 
       add constraint FKn4dw5i2knad5dl95xit4v1b0p 
       foreign key (cid) 
       references t_customer (cid)
6.2.1 一对多表操作

1)一对多级联保存 -- 复杂写法
直接在session中设置并保存

package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.wanxp.entity.Customer;
import cn.wanxp.entity.Linkman;
import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;
public class HibernateDemoDay03 {
    @Test
    public void testOneToManyAdd() {
        SessionFactory sessionFactory = null;
        Session session = null;
        Transaction tx = null;
        Customer customer = new Customer();
        Linkman linkman = new Linkman();
        try {
            sessionFactory = HibernateUtils.getSessionFactory();
            session = sessionFactory.openSession();
            tx = session.beginTransaction();
            //1创建客户和联系人对象并设置基本域值
            linkman.setLinkmanName("李四");
            linkman.setLinkmanGender("男");
            linkman.setLinkmanPhone("131313");
            
            customer.setCustomerName("联想");
            customer.setCustomerSource("web");
            customer.setCustomerLevel("vip");
            customer.setCustomerPhone("3333");
            customer.setCustomerMobile("1111");
            
            //2在客户表中和联系人表中连接部分分别添加对方
            linkman.setCustomer(customer);
            customer.getSetLinkman().add(linkman);
            //3使用save存储进数据表中
            session.save(customer);
            session.save(linkman);
            
            tx.commit();
        }catch(Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally {
            session.close();
            sessionFactory.close();
        }
    }
}

2)一对多级联保存 -- 简单写法
第一步在客户映射文件中进行配置
修改Customer.hbm.xml配置文件中的set标签,在set标签中添加 cascade="save-update"属性

<set name="setLinkman" cascade="save-update">

上面的HibernateDemoDay03.java就可以修改为

package cn.wanxp.hibernatetest;
import java.io.UnsupportedEncodingException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.wanxp.entity.Customer;
import cn.wanxp.entity.Linkman;
import cn.wanxp.entity.User;
import cn.wanxp.utils.HibernateUtils;
public class HibernateDemoDay03 {
   @Test
   public void testOneToManyAdd() {
       SessionFactory sessionFactory = null;
       Session session = null;
       Transaction tx = null;
       Customer customer = new Customer();
       Linkman linkman = new Linkman();
       try {
           sessionFactory = HibernateUtils.getSessionFactory();
           session = sessionFactory.openSession();
           tx = session.beginTransaction();
           //1创建客户和联系人对象并设置基本域值
           linkman.setLinkmanName("李四");
           linkman.setLinkmanGender("男");
           linkman.setLinkmanPhone("131313");
           
           customer.setCustomerName("联想");
           customer.setCustomerSource("web");
           customer.setCustomerLevel("vip");
           customer.setCustomerPhone("3333");
           customer.setCustomerMobile("1111");
           
           //2在客户表中添加联系人实体类对象,相互
     linkman.setCustomer(customer);
           customer.getSetLinkman().add(linkman);
           //3使用save存储进数据表中
           session.save(customer);
           
           tx.commit();
       }catch(Exception e) {
           e.printStackTrace();
           tx.rollback();
       }finally {
           session.close();
           sessionFactory.close();
       }
   }
}

效果如下

hibernate-one-to-many-add

3)一对多删除
如,删除某个客户,把客户里面所有的联系人删除

① 在客户映射文件中进行配置(多个值用逗号隔开)
修改Customer.hbm.xml配置文件中的set标签,在set标签中添加 cascade="save-update,delete"属性

<set name="setLinkman" cascade="save-update,delete">

② 在代码中直接删除
先根据id查找客户然后删除

@Test
public void testOneToManyDelete() {
  SessionFactory sessionFactory = null;
  Session session = null;
  Transaction tx = null;
  Customer customer = new Customer();
  Linkman linkman = new Linkman();
  try {
    sessionFactory = HibernateUtils.getSessionFactory();
    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    
    customer = session.get(Customer.class, 1);
    session.delete(customer);
    
    tx.commit();
  }catch(Exception e) {
    e.printStackTrace();
    tx.rollback();
  }finally {
    session.close();
    sessionFactory.close();
  }
}

效果如下

效果

3)一对多修改
(1)如将多方的ABC三个记录中的C的外键Y修改为一方的XY中的X

Customer X=session.get(Customer.class,1);
Linkman C=session.get(Linkman.class,3);
X.getSetLinkman().add(C);

(2)一对多的里面,可以让其中一方放弃维护,用以提高效率。一般让一放弃维护,多的维护
(3)具体实现放弃维护
set标签中的inverse属性:放弃true,不放弃false(默认)

<set name="setLinkman" inverse="true" cascade="save-update,delete">
6.2.2 多对多表
  1. 多对多映射配置
    (1)创建实体类,用户和角色
    Person.java
package cn.wanxp.entity;
import java.util.HashSet;
import java.util.Set;
public class Person {
    public Integer getPid() {
        return pid;
    }
    public void setPid(Integer pid) {
        this.pid = pid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<Role> getSetRole() {
        return setRole;
    }
    public void setSetRole(Set<Role> setRole) {
        this.setRole = setRole;
    }
    private Integer pid;
    private String name;
    private Set<Role> setRole = new HashSet<Role>();
}

Role.java

package cn.wanxp.entity;
import java.util.HashSet;
import java.util.Set;
public class Role {
    public Integer getRid() {
        return rid;
    }
    public void setRid(Integer rid) {
        this.rid = rid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<Person> getSetPerson() {
        return setPerson;
    }
    public void setSetPerson(Set<Person> setPerson) {
        this.setPerson = setPerson;
    }
    private Integer rid;
    private String name;
    private Set<Person> setPerson = new HashSet<Person>();
}

(2)让两个实体类之间相互表示
①用户里表示所有角色,使用set集合
②一个角色有多个用户,使用set集合
(3)配置映射
①基本配置
②配置多对多关系
person.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
     <class name="cn.wanxp.entity.Person" table="t_person">
         <id name="pid" column="pid">
            <generator class="native"></generator>
         </id>
         <property name="name" column="name"></property>
         <!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人 
                set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
                table:第三张表名
         -->
         <set name="setRole" table="t_person_role" cascade="all">
            <!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键 
                key属性 column当前映射文件在第三张表的外键名称
            -->
            <key column="person_id"></key>
            <!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键 
                many-to-many属性 column值Role在第三张表的外键名称
            -->         
            <many-to-many class="cn.wanxp.entity.Role" column="role_id" />
         </set>
     </class>
</hibernate-mapping>

role.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
     <class name="cn.wanxp.entity.Role" table="t_role">
         <id name="rid" column="rid">
            <generator class="native"></generator>
         </id>
         <property name="name"  column="name"></property>
         <!-- 在客户映射文件中,表示所有联系人,使用set标签表示所有联系人 
                set标签里面有name属性:属性值写在客户实体类里面表示联系恶人的set集合名称
         -->
         <set name="setPerson" table="t_person_role" cascade="all">
            <!-- 
            key属性 column当前映射文件在第三张表的外键名称
            -->
            <key column="role_id"></key>
            <!-- 一对多建表,有外键。hibernate机制:双向维护外键,在一和多都有配置外键 
                many-to-many属性 column值Person在第三张表的外键名称
            -->  
            <many-to-many class="cn.wanxp.entity.Person" column="person_id"/>
         </set>
     </class>
</hibernate-mapping>

--在用户里表示所有角色,使用set标签
--在角色里表示所有用户,使用set标签
(4)核心配置中引入配置文件
2)多对多级联保存

public void testManyToManyAdd() {
  SessionFactory sessionFactory = null;
  Session session = null;
  Transaction tx = null;
  try {
    sessionFactory = HibernateUtils.getSessionFactory();
    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    //创建对象
    Person p1 = new Person();
    Person p2 = new Person();           
    p1.setName("张三");
    p2.setName("李四");
    
    Role r1 = new Role();
    Role r2 = new Role();
    Role r3 = new Role();
    r1.setName("肉坦");
    r2.setName("输出");
    r3.setName("辅助");
    
    //关联对象
    p1.getSetRole().add(r1);
    p2.getSetRole().add(r2);
    p1.getSetRole().add(r3);

    //保存对象
    session.save(p1);
    session.save(p2);
      
    tx.commit();
  }catch(Exception e) {
    e.printStackTrace();
    tx.rollback();
  }finally {
    session.close();
    sessionFactory.close();
  }
}

效果如下

hibernate-many-to-many-add

3)多对多级联删除
删除方式同一对多删除
4)维护第三张表关系
用户表添加角色 搜索用户表,搜索角色表,在用户表中添加角色,如p1.getSetRole().add(r5);
用户表删除角色 搜索用户表,搜索角色表,在用户表中删除角色,如p1.getSetRole().remove(r2);

7 hibernate 查询方式

7.1 对象导航查询

如一对多表格 客户与联系人,根据某个id查询 客户以及下面的联系人
先按照id搜索客户,然后获取set集合即可

Customer customer = session.get(Customer.class , 1);
Set<Linkman> setLinkman = customer.getSetlinkman();

7.2 OID查询

根据id查询某一条记录
按照id搜索客户

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

7.3 hql查询

Query对象,写hql语句查询
hql与sql类似
区别:hql操作实体类和属性,sql操作数据库表和字段
过程:创建Query对象,写hql语句 ,调用Query对象的方法获得结果

7.3.1 查询所有
//from 实体类名称
Query query = session.createQuery("from Customer");
//调用方法得到结果  
List<Customer> list = query.list();
7.3.2 条件查询
//from 实体类名称 where 实体类属性名称=? [and 实体类属性名称=? ...] 或者 from 实体类名称  实体类属性名称 like ?
Query query = session.createQuery("from Customer c where c.cid=? and c.customerName=?"); 
//问好问号位置从0开始
query.setParameter(0,1);
query.setParameter(1,"百度");
//调用方法得到结果  
List<Customer> list = query.list();
7.3.3 排序查询
//在上面基础上 加入 order by 实体类属性名称 desc|asc   (desc 降序,asc 升序) 
Query query = session.createQuery("from Customer c where c.cid=? and c.customerName=?" order by c.cid desc); 
7.3.4 分页查询
Query query = session.createQuery("from Customer");
//设置起始位置
query.setFirstResult(0);
//设置每页记录数量
query.setMaxResult(3);
//调用方法得到结果  
List<Customer> list = query.list();

7.3.5 投影查询

sql:select customerName from Customer; --查询部分字段而不是所有字段

Query query = session.createQuery("select customerName from Customer");
list<Object> list = query.list();//注意这里的区别
7.3.6 聚集函数使用

常用count sum avg max min

//hql:select count(*) from 实体类名   
Query query = session.createQuery(" select count(*) from Customer"); 
Object obj = query.uniqueResult();

7.4 QBC查询

1)使用hql需要写hql语句,但使用qbc是不需要写语句,只需要方法就行
2)使用qbc操作实体类和属性
3)使用Criteria对象进行操作

7.4.1 查询所有
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法
List<Customer> list = criteria.list();
7.4.2 条件查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件  
//add方法添加条件,参数Restrictions对象的方法,方法里传入实体类属性和值
criteria.add(Restrictions.eq("cid",1));
List<Customer> list = criteria.list();
7.4.3 排序查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件  
//addOrder方法添加条件,参数Order对象的方法,方法里传入实体类属性asc升序desc降序
criteria.addOrder(Order.asc("cid"));
List<Customer> list = criteria.list();
7.4.4 分页查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件  
//setFirstResult(0)起始位置0,setMaxResult(3)获得3条记录
criteria.setFirstResult(0);
criteria.setMaxResult(3);
List<Customer> list = criteria.list();
7.4.5 统计查询
//创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//调用Criteria对象的方法设置条件  
criteria.setProjection(Projections.rowCount());
Object obj = criteria.uniqueResult();
Long lobj = (Long) obj
int count = lobj.intValue();
System.out.println(count);
7.4.5 离线查询
//创建DetachedCriteria对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();

7.5 本地sql查询

SQLQuery对象,使用普通sql实现

8 多表查询

8.1 内连接

8.1.1 内连接

sql语句:select * from t_customer as c inner join t_linkman as l on c.cid=l.lid;

Query query = session.createQuery("from Customer c inner join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.1.2 迫切内连接

1)与内连接底层是一样的
2)区别:使用内连接返回list中每部分是数组,迫切内连接返回的是对象
用法:加入关键字fetch

Query query = session.createQuery("from Customer c inner join fetch c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();

8.2 左外连接

8.2.1 左外连接

sql语句:select * from t_customer as c left outer join t_linkman as l on c.cid=l.lid;

Query query = session.createQuery("from Customer c left outer join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.2.2 迫切左外连接

效果用法区别与内连接类似

8.3 右外连接

8.3.1 右外连接

sql语句:select * from t_customer as c right outer join t_linkman as l on c.cid=l.lid;

Query query = session.createQuery("from Customer c right outer join c.setLinkman");
//list()返回的Object对象的数字
List list = query.list();
8.3.2 迫切右外连接

效果用法区别与内连接类似

9 hibernate检索策略

9.1 立即查询

如根据id查询,调用get方法时,一调用get方法立即发送查询语句给数据库

9.2 延迟查询

如根据id查询,调用load方法时,调用load方法,不会立即发送查询语句给数据库,只有得到对象里面的值时才会发送查询语句
分类:类级别延迟,关联级别延迟

9.2.1 类级别延迟

就是上面那种

9.2.2 关联类级别延迟

如查询Customer时,是否延迟查询关联的Linkman

默认状态

Customer customer = session.get(Customer.class , 1);
//执行到这里时,只发送了查询表t_customer
Set<Linkman> setLinkman = customer.getSetlinkman();
//执行到这里时,只发送了查询表t_customer
System.out.println(list.size());//执行到这里将会发送查询t_linkman表命令

修改延迟
在set标签上修改属性
fetch :值select
lazy:值
----true:延迟,默认
----false:不延迟
----extra:极其延迟,要什么值就去查什么值

10 批量查询

如获取所有客户的联系人
方式1 普通方式,效率较低

//下面这种方式经过测试效率不高,因为每次查询一条客户再查询联系人都要发送查询命令
Criteria criteria =session.createCriteria(Customer.class);   
List<Customer> list = criteria.list();
for(Customer customer:list){
  Systerm.out.println(customer.getId());
  Set setLinkman =customer.getSetLinkman();
  for(Linkman linkman : setLinkman){
    System.out.println(linkman.getId());
  }
}

方式2 修改配置Customer文件 ,在set标签中加入属性batch-size,batch-size的值只要是整数即可,其值越大,发送的语句命令越少,效率越高

<set name = "setLinkman" batch-size="2">  
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容