Hibernate快速入门笔记

[toc]

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的核心配置文件它有两种方式:

  1. hibernate.cfg.xml
  2. 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="native"></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> 
复制代码
  1. 统一声明包名,这样在<class>中就不需要写类的全名. <hibernate-mapping package="cn.itheima.domain">
  2. 关于<class>标签配置 name属性:类的全名称 table 表的名称,可以省略,这时表的名称就与类名一致 catalog属性:数据库名称 可以省略.如果省略,参考核心配置文件中url路径中的库名称
  3. 关于<id>标签 首先它必须存在。<id>是用于建立类中的属性与表中的主键映射。 name 类中的属性名称 column 表中的主键名称 column它也可以省略,这时列名就与类中属性名称一致 length 字段长度 type属性 指定类型 <generator>它主要是描述主键生成策略.
  4. 关于<property>标签 它是描述类中属性与表中非主键的映射关系

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

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

  1. java中的数据类型
  2. hibernate中的数据类型
  3. SQL的数据类型

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

hibernate工作原理

  1. 通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
  2. 由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>读取解析映射信息。
  3. 通过config.buildSessionFactory();//得到sessionFactory。
  4. sessionFactory.openSession();//得到session。
  5. session.beginTransaction();//开启事务。
  6. persistent operate;
  7. session.getTransaction().commit();//提交事务
  8. 关闭session;
  9. 关闭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连接池,应该怎样处理?

  1. 我们要导入c3p0的相关jar包 在hibernate/lib/options下有关于c3p0连接池jar包

  2. 在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编写规则

  1. 必须提供一个无参数的public构造方法
  2. 所有属性要private ,对外提供public 的get/set方法
  3. 在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID
  4. PO类中的属性尽量使用基本数据类型的包装类. Int-Integer double--Double float-Float
  5. PO类它不能使用final修饰符

OID作用

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

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

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

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

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

  1. get直接得到了一个持久化类型对象,它就是立即查询操作。 load它得到的是持久化类开的代理类型对象(子类对象)。它默认采用了一种延迟策略来查询数据。

  2. 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持久化对象状态

持久化对象三种状态

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

  2. 持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。 简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。

  3. 托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数据库中有可能存在,也有可能不存在。 对于托管态对象,它发生改变时hibernet不能检测到。

持久化类三种状态切换

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

  1. 是否有OID
  2. 判断是否与session关联

  1. 瞬时态(new 出来的) 瞬时------持久 save saveOrUpdate 瞬时-----脱管(游离) 手动设置oid

  2. .持久态 它是由session管理 持久-------瞬时 delete() 被删除后持久化对象不在建议使用 持久-----脱管 注意:session它的缓存就是所说的一级缓存 evict(清除一级缓存 中指定的一个对象) clear(清空一级缓存) close(关闭,清空一级缓存)

  3. .脱管态 (它是无法直接获取) 脱管-----瞬时 直接将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

一级缓存特点

  1. 当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
  2. 当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
  3. 当调用session的close方法时,session缓存清空。 clear 清空一级缓存. evict 清空一级缓存中指定的一个对象。 refresh重新查询数据库,用数据库中信息来更新一级缓存与快照

Hibernate常用API-Session补充

update

  1. udpate操作它主要是针对于脱管对象,持久对象具有自动更新能力。
  2. Update操作时,如果对象是一个脱管对象,可以操作,它会将脱管对象转换成持久对象在操作。
  3. 如果在session中出现相同的oid两个对象,会产生异常。
  4. 脱管对象的oid如果在数据表中不存在,会报异常。
  5. 所以:在操作中,建议我们通过持久化对象来直接修改其操作。

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可以完成级联操作 它可常用取值

  1. none这是一个默认值
  2. save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.
  3. delete 级联删除
  4. delete-ophan 删除与当前对象解除关系的对象。
  5. all 它包含了save-update delete操作
  6. 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操作。

  1. 导航对象图检索方式,根据已加载的对象导航到其它对象
  2. OID检索方式,按照对象的OID来检索对象
  3. HQL检索方式,使用面向对象的HQL查询语言
  4. QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
  5. 本地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步骤

  1. 通过Session得到一个Criteria对象 session.createCriteria()
  2. 设定条件 Criterion实例 它的获取可以通过Restrictions类提供静态。 Criteria的add方法用于添加查询条件
  3. 调用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多表操作分类

  1. 交叉连接
  2. 内连接 a) 显示内连接 b) 隐式内连接 c) 迫切内连接
  3. 外连接 左外连接 迫切左外连接 右外连接 注意:在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的方式,在实际开发中我们一般使用的是前两种。

  1. Session对象的生命周期与本地线程绑定(ThreadLocal)
  2. Session对象的生命周期与JTA事务绑定(分布式事务管理)
  3. Hibernate委托程序来管理Session的生命周期

本地线程绑定Session

  1. 需要在hibernate.cfg.xml文件配置
  2. 在获取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可取值有:

  1. SELECT 多条简单的sql (默认值)
  2. JOIN 采用迫切左外连接。如果fetch选择的是join方案,那么lazy它会失效。
  3. SUBSELECT 将生成子查询的SQL

lazy可取值有:

  1. TURE 延迟检索 (默认值)
  2. FALSE 立即检索
  3. EXTRA 加强延迟检索(及其懒惰)

One的上的fetch与lazy

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

Fetch可取值

  1. select 默认值,代表发送一条或多条简单的select语句
  2. join 发送一条迫切左外连接。如果fetch值为join,那么lazy失效。

lazy可取值

  1. false 不采用延迟加载
  2. proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定
  3. no-proxy 不用研究

批量抓取

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

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

无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置,如果是要查询子信息,那么我们是在上来设置batch-size,如果是从子方来查询父方, 也是在父方设置在设置batch-size。

转载于:https://juejin.im/post/5acb814cf265da23994ed666

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值