您的位置:首页 > 其它

Hibernate框架快速入门笔记

2018-04-08 23:42 423 查看
一、Hibernate文件目录结构

二、Hibernate的配置详解
核心配置文件 hibernate.cfg.xml

映射配置文件 xxx.hbm.xml

三、Hibernate的执行原理和常用API
Configuration

SessionFactory

Session

Transaction

Query(重点)

Criteria

四、Hibernate持久化类与主键生成策略
Hibernate持久化类

Hibernate主键生成策略

Hibernate持久化对象状态

五、Hibernate一级缓存

六、Hibernate关联映射–数据对象三种关系
一对多
Hbm映射文件编写

七、Hibernate注解开发
一对多(多对一)

Hibernate关联映射-多对多

Hibernate关联映射-一对一

八、Hibernate检索方式概述
导航对象图检索方式

OID检索方式

HQL

QBC

本地SQL

多表操作

九、Hibernate事务管理

十、Hibernate优化方案
HQL优化

检索策略
抓取策略

Hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成操作,就是与数据库的交互操作。它是在dao层去使用的。

对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping)。简单说,我们使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作。

一、Hibernate文件目录结构



documentation目录:存放hibernate的相关文件与API

lib目录:存放hibernate编译和运行所依赖的jar包,其中required子目录下包含了运行hibernate项目必须的jar包

project目录:存放hibernate各种相关的源代码与资源.

在lib/required目录中,包含必需的jar包

快速搭建项目需要导入的包

导入lib/required下所有的jar

导入数据库的驱动jar包

日志相关jar包 3

将hibernate/project/etc/log4j.properties文件导入到工程src下

二、Hibernate的配置详解

Hibernate中我们使用时主要有两种配置文件

核心配置文件 hibernate.cfg.xml

对于hibernate的核心配置文件它有两种方式:

hibernate.cfg.xml

hibernate.properties

我们在开发中使用比较多的是hibernate.cfg.xml这种方式,原因它的配置能力更强,易于修改

我们主要学习的是hibernate.cfg.xml配置

1. 可以加载数据库相关信息

2. hibernate相关配置

3. 加载映射配置文件

<session-factory>
<!-- 配置关于数据库连接的四个项 driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3366/hibernateTest</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123</property>

<!-- 可以将向数据库发送的sql显示出来 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 用于设置事务提交方式 -->
<property name="hibernate.connection.autocommit">false</property>

<!-- 配置hibernate的映射文件所在位置 -->
<mapping resource="com/lbb/hibernate/domain/Customer.hbm.xml" />
</session-factory>


对于hibernate.cfg.xml配置文件中的内容可以参考hibernate/project/etc/hibernate.properties的配置

4. 配置
<property name="hibernate.hbm2ddl.auto">update</property>
属性后,我们可以进行表的自动创建

Create-drop 每次都会创建一个新的表,执行完成后删除。一般在测试中使用

Create 每次都会创建一个新的表,一般是在测试中使用

update 如果数据库中有表,不创建,没有表创建,如果映射不匹配,会自动更新表结构(只能添加)

validate 只会使用存在的表,并且会对映射关系进行校验.

映射配置文件 xxx.hbm.xml

映射配置文件它的名称是类名.hbm.xml,它一般放置在实体类所在的包下。 这个配置文件的主要作用是建立表与类的映射关系。

<hibernate-mapping package="cn.itheima.domain">
<!-- name属性它是实体类的全名 table 表的名称 catalog 数据库名称 -->
<class name="Customer" table="t_customer"
catalog="hibernateTest">
<!-- id它是用于描述主键 -->
<id name="id" column="id" type="int">  <!-- java数据类型 -->
<!-- 主键生成策略 -->
<generator class="nati
2288b
ve"></generator>
</id>

<!-- 使用property来描述属性与字段的对应关系 -->
<property name="name" column="name" length="20" type="string"></property>  <!-- hibernate数据类型 -->
<property name="address">
<column name="address" length="50" sql-type="varchar(50)"></column> <!-- sql数据类型 -->
</property>
<property name="sex" column="sex" length="20"></property>
</class>
</hibernate-mapping>


统一声明包名,这样在
<class>
中就不需要写类的全名.

<hibernate-mapping package="cn.itheima.domain">


关于
<class>
标签配置

name属性:类的全名称

table 表的名称,可以省略,这时表的名称就与类名一致

catalog属性:数据库名称 可以省略.如果省略,参考核心配置文件中url路径中的库名称

关于
<id>
标签

首先它必须存在。
<id>
是用于建立类中的属性与表中的主键映射。

name 类中的属性名称

column 表中的主键名称 column它也可以省略,这时列名就与类中属性名称一致

length 字段长度

type属性 指定类型

<generator>
它主要是描述主键生成策略.

关于
<property>
标签

它是描述类中属性与表中非主键的映射关系

关于hibernate的映射文件中类型问题

对于type属性它的取值,可以有三种:

1. java中的数据类型

2. hibernate中的数据类型

3. SQL的数据类型



三、Hibernate的执行原理和常用API

hibernate工作原理

通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。

由hibernate.cfg.xml中的
<mappingresource="com/xx/User.hbm.xml"/>
读取解析映射信息。

通过config.buildSessionFactory();//得到sessionFactory。

sessionFactory.openSession();//得到session。

session.beginTransaction();//开启事务。

persistent operate;

session.getTransaction().commit();//提交事务

关闭session;

关闭sessionFactory;

Hibernate的核心类和接口一共有6个

分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。这6个核心类和接口在任何开发中都会用到

// 保存一个Customer
@Test
public void saveCustomerTest() {
// 使用hibernate的api来完成将customer信息保存到mysql中操作
Configuration config = new Configuration().configure(); // 加载hibernate.cfg.xml

SessionFactory sessionFactory = config.buildSessionFactory();

Session session = sessionFactory.openSession(); // 相当于得到一个Connection。

// 开启事务
Transaction transaction = session.beginTransaction();

// 操作
Customer c = new Customer();
c.setName("张三");
c.setAddress("北京");
c.setSex("男");
session.save(c);

// 事务提交
transaction.commit();
session.close();
sessionFactory.close();
}


Configuration

它主要是用于加载hibernate配置

Configuration config=new Configuration().config(); 主要加载src下的hibernate.cfg.xml

Configuration config=new Configuration();主要加载的src下的hibernate.properties

Configuration config=new Configuration().config(核心配置文件名称);加载指定的名称的配置文件

问题:我们是在hibernate.cfg.xml文件中有xxx.hbm.xml文件的位置。如果我们使用的是hibernate.properties这种核心配置,它如何加载映射配置?

Configuration config=new Configuration();//主要加载的src下的hibernate.properties
// 手动加载映射
// config.addResource("cn/itheima/domain/Customer.hbm.xml"); 直接加载映射配置文件
// config.addClass(Customer.class); //这种方式它会直接在实体类所在包下查找规范映射配置文件


SessionFactory

SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

SessionFactory它不是轻量级的,不要频繁创建关闭它。在一个项目中有一个SessionFactory就可以,通过SessionFactory来获取Session进行操作。

public class HibernateUtils {

private static Configuration config;
private static SessionFactory sessionFactory;

static{

config=new Configuration().configure();
sessionFactory=config.buildSessionFactory();
}

//  是从连接池中获取一个连接
public static Session openSession(){
return sessionFactory.openSession();
}

//  获取一个与线程绑定的Session
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}


SessionFactory内部还维护了一个连接池,如果我们要想使用c3p0连接池,应该怎样处理?

我们要导入c3p0的相关jar包

在hibernate/lib/options下有关于c3p0连接池jar包

在hibernate.cfg.xml文件中配置c3p0连接

可以查看etc/hibernate.properties中关于c3p0的配置

<!-- 设置连接提供者 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--  c3p0连接池的配置 -->
<property name="hibernate.c3p0.max_size">20</property> <!-- 最大连接池 -->
<property name="hibernate.c3p0.min_size">5</property> <!-- 最小连接数 -->
<property name="hibernate.c3p0.timeout">120</property> <!-- 超时 -->
<property name="hibernate.c3p0.idle_test_period">3000</property> <!-- 空闲连接 -->


Session

Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。

问题:我们如何解决session的安全问题?

我们只需要在方法内部来使用Session就可以。

问题:Session如何获取到?

SessionFactory.openSession() ; 相当于直接通过SessionFactory创建一个新的Session,使用完成后要手动调用close来关闭。

SessionFactory.getCurrentSession(); 获取一个与线程绑定的Session,当我们提交或事务回滚后会自动关闭。

Session常用的方法

save 保存对象

update 修改操作

delete删除

get/load 根据id进行查询

savenOrUpdate 执行save或update操作

createQuery()获取一个Query对象

createSQLQUery()获取一个可以操作sql的SQLQuery对象

createCriteria() 获取一个Criteria它可以完成条件查询

Transaction

Transaction接口主要用于管理事务,它是hibernate的事务接口,对底层的事务进行了封装。使用它可以进行事务操作。

commit 事务提交

rollback 事务回滚

问题:如果在程序中没有开启事务,是否存在事务?

有事务,session的每一个操作就会开启一个事务。

默认情况下事务是不会自动提交的。

<!-- 用于设置事务提交方式 -->
<property name="hibernate.connection.autocommit">false</property>


Query(重点)

Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。

通过Query主要完成查询操作.

我们通过Query可以执行hql语句.

Query query=Session.createQuery(hql);

下面这个可以执行sql语句

SQLQUery sqlQuery=Session.createSQLQuery(sql);

SQLQuery是Query的子.

使用hql完成查询所有操作

@Test
public void test1() {
Session session = HibernateUtils.openSession();
Query query = session.createQuery("from Customer");// from后面是类名
List<Customer> list = query.list();
System.out.println(list);
session.close();
}


分页查询

// 分页查询 一页显示10条 要得到第二页数据
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

Query query = session.createQuery("from Customer");
query.setFirstResult(10);
query.setMaxResults(10);
List list = query.list();
System.out.println(list);

session.getTransaction().commit();
session.close();
}


查询指定列信息

Select name ,address from Customer; 得到的是
List<Object[]>
结果

要想得到List结果

1. 在Customer类中生成以name,address为参数的构造,注意,无参数构造也要有。

2. Select new Customer(name,address) from Customer;

// 查询指定列信息
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

Query query = session.createQuery("select name,address from Customer");
List<Object[]> list = query.list();
System.out.println(list);

Query query2 = session.createQuery("select new Customer(name,address) from Customer");
List list2 = query2.list();
System.out.println(list2);

session.getTransaction().commit();
session.close();
}


条件查询

无名称参数 from Customer where name=?

对其进行赋值 query.setParameter(0,”张三”)

有名称参数 from Customer where name=:myname;

对其进行赋值 query.setParameter(“myname”,”李四”);

如果查询结果可以保证就是唯一 的,我们可以使用

query. uniqueResult()来得到一个单独对象.

// 条件查询
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

//          Query query = session.createQuery("select new Customer(name,address) from Customer where name = ?");
Query query = session.createQuery("from Customer where name = ?");
query.setParameter(0, "姓名0");
Customer customer = (Customer) query.uniqueResult();
System.out.println(customer);

Query query2 = session.createQuery("from Customer where name = :myname");
query2.setParameter("myname", "姓名0");
Customer customer2 = (Customer) query.uniqueResult();
System.out.println(customer2);

session.getTransaction().commit();
session.close();
}


执行本地SQL

要想执行本地sql

SQLQuery sqlQuery=session.createSqlQuery(String sql);

使用addEntity方法来将结果封装到指定的对象中,如果不封装,得到的是
List<Object[]>


如果sql中有参数,我们使用setParameter方法完成参数传递。

如果结果就是一个可以使用uniqueResult()来得到一个单独对象。

// 执行本地sql----查询全部
@Test
public void test6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 执行select * from t_customer;
SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer");
// List<Object[]> list = sqlQuery.list();
// System.out.println(list);

// 想要将结果封装到Customer对象中
sqlQuery.addEntity(Customer.class);
List<Customer> list = sqlQuery.list();
System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 执行本地sql----条件查询
@Test
public void test7() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 执行select * from t_customer where name=?;
SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer where name=?");
// 对参数进行赋值
sqlQuery.setParameter(0, "姓名1");

// 想要将结果封装到Customer对象中
sqlQuery.addEntity(Customer.class);
// List<Customer> list = sqlQuery.list();
// System.out.println(list);

Customer c = (Customer) sqlQuery.uniqueResult();
System.out.println(c);
session.getTransaction().commit();
session.close();
}


Criteria

Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。值得注意的是Criteria接口也是轻量级的,它不能在Session之外使用。

首先我想使用Criteria,必须得到Criteria

Criteria criteria=Session.createCriteria()

查询所有操作

Session.createCriteria(实体类.class)得到一个Criteria对象,调用list查询所有

分页操作与query的方法一样

setFirstResult() setMaxResults()

条件查询

criteria.add(Restrictions.eq(“name”,”xxxx”));

criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))

我们使用Criteria可以更加面向对象去操作,它非常适合进行多条件组合查询。

@Test
public void test8() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

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

// 查询所有
//          List list = criteria.list();
//          System.out.println(list);

// 分页查询
//          criteria.setFirstResult(10);
//          criteria.setMaxResults(10);
//          List list2 = criteria.list();
//          System.out.println(list2);

// 多条件查询
// 1.查询name='姓名1'
//          criteria.add(Restrictions.eq("name","姓名1"));
//          Customer customer = (Customer) criteria.uniqueResult();
//          System.out.println(customer);

// 2.查询address='上海'
//          criteria.add(Restrictions.eq("address", "上海"));
//          Customer customer = (Customer) criteria.uniqueResult();
//          System.out.println(customer);

//          criteria.add(Restrictions.eq("name","姓名1"));
//          criteria.add(Restrictions.eq("address", "上海"));
//          Customer customer = (Customer) criteria.uniqueResult();
//          System.out.println(customer);
//          List list = criteria.list();
//          System.out.println(list);

//查询name='姓名1' 或者   address='上海'
criteria.add(Restrictions.or(Restrictions.eq("name", "姓名1"),Restrictions.eq("address","上海")));
List<Customer> list = criteria.list();
System.out.println(list);

session.getTransaction().commit();
session.close();
}


四、Hibernate持久化类与主键生成策略

Hibernate持久化类

什么是持久化类?

持久化类Persistent Object (PO)其实就相当于PO=POJO+hbm映射配置。

hibernate中的PO编写规则

必须提供一个无参数的public构造方法

所有属性要private ,对外提供public 的get/set方法

在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID

PO类中的属性尽量使用基本数据类型的包装类.

Int-Integer double–Double float-Float

PO类它不能使用final修饰符

OID作用

OID指的是与数据库中表的主键对应的属性。

Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate认为它们是同一个对象。

为什么PO类属性它要使用包装类型

使用基本数据类型是没有办法去描述不存在概念,如果使用包装类型,它就是一个对象,对于对象它的默认值是null。

PO类不可以使用final修饰?(hibernate中的get/load方法的区别)

get直接得到了一个持久化类型对象,它就是立即查询操作。

load它得到的是持久化类开的代理类型对象(子类对象)。它默认采用了一种延迟策略来查询数据。

get方法在查询时,如果不存在返回null。

load方法在查询时,如果 不存在,会产生异常ObjectNotFoundException。

Hibernate主键生成策略

Hibernate中定义的主键类型包括:自然主键和代理主键。

自然主键:具有业务含义 字段 作为主键,比如:学号、身份证号。

代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:mysql自增主键,oracle序列生成的主键、uuid()方法生成的唯一序列串。
建议:企业开发中使用代理主键!


主键生成器描述
increment代理主键。由hibernate维护一个变量,每次生成主键时自动以递增。问题:如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突。
建议不采用。
优点:可以方便跨平台。缺点:不适合高并发访问
identity代理主键。由底层数据库生成表识符。条件是数据库支持自动增长数据类型。比如:
mysql的自增主键,oracle不支持主键自动生成。如果数据库支持自增建议采用。
优点:由底层数据库维护,和hibernate无关。缺点:只能对支持自动增长的数据库有效,例如mysql
sequence代理主键。Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。
比如oracle的序列。如果数据库支持序列建议采用。
优点:由底层数据库维护,和hibernate无关。缺点:数据库必须支持sequence方案例如oracle。
native代理主键。根据底层数据库对自动来选择identity、sequence、hilo.
由于生成主键策略的控制权由hibernate控制,所以不建议采用。
优点:在项目中如果存在多个数据库时使用。缺点:效率比较低。
uuid代理主键。Hibernate采用128bit位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符。
此策略可以保证生成主键的唯一性,并且提供了最好的数据库插入性能和数据库平台的无关性。建议采用。
优点:与数据库无关,方便数据库移植,效率高,不访问数据库就可以直接生成主键值,并且它能保证唯一性。缺点:uuid长度大(32位),占用空间比较大,对应数据库中类型 char varchar
assigned自然主键。由java程序负责生成标识符。不建议采用。尽量在操作中避免手动对主键操作

Hibernate持久化对象状态

持久化对象三种状态

瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录。它使用完成后,会被jvm直接回收掉,它只是用于信息携带。

简单说:无OID 与数据库中的信息无关联,不在session管理范围内。

持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。

简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。

托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数据库中有可能存在,也有可能不存在。

对于托管态对象,它发生改变时hibernet不能检测到。

持久化类三种状态切换

判断持久化类对象三种状态:

1. 是否有OID

2. 判断是否与session关联



瞬时态(new 出来的)

瞬时——持久 save saveOrUpdate

瞬时—–脱管(游离) 手动设置oid

.持久态 它是由session管理

持久——-瞬时 delete() 被删除后持久化对象不在建议使用

持久—–脱管 注意:session它的缓存就是所说的一级缓存

evict(清除一级缓存 中指定的一个对象)

clear(清空一级缓存)

close(关闭,清空一级缓存)

.脱管态 (它是无法直接获取)

脱管—–瞬时 直接将oid删除

脱管—-持久 update saveOrUpdate lock(过时)

五、Hibernate一级缓存

Hibernate的一级缓存就是指session缓存。在Session——EventSource——SessionImpl实现类中,有如下属性

private transient ActionQueue actionQueue;
private transient StatefulPersistenceContext persistenceContext;


actionQueue它是一个行列队列,它主要记录crud操作的相关信息。

persistenceContext它是持久化上下文,它其实是真正缓存。

在session中定义了一系列的集合来存储数据,它们构成session缓存。只要session没有关闭,它就会一直存在。当我们通过hibernate中的session提供的一些API例如 save get update等进行操作时,就会将持久化对象保存到session中,当下一次在去查询缓存中具有的对象(OID值来判断),就不会去从数据库查询,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库访问。

持久化对象具有自动更新数据库能力



一级缓存常用API

一级缓存特点

当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。

当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。

当调用session的close方法时,session缓存清空。

clear 清空一级缓存.

evict 清空一级缓存中指定的一个对象。

refresh重新查询数据库,用数据库中信息来更新一级缓存与快照

Hibernate常用API-Session补充

update

udpate操作它主要是针对于脱管对象,持久对象具有自动更新能力。

Update操作时,如果对象是一个脱管对象,可以操作,它会将脱管对象转换成持久对象在操作。

如果在session中出现相同的oid两个对象,会产生异常。

脱管对象的oid如果在数据表中不存在,会报异常。

所以:在操作中,建议我们通过持久化对象来直接修改其操作。


saveOrUpdate

如果对象是一个瞬时对象 ——–执行save操作

如果对象是一个脱管对象———执行update

如果是一个持久对象——-直接返回

delete

删除一个脱管对象,与session关联,在删除。
注意:如果执行delete操作,先删除一级缓存,在删除数据库中的数据。删除后对象id变为空,对象变为瞬时态


六、Hibernate关联映射–数据对象三种关系

Hibernate框架基于ORM设计思想,它将关系型数据库中的表与我们java中的类进行映射,一个对象就对应着表中的一条记录,而表中的字段对应着类中的属性。数据库中表与表之间存在着三种关系,也就是系统设计中的三种实体关系。

一对多

实体类创建

public class Customer {
private Integer id; // 主键
private String name; // 姓名
// 描述客户可以有多个订单
private Set<Order> orders = new HashSet<Order>();

public class Order {
private Integer id;
private Double money;
private String receiverInfo; // 收货地址
// 订单与客户关联
private Customer c; // 描述订单属于某一个客户


Hbm映射文件编写

<!-- 一个客户关联多个订单 -->
<set name="orders" inverse="false" cascade="all">
<key column="cid" />
<one-to-many class="cn.itheima.oneToMany.Order" />
</set>
<!-- 使用set来描述在一的一方中关联的多 Set<Order>,
它的name属性就是set集合的名称
key:它主要描述关联的多的一方产生的外键名称,注意要与多的一方定义的外键名称相同
one-to-many 描述集合中的类型 -->

<!-- 多对一 -->
<many-to-one fetch="join" lazy="false" name="c" class="cn.itheima.oneToMany.Customer" column="cid" cascade="save-update">
</many-to-one>
<!--
name属性它描述的是Order类中的一的一方的属性名称   Customer c;
class 代表一的一方的类型
column 描述的是一对多,在多的一方产生的外键的名称 c_customer_id
-->


使用级联操作,设置
cascade=save-update
那么在保存一方的同时可以同时保存另一方。

我们在开发中要配置双向关联配置。———可以通过任意一方来操作对方

在操作代码,尽量来要进行单向关联。——可以尽量资源浪费。

在双向关联中,会存在多余的update语句。我们可以使用
inverse
属性来设置,双向关联时由哪一方来维护表与表之间的关系。

Inverse它的值如果为true代表,由对方来维护外键。

Inverse它的值如果为false代表,由本方来维护外键。

原则:inverse设置在主表的一方,外键在哪一个表中,我们就让哪一方来维护外键。

对象导航



cascade总结

使用cascade可以完成级联操作

它可常用取值

none这是一个默认值

save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.

delete 级联删除

delete-ophan 删除与当前对象解除关系的对象。

all 它包含了save-update delete操作

all-delete-orphan 它包信了delete-orphan与all操作

cascade与inverse有什么区别

cascade它是完成级联操作

Inverse它只有在双向关联情况下有作用,它来指定由哪一方维护外键。

七、Hibernate注解开发

PO类注解配置

@Entity 声明一个实体

@Table来描述类与表对应

@Id来声明一个主键

@GenerateValue 用它来声明一个主键生成策略

默认情况下相当于native

可以选择的主键生成策略 AUTO IDENTITY SEQUENCE

@Column来定义列

注意:对于PO类中所有属性,如果你不写注解,默认情况下也会在表中生成对应的列。

列的名称就是属性的名称

@Temporal来声明日期类型

可以选择

TemporalType.DATA 只有年月日

TemporalType.TIME 只有小时分钟秒

TemporalType.TIMESTAMP 有年月日小时分钟秒

@Transient设定类的属性不在表中映射

@Entity // 定义了一个实体
@Table(name = "t_book", catalog = "hibernateTest")
public class Book {

@Id // 主键
// @GeneratedValue //native
@GeneratedValue(strategy = GenerationType.IDENTITY) // identity
private Integer id; // 主键

@Column(name = "c_name", length = 30, nullable = true)
private String name;

@Temporal(TemporalType.TIMESTAMP) // 是用来定义日期类型
private Date publicationDate; // 出版日期

@Type(type="double")
private Double price; // 价格 如果没有添加注解,也会自动的生成在表中

@Transient
private String msg; // 现在这个属性不想生成在表中


如果我们主键生成策略想使用UUID类型。

@Id
@GenericGenerator(name = "myuuid", strategy = "uuid")
@GeneratedValue(generator = "myuuid")
private String id;


对于我们以上讲解的关于属性配置的注解,我们也可以在其对应的getXxx方法去使用。我们最终需要在hibernate.cfg.xml文件中将我们类中的注解配置引用生效。
<mapping class="cn.itheima.oneToMany.Customer" />

<mapping class="cn.itheima.oneToMany.Order" />


一对多(多对一)

// 描述客户可以有多个订单
/*
* targetEntity相当于<one-to-many class="">
* mappedBy相当于inverse=true
*/
@OneToMany(targetEntity=Order.class,mappedBy="c",orphanRemoval=true)
@Cascade(org.hibernate.annotations.CascadeType.DELETE)
private Set<Order> orders = new HashSet<Order>();

// 订单与客户关联
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id") // 指定外键列
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private Customer c; // 描述订单属于某一个客户


对于这个示例我们需要在Customer中配置cascade操作,save-update

第一种方式,可以使用JPA提供的注解。在@OneToMany中
cascade=CascadeType.ALL


第二种方式:可以使用hibernate提供的注解.如上代码。

扩展:关于hibernate注解@Cascade中的DELETE_ORPHAN过时,我们在@OneToMany中添加
orphanRemoval=true
解决

Hibernate关联映射-多对多

使用@ManyToMany来配置多对多,只需要在一端配置中间表,另一端使用mappedBy表示放置外键维护权。

创建PO类

描述学生与老师.

@ManyToMany(targetEntity = Student.class, mappedBy = "teachers") // 代表由对方来维护外键
@Cascade(CascadeType.ALL)
private Set<Student> students = new HashSet<Student>();

@ManyToMany(targetEntity = Teacher.class)
// 使用JoinTabl来描述中间表,并描述中间表中外键与Student,Teacher的映射关系
// joinColumns它是用来描述Student与中间表中的映射关系
// inverseJoinColums它是用来描述Teacher与中间表中的映射关系
@JoinTable(name = "s_t", joinColumns = {
@JoinColumn(name = "c_student_id", referencedColumnName = "id") }, inverseJoinColumns = {
@JoinColumn(name = "c_teacher_id", referencedColumnName = "id") })
@Cascade(CascadeType.ALL)
private Set<Teacher> teachers = new HashSet<Teacher>();




Hibernate关联映射-一对一

一对一操作有两种映射方式:

1. 在任意一方添加外键

2. 主键映射

创建PO类

以人与身份证号为例

@OneToOne(targetEntity = IDCard.class, mappedBy = "user")
private IDCard idCard;

@OneToOne(/*targetEntity = User.class*/)
@JoinColumn(name = "c_user_id")
@Cascade(CascadeType.SAVE_UPDATE)
private User user;


八、Hibernate检索方式概述

对数据库操作中,最常用的是select.使用hibernate如何select操作。

导航对象图检索方式,根据已加载的对象导航到其它对象

OID检索方式,按照对象的OID来检索对象

HQL检索方式,使用面向对象的HQL查询语言

QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口

本地SQL检索方式,使用本地数据库的SQL查询语句

导航对象图检索方式

Customer c=session.get(Customer.class,2);

c.getOrders().size()

通过在hibernate中进行映射关系,在hibernate操作时,可以通过导航方式得到

其关联的持久化对象信息。

OID检索方式

Session.get(Customer.class,3);

Session.load(Order.class,1);

Hibernate中通过get/load方法查询指定的对象,要通过OID来查询。

HQL

HQL是我们在hibernate中是常用的一种检索方式。

HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力

因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语 句的查询方式,同时也提供了更加面向对象的封装。完整的HQL语句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。

基本步骤:

1. 得到Session

2. 编写HQL语句

3. 通过session.createQuery(hql)创建一个Query对象

4. 为Query对象设置条件参数

5. 执行list查询所有,它反胃的是List集合 uniqueResut()返回一个查询结果。

PO类

@Entity
@Table(name = "t_customer")
@NamedQuery(name = "myHql", query = "from Customer")
@SqlResultSetMapping(name = "customerSetMapping", entities = { @EntityResult(entityClass = Customer.class, fields = {
@FieldResult(name = "id", column = "id"), @FieldResult(name = "name", column = "name") }) })
@NamedNativeQuery(name = "findCustomer", query = "select * from t_customer", resultSetMapping = "customerSetMapping")
@Proxy(lazy = true)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // 主键

private String name; // 姓名

@OneToMany(targetEntity = Order.class, mappedBy = "c")
private Set<Order> orders = new HashSet<Order>();

@Entity
@Table(name = "t_order")
@NamedQuery(name="findOrderByCustomer",query="from Order where c=:c")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private Double money;

private String receiverInfo; // 收货地址
// 订单与客户关联
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id")
@Cascade(CascadeType.SAVE_UPDATE)
private  Customer c;


public class HQLTest {

// 命名查询
@Test
public void test9() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.我要查询张龙这个客户的订单
Customer c = session.get(Customer.class, 1);

Query query = session.getNamedQuery("findOrderByCustomer"); // from Order  where c=:c
// 2.现在hql它的参数是一个实体
List<Order> list = query.setEntity("c", c).list();

System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 命名查询
@Test
public void test8() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.getNamedQuery("myHql");
List<Customer> list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}

// 投影查询
@Test
public void test7() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 1.查询出所有的Customer的name
// String hql = "select name from Customer";

// List list = session.createQuery(hql).list();

// System.out.println(list); // [张龙, 张三丰]

// 如果只查询一个列,得到的结果List<Object>

// 2.查询所有的Customer的id,name
// String hql = "select id,name from Customer";

// List<Object[]> list = session.createQuery(hql).list();

// for(Object[] objs:list){
// for(Object obj:objs){
// System.out.print(obj+" ");
// }
// System.out.println();
// }
// 如果是查询多列,得到的结果是List<Object[]>

// 3.使用投影将查询的结果封装到Customer对象
String hql = "select new Customer(id,name) from Customer"; // 必须在PO类中提供对应的构造方法
List<Customer> cs = session.createQuery(hql).list();
System.out.println(cs);

session.getTransaction().commit();
session.close();
}

// 分组统计操作
@Test
public void test6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 统计操作----统计一共有多少订单 count
// String hql="select count(*) from Order";

// Object count = session.createQuery(hql).uniqueResult();
// System.out.println(count);

// 分组统计----每一个人的订单总价
String hql = "select sum(money) from Order group by c";
List list = session.createQuery(hql).list();
System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 分页检索
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

Query query = session.createQuery("from Order");
// 每页显示6条件 ,我们要得到第二页数据
query.setFirstResult((2 - 1) * 6); // 设定开始位置
query.setMaxResults(6); // 设置条数

List<Order> list = query.list();
System.out.println(list);

session.getTransaction().commit();
session.close();

}

// 条件查询
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.根据位置来绑定参数
// 1.1创建hql
// String hql = "from Order where money>? ";

// 1.2.执行hql
// List<Order> list = session.createQuery(hql).setParameter(0,
// 2000d).list();
// 可以使用例如setString() setDouble这样的方法去添加参数,参数的序号是从0开始.

// 2.根据名称来绑定
// 1.1创建hql
String hql = "from Order where money>:mymoney ";

// 1.2.执行hql
List<Order> list = session.createQuery(hql).setParameter("mymoney", 2000d).list();
// 可以使用例如setString() setDouble这样的方法去添加参数

System.out.println(list);
session.getTransaction().commit();
session.close();

}

// 排序检索--//查询订单,根据订单的价格进行排序
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 1.定义hql
String hql = "from Order order by money desc"; // desc 降序 默认是asc 升序
// 2.执行hql查询订单,根据价格进行排序
List<Order> list = session.createQuery(hql).list();

System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 基本检索
@Test
public void test2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 1.编写HQL
String hql = "from Customer"; // from是关键字,后面是类名,关键字是不区分大小写,但是类名是区分
// 2.通过session.createQuery(hql)
// Query query = session.createQuery(hql);
// 3.通过list方法得到数据
// List<Customer> list = query.list();

List<Customer> list = session.createQuery(hql).list();

System.out.println(list.get(0));

session.getTransaction().commit();
session.close();
}

// 准备数据(2个Customer,每一个Customer有10个Order)
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 操作
Customer c = new Customer();
c.setName("张三丰");

for (int i = 0; i < 10; i++) {
Order order = new Order();
order.setMoney(2000d + i * 10);
order.setReceiverInfo("上海");
order.setC(c);
session.save(order);
}

session.getTransaction().commit();
session.close();
}
}


QBC

QBC(query by criteria),它是一种更加面向对象的检索方式。

QBC步骤

通过Session得到一个Criteria对象 session.createCriteria()

设定条件 Criterion实例 它的获取可以通过Restrictions类提供静态。

Criteria的add方法用于添加查询条件

调用list进行查询 criterfia.list.

public class QBCTest {

// 离线的检索
@Test
public void test6() {
// 1.得到一个DetachedCriteria
DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
dc.add(Restrictions.like("name", "张_"));

// 2.生成Criteria执行操作
Session session = HibernateUtils.openSession();
session.beginTransaction();
Criteria criteria = dc.getExecutableCriteria(session);
List<Customer> list = criteria.list();

System.out.println(list);
session.getTransaction().commit();
session.close();

}

// 统计检索
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 1.统计订单总数
Criteria criteria = session.createCriteria(Order.class);
// Object obj =
// criteria.setProjection(Projections.rowCount()).uniqueResult();
// //统计总行数 count(id)
// System.out.println(obj);

// 2.订单的总价格----分组统计根据客户
// criteria.setProjection(Projections.sum("money")); //统计总金额

criteria.setProjection(
Projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c")));

List<Object[]> list = criteria.list(); // 在这个集合中保存的是Object[money的统计信息,客户信息]

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

session.getTransaction().commit();
session.close();
}

// 分页检索
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult((2 - 1) * 6);
criteria.setMaxResults(6);
List<Order> list = criteria.list();

System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 条件检索
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.查询名称叫张某客户 张_
Criteria criteria = session.createCriteria(Customer.class);
Criterion like = Restrictions.like("name", "张_"); // 其它的条件 lt < gt > le
// <= ge>= eq==
criteria.add(like);// 添加条件
Customer c = (Customer) criteria.uniqueResult();
System.out.println(c);

// 2.查询订单价格在1050以上的,并且它的客户是张某
Criteria cri = session.createCriteria(Order.class);
SimpleExpression lt = Restrictions.gt("money", 1050d); // >1050
SimpleExpression eq = Restrictions.eq("c", c); // 与c客户相同
LogicalExpression and = Restrictions.and(lt, eq);
cri.add(and);
// List<Order> orders =
// cri.add(Restrictions.and(Restrictions.gt("money", 1050d),
// Restrictions.eq("c", c))).list();
List<Order> orders = cri.list();
System.out.println(orders);

session.getTransaction().commit();
session.close();

}

// 排序检索
@Test
public void test2() {
// 查询订单信息 根据订单的价格进行排序
Session session = HibernateUtils.openSession();
session.beginTransaction();

Criteria criteria = session.createCriteria(Order.class);
// 指定排序
// criteria.addOrder(org.hibernate.criterion.Order.desc("money")); // 降序
criteria.addOrder(org.hibernate.criterion.Order.asc("money")); // 升序

List<Order> list = criteria.list();

System.out.println(list);

session.getTransaction().commit();
session.close();
}

// 基本检索
@Test
public void test1() {
// 查询所有Customer
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.得到一个Criteria对象
Criteria criteria = session.createCriteria(Customer.class);

// 2.调用list方法
List<Customer> list = criteria.list();

System.out.println(list);

session.getTransaction().commit();
session.close();
}
}


本地SQL

//测试本地sql命名查询
@Test
public void test2(){
Session session = HibernateUtils.openSession();
session.beginTransaction();

Query query = session.getNamedQuery("findCustomer");
List list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}


多表操作

HQL多表操作

Hql多表操作分类

交叉连接

内连接

a) 显示内连接

b) 隐式内连接

c) 迫切内连接


外连接

左外连接

迫切左外连接


右外连接

注意:在hibernate中有迫切连接的概念,而sql中没有。


//hql多表查询
public class HQLJoinTest {

// 演示迫切左外连接
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 注意:fetch不可以与单独条件的with一起使用
List<Customer> list = session
.createQuery("select distinct c from Customer c left outer join fetch c.orders where c.id=1").list(); // 左外连接

for (Customer c : list) {
System.out.println(c);
}

session.getTransaction().commit();
session.close();
}

// 演示外连接
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

List<Object[]> list = session.createQuery("from Customer c left outer join c.orders").list(); // 左外连接
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}
session.getTransaction().commit();
session.close();
}

// 迫切内连接
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// 迫切内连接 inner join fetch 注意:使用迫切连接结果可能出现重复,所以要使用distinct来去重复
String hql = "select distinct c from Customer c inner join fetch c.orders";
// 底层也是执行的inner join 只不过结果封装到对象中。

Query query = session.createQuery(hql);
List<Customer> list = query.list(); // 结果是List<>,集合中装入的From后面的对象。

for (Customer o : list) {
System.out.println(o);
}

session.getTransaction().commit();
session.close();
}

// 测试隐式内连接
@Test
public void test2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// sql连接 select * from t_customer,t_order where 条件
String hql = "from Order o where o.c.id=1";
Query query = session.createQuery(hql);
List list = query.list();
System.out.println(list);
/*
String hql = "from Customer c,Order o where c.id=1 and c.id = o.customer.id";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}
*/
session.getTransaction().commit();
session.close();
}

// 测试显示内连接
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();

// sql连接 select * from t_customer inner join t_order on 条件

String hql = "from Customer c inner join c.orders with c.id=1";

Query query = session.createQuery(hql);
List<Object[]> list = query.list(); // 结果是List<Object[]>
// 而Object[]中装入的是Customer与Order对象。

for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}

session.getTransaction().commit();
session.close();
}
}


九、Hibernate事务管理

Hibernate中设置事务隔离级别

在hibernate.cfg.xml文件中配置

<!-- 设置事务隔离级别 -->
<property name="hibernate.connection.isolation ">4</property>


它可取的值有 1 2 4 8

1.代表的事务隔离级别为READ UNCOMMITTED

2.代表的事务隔离级别为READ COMMITTED

4.代表的事务隔离级别为 REPEATABLE READ

8.代表的事务隔离级别为 SERIALIZABLE

Hibernate中session管理

Hibernate提供了三种管理session的方式,在实际开发中我们一般使用的是前两种。

Session对象的生命周期与本地线程绑定(ThreadLocal)

Session对象的生命周期与JTA事务绑定(分布式事务管理)

Hibernate委托程序来管理Session的生命周期

本地线程绑定Session

需要在hibernate.cfg.xml文件配置

在获取session时不要在使用openSession而是使用getCurrentSession()方法。

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

Session currentSession = sessionFactory.getCurrentSession();


使用getCurrentSession获取的与线程绑定的session对象,在事务关闭时,session对象也会close,简单说,就不需要我们在手动close。

十、Hibernate优化方案

HQL优化

1.使用参数绑定

2.尽量少使用NOT

3.尽量使用where来替换having

4.减少对表的查询

5.使用表的别名

6.实体的更新与删除

检索策略

延迟加载

延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。

load方法采用的策略延迟加载、

get方法采用的策略立即加载。

检索策略分为两种:

1. 类级别检索

2. 关联级别检索

类级别检索

类级别检索是通过session直接检索某一类对应的数据,例如

Customer c=session.load(Customer.class,1)

Session.createQuery(“from Order”)

类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过
<class>
元素的lazy属性来设置 ,默认值是true。如果将lazy设置为false,代表类级别检索也使用立即检索。这时load与get就一样,都是立即检索。

关联级别检索

查询到某个对象,获得其关联的对象或属性,这种称为关联级别检索,例如

c.getOrders().size()

c.getName()

对于关联级别检索我们就要研究其检索策略(抓取策略)

抓取策略

抓取策略介绍

指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。

注解配置抓取策略

@Entity
@Table(name = "t_customer")
@Proxy(lazy = false)
@BatchSize(size=3)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // 主键

private String name; // 姓名

@OneToMany(targetEntity = Order.class, mappedBy = "c")
@Fetch(FetchMode.SELECT)
@LazyCollection(LazyCollectionOption.TRUE)
@BatchSize(size=3)
private Set<Order> orders = new HashSet<Order>();

@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

private Double money;

private String receiverInfo; // 收货地址
// 订单与客户关联
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id")
@Cascade(CascadeType.SAVE_UPDATE)
@Fetch(FetchMode.JOIN)
@LazyToOne(LazyToOneOption.FALSE)
private  Customer c;


set上的fetch与lazy

set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。根据一的一方去查询多的一方

Fetch可取值有:

SELECT 多条简单的sql (默认值)

JOIN 采用迫切左外连接。如果fetch选择的是join方案,那么lazy它会失效。

SUBSELECT 将生成子查询的SQL

lazy可取值有:

TURE 延迟检索 (默认值)

FALSE 立即检索

EXTRA 加强延迟检索(及其懒惰)

One的上的fetch与lazy

在多的一方如何查询一的主方信息。例如:获取到一个订单对象,要查询客户信息。

Fetch可取值

select 默认值,代表发送一条或多条简单的select语句

join 发送一条迫切左外连接。如果fetch值为join,那么lazy失效。

lazy可取值

false 不采用延迟加载

proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定

no-proxy 不用研究

批量抓取

我们在查询多个对象的关联对象时,可以采用批量抓取方式来对程序进行优化。

要想实现批量抓取,可以在配置文件中 batch-size属性来设置 ,可以使用注解 @BatchSize(size=4)。

无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置,如果是要查询子信息,那么我们是在上来设置batch-size,如果是从子方来查询父方, 也是在父方设置在设置batch-size。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Hibernate